Skip to content

Agent integration

Kevin Straub edited this page Aug 1, 2026 · 7 revisions

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.

The prompt block

mduct index
MCP 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.

Claude Code

mduct hook install claude          # --remove to undo

Two 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.

Everything else

There is no plugin API and nothing needs one. Any agent that can run a shell command can use mduct:

  1. Put mduct index output in the system prompt.
  2. Let the agent run mduct tools <server> when it needs the tool list, and mduct schema <server> <tool> when it needs the fields.
  3. Calls are mduct call <server> <tool> key=value.

Three commands. If your harness has a bash tool, it already supports mduct.

Teaching the pipe habit

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 25000

The tool namespace

A 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.

"kb": { "command": "", "mcpCatalog": true }
mduct hook install claude      # writes the hooks AND registers the catalogue

Hooks go to settings.json, MCP servers to .claude.json. The install handles both; --remove undoes both. 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.

Isolating an agent

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=neutrinos

One 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.

Nudging it toward the better tool

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.

Clone this wiki locally