Yaw MCP
OfficialYaw MCP is a local MCP broker that fronts all your other MCP servers, exposing meta-tools to discover, rank, load, and call their tools from one connection.
mcp_connect_discover: list installed servers, optionally ranked by task context, with tool counts, token estimates, compliance grades, and usage hints.
mcp_connect_dispatch: describe a concrete task and automatically load the best-matching server's tools in one call.
mcp_connect_activate / deactivate: load or unload specific servers' tools by namespace, with optional per-server tool filtering.
mcp_connect_read_tool: inspect a single tool's full schema without loading its server into the session.
mcp_connect_exec: run a declarative pipeline of up to 16 tool calls in one round-trip, splicing prior step outputs via
$ref.mcp_connect_bundles: browse curated multi-server presets and match them against your installed servers.
mcp_connect_suggest: surface recurring multi-server workflows learned from usage as ready-to-run packs.
mcp_connect_secrets: preview which local-vault secrets each server's
${secret:NAME}refs resolve to — names only, never values.mcp_connect_health: show per-loaded-server call counts, error rates, latency, and last error.
Outside the MCP session, the CLI also manages server installs, client wiring, compliance audits, and the encrypted secret vault.
Provides tools for interacting with GitHub, enabling management of repositories, issues, pull requests, and other GitHub resources.
Provides tools for interacting with Linear, enabling issue and project management through the Linear platform.
Provides tools for interacting with Slack, enabling messaging, channel management, and other Slack functionalities.
Provides tools for interacting with Stripe, enabling payment processing, subscription management, and other Stripe operations.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Yaw MCPdispatch: find my recent GitHub PRs"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@yawlabs/mcp
One install. Every MCP server. Managed from one place.
Yaw MCP (the yaw-mcp CLI) is an MCP server that fronts every other MCP server you use. Point each AI client (Claude Code, Claude Desktop, Cursor, VS Code) at it once, and your servers load lazily from a single connection instead of a hand-edited mcpServers block per client.
It runs entirely on your machine. No account, no sign-in, no telemetry -- your servers live in a local bundles.json and your credentials in a local encrypted vault. Running as an MCP server, its only outbound calls are two background npm checks: one for its own updates (YAW_MCP_AUTO_UPGRADE=0 to disable) and, if you have run yaw-mcp sidecars install, a once-a-day check that the managed sidecar packages are current (YAW_MCP_SIDECAR_REFRESH=0 to disable). yaw-mcp add fetches the public server catalog on demand. Nothing else leaves your machine.
It earns its keep when you hit any of these:
More than one client. Define a server once in
bundles.json; every client on the machine picks it up. No copy-pasting the same JSON into four config files. (Syncbundles.jsonacross machines with your dotfiles if you want it everywhere.)Tool-context bloat. The
dispatchmeta-tool ranks your servers against the task and loads only the top match. A 30-server setup keeps a handful of tools in context at a time instead of hundreds.Tokens you'd rather not leave in disk configs. Credentials live encrypted in a local vault and inject at spawn time. Rotate once; every client picks up the new value.
A trust signal before you activate. Every scored server shows an A-F compliance grade. Set
YAW_MCP_MIN_COMPLIANCE=Bto refuse anything below.
One client, a few servers? claude mcp add is fine -- yaw-mcp's value shows up when that setup stops scaling.
How it works
Your MCP client (Claude Code, Cursor, ...)
|
| single stdio connection
v
@yawlabs/mcp --lazy load--> GitHub | Slack | Stripe | ... (your servers)Add servers with
yaw-mcp add <slug>(or edit~/.yaw-mcp/bundles.jsondirectly).yaw-mcp reads that file on startup.
The model uses a handful of meta-tools to control which servers' tools are in context. Adding a server puts it on your list; loading it brings its tools into the session.
Meta-tool | What it does |
| Describe a task in plain English; picks the best server, loads its tools, exposes them in one call. The fast path. |
| List available servers, optionally ranked by a context string. Auto-loads the top match when one clearly wins. Tool-name lists are capped at five per server; pass |
| Load / unload specific servers by namespace. |
| Return one tool's schema without loading its server. |
| Run a short declarative pipeline of tool calls in one round-trip ( |
| List curated multi-server presets (PR review, DevOps incident, ...) and match them against your config. |
| Surface recurring multi-server workflows learned from usage, with a ready-to-run |
| Show which local-vault secrets each server's |
| Call counts, error rates, and latency per loaded server. |
What tools/list carries. By default (gateway) it is the meta-tools above plus the tools of the servers loaded this session -- never the whole catalog, which on a large install ran to ~27,000 tokens before the first prompt. The routing advice (dispatch for a concrete task, discover to browse, exec for a known chain, loading is per session) is sent once as the server's MCP instructions, which Claude Code puts in the system prompt, so the per-tool descriptions stay short. A client that cannot defer tool descriptions and inlines every listed tool into every request gets lite instead: only mcp_connect_exec, mcp_connect_find_tool and mcp_connect_read_tool are listed, which between them find, describe and call any installed tool (exec loads a cached server on first use). yaw-mcp picks lite on its own when the client identifies itself as typed-cli in the MCP initialize handshake; the unlisted meta-tools stay callable by name. YAW_MCP_TOOL_EXPOSURE overrides either default.
Ranking. dispatch/discover rank with BM25, computed locally. On top of the ranker, three signals adjust scores:
Health-aware -- servers that recently failed or error often get down-ranked (never boosted above raw).
Learning -- servers that succeeded before get a small nudge (+10% max), persisted across restarts.
Sampling tiebreak -- when the top two are within 10% and your client supports MCP sampling, yaw-mcp asks your own model to pick (no extra provider key or cost).
Servers auto-unload after ~10 tool calls to other servers, so context stays clean even if you forget. The threshold is adaptive per namespace ([5, 50]): bursty servers get more patience, long-idle ones unload at the baseline. A server that is expensive to start can opt out entirely with yaw-mcp set <server> pinned=true -- it then stays loaded however idle it gets, and mcp_connect_health still reports how idle that is.
Related MCP server: MCP Gateway
Install
One command (recommended)
npx -y @yawlabs/mcp@latest install <claude-code|claude-desktop|cursor|vscode|windsurf|gemini-cli|zed|cline|continue|codex-cli|typed>This edits the chosen client's config (correct path + JSON shape for your OS) to launch yaw-mcp. On Windows it wraps npx in cmd /c (without which MCP clients hit ENOENT on the npx.cmd shim), except for Continue, Codex CLI and typed, which resolve that shim themselves and get a bare npx. Run it once per client. mcp is accepted too, as another name for Claude Code's project scope (<project>/.mcp.json).
install typed writes typed's own ~/.config/typed/mcp.json (the same path on every OS, whatever CLAUDE_CONFIG_DIR says), which typed ranks above the user-scope files it shares with Claude Code -- so it wins over an mcp entry Yaw Terminal manages in ~/.claude.json, while a project's .mcp.json still wins over it. On a Yaw Terminal machine that is a trade when install writes an npx entry (no oam, or no global install to point it at): the entry replaces Yaw Terminal's local launch for typed, and npx resolves @latest on every typed start, which is slow and can exceed typed's MCP connect timeout. Install says so when it happens; npm i -g @yawlabs/mcp with oam installed, then a re-run, writes a fast absolute-path entry instead, or raise MCP_TIMEOUT. It needs a typed CLI newer than 1.5.0 -- typed 1.5.0 and older do not read ~/.config/typed/mcp.json at all; with one of those, use install claude-code, whose files typed also reads. install typed checks the typed CLI bundle on this machine (~/.config/typed/typed-cli/cli.mjs, or $TYPED_CLI_BUNDLE) and warns when it cannot read the file yet, naming typed update, which installs the newest typed release. Like install claude-code, it adds mcp__mcp__* to permissions.allow in Claude Code's user settings.json, which typed reads for permissions too -- $CLAUDE_CONFIG_DIR/settings.json when that is set, so outside a Yaw Mode pane the grant is scoped to that config dir while typed's mcp.json is not, and install says so. The two clients share that one grant, so uninstall of either keeps it while the other still has its entry (or typed still loads an mcp entry from another file it reads), and says so. In a Yaw Mode pane, where CLAUDE_CONFIG_DIR is a per-pane overlay (a yaw-mode-* directory) whose settings.json is discarded with the pane, install and uninstall of either client at user scope add and remove the grant in ~/.claude/settings.json too (augment). In a fresh pane, which keeps nothing of its overlay, install says what goes with the pane -- the grant at user scope, and for install claude-code its entry in the overlay's .claude.json as well (at local scope, that entry alone) -- and to install from a normal shell to keep it.
install codex-cli writes an [mcp_servers.mcp] table into Codex's config.toml ($CODEX_HOME/config.toml, else ~/.codex/config.toml; the project's .codex/config.toml at --scope project) and, when the file does not set it, adds mcp_optional_startup_grace_ms = 0 on one line above the first table: without it, Codex 0.151 and later give optional MCP servers one shared second to start and leave a slower yaw-mcp's tools out of the whole session. The key is add-only -- a value you set yourself is left alone, and install says so -- and --dry-run shows the line before anything is written. A re-run over an entry that is already correct adds it to a file set up before install did; install --list marks such a row installed (setting missing), and doctor names the run. uninstall codex-cli leaves the key in place.
Claude Desktop on Linux is not supported yet. Anthropic ships a Linux beta but has not documented where it reads claude_desktop_config.json, so install claude-desktop refuses on Linux rather than write a guessed path, and --all skips it. Add the entry by hand, or use another client.
Useful flags:
--scope user|project|local-- which file to write. Claude Code supports user, project and local; Cursor, VS Code, Gemini CLI, Zed, Continue and Codex CLI support user and project; Claude Desktop, Windsurf, Cline and typed are user-only.--dry-run-- print what would be added (never the rest of the file) and exit without writing.--repair/--force/--skip-- what to do about an existingmcpentry that differs from the one install writes.--repairbrings it up to date and keeps the string values in itsenvblock (where the secret vault has you putYAW_MCP_VAULT_PASSPHRASE); on an entry that already matches it does what a plain re-run does -- nothing, unless the file lacks a top-level setting install adds (Codex CLI's startup grace, above), which it writes.--forceoverwrites it outright,envincluded, naming each key it drops.--skipleaves it, and the rest of the file, untouched. Without one of them install prompts on a TTY; off a TTY it shows what differs and refuses with exit 2 (a real failure, such as a malformed config, exits 1). Under--allthe run exits 2 when every client that did not succeed was refused this way, and 1 if any one failed.
After it writes, install reports two things it did not change. First, how many servers ~/.yaw-mcp/bundles.json gives yaw-mcp to serve -- and when that is none, the yaw-mcp add <slug> step still to take -- though no longer before a restart, since yaw-mcp re-reads that file while the session runs and picks up a server added afterwards on the next mcp_connect_* call. Second, how many other MCP servers were already configured in the client file it just edited; those keep launching directly from the client, and installing yaw-mcp does not move them behind the broker. The count is a number, never the server names. Under --all the bundles.json line prints once for the run, while the per-client count prints under each client.
Or work across every client at once:
yaw-mcp install --list # detect clients + show install state (read-only)
yaw-mcp install --all # install into every client yaw-mcp supports on this OSThe launch entry is keyed
"mcp", so its tools surface under themcp__mcp__namespace. Installs made before the rename used"mcp.hosting"/"yaw-mcp";yaw-mcp installdetects and migrates those.
Manual install
The JSON shape (top-level mcpServers, except VS Code uses servers in .vscode/mcp.json):
{
"mcpServers": {
"mcp": { "command": "npx", "args": ["-y", "@yawlabs/mcp@latest"] }
}
}On Windows, use "command": "cmd", "args": ["/c", "npx", "-y", "@yawlabs/mcp@latest"].
Running yaw-mcp on oam
yaw-mcp can host itself on oam, a Rust+V8 JavaScript runtime built for this shape of workload — short-lived, mostly-idle MCP processes.
yaw-mcp install writes the oam entry for you when two things are true:
oam is installed —
curl -fsSL https://oamjs.org/install.sh | sh, orirm https://oamjs.org/install.ps1 | iexon Windows. Those install the current release, which always satisfies the minimum. An older build is refused rather than silently used: the minimum is the last oam release yaw-mcp's own check (npm run verify:oam-floorin the repo) hosted a real MCP server on -- not the latest release, so a new oam release does not by itself raise it -- and nothing older than that is supported.yaw-mcp doctorprints the exact minimum under OAM RUNTIME, andoam self-updateis the fix when yours is below it.yaw-mcp is durably installed —
npm i -g @yawlabs/mcp, or a projectnode_modules. A path in the npx cache is deliberately not used: that directory is evictable, and an entry pointing into it breaks the moment npm cleans it.
Neither is required, and nothing breaks without them — the npx entry is written as before, and install tells you which one you got. Afterwards yaw-mcp doctor marks a client whose entry launches the broker on oam with (runs on oam), and its OAM RUNTIME section reports the binary, version, and minimum.
The entry it writes:
{
"mcpServers": {
"mcp": { "command": "/path/to/oam", "args": ["run", "--no-check", "/path/to/@yawlabs/mcp/dist/index.js"] }
}
}Two consequences worth knowing. It pins a path, so it does not re-resolve @latest on every spawn the way the npx entry does — npm update -g still picks up new versions, because it rewrites that path in place, and an app upgrade that deletes the directory the path named (a scoop current junction is used instead, where one exists) is repaired by the broker's startup sweep or by yaw-mcp heal. And this setting is about the broker itself; which runtime the sidecars get is decided separately, below.
Which runtime the sidecars get
When oam is installed and meets the minimum, yaw-mcp hosts the MCP servers it spawns on it. Nothing needs configuring — install oam and the sidecars move over. Without oam they run on node/npx exactly as before, and no warning is printed, because nothing was asked for.
Only Node-based launches are rewritten. A server whose command is docker, uvx, or a native binary is left alone, as is an npx package that cannot be found on disk.
Two more npx shapes stay on npx, because npx re-resolves its spec against the registry while oam run <entry> just runs whatever sits at the path it is handed. A spec that constrains the version keeps npx unless the copy on disk can be shown to satisfy it — an exact pin moves to oam only when the resolved copy declares that same version, and a range or partial (^1.2.3, ~1.2, 1.x) always keeps npx, since honouring one would mean evaluating semver ranges against the tree. A spec naming a git or path target (npx -y ./local-server, github:owner/repo) keeps npx too: it is not a package name, so there is nothing to look it up as. A plain @latest, or no version at all, is the everyday case and does move over.
One tradeoff worth knowing. npx -y <pkg>@latest re-resolves that tag on every spawn, so those servers update themselves. oam run <entry> cannot — oam has no fetch-on-demand, so it runs the copy already on disk, and because npx then stops running for that server, the copy stops being refreshed. The version pins itself until something fetches a newer one. yaw-mcp logs the resolved version once per package at startup so this is visible rather than silent, and picks the newest copy present.
Installing the servers durably
yaw-mcp sidecars installInstalls the npx-launched servers from your bundles.json into ~/.yaw-mcp/sidecars, and yaw-mcp resolves from there in preference to npm's npx cache. That gives one copy per package at a version that is written down, instead of whichever of the several copies in the cache happened to be newest — and re-running the command is how you move them forward.
It is not automatic and nothing requires it. Acquiring packages means network and minutes, and the connect path is what an MCP client blocks on while waiting for its tools; a first connect that silently turned into an npm install would be the wrong trade. Without it, resolution falls back to the npx cache exactly as before.
Only npx servers naming a registry package are installed. An npx launch pointing at a git or path target — npx -y github:owner/repo, npx -y ./local-server — is skipped and named in the output; those keep using npx, since resolving them would mean fetching the target just to learn the name it declares. Two servers pinning the same package at different versions is also reported: one flat node_modules holds a single version, so the command tells you which one it installed rather than letting the loser start on something it did not ask for.
yaw-mcp doctor prints the installed version of each configured package under OAM RUNTIME, so a pinned or missing one is visible. To keep npx's self-updating behavior for a particular server instead, set runtime: "node" on it.
--json emits the same keys on every run — root, installed, reason, error, conflicts, skipped — so a script can read the result without first working out which path it took.
To override, in ~/.yaw-mcp/bundles.json:
{
"defaultRuntime": "node",
"servers": [{ "namespace": "postgres", "runtime": "oam" }]
}runtimeon a server wins over everything, and"node"is the per-server escape hatch.defaultRuntimeat the top level sets the machine-wide default;"node"opts the whole machine out.YAW_MCP_DEFAULT_RUNTIME=node|oamoverridesdefaultRuntimefor one process.
yaw-mcp doctor prints the resolved runtime for every configured server, with the reason — including the cases where oam was wanted but not used, so a silent fallback is visible rather than guessed at.
CLI
yaw-mcp with no subcommand runs as the MCP server, serving the servers in your ~/.yaw-mcp/bundles.json. Most read-only subcommands accept --json. Run yaw-mcp <cmd> --help for per-command flags.
Setup
yaw-mcp install <client> # connect a client to yaw-mcp (see above)
yaw-mcp uninstall <client> # unwire a client; leaves servers in bundles.json untouched
yaw-mcp doctor [--json] # diagnose config, clients, learning, reliability, upgrade
yaw-mcp status [--json] # read-only snapshot of loaded servers, activity, vault state, and call countsServers -- managed in ~/.yaw-mcp/bundles.json, browse the catalog at yaw.sh/mcp/catalog:
yaw-mcp add <slug> [--env KEY=value] [--dry-run] # add a catalog server to bundles.json
yaw-mcp add <name> --command "npx -y my-mcp" # ...or define a local server yourself
yaw-mcp add <name> --url https://host/mcp # ...or a remote one
yaw-mcp import <client> [--dry-run] # adopt the servers a client already has
yaw-mcp remove <slug-or-namespace-or-name> # drop a server
yaw-mcp list [--json] # list configured servers with their cached compliance grade
yaw-mcp try <slug> [--client <name>] [--ttl 1h] # wire a one-off trial straight into your client (expires)
yaw-mcp try-cleanup <slug> # remove a trial early (doctor GCs expired ones)
yaw-mcp trust [--yes|--list|--revoke [<path>]] # approve the project-local .yaw-mcp/bundles.json found from cwd so yaw-mcp loads it (pinned to its exact contents)add is not install: install <client> connects an AI client to yaw-mcp; add <slug> adds an MCP server to yaw-mcp itself. try points the client directly at the upstream server, bypassing yaw-mcp, so you can evaluate it in isolation. try-cleanup, and doctor for an expired trial, take the entry back out of the client's config, Codex CLI's config.toml included; when try-cleanup cannot, it keeps the trial and exits 1 rather than report a cleanup that did not happen. A --env value lands in your shell history and process argv like any argument. With add it is stored in plain text in bundles.json (written owner-only, mode 0600, on macOS and Linux; on Windows that mode is not applied -- Node maps it to the read-only attribute only -- so the file is protected by the NTFS permissions it inherits from your user profile) -- keep real credentials in the local secret vault and pass --env KEY='${secret:NAME}' instead (the single quotes are for bash, zsh and PowerShell; in cmd.exe pass it unquoted, since $ is not special there and cmd.exe would keep the quotes as part of the value). A missing row in yaw-mcp secrets audit whose name starts with <malformed ref> is a reference that did not parse -- fix the typo in bundles.json. With try the value is written inline, in plain text, into the client's own config for the trial's lifetime, and is not vault-resolved (the client spawns the server, not yaw-mcp).
Servers you already have. If a client is already configured with MCP servers, yaw-mcp import <client> reads that client's own config and adds every server in it to your bundles.json -- command, args, url, headers and env as they stand, so the imported server starts exactly as it did before. yaw-mcp's own entry is never imported. A server bundles.json already has is merged, the client's copy winning; when that would change the stored entry, import shows what differs and asks first on a terminal ([o]verwrite, [s]kip or [a]bort, and a bare Enter skips that server), and off one it writes nothing and exits 2 unless you pass --force. --dry-run shows what would come across without writing anything, and with --remove-originals it also lists the client entries a real run would remove, or says why it would remove none.
After an import the client is still launching those servers itself, so each one would run twice -- once directly, once through yaw-mcp. import says so and offers to remove the originals from the client config: on a terminal it asks (a bare Enter is no), and off one it leaves them alone and names the --remove-originals flag. It refuses to remove them at all if the client has no yaw-mcp entry, since that entry is the only way it would still reach them -- run yaw-mcp install <client> first.
An imported entry carries no catalog slug, so its handles are its namespace and its name (the key the client used). remove, set, enable and disable all accept either: yaw-mcp remove "GitHub Copilot" works as well as yaw-mcp remove githubcopilot.
A credential that was sitting in the client config comes across as a plain value in bundles.json (file mode 0600); the import prints the key names -- never the values -- and points at yaw-mcp secrets set for moving them into the vault.
Servers the catalog does not list. The catalog is a curated front door, not the only one. Pass --command for a local (stdio) server or --url for a remote (HTTP) one, and add defines the server from your flags instead of looking up a slug -- no catalog fetch, so this also works offline:
yaw-mcp add mytool --command "npx -y @scope/my-mcp@latest" --description "what it is for"
yaw-mcp add linear --url https://mcp.linear.app/mcp \
--header 'Authorization: Bearer ${secret:linear}'--header is repeatable and remote-only: a remote server spawns no process, so --env cannot reach it (see headers). --transport sse selects SSE over the streamable-HTTP default. --description is worth setting either way, since dispatch ranks servers on it.
A project-local .yaw-mcp/bundles.json (committed with a repo) is ignored until yaw-mcp trust approves it, since every server in it is a command yaw-mcp spawns as you. Approval is pinned to the file's exact contents, so an edited file needs approving again; --list shows approvals (stale ones flagged) and --revoke withdraws one. Your own ~/.yaw-mcp/bundles.json is never gated.
Calling a server from a shell -- for a git hook, a Makefile, a cron job or an agent loop that does not speak MCP:
yaw-mcp call <namespace> <tool> '{"key":"value"}' # call one tool, print the result
yaw-mcp call github search --args-stdin < args.json # ...or pipe the argument object in
yaw-mcp call github search --json # the raw MCP envelope, not just its textOne call, one spawn: the server is started for the call and torn down again, so nothing stays loaded and two calls are two spawns (for a batch, the mcp_connect_exec pipeline is the right tool). The tool's text goes to stdout verbatim so it can be piped; diagnostics go to stderr. Exit 0 when the tool answered, 1 when it could not be called or answered with an error, 2 when your own config refused the call.
It gets the same policy a proxied call gets, and gets it before the server is spawned: a disabled server, one your project profile blocks, one below YAW_MCP_MIN_COMPLIANCE, and any tool on the blockedTools deny list are all refused. A ${secret:NAME} in the server's env resolves from the vault exactly as it does for the broker.
Inspection & maintenance
yaw-mcp bundles [list|match] [--json] # browse curated bundles; match partitions against your enabled servers
yaw-mcp upgrade [--run] [--json] # show (or run) the command that bumps @yawlabs/mcp
yaw-mcp heal [--dry-run] [--json] # re-point yaw-mcp client entries whose launch file an app upgrade deleted (runs at startup too)
yaw-mcp reset-learning # clear cross-session learning (~/.yaw-mcp/state.json)
yaw-mcp completion <bash|zsh|fish|powershell> # print a shell-completion scriptCompliance
yaw-mcp compliance <target> # run the 88-test compliance suite against a server
yaw-mcp audit <namespace> # audit a stdio server from bundles.json, cache its A-F grade in grades.jsonheal exists for the entry that pins a path (see Running yaw-mcp on oam): an app upgrade that deletes the directory the path named leaves the client unable to start the broker, and the client cannot report that because it cannot start the thing that would. The broker runs the same sweep at every startup, cross-client, so a client whose entry still works repairs the ones that do not; heal is for the machine whose only configured client is the broken one. It touches only an entry yaw-mcp wrote that is currently broken -- a working entry is never rewritten, however unusual it looks -- and prints each old and new path. A stale entry it finds and cannot re-point -- a read-only config file on Windows, say -- is reported on stderr with the step past it, listed under failed in --json, and makes the run exit 1. --dry-run reports without writing; --quiet keeps the exit code and --json and drops the transcript. YAW_MCP_AUTO_HEAL=0 turns the startup sweep off; YAW_MCP_READONLY_DIAGNOSTICS=1 turns both off.
To install completion, redirect to your shell's completions dir, e.g. yaw-mcp completion zsh > "${fpath[1]}/_yaw-mcp", or yaw-mcp completion powershell >> $PROFILE.
Configuration -- .yaw-mcp/
yaw-mcp keeps its config under a .yaw-mcp/ directory (mirroring .git/, .vscode/, .claude/). It reads config.json from three optional scopes, highest precedence first:
Scope | Path | Use |
local |
| Machine-local overrides; gitignore it. |
project |
| Shared via git with the repo. |
global |
| Personal default for every project. |
The project .yaw-mcp/ is found by walking up from the cwd -- stopping just before $HOME when started under it (so a .yaw-mcp/ at $HOME is treated as global only); a cwd outside $HOME walks to the filesystem root with an ownership check. Files may contain // and /* */ comments. Full schema:
{
// optional -- gives editors key completion + inline validation
"$schema": "https://raw.githubusercontent.com/YawLabs/mcp/main/schemas/yaw-mcp.config.v1.json",
"version": 1, // schema version; newer versions log a warning
"servers": ["gh", "pg", "linear"], // allow-list of namespaces (most-specific scope wins)
"blocked": ["prod_db"], // deny-list (UNION across all scopes -- fail-safe on deny)
"installNudge": true // opt-in: discover may suggest installing MCP servers for
// CLIs found in your recent shell history (off by default)
}That shape ships as a JSON Schema -- schemas/yaw-mcp.config.v1.json, served from the raw URL in the $schema line above -- so any editor that honors $schema completes the keys and flags a typo as you type. A namespace is [a-z][a-z0-9_]{0,29} (so prod_db, not prod-db); the schema rejects anything else, while the loader only warns and keeps loading.
Malformed files log a warning and fall through (fail-open). yaw-mcp reads this file once at startup, so restart the client after editing it -- unlike bundles.json, which is re-read while the session runs. mcp_connect_health shows which files are applied.
Project guide -- YAW-MCP.md
Drop a YAW-MCP.md next to config.json in either .yaw-mcp/ and yaw-mcp surfaces it via a yaw-mcp://guide MCP resource. The discover/dispatch descriptions tell the model to read it first, so project routing conventions ("use the gh server, not bash") and credential guidance stick without restating them each session. A user guide (~/.yaw-mcp/YAW-MCP.md) and a project guide are concatenated with the project one last; a missing file is skipped silently.
Finding a server
yaw-mcp search sql # slug, name, tags, category, description
yaw-mcp search # list the whole catalog
yaw-mcp search sql --json # machine-readableEach match prints its runtime, tool count and the credentials it needs by name, so you know what an add will ask for before you run it. Nothing is written; yaw-mcp add <slug> is what installs. A slug that misses now suggests the closest real one rather than only naming a URL.
Changing a server without editing JSON
yaw-mcp set github isActive=false # or: yaw-mcp disable github
yaw-mcp set github pinned=true # never idle-unload this one
yaw-mcp set github runtime=oam # host it on the oam runtime
yaw-mcp set github connectTimeoutMs=60000 # slower handshake, this server only
yaw-mcp set github env.GITHUB_TOKEN='${secret:gh}' # point at the vault
yaw-mcp set github env.OLD_VAR= # remove one variableOnly the entry you name is rewritten, so comments and formatting elsewhere in bundles.json survive -- unlike add and remove, which rewrite the whole file. enable and disable are the same edit as set <server> isActive=true|false.
Settable: isActive, pinned, runtime, connectTimeoutMs, description, and one env.KEY at a time. Everything else is refused, including command, args and url -- those decide which program yaw-mcp launches as you, and belong to add/remove or a deliberate edit. A trailing = clears a field; clearing a stored env value asks first, since it does not come back.
Blocking individual tools
blocked turns a whole server off. blockedTools turns off individual tools on servers you otherwise want:
// .yaw-mcp/config.json
{ "blockedTools": ["gh_delete_repo", "pg_drop_*"] }Entries are the flattened <namespace>_<tool> names that appear in the tool list, matched literally and case-sensitively, with an optional single trailing * for a prefix match. A bare * is refused, and a bare tool name does not match across servers -- <namespace>_<tool> cannot be split back apart reliably, because a namespace may itself contain _. The broker's own mcp_connect_* tools cannot be blocked.
The two keys act on different events. blocked is checked when a server would start; blockedTools is checked when a tool would be called, which is also what makes it cover a tool reached inside an mcp_connect_exec pipeline. A pipeline naming a blocked tool is refused before any step runs, rather than failing partway through. Denies merge across config scopes, so a project config can add one but never remove one, and there is no allow-list counterpart.
A blocked tool is withheld from the tool list, so the model does not see it as an option, but it keeps its route: calling it by name returns an explicit refusal rather than an unknown-tool error that reads like a typo. discover still shows it in a server's known-tools line, marked [blocked], since that line describes what the server offers.
Local secret vault
Rather than putting credentials in a client config, keep a value in an encrypted file on your own machine and reference it with a ${secret:NAME} placeholder. A local server takes it in env, which becomes the child process's environment:
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${secret:gh}",
"AUTH_HEADER": "Bearer ${secret:gh}" // placeholders compose inline
}A remote (HTTP/SSE) server takes it in headers, which are sent on every request the transport makes:
{ "namespace": "linear", "type": "remote", "url": "https://mcp.linear.app/mcp",
"headers": { "Authorization": "Bearer ${secret:linear}" } }The two are the local and remote halves of one mechanism: same vault, same placeholder, same refusal. env on a remote entry is ignored (it warns and tells you to use headers), and headers on a local entry is ignored the same way. A header name is dropped at load, with a warning, if its value is blank, if the name is not a valid HTTP header name, or if it is Mcp-Session-Id or Mcp-Protocol-Version in any casing -- the transport sets those, and a duplicate would corrupt the session rather than override it.
When the server starts -- a spawn for a local one, a connect for a remote one -- if YAW_MCP_VAULT_PASSPHRASE is set in yaw-mcp's own env, it decrypts the referenced names and substitutes them into the child's env or onto the request headers. If the passphrase is absent or a name isn't stored, the start is refused -- the literal ${secret:NAME} is never passed through, since some servers would treat the placeholder as a real token. The value never leaves your machine, and it is stripped out of error text before that text reaches a log or the model.
Remote servers: headers
A remote (HTTP/SSE) server spawns no process, so it has no env to put a credential in -- yaw-mcp warns and ignores env on a remote entry. Its credential channel is headers, which takes the same ${secret:NAME} references:
{
"namespace": "linear",
"type": "remote",
"url": "https://mcp.linear.app/mcp",
"headers": {
"Authorization": "Bearer ${secret:linear}"
}
}Headers are resolved through the same fail-closed path: a missing or malformed reference refuses the connect, so no request is made at all rather than one carrying the literal to a third party. They apply to both transports, including the SSE stream.
Where the passphrase comes from. Three ways, in the order you'll meet them:
YAW_MCP_VAULT_PASSPHRASEin yaw-mcp's own env -- theenvblock of the yaw-mcp entry in your MCP client config, not the upstream server's (yaw-mcp strips its own secrets from the env of every child it starts: every server it spawns, the npm runs behind its self-upgrade and the daily sidecar refresh, and the compliance suite). This is the only option for a spawn, which happens over stdio with no terminal to prompt on.An in-session prompt for the vault. If your client supports MCP elicitation, a locked vault asks for the passphrase and retries the server. The passphrase is never typed into your client's dialog: it goes into a masked field on a one-shot page yaw-mcp serves on
127.0.0.1. A client with URL-mode elicitation opens that page from its own prompt. A client with form mode only (Claude Code, for one) asks whether to open it, and on yes yaw-mcp opens it in your default browser; with no browser to open (no graphical session), the call fails and names the env var instead. The page takes one submission and expires after 3 minutes. A mistyped passphrase gets one more try per session; a decline stops the asking. It's held in memory for that session only -- never written to disk, and never handed to the server being started.An in-session prompt for a child's own missing credential. The same masked page (and the same URL / form mode rules above) is used when a child server's stderr names a key yaw-mcp does not have: a
GITHUB_TOKEN is requiredline, anAWS_ACCESS_KEY_ID is requiredline, and the like. One masked field per missing key, the same URL-mode completion notification, the same page hardening (no CORS, no script,Cache-Control: no-store, 256-bit random token, Host header check, 3-minute TTL). The value lands in the child's env for the session -- not the vault slot, and not any other server -- and only for a key the server'sbundles.jsonentry leaves unset or empty: a non-empty value there wins, and that key is never asked for. A value the child still reports missing on the retry is dropped. Each server gets two asks per session. A decline ends the asking for the session; a no-page or no-browser is a wall that latches the same way; an expired page spends its ask but does not latch, since the user may simply have been away.The CLI prompt, for
yaw-mcp secretsruns: no-echo on a real TTY. If the terminal will not switch echo off, the prompt refuses rather than show what you type. Note that Git Bash/MSYS can't offer one (Node sees pipes, not a TTY) -- use PowerShell,winpty, or the env var there.
yaw-mcp doctor has a SECRET VAULT section showing what's stored, which servers reference it, and whether the passphrase it can see is set and unlocks the vault -- start there when a server won't load. It reports names and a yes/no, never a value.
yaw-mcp secrets set <name> # store a value (no-echo prompt, piped stdin, or --value)
yaw-mcp secrets get <name> # decrypt and print one value
yaw-mcp secrets list # show entry NAMES only (values stay encrypted)
yaw-mcp secrets remove <name> # delete an entry
yaw-mcp secrets lock # forget this process's cached passphrase -- effectively a no-op from the CLI; cannot reach a running server or change the vault
yaw-mcp secrets rotate # re-encrypt the whole vault under a NEW passphrase
yaw-mcp secrets reset # forgot the passphrase? move the vault aside, list its entry names, start a new one
yaw-mcp secrets audit [--secret NAME] [--server NS] [--json] # who consumed which secret, whenThe passphrase derives the key via scrypt and is cached in memory for one process; the on-disk file holds only ciphertext (AES-256-GCM, per-entry IV + tag, one vault salt). rotate decrypts every entry first and aborts untouched if any fails -- and it re-wraps the encryption, not the underlying tokens (a leaked token is still leaked; rotate it at its source). audit reads an append-only 0600 NDJSON log of secret NAME + namespace + timestamp -- never a value -- and writes fail-open so a broken log never blocks a spawn.
Forgot the passphrase? There is no recovery: the file holds only ciphertext under a key derived from the passphrase, and nothing else -- no server, no recovery key -- can open it. yaw-mcp secrets reset is the way back to a working vault. It prints the entry names the vault holds (plaintext keys, so no passphrase is needed) so you know what to set again, asks you to type RESET (or takes --force from a script; without it, no TTY means a refusal), moves the old file aside as secrets.json.reset-<timestamp> next to the vault -- it still opens under the old passphrase, should that turn up -- and creates an empty vault under a new passphrase, confirmed twice on a TTY or taken from YAW_MCP_VAULT_PASSPHRASE when it is set. It refuses when the passphrase it is given already opens the vault: that is rotate, not a reset. Afterwards, a yaw-mcp server that is already running keeps the passphrase it started with and needs a restart, and a YAW_MCP_VAULT_PASSPHRASE in a client config's env block still holds the old value. yaw-mcp doctor says whether the passphrase it can see unlocks the vault.
Threat model. The vault protects the on-disk file against offline brute-force after exfiltration (stolen laptop, leaked backup): useless without the passphrase, tamper-evident via GCM. It does not defend against a process running as you while the passphrase is cached, a keylogger, or a value already leaked at its source.
Runtime detection & uv bootstrap
On startup yaw-mcp probes for node, npx, python, uvx, and docker (best-effort, 3s per probe) so it can warn when a server's runtime is missing. yaw-mcp itself needs only Node.js.
Python servers (sqlite, time, sentry, ...) launch via Astral's uv/uvx. On first encounter, if uv isn't on your PATH, yaw-mcp downloads Astral's standalone release, verifies the sha256, and caches it -- reusing your own uv if you have one. uvx ARGS is always rewritten to uv tool run ARGS, so only uv needs to be reachable.
Trust & security
MCP servers are third-party code you choose to run. yaw-mcp doesn't sandbox them -- that's your OS and network. What it gives you is visibility and a gate:
Compliance grades (A-F) -- the
@yawlabs/mcp-compliancesuite (88 tests) grades a server;yaw-mcp listshows it in aGRADEcolumn anddiscovershows it inline (github [ready] [A]).YAW_MCP_MIN_COMPLIANCE=Bmakesactivaterefuse anything below the floor. Ungraded servers pass (don't punish unknown); audit them yourself withyaw-mcp compliance <target>.Source transparency --
listanddiscovershow the exactcommand,args, andurleach server launches with. Nothing is wrapped.Local credentials -- vault values are encrypted at rest (scrypt + AES-256-GCM), injected at spawn, and never logged. Nothing is transmitted off the machine.
Failure output reaches your client -- when a server fails to start, the tail of its stderr is attached to the activation error, so it lands in your client's context (and your model's) as well as the log. That tail is the server's own output -- never your files, buffers or shell history -- and it is redacted first: values yaw-mcp injected from
bundles.jsonor the vault, resolved request headers, and credential-named variables inherited from your shell (GITHUB_TOKEN,AWS_SECRET_ACCESS_KEY,NPM_TOKEN, ...) are each replaced with***NAME***, which names the credential to rotate without showing it. An inherited name whose last segment isPATH,FILE,DIR,HOST,PORT,PREFIXorSIZE(SSH_KEY_PATH,TOKEN_BUCKET_SIZE) does not count as credential-named. Redaction is exact-substring on values of 8 characters or more, so a credential parked under a name that does not read as one is not covered. There is no opt-out: the tail is what makes a failed start diagnosable, and the missing-credential prompt below reads the same text.Response pruning (
YAW_MCP_PRUNE_RESPONSES, on by default) -- trims token cost by dropping no-information keys (null / empty array / empty object) and trimming trailing whitespace and long blank runs in text results. The text rules classify the block before editing it, so a unified diff comes back byte-faithful, fenced code blocks are left alone, and Markdown hard line breaks survive. Structured values are never altered or redacted; it is a token-savings feature, not a security control.Namespace isolation -- tools are namespace-prefixed (
gh_create_issue, never barecreate_issue), so a server can't impersonate another's tools.mcp_connect_read_toolinspects a schema before any code runs.
yaw-mcp does not block outbound traffic, firewall DNS, analyze source, or pin hashes -- a malicious server you chose to run can reach any URL your machine can. Review the command before adding a server, run untrusted ones under a restricted user or container, and prefer graded servers when alternatives are equivalent. Report a security issue in yaw-mcp itself via GitHub private advisories (see SECURITY.md).
Missing credentials
If a server exits with something like GITHUB_TOKEN is required and your client advertises MCP elicitation, yaw-mcp prompts for the value and retries, rather than failing the call outright.
Environment variables
Common ones (run yaw-mcp --help for the full list):
Variable | Description |
| Max concurrently active servers. Default |
| Minimum grade ( |
| Passphrase for the local secret vault. Required for spawn-time |
|
|
|
|
|
|
|
|
|
|
|
|
| Path to the |
| Raises the V8 heap cap oam applies to a hosted server (4 GiB by default; can be lower inside a memory-limited Linux container). Set it in that server's |
|
|
|
|
| Ceiling on a single proxied tool result. Over it, the reply is cut and carries a marker saying so, with the byte total and what was dropped -- a silently truncated result reads to the model as a complete one. Default |
| Ceiling on the estimated tokens of the whole loaded tool surface, checked alongside |
| How much of the catalog |
| Milliseconds to wait for a server's MCP handshake. Default |
| Milliseconds to wait for a server's tool/resource/prompt inventory calls after the handshake. Default |
| Milliseconds to wait for a single proxied |
| Non-matching tool calls a loaded server tolerates before it is unloaded. Default |
|
|
|
|
|
|
Requirements
Node.js 20+.
No account. Everything runs locally.
License
Source-available, not open source. Copyright (c) 2026 Yaw Labs, all rights reserved.
The source is published so you can read and audit it before running it -- yaw-mcp spawns processes on your machine and handles your credentials, and you should not have to take that on faith. You may use it freely, personally or commercially, and redistribute unmodified copies. You may not offer it to third parties as a competing product. See LICENSE.md for the terms, and CONTRIBUTING.md for the DCO sign-off contributors use.
Links
yaw.sh/mcp/catalog -- browse the server catalog
@yawlabs/mcp-compliance -- test your MCP servers for spec compliance
CHANGELOG -- release notes
GitHub -- source and issues
Available Tools
10 toolsmcp_connect_activateAIdempotent
Load one or more installed MCP servers' tools into the current session by namespace. Each server adds its tools to your context, so load only what the current task needs. When you move on, unload servers you're done with via mcp_connect_deactivate before loading new ones. Tools are prefixed by namespace (e.g., "gh_create_issue"). Pass "server" for one or "servers" for multiple. Optionally pass tools: [...] to expose only those tools by name — the rest stay proxyable via mcp_connect_dispatch. If YAW_MCP_MIN_COMPLIANCE is set, activation refuses servers whose reported grade is below the floor (ungraded servers always pass); the refusal message names the grade and the env var to unset.
| Name | Required | Description | Default |
|---|---|---|---|
| tools | No | Optional per-server tool filter (bare tool names, not namespace-prefixed). When set, only the listed tools surface in tools/list — others stay reachable via mcp_connect_dispatch. Omit (or re-activate without it) to expose the full tool set. Only applied when activating a single server. | |
| server | No | Single server namespace to activate (e.g., "gh") | |
| servers | No | Multiple server namespaces to activate at once (e.g., ["gh", "slack"]) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate idempotency and non-destructiveness. The description adds useful behavioral context: loading adds tools to context, tools are namespace-prefixed, and behavior under the YAW_MCP_MIN_COMPLIANCE environment variable. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is four sentences long, each adding value. It is front-loaded with the main purpose and well-structured. Minor redundancy (e.g., 'tools are prefixed by namespace' could be integrated), but overall efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description adequately covers behavior, filtering, and environment variable handling. It explains namespace prefixing and activation refusal criteria. Lacks explicit return value description, but for a loading tool, this is acceptable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds meaning by explaining that tools are namespace-prefixed, the distinction between 'server' and 'servers', and how the 'tools' filter works. This goes beyond the schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool loads MCP servers' tools into the session by namespace. It uses a specific verb ('load') and resource ('installed MCP servers' tools'), and naturally distinguishes from siblings like mcp_connect_deactivate and mcp_connect_dispatch.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use ('load only what the current task needs'), when-not-to-use (when done, unload via mcp_connect_deactivate before loading new ones), and mentions alternatives. It also details optional filtering via the tools parameter.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mcp_connect_bundlesARead-onlyIdempotent
List curated multi-server 'bundles' — presets like pr-review (github + linear) or devops-incident (github + pagerduty + slack) that commonly ship together. Use this BEFORE mcp_connect_discover when the user's intent maps to a known workflow (on-call triage, PR review, data pipeline debugging) — it returns a ready-to-run mcp_connect_activate namespaces=[...] call per bundle. With action="match" (recommended after the user's installed list is known) the response partitions bundles into READY (every namespace already in the user's bundles.json — activate now) and PARTIAL (some present, some missing — names the missing namespaces so you can tell the user to run yaw-mcp add <slug>; the slug catalog is at https://yaw.sh/mcp/catalog/). With action="list" (default) it returns the full curated catalog. Bundles are static client-side data, not a network call.
| Name | Required | Description | Default |
|---|---|---|---|
| action | No | Either "list" (return the full curated catalog; default) or "match" (partition bundles against installed servers into ready-to-activate vs partially-installed). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and idempotentHint=true, and the description adds that bundles are static client-side data with no network call. This confirms safety and clarifies the tool's nature beyond annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but well-structured, front-loading the main purpose and then providing detailed usage for each action. Every sentence adds value, and there is no redundancy. It earns its length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple (1 param, no output schema), but the description covers all necessary context: when to use, how to use the two modes, what each returns, and next steps. No gaps remain.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With only one parameter (action), schema coverage is 100%, but the description significantly expands on its behavior: explains the difference between 'list' (full catalog) and 'match' (partition into READY/PARTIAL), mentions the bundles.json context, and provides instructions for handling PARTIAL results. This adds substantial value beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it lists curated multi-server bundles and distinguishes from sibling mcp_connect_discover by specifying when to use each. Examples like 'pr-review' and 'devops-incident' make the purpose concrete.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use this tool (before mcp_connect_discover when user intent maps to a known workflow) and when to use the two action modes ('list' vs 'match'), including how to proceed with results (activate or tell user to add missing slugs). Naming alternatives strengthens guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mcp_connect_deactivateAIdempotent
Unload one or more MCP servers' tools from the current session to free context. The server stays configured in ~/.yaw-mcp/bundles.json and can be reloaded via mcp_connect_activate when needed again. Unload servers you're done with; yaw-mcp also auto-unloads any server idle for 10+ tool calls to other servers. Pass "server" for one or "servers" for multiple.
| Name | Required | Description | Default |
|---|---|---|---|
| server | No | The namespace of the server to deactivate | |
| servers | No | Multiple server namespaces to deactivate at once (e.g., ["gh", "slack"]) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations include idempotentHint=true and destructiveHint=false. The description adds that the server 'stays configured in ~/.yaw-mcp/bundles.json' and can be reloaded, confirming non-destructive behavior. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is four sentences, front-loaded with the main purpose. Each sentence adds value, though could be slightly more concise. Well-structured overall.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has no output schema, but the description covers parameters, behavior, and persistence. With good annotations, it provides sufficient context for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% with both parameters documented. The description adds 'Pass "server" for one or "servers" for multiple', clarifying the singular/plural usage beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Unload one or more MCP servers' tools from the current session', using a specific verb (Unload) and resource (MCP servers' tools). It also distinguishes from the sibling mcp_connect_activate by mentioning reload capability.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description advises 'Unload servers you're done with' and mentions automatic unloading after 10 idle calls, providing clear usage context. It does not explicitly list when not to use or compare to all siblings, but the guidance is sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mcp_connect_discoverARead-onlyIdempotent
List the MCP servers configured in the user's local ~/.yaw-mcp/bundles.json and ready to use. Call this when browsing what's available or when the task isn't specific yet. If the task is already clear ("file a github issue", "query postgres", "post to slack"), prefer mcp_connect_dispatch — it picks the right server and loads its tools in one call. Load only the servers the CURRENT task needs; each one adds tools to your context. Shows names, namespaces, tool counts, a token-cost estimate per server (e.g. "22 tools, ~2.8k tokens") so you can budget context before activating — tilde values are estimates based on cached tool metadata, unprefixed values reflect live tool schemas. Scored servers carry an inline [A]–[F] compliance grade from the Yaw MCP test suite — treat it as a trust signal and prefer higher-graded alternatives when otherwise equivalent (ungraded servers are unmarked, not penalized). Also surfaces whether each server is loaded, any local CLI it shadows (prefer the MCP tools over the CLI when a shadow is listed), and usage hints ("used Nx" or "often loaded with X") when the signals are present (counts persist across yaw-mcp restarts). Recurring packs that have been loaded together ≥2 times get their own block at the top with a ready-to-run activate call — skip the extra mcp_connect_suggest round-trip when the signal is already there. If a yaw-mcp://guide resource is listed, read it FIRST: it carries project/user-specific routing rules and credential conventions that override generic defaults.
| Name | Required | Description | Default |
|---|---|---|---|
| context | No | Optional: describe the current task or conversation context. Servers will be sorted by relevance to help you pick the right one. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=true, idempotentHint=true, destructiveHint=false. The description goes beyond by detailing cost estimates (tilde vs. inline values), compliance grades [A]-[F], shadowed CLIs, usage hints, and recurring pack blocks. It also explains context impact (adding tools to context). This provides rich behavioral context beyond annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is detailed and front-loaded with the main purpose. Every sentence adds value, but it is somewhat lengthy. Given the tool's complexity and multiple facets, it is appropriately concise without being wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's nature (discovery), one optional parameter, no output schema, and annotations, the description covers all necessary aspects: what it does, when to use, behavioral details, parameter usage, cross-references to siblings, and special conditions (guide resource, recurring packs). It is comprehensive.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% (one optional parameter 'context' with a clear description). The description adds that servers will be sorted by relevance, which aligns with the parameter's purpose, but does not add significant new meaning beyond the schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly states that the tool 'List the MCP servers configured in the user's local ~/.yaw-mcp/bundles.json and ready to use.' It uses a specific verb ('list') and resource ('MCP servers'), and distinguishes from siblings by recommending `mcp_connect_dispatch` when the task is clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit guidance on when to use this tool ('call this when browsing what's available or when the task isn't specific yet') and when to use an alternative ('If the task is already clear... prefer mcp_connect_dispatch'). It also advises to read the guide resource first if available.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mcp_connect_dispatchAIdempotent
PREFERRED entry point when the task is already concrete. Picks the best-matching installed MCP server(s) for a natural-language task and loads their tools in ONE call — no separate discover + load step. Describe what you want to do ("create a github issue for the login bug", "post a summary to slack", "query the prod postgres") and yaw-mcp will rank the user's installed servers with BM25, load the top match into the session, and expose its tools so you can call them. Use mcp_connect_discover only when browsing what's installed without a specific task. When an installed MCP server shadows a local CLI (e.g. npmjs shadows npm, tailscale shadows tailscale, github shadows gh), prefer dispatching to the server over running the CLI via Bash. Default budget is 1 to keep the tool list focused; raise it only if the task genuinely spans multiple servers. If yaw-mcp://guide is listed as a resource, read it first — the project may have explicit routing rules (e.g. "use gh not bash for GitHub").
| Name | Required | Description | Default |
|---|---|---|---|
| budget | No | How many top-ranked servers to load into the session. Defaults to 1. Cap is 10. Raise only when one task genuinely spans multiple servers. | |
| intent | Yes | What you want to accomplish, in plain English (e.g., "file a github issue titled Fix login bug") |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description explains that the tool ranks servers with BM25, loads the top match into the session, and exposes its tools. It also mentions that the process is a single step (no separate discover+load). Annotations already indicate idempotency and non-destructiveness. The description adds useful behavioral context but does not explicitly state what happens on repeated calls or error cases.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, well-structured paragraph that front-loads the key purpose. It conveys all necessary information without being overly verbose. While it could potentially be broken into bullet points for easier scanning, it remains concise and readable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (no output schema, two parameters), the description covers the main purpose, usage guidelines, and behavioral details. It lacks explicit mention of what happens if no server matches or what the return result is, but these are minor gaps for a dispatch tool. The description is largely complete for an AI agent to use correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 100% coverage with descriptions for both parameters. The description adds value by providing concrete examples for the intent parameter and additional usage guidance for the budget parameter (default, cap, when to raise). This enriches the schema's basic descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states that mcp_connect_dispatch is the preferred entry point for concrete tasks, picks the best-matching MCP server(s) based on natural language intent, and loads their tools in a single call. It distinguishes itself from mcp_connect_discover, which is for browsing without a specific task. The verb and resource are specific, and examples are provided.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells when to use this tool (task is concrete) and when to use an alternative (mcp_connect_discover for browsing). It also advises on budget usage (default 1, raise only for multi-server tasks) and recommends preferring MCP servers over local CLI tools when they shadow them. This provides clear guidance for the agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mcp_connect_execA
Run a short DECLARATIVE pipeline of upstream tool calls in a single round-trip. Use this when you already know the exact 2-4 tool calls to make and one call's output feeds another's args — e.g. a = gh_list_prs(); b = gh_get_pr(a[0].number); return b. NOT a code sandbox: there is no expression language, no loops, no branching, no arithmetic. The only control flow is sequential step execution; the only data-flow primitive is {"$ref": "<stepId>[.path.to.value]"} which substitutes a prior step's output (or a nested field of it) into the next step's args. Paths support dot keys and [N] / .N array indexing. Each step's tool must be a namespaced, already-loaded tool name (the exec does not auto-activate — call mcp_connect_activate first). Max 16 steps per exec. If any step fails, the whole pipeline fails and returns { ok: false, failedStep, error, partial: { ...completed outputs } }. On success returns { ok: true, result: <return-step output>, steps: { ...all outputs } }. Prefer this over back-to-back tool calls when the chain is deterministic — it saves prompt-token replay and client round-trips.
| Name | Required | Description | Default |
|---|---|---|---|
| steps | Yes | Ordered list of tool calls to run. Each step is `{ id?: string, tool: string, args?: object }`. `args` values may be `{"$ref": "<stepId>.path"}` to inject a prior step's output. | |
| return | No | Optional: id of the step whose output should be surfaced as `result`. Defaults to the last step's id (or its positional index). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations are present but the description adds substantial behavioral context: sequential step execution, data-flow via $ref, max 16 steps, failure behavior (returns partial results with error details), and success return format (result plus all steps). No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and concise despite its length. Each sentence serves a purpose: defining the tool, explaining usage, constraints, failure mode, and preference over alternatives. It is front-loaded with key information and avoids redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity and absence of an output schema, the description fully covers return values (ok/result/steps/failedStep/error), failure behavior, and data-flow semantics. It provides enough information for an agent to use the tool correctly without additional context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, but the description adds meaning beyond the schema by explaining how to use $ref for data-flow, default id behavior (positional index), and constraints like max steps. This clarifies the semantics beyond the structural schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the tool executes a 'DECLARATIVE pipeline of upstream tool calls in a single round-trip.' It specifies the verb (run), resource (pipeline of tool calls), and clearly distinguishes it from siblings by noting it is not a code sandbox and requires prior activation. The purpose is specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use ('when you already know the exact 2-4 tool calls...') and when not to use ('NOT a code sandbox,' no loops, branching, or arithmetic). It also mentions prerequisites ('call mcp_connect_activate first') and provides guidance on preferring this over back-to-back calls for deterministic chains.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mcp_connect_healthARead-onlyIdempotent
Show health stats for MCP servers loaded in the current session: total calls, error count, average latency, and last error. Installed-but-unloaded servers aren't included — load them first if you need their stats.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false; description adds value by specifying operational scope (loaded servers) and the exact stats included, without contradicting annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences only; first states purpose and output, second clarifies exclusion. Every sentence earns its place with no waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only tool with good annotations, the description lists all returned fields, making it complete despite lacking an output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
No parameters exist, so baseline is 4. Description adds no parameter info, which is appropriate given schema coverage is 100%.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description explicitly states verb ('Show') and resource ('health stats for MCP servers loaded'), and distinguishes from siblings by clarifying that unloaded servers are excluded.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear context for when to use (show health stats) and a condition ('load them first') for unloaded servers, though does not name the specific sibling tool to use for loading.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mcp_connect_read_toolARead-onlyIdempotent
Return one tool's full input schema without loading its server into the session. Use this when you need to inspect an MCP tool's arguments before deciding whether to activate its server, or to compare schemas across two tools. For already-loaded servers this is free (schema is in memory). For not-loaded servers yaw-mcp spawns a transient upstream connection, reads the schema, and tears the connection down — no tools are added to your context, and mcp_connect_health will not show the server as loaded. When you're ready to actually call the tool, pass the server namespace to mcp_connect_activate (or use mcp_connect_dispatch with the task intent).
| Name | Required | Description | Default |
|---|---|---|---|
| tool | Yes | Tool name. The namespace prefix is optional — both "create_issue" and "gh_create_issue" are accepted. | |
| server | Yes | Namespace of the server that exposes the tool (e.g., "gh", "slack"). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint. Description adds valuable context about transient connections, lack of context pollution, and health check impact—no contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well-structured: first sentence defines core action, then use cases, then behavioral details. Every sentence adds value with no redundant text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complexity of 8 sibling tools, 2 required parameters, and no output schema, the description fully covers purpose, usage conditions, parameter flexibility, and side effects.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds value by explaining the optional namespace prefix in the tool parameter (e.g., 'create_issue' or 'gh_create_issue').
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb-resource combination ('Return one tool's full input schema') and distinguishes this tool from siblings like mcp_connect_activate and mcp_connect_dispatch by clarifying what it does without loading the server.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use this tool (inspect arguments before activating, compare schemas) and what happens for already-loaded vs not-loaded servers. Names alternatives: mcp_connect_activate or mcp_connect_dispatch.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mcp_connect_secretsARead-onlyIdempotent
List, per installed server, which local-vault secrets its ${secret:NAME} env references resolve to -- by NAME only, never a value. Use this to confirm a server will get the credentials it needs before activating it, or to spot a typo'd / un-set secret reference. injectedSecrets are the names the local vault HAS and the server references; missing are names the server references but the vault LACKS (set them via yaw-mcp secrets set <name>). This is a values-free preview: it reads the vault's KEY LIST and the server's env-reference NAMES, and never decrypts or returns any secret value. Servers with no ${secret:...} references are omitted. Requires no passphrase (no decryption happens).
| Name | Required | Description | Default |
|---|---|---|---|
| server | No | Optional: restrict the report to a single server namespace (e.g. "gh"). Omit to report every installed server that references a vault secret. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint. The description adds value beyond these by emphasizing it is a values-free preview, reads only key names and env-reference names, never decrypts or returns secret values, and requires no passphrase. No contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is moderately long but every sentence adds necessary context. It is well-structured with clear explanations, though could be slightly tightened without losing clarity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description fully explains the return structure (injectedSecrets and missing keys) and the tool's behavior in various scenarios (e.g., servers with no references omitted). It is complete for its intended use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (one optional parameter). The description adds meaning by explaining the effect of omitting vs. providing the 'server' parameter, which goes beyond the schema's description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists per installed server which local-vault secrets are resolved by env references, specifying it works by NAME only and never returns values. This is a specific verb+resource combination that distinguishes it from sibling tools like mcp_connect_activate or mcp_connect_health.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly advises using this to confirm a server will get needed credentials before activation or to spot typo'd/unset secret references. While it doesn't mention direct alternatives, it gives clear context for when to invoke the tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mcp_connect_suggestARead-onlyIdempotent
Surface recurring multi-server tool-call patterns as suggested 'packs' to activate in one step. Observation-only — this never loads or unloads anything. When the same 2-3 servers get used together in short bursts more than once, the pattern is surfaced here so the next workflow can call mcp_connect_activate once with the whole pack's namespaces instead of juggling discover + load for each server. Patterns persist across yaw-mcp restarts (via ~/.yaw-mcp/state.json) so a fresh process already knows what you usually use together. As a general rule: prefer loaded MCP servers over matching local CLIs (a loaded npmjs server replaces npm audit, tailscale replaces the tailscale CLI, etc.) — see mcp_connect_discover for which CLIs each installed server shadows. Returns a friendly 'no patterns yet' message when nothing has recurred.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Description adds context beyond annotations: never loads/unloads, patterns persist across restarts, returns a friendly message when no patterns. No contradiction with readOnlyHint and destructiveHint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Description is efficient but slightly long. Front-loads purpose well. Every sentence adds value, but could be tightened slightly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple observation tool with no parameters and no output schema, the description covers behavior, persistence, and return message completely. Annotations provide additional context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
No parameters, so baseline is 4. Description does not need to add parameter information.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states the tool surfaces recurring multi-server tool-call patterns as suggested packs. Distinguishes from siblings by emphasizing observation-only nature and mentions specific sibling tool for activation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use (when patterns have recurred) and when not (never loads/unloads). Provides alternative (mcp_connect_activate) and general rule about preferring loaded servers over CLIs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
10 tool updates
v0.73.2- First observed
mcp_connect_activate - First observed
mcp_connect_bundles - First observed
mcp_connect_deactivate - First observed
mcp_connect_discover - First observed
mcp_connect_dispatch - First observed
mcp_connect_exec - First observed
mcp_connect_health - First observed
mcp_connect_read_tool - First observed
mcp_connect_secrets - First observed
mcp_connect_suggest
TDQS
Scored across 10 tools
Each tool has a clearly distinct purpose: discover, activate, deactivate, health, dispatch, read tool schema, suggest patterns, list bundles, secrets, and exec. No overlapping functionality.
All tool names follow the consistent pattern 'mcp_connect_<verb>', which is predictable and makes the toolset easy to navigate.
10 tools is ideal for the domain of MCP server session management, covering all necessary operations without being overwhelming or sparse.
The set covers discovery, activation, deactivation, health, dispatch, schema inspection, pattern suggestions, bundles, secrets, and pipelines. Minor gaps exist (e.g., no tool for installing or updating servers), but it is complete for session-level management.
Maintenance
Related MCP Connectors
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Hosted MCP server for task-first delegation to remote workstations and workers.
Search, vet & assemble MCP servers from your agent: verified tools, risk labels, and trust scores.
Cloud-hosted MCP server for durable AI memory
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceActs as a proxy for multiple MCP servers, reducing context window usage from 15,000+ tokens to ~500 tokens by dynamically loading servers on-demand and exposing only 3 tools instead of all tool definitions.5GPL 3.0
- AlicenseNot gradedqualityDmaintenanceReduces LLM context window overhead by proxying multiple MCP servers through a few efficient dispatch tools instead of registering hundreds of individual tool schemas. It supports multi-account routing and tool discovery for both CLI-based and persistent MCP server configurations.MIT
- AlicenseAqualityBmaintenanceA single MCP server gateway that reduces context bloat by providing progressive tool discovery and invocation, dynamically provisioning downstream servers on demand.2670 PyPI20MIT
- AlicenseAqualityCmaintenanceAggregates and routes multiple MCP servers with intelligent tool recommendation and batch parallel execution, enabling unified access and efficient tool usage.28 npm4MIT