agentx scan reads your agent clients’ directories, their MCP configuration files and the library and prints what it finds. It writes nothing and never starts a server unless you ask for a handshake.
Scan the machine
skills: block lists every skill the configuration can see, one per line: the skill name, the kind of placement, the scope and the path the client reads. A skill that comes from a plugin ends with the plugin’s name.
Then, when the configuration declares MCP servers, a servers: block with one line per server: its name, its transport (stdio, sse or streamable-http) and its command line or URL. Then, when the configuration has plugins, a plugins: block with one line per plugin: its name, its version, how many skills and servers the plugin provides in this configuration, and (disabled) when the client has turned it off.
In a terminal the names are bold, the block labels cyan, the kind and scope dim, enabled green, (disabled) yellow and a plugin marker magenta, so the names and states stand out from the paths.
A skill’s name and a server’s command line come from files agentx did not write, so they are printed with control characters replaced by spaces, and a path holding one is printed in quotes with escapes. See Output and exit codes. Read the values as they are on disk with --json.
A configuration is detected when its configuration directory exists, such as ~/.claude for Claude Code or ~/.cursor for Cursor.
Read the kind
See who reads what
A client can read other clients’ directories. Cursor reads~/.claude/skills and ~/.codex/skills next to its own ~/.cursor/skills, so a skill you put in the Claude Code directory appears under Cursor too. Some clients, such as Codex and Gemini CLI, read the library at ~/.agents/skills directly, so every library skill appears under them without any link in their own directories.
Read the servers
agentx reads the user-scope MCP configuration of Claude Code (~/.claude.json), Codex (~/.codex/config.toml), Cursor (~/.cursor/mcp.json), Gemini CLI (~/.gemini/settings.json), Windsurf (~/.codeium/windsurf/mcp_config.json) and GitHub Copilot (~/.copilot/mcp-config.json). Other clients contribute skills only.
The inventory shows the command and arguments of a local server and the URL of a remote one. It never shows the values of environment variables or headers, in any output mode: a scan does not print your secrets. In JSON output only their names appear, as env_keys and header_keys.
The same server declared in two clients is one server with two occurrences: agentx recognises a remote server by its address and a local npx, uvx, pipx or docker server by its package or image, whatever the version. A server that cannot be identified that way is unique to this machine.
A configuration file that cannot be parsed is reported as a warning and the scan continues.
Handshake the servers
- A local server starts in its declared
cwd, with the environment variables its declaration sets. A relativecwdresolves against the plugin’s directory for a plugin’s server, else against the directory you run agentx in. - Placeholders such as
${API_KEY}are filled in from your environment, in the syntax of the client that declares the server:${VAR}for Claude Code,${env:VAR}for Cursor and Windsurf,$VARor${VAR}for Gemini CLI and Copilot CLI. The inventory shows the declaration as written. Credentials Claude Code never sends to a remote server, such asAWS_SECRET_ACCESS_KEY, agentx doesn’t send either, nor anyANTHROPIC_*orCLAUDE_*variable. Run with--verboseto see which variables were withheld. - A remote server receives its declared headers at its declared URL. agentx never tries another URL. For Codex,
env_http_headersandbearer_token_env_varare read from your environment. - A server on the legacy
ssetransport is handshaken over SSE. A streamable HTTP server that refuses the opening request with 400, 404 or 405, and no MCP error, is tried once more over SSE at the same URL. - A server you turned off in its client, shown with
(disabled), is never started. agentx reads this from Codex (enabled = false), Gemini CLI (mcp.excluded,mcp.allowedandgemini mcp disable) and Copilot CLI (/mcp disable). Claude Code, Cursor and Windsurf keep it outside the files agentx reads.
signature, when it was taken as signature_at, and the tools list.
The signatures are kept in handshakes.json in agentx home. A later scan without --handshake shows them until the next handshake replaces them; each occurrence’s handshake field says whether that scan handshook the server itself.
Fix a failed handshake
A server that fails its handshake shows no tool count. Its row says why, a warning names its file, and a hint says what to do. The server keeps its previous signature and the scan still succeeds.
In JSON output the occurrence carries
handshake_error with reason, message and hint.
Read the plugins
Plugins are listed under the configuration they are installed in, with their version. The skills and MCP servers a plugin provides appear in the configuration’s list like any other, marked with the plugin’s name. A skill that is installed both standalone and through a plugin is one skill with two placements.
A plugin’s version is what its manifest or install record says. For a Codex or Cursor plugin without one, the cache directory name stands in:
local, a commit id or a release tag. A plugin manifest that cannot be read is reported as a warning naming the file, and the plugin is still listed by its directory name.
Include a project
.claude/skills, .cursor/skills and .agents/skills, with scope project. The project is only read. Without the flag, only user scope is scanned.
Rescan after a change
Warnings
The--- block at the top of a SKILL.md is read as YAML, so name and description are picked up however they are written: on one line, quoted, as a | or >- block, or continued on the indented lines under the key. A metadata: block or any other nested key is ignored.
A frontmatter block far larger or more deeply nested than any real skill is refused unread, so a repository cannot make a scan run out of memory.
A broken symlink, a SKILL.md whose block is not valid YAML or a symlink that points outside its skill does not stop the scan. Each one is reported on stderr as warning: ... and the skill is skipped or named after its directory. Several broken symlinks in one skills directory are reported as one warning that names the directory and lists the links:
Script it
Add--json to get one snapshot event with the machine, every configuration, every skill, MCP server and plugin with its occurrences, the edges between them and the warnings. It also lists the skills of your library under library, each as agentx skill list reports it: what agentx knows it as, where it came from, whether it was edited since, whether a client’s placement was replaced or is missing (displaced, missing), whether its source is still added (source removed when it is not), whether its source still holds it (upstream removed when the last update check found it gone), the newer version the last update check found (candidate), whether an update left a merge of your edits pending (pending_merge), its placements and the universal clients that see it whatever its placements.
library is empty and a warning names the repo and gives git’s error. Read that error, run agentx doctor, and check the account repo the warning names: doctor does not catch every way a repo can break.
Every node carries a stable identity, and two scans of an unchanged machine are byte-identical apart from instance_id. Set AGENTX_INSTANCE_ID to fix that too.