-
Notifications
You must be signed in to change notification settings - Fork 0
Agent integration
An agent can reach a hundred tools while carrying one line per server in its context. That line still has to get in there somehow.
mduct indexMCP tools via `mduct` CLI (list+args: mduct tools <server>; call: mduct call <server> <tool> key=value key:=<json>):
gitlab — GitLab: MRs, pipelines, issues, repos
notes — shared notes
CLI tools via `mduct` CLI (run: mduct run <tool> [args…]):
kubectl — read-only cluster access
A server with few enough tools carries its signatures here too, so the agent can
see search(query, limit?) instead of only learning that a server called
notes exists. Past 25 tools it collapses to a count and a pointer. The
signatures come from a cache the daemon fills as it is used; mduct index --refresh fills it up front on a fresh machine.
Paste it into a system prompt, a CLAUDE.md, an AGENTS.md, whatever your
harness reads. Each line is that server's note field. Write those notes for
someone who has to pick a tool from this list and nothing else.
mduct hook install claude # --remove to undoTwo hooks land in ~/.claude/settings.json:
SessionStart prints mduct index, so the block is there without you
maintaining it. It also warns when a server is attached directly to Claude
while mduct serves it too, since you would be paying for those schemas anyway.
PreToolUse matches mcp__* and answers with the replacement command,
mduct call <server> <tool> key=value. Only for servers mduct actually serves.
Everything else passes through untouched.
Other people's hooks in that file are left alone. Re-running the install replaces mduct's own entries and nothing else.
The installer writes the path of whichever binary you ran it with. Install
first, then run mduct hook install claude from the installed one. Running it
from a source checkout pins your hooks to your working tree, which is rarely
what you meant.
mduct hook install codex # --remove to undoCodex keeps everything in ~/.codex/config.toml, hooks and MCP servers in one
file, so this install is the simpler of the two. The same two hooks land there,
plus [mcp_servers.mduct] when a server declares mcpCatalog.
Everything mduct writes sits between two comment markers:
# >>> mduct managed — do not edit inside this block >>>
[[hooks.SessionStart]]
…
# <<< mduct managed <<<Re-running the install replaces that block and touches nothing outside it. If
your config already defines [hooks] or [mcp_servers.mduct] somewhere else,
the install refuses instead of writing a file TOML would reject for duplicate
tables — move yours, then run it again.
The hook contract is the same as Claude's, which is why one implementation
serves both. The only real difference is the name of the shell tool: Codex says
shell_command or exec_command where Claude says Bash, and it passes argv
instead of a string. Shadow rules are written against the command line, so a
rule written once fires in both.
There is no plugin API and nothing needs one. Any agent that can run a shell command can use mduct:
- Put
mduct indexoutput in the system prompt. - Let the agent run
mduct tools <server>when it needs the tool list, andmduct schema <server> <tool>when it needs the fields. - Calls are
mduct call <server> <tool> key=value.
Three commands. If your harness has a bash tool, it already supports mduct.
An agent that runs mduct call srv list_things gets the whole list in its
context. Run the same thing with --json | jq -c 'map({id,title})' and it gets
what it asked for. That difference is real money, so the index block mentions
it and mduct help carries worked examples.
warnAbove is the backstop. An oversized result prints a warning with a
ready-made jq projection instead of quietly costing 40k characters.
mduct config warnAbove 25000A prompt block is prose. Tool selection happens in the tool namespace, and
nothing written into a prompt reaches it. mduct mcp is a catalogue for exactly
that: an MCP server whose tools/list mirrors the real tools so their names sit
where an agent looks, with each description carrying the shell command to run.
It never executes — calls stay in the shell, because the pipe is most of the
value.
mduct hook install claude # writes the hooks AND registers the catalogue
mduct hook install codex # the same, into one TOMLUnder Claude, hooks go to settings.json and MCP servers to .claude.json. The
install handles both; --remove undoes both. Codex needs only the one file.
If a server declares mcpCatalog and the server is not registered, session start
says so rather than leaving you with a catalogue that silently is not there.
It reloads while running: the server watches the config and its tool cache and
announces a changed catalogue, so flipping mcpCatalog or calling a server for
the first time updates a live session. A rewrite that produces the same catalogue
stays silent.
Mirror the servers no request ever names. "Look at the GitLab MR" pulls
GitLab in by itself; nobody asks for a code index by name, so that is the one
that keeps losing to grep. Cost keeps the list short: an entry is roughly six
times its prose line, and a 189-tool server would be 29 kB in every context.
Give it a profile and it gets its own config, secrets, OAuth tokens and daemon:
MDUCT_PROFILE=research mduct add notes --url https://mcp.example.com/mcp
MDUCT_PROFILE=research mduct call notes search q=neutrinosOne agent cannot see or use another's servers or credentials. Add a guard per
server and the blast radius of a confused agent stops depending on its own good
judgement.
If a server keeps losing to a habit (an index server to grep, an API to
curl), it can say so at the moment of the call. See Shadowing.