Multi-Agent Registry 🤖
Unified detection, configuration, plugin, and chat history discovery registry for AI coding agent CLIs.
multi_agent_registry gives any tool a single place to ask "which AI coding agents are installed on this machine, where do they keep their config/plugins, and where did they leave their chat history?" — instead of every consumer re-implementing per-agent path guessing.
Installation
pip install multi-agent-registry
# or using uv
uv add multi-agent-registry
The PyPI distribution name is multi-agent-registry; the importable module name is multi_agent_registry:
import multi_agent_registry
Features
- Multi-Agent CLI Detection: A registry of 15 agent CLIs — Claude Code, Antigravity (
agy), OpenCode, GitHub Copilot, Grok Build, Cursor, Windsurf, Aider, Codex, Continue, Cline, Roo Code, Goose, ShellGPT, and Open Interpreter — with binary name, description, and config paths for each. - Installation & MCP Inspection:
inspect_agent_cli()/inspect_all_agent_clis()check whether each agent's binary is onPATH, whether it's registered as an MCP server, and whether a plugin is installed for it. - Chat Log Discovery:
discover_agent_chats()scans the on-disk chat log locations for agents that expose them (currently Claude Code, Antigravity, OpenCode, Aider, Cline, and Roo Code), with recursive-glob patterns pruned to skipnode_modules/.venv/.git/.gwtfor speed. - Chat Inspection Helpers:
get_chat_workspace()andget_chat_last_active()read each agent's own on-disk format to resolve which project a chat belongs to and when it was truly last active (not just file mtime). - Plugin Enable/Disable State: Per-agent plugin opt-out, persisted to
~/.config/task-agent/config.json, for tools that install agent-specific plugins/skills.
Quickstart
from multi_agent_registry import get_agent_cli_registry, discover_agent_chats, get_chat_workspace
# What agents does this machine have?
for agent_id, info in get_agent_cli_registry().items():
print(agent_id, info.name, info.binary)
# Where has Claude Code been chatting, and about which projects?
for chat in discover_agent_chats(agent_id="claude"):
workspace = get_chat_workspace(chat)
print(chat.path, "->", workspace)
API Reference
Registry & detection
from multi_agent_registry import AgentCLIInfo, get_agent_cli_registry, inspect_agent_cli, inspect_all_agent_clis
registry: dict[str, AgentCLIInfo] = get_agent_cli_registry()
info = registry["claude"]
info.id, info.name, info.binary, info.description
info.config_paths # list[Path] of possible config file locations
info.mcp_support, info.mcp_command_example
info.plugin_support, info.plugin_path, info.skills_path, info.plugin_template
info.chat_log_patterns # list[str] glob patterns, [] if not yet supported
info.chat_parser_type # "json" | "jsonl" | "markdown"
status = inspect_agent_cli("claude") # dict: installed, mcp_registered, plugin_installed, ...
all_status = inspect_all_agent_clis() # same, for every registered agent
Chat discovery
from multi_agent_registry import DiscoveredChat, discover_agent_chats
from pathlib import Path
# All chats for one agent, scanned globally (patterns rooted at ~ or /)
# or within specific project roots (patterns relative to a project).
chats: list[DiscoveredChat] = discover_agent_chats(
agent_id="aider", # omit to scan every agent
search_roots=[Path.home() / "repos"], # only used for project-relative patterns
)
# chat.agent_id, chat.path, chat.parser_type
Agents currently wired up for chat discovery: claude, agy (Antigravity), opencode, aider, cline, roo. The rest are registered for detection/config/plugin purposes but don't yet have chat_log_patterns populated — contributions welcome.
Chat inspection
from multi_agent_registry import get_chat_workspace, get_chat_last_active
get_chat_workspace(chat) # -> Path | None, the project dir the chat belongs to
get_chat_last_active(chat) # -> datetime | None, true last-message time (jsonl only for now);
# callers should fall back to file mtime when None
Global command allowlist
Every agent expresses "run this without asking" in its own dialect, in a global
config file that is not the MCP config_paths entry. permissions.py holds one
canonical list plus a renderer per dialect.
The list itself is plain text, one command per line, in
src/multi_agent_registry/allowlist/ — one file per tier. Edit them in any editor;
there are no CRUD commands to learn.
src/multi_agent_registry/allowlist/
inspect.txt # read-only; mutates nothing
build.txt # runs project-defined code (make, pytest)
network.txt # network egress (curl) -- opt-in
# inspect.txt
=pwd # leading "=" means the bare command only
=date
ls # no sigil: the command plus any arguments
git log
The tier is the filename, so it never repeats on a line. # starts a comment,
blank lines are ignored. Only inspect and build are granted by default.
That = is the one piece of syntax, and it exists because the dialects disagree
about matching semantics rather than just spelling: args="any" becomes Claude's
Bash(git log:*), OpenCode's {"git log": "allow", "git log *": "allow"} pair,
and Gemini's commandPrefix entry. Without it every renderer would have to guess
prefix-vs-exact, and they would guess differently.
from multi_agent_registry import (
load_allowlist, render_for_agent, diff_all_permissions,
)
render_for_agent("claude") # ['Bash(pwd)', 'Bash(ls:*)', 'Bash(git log:*)', ...]
render_for_agent("agy") # ['command(pwd)', 'command(ls)', ...]
for d in diff_all_permissions():
print(d.agent_id, d.surface.scope, len(d.missing))
| Agent | Global surface | Dialect |
|---|---|---|
claude |
~/.claude/settings.json → permissions.allow |
Bash(git log:*) |
agy |
~/.gemini/antigravity-cli/settings.json → permissions.allow |
command(git log) |
opencode |
~/.config/opencode/opencode.json → top-level permission.bash |
{"git log *": "allow"} |
gemini |
~/.gemini/policies/*.toml → commandPrefix |
["git log"] |
copilot |
~/.copilot/permissions-config.json — per-directory only |
no global form |
Inspect and diff from the CLI:
mar permissions # every agent, what's missing vs. canonical
mar permissions --agent claude # one agent
mar permissions --agent agy --render # emit that agent's native block
mar permissions --network # include the opt-in network tier
mar permissions is read-only — it never writes to an agent's config.
Notes for tools built on this library
- Absolute/home-relative
chat_log_patterns(e.g.~/.claude/projects/**/*.jsonl) are scanned once, globally —search_roots/project_dironly affects patterns relative to a project (e.g. Aider's**/.aider.chat.history.md). discover_agent_chats()shells out tofindfor bare recursive-filename patterns (pruning noise directories natively) rather thanglob.glob(recursive=True), which must fully traverse a tree before filtering — orders of magnitude faster on large repo trees.- Permission surfaces are tracked separately from
config_paths: for Claude Code the allowlist is in~/.claude/settings.jsonwhileconfig_pathspoints at~/.claude.json, and Copilot's real config root is~/.copilot/, not~/.config/github-copilot/. - Antigravity's
unsandboxed(x)is a strictly wider grant thancommand(x)— it escapes the terminal sandbox. Renderers emit onlycommand(x), but the diff treats an existingunsandboxed(x)as already covering it rather than pushing a redundant grant. get_chat_workspace()/get_chat_last_active()read each agent's actual on-disk message format (e.g. scanning forward past Claude Code's leading metadata-only jsonl lines for the firstcwd/timestamp) rather than assuming a fixed line/file layout.
Metadata
Release files for multi-agent-registry 0.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| multi_agent_registry-0.6.1.tar.gz | 28.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| multi_agent_registry-0.6.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 54.7 kB
Release files / multi_agent_registry-0.6.1.tar.gz
| Download URL | multi_agent_registry-0.6.1.tar.gz |
|---|---|
| Size | 28.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1ab7dbe1adadbcb2a2f7466e3150c6c8f22f344f348fb8697eee2e5826d6c14b
|
|
BLAKE2b-256 checksum How to use checksums |
f2ceea39b2c5b508ae901d132cf08172cd378b722556253ce5c04dc4bb396e27
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency logRelease files / multi_agent_registry-0.6.1-py3-none-any.whl
| Download URL | multi_agent_registry-0.6.1-py3-none-any.whl |
|---|---|
| Size | 26.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
133e51925eec2c9f2cde1451ee38ac51e5d247c82c06b4177bf92797db734581
|
|
BLAKE2b-256 checksum How to use checksums |
ae15d81329314e1bc8ea646e873ad379cf1e21968c2d2e7cc60154e899016c7e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency log