> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentx.wft/llms.txt
> Use this file to discover all available pages before exploring further.

# agentx serve

> Keep watching and get a new snapshot whenever something changes.

`agentx serve` is what the desktop app runs in the background. It scans the machine once, then rescans whenever anything it inventories changes, and prints the snapshot again only when the inventory changed. You rarely need to run it yourself.

## What counts as a change

Serve watches agentx itself, the library and every skills directory an agent client reads, including the ones clients keep under their own configuration such as `~/.claude/skills`. An edit inside a skill counts, not only a skill added or removed. Edit a skill with any editor or another tool and the next snapshot shows it.

A change outside the inventory, such as a skill in a directory no installed client reads, causes a rescan that prints nothing.

## Run it

```sh theme={null}
agentx serve --json
```

The first line is the whole snapshot. Every later snapshot line means the inventory changed; a change that leaves the inventory as it was prints nothing. Close the process's stdin, or press Ctrl-C, to stop it. Either way serve prints its `result` and exits `0`.

Without `--json` it prints one line per event instead:

```text theme={null}
snapshot 1: 3 configurations, 5 skills
snapshot 2: 3 configurations, 6 skills
```

## Drift

With `--json`, serve also tells you when a skill in your library changes state. After a snapshot in which a skill's `state` or `drift` changed, serve prints one `drift` line for that skill:

```json theme={null}
{"type":"drift","schema_version":1,"instance_id":"...","scan_counter":4,"name":"pdf","kind":"managed","state":"modified","drift":[],"previous_state":"current","previous_drift":[]}
```

`state` and `drift` are the skill's states now, `previous_state` and `previous_drift` what the snapshot before showed, and `scan_counter` names the snapshot the change is in. Save an edit to a skill that was `current` and the `drift` line follows within a second, right after the snapshot that shows it; later edits to the same skill leave it `modified` and bring no new `drift` line. Saving a file git ignores, such as a `.DS_Store` or an editor's swap file, brings no `drift` line: see [Files that do not count](/cli/skill#files-that-do-not-count). The states are those of [`agentx skill list`](/cli/skill#list-your-skills).

A `drift` line is a notification: the snapshot before it already holds the skill as it now is. The first snapshot, and a skill that is new in the library or gone from it, bring no `drift` line. If a managed skill's directory is deleted from the library, the snapshot's `warnings` names the skill, with the commands that install it again or stop managing it; see [A skill whose library directory is gone](/cli/skill#a-skill-whose-library-directory-is-gone).

## Update checks

Serve checks your managed skills for updates as [`agentx skill check`](/cli/skill#check-for-updates) does: once when it starts, right after the first snapshot, and then every thirty minutes. Each check also fetches every source you added, including the ones you have not installed anything from, as [`agentx source fetch --all`](/cli/source#fetch-a-source-again) would, so that search results and updates follow what your sources hold now. With `--json`, each check prints one `update_available` line per skill that has an update, the same event `skill check` prints, with serve's `instance_id` added:

```json theme={null}
{"type":"update_available","schema_version":1,"instance_id":"...","name":"pdf","kind":"managed","source":"https://github.com/anthropics/skills","subpath":"skills/pdf",...,"files":[{"path":"SKILL.md","status":"modified"}]}
```

A machine without any source has nothing to fetch, and serve runs no git for it. Every check lists every available update again, so the lines repeat on every check while the update is available. A check records what it found, so the snapshot that follows shows each skill's `candidate` and any `upstream removed` state. Nothing is ever applied.

A source that cannot be fetched, because you are offline or the repository moved, is a warning starting `update check:` that names it and any skills it left unchecked. Serve warns once, not on every check: it warns again only when the reason changes, and when the source can be fetched again it says so on a line of its own:

```text theme={null}
warning: update check: https://github.com/me/skills: git fetch: fatal: unable to access 'https://github.com/me/skills/': Could not resolve host: github.com; not checked: pdf
info: update check: https://github.com/me/skills can be fetched again
```

The checks in between log the failure as a debug line, which `--verbose` shows. Serve keeps running meanwhile, and the snapshot still shows each skill's `candidate`. A skill whose new version agentx refuses to import, and a source that was fetched but could not be read, are warned about on every check. The other `update check:` warnings say what an update leaves out or renames, or that a whole check failed.

Checks run in the background: scans and requests are answered while a check waits on the network, and a check still running when the next one is due is not started twice.

To check more or less often, set `AGENTX_CHECK_INTERVAL` to a duration such as `10m` or `2h` before starting serve. Serve refuses to start with exit code 1 when it is not a positive duration.

Without `--json` serve prints one line per update:

```text theme={null}
update pdf: 3f2a9c1 -> 8b1e0f4, 2 files
```

## Ask for a fresh snapshot

Write one JSON line to its stdin:

```json theme={null}
{"type": "refresh", "request_id": "any unique id"}
```

Serve scans again and answers with a `refresh_complete` line carrying your `request_id` and the counter of the current snapshot. If the inventory changed, the new snapshot line comes first. A line serve does not understand gets an `error` line; serve keeps running.

Serve takes two requests: `refresh`, and `search` below.

## Search your sources

Write one JSON line to its stdin:

```json theme={null}
{"type": "search", "request_id": "any unique id", "query": "commit"}
```

Serve answers with a `search` line carrying your `request_id`, the `query` and `results`: every skill of every source you added whose name or description contains the query, ignoring case. Each result names the `source` it is in by URL, the skill's `subpath` in that repository, its `name`, its `description` and the `tree` id of the skill directory. Results are sorted by source, then name. Nothing matching is an empty `results` array.

Pass a result's `source` and `subpath` to an install to take that skill.

Serve answers from memory, so a search is immediate: it reaches neither git nor the network, and it does not wait for a scan in progress. It lists the skills of each source when it starts and again whenever you add, re-fetch or remove one, so results follow `agentx source add`, `agentx source fetch` and `agentx source remove` on their own. Its [update checks](#update-checks) fetch every source too, so a skill added upstream shows up in results within one check interval.

A source you have not fetched has no skills to search, and serve says so once as a warning, not again each time an update check brings another source new skills. Its next [update check](#update-checks) fetches it when it can, and otherwise warns about that too, once. It cannot fetch a source that [`agentx import`](/cli/export#import-onto-another-machine) brought until you add it again. Run `agentx source add <url>` to fetch it, or `agentx source add <url>#<pin>` for a pinned source so that it stays pinned. A source serve could not read is warned about on every scan until it can read it, since each scan tries it again, so it comes back without your having to restart serve.

A search without a `request_id` or without a `query` gets an `error` line; serve keeps running.

Without `--json` serve prints one line per answer:

```text theme={null}
search 1: 3 results
```

## Run one pass

```sh theme={null}
agentx serve --once --json
```

Scans once, prints the snapshot and exits. It shows what the app receives on start; for an inventory to read, `agentx scan` prints the same snapshot. With nothing to compare against, it prints no `drift` line, and it runs no update check.

## One serve per home

Only one serve runs per agentx home. Starting a second one exits with code 6 and names the lock file, `serve.lock` in agentx home. Stop the running one first.

## When watching is not possible

If a directory cannot be watched, serve exits with code 6 and an error naming the directory and the cause. On Linux this usually means the inotify watch limit is reached; raise `fs.inotify.max_user_watches`. Serve never polls instead.

On macOS the released binary watches through FSEvents, which handles a library of any size. A macOS binary built without cgo watches through kqueue instead, which needs one open file per watched file and reaches the default limit of 256 with a few dozen skills; raise the limit with `ulimit -n`, or use the released binary.

A skills directory that does not exist yet is not watched. Serve picks it up at the next rescan after it appears.


## Related topics

- [agentx](/index.md)
- [agentx skill](/cli/skill.md)
- [agentx doctor](/cli/doctor.md)
- [agentx scan](/cli/scan.md)
- [agentx source](/cli/source.md)
