mcp-working-set
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., "@mcp-working-setsearch for a tool to upload files"
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.
mcp-working-set
An MCP proxy that sits in front of any MCP server and lists only the tools you actually use, plus two meta-tools (search_tools, call_tool) that reach everything else in one hop.
The problem
A client that connects to an MCP server loads every tool's name, description and input schema into the model's context, every session. A gateway with a few hundred tools costs on the order of 100k tokens before the first message, and most of those tools are never called.
S-T: every tool in the list is text the model has to read at the start of each conversation, whether it uses the tool or not. 300 tools can be a whole book's chapter of text, paid for every time.
Related MCP server: mcpproxy-go
The answer
Working set.
tools/listreturnssearch_tools,call_tool, and the tools inworking-set.json(promoted and pinned names) that the upstream currently lists. Nothing else.Usage promotion.
mws promotereads client transcripts and the proxy's own call log and promotes the tools that succeed across several sessions. Tools that stop being used fall out after a grace period.One-hop search.
search_tools {query}ranks the full upstream catalog (BM25 over names, parameter names and descriptions, with a small synonym table) and returns the best matches with their input schemas.call_tool {name, arguments}runs any of them.Everything stays callable. A direct
tools/callfor an unlisted tool is forwarded upstream too, so a client holding an older listing keeps working after a demotion.
S-T: instead of handing the model the whole phone book, the proxy hands it the 20 numbers you dial most, plus a way to look up the rest.
Quick start
Requires Bun 1.3 or newer.
git clone https://github.com/sblattj/mcp-working-set.git && cd mcp-working-set
bun install
bun run src/cli.ts init # writes ~/.config/mcp-working-set/config.json
# edit "upstream" in that file, then:
bun run src/cli.ts serve # listens on http://127.0.0.1:4484/mcpbun link puts an mws command on your PATH (mws serve, mws search ...). The package is not on npm yet, so bunx mcp-working-set does not work until it is.
Point your client at the proxy instead of the upstream server.
Claude Code
claude mcp add --transport http toolbox http://127.0.0.1:4484/mcpUse the same name as serverName in the config (default toolbox): the promoter finds calls in Claude Code transcripts by the mcp__<serverName>__<tool> prefix.
Codex (~/.codex/config.toml, in Codex versions that support Streamable HTTP servers)
[mcp_servers.toolbox]
url = "http://127.0.0.1:4484/mcp"Any other client: a Streamable HTTP MCP server at http://127.0.0.1:4484/mcp. Clients on the stateless 2026-07-28 protocol and older (2025) clients are both served.
Then keep the working set fresh, for example from cron or a launchd/systemd timer:
mws promote # rewrites working-set.json; the running proxy picks it up on its next tools/list
mws promote --dry-run # show the decision table without writing
mws report # what is listed, its size, call and search stats (--json for machines)
mws search "share a file" # run search_tools against the live catalog and print the replyUpstreams
The proxy talks to the upstream through the official MCP TypeScript SDK client (@modelcontextprotocol/client) with protocol negotiation on auto:
upstream | how |
Streamable HTTP, 2026-07-28 (stateless) | probed with |
Streamable HTTP, 2025 (session-based) | falls back to the |
stdio command | spawned as a child process, same negotiation |
On the stateless protocol the proxy also names the calling client in the forwarded request (clientInfo.name = mcp-working-set/<client>). On the session-based protocol and stdio the upstream only sees the proxy's own name.
Configuration
One JSON file. Path: --config <path>, else $MWS_CONFIG, else ~/.config/mcp-working-set/config.json. A missing file means all defaults. Every field is optional.
{
"upstream": { "url": "http://127.0.0.1:4483/mcp" },
"listen": { "host": "127.0.0.1", "port": 4484 },
"stateDir": "~/.local/state/mcp-working-set",
"serverName": "toolbox",
"backendHints": { "mail": "mailbox and filters", "drive": "files and docs" },
"pins": [],
"pinFiles": ["~/.claude/skills/*/SKILL.md"],
"promote": { "minSessions": 2, "minCalls": 1, "windowDays": 30, "keepDays": 60, "pinDecayDays": 90 },
"transcripts": [{ "type": "claude-code", "root": "~/.claude/projects" }]
}field | meaning |
|
|
| where the proxy listens. Loopback only: the listener validates |
| holds |
| the name your client registered the proxy under; used in the generated server instructions and to find |
| short labels per backend prefix ( |
| tool names that are always listed and never decay |
| globs of Markdown files whose front matter may carry |
| promotion thresholds, see below |
| where to read past tool calls from; the |
|
|
|
|
| extra query synonyms, e.g. |
How promotion works
mws promote collects every tool call it can see: calls in client transcripts, plus the proxy's own calls.jsonl. Then, per tool:
Sessions, not calls. A tool is promoted when it had successful calls in at least
minSessionsdistinct sessions (and at leastminCallssuccessful calls) within the lastwindowDays. One long session that hammers a tool does not promote it; a tool you reach for on different days does.Only successes count. A call that returned an error (or
isError) is ignored, so a tool the model keeps fumbling does not earn a slot.Hysteresis. Once promoted, a tool stays listed while it had at least one successful session in the last
keepDays(longer thanwindowDays). It is demoted only after that. This stops tools from flapping in and out of the list.Pins.
pinsin the config are listed unconditionally. Front-matter pins frompinFilesare listed while they are used at least once everypinDecayDays; an unused one goes stale and drops off, so an old skill does not hold a slot forever.Reality check. Names the upstream no longer lists are dropped from the result.
The proxy re-reads working-set.json whenever its modification time changes, so the next tools/list from any client reflects a promotion without a restart.
S-T: a tool earns its place by being useful on different days, keeps it for a while after you stop using it, and loses it if you stop for long enough.
Search
search_tools {query}:
Exact name, with or without the backend prefix (
mail_list_labelsorlist_labels): that tool alone, with its full upstream schema, uncapped.A backend name (
browser): that backend's tools in upstream order.browser clickranks only inside that backend.Anything else: BM25F ranking. Name tokens weigh most, then parameter names, then the first 600 characters of the description. A name that covers every query word gets a bonus.
Reply budget. The reply is compact JSON with full schemas for the top 3 matches only, and stays under 6 KB by trimming long descriptions, then dropping schemas (the caller can ask by exact name), then dropping the lowest entries.
No query: the whole catalog grouped by backend, names only, with listed tools marked
*.
Listed tools get lossless schema slimming (no $schema, no titles that only repeat the key, no default: null, anyOf [X, null] collapsed to X when that loses nothing). search_tools returns schemas exactly as the upstream sent them.
Resilience and logs
tools/listagainst the upstream has a 5 s budget. On failure or timeout the proxy serves the last-good catalog (in memory, elsecatalog.json, written0600after every successful list). With no catalog at all it lists only the two meta-tools.calls.jsonlgets one line per forwarded call,{ts, tool, via, ok, ms, client}, and one per search,{ts, event: "search", bytes, results, client}. Never arguments, results or query text.clientis the client name from the request's_metaenvelope, else the HTTPUser-Agent.
How it compares
project | what the model sees | how the list is chosen |
ToolHive optimizer |
| no direct tools; every call goes through search |
mcp-gateway | a static | hand-configured |
LiteLLM MCP |
| hand-configured |
this | working set plus | promoted from usage across sessions, with hysteresis, pins and pin decay |
Measured on one setup
One private setup (a gateway over several backends, one user, Claude Code). Token counts are estimates (bytes / 4), not tokenizer counts.
before | after | |
upstream catalog | 298 tools, ~529 KB (~132k tokens est.) | |
listed in | hand-pinned 27 tools, ~36 KB | working set of 20 tools, ~21 KB (~5.3k tokens est.) |
search top-1 (60-query private gold set) | 53% | 82% |
search top-3 | 72% | 98% |
largest search reply | 118 KB | 6 KB |
Your numbers will differ; mws report prints yours.
Development
bun install
bun test # offline: stub upstreams (stateless HTTP, session HTTP, stdio, dead, hung)
bun run typechecktest/fixtures/catalog.json is a synthetic 60-tool catalog (mail, calendar, drive, browser, codesearch) and test/fixtures/search-gold.json a 50-query gold set for it; bun test prints the top-1 and top-3 hit rates and fails below 70% and 90%.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server for progressive tool usage at any scale (see https://klavis.ai)
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Search, vet & assemble MCP servers from your agent: verified tools, risk labels, and trust scores.
Cross-tool persistent memory and context for AI assistants over MCP.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA configurable proxy that filters GitHub MCP server tools via YAML whitelist, reducing context consumption from 100+ tools to only the ones you need while providing meta-tools to discover available functionality.MIT
- AlicenseAqualityAmaintenanceLocal-first MCP proxy with BM25 tool discovery, quarantine security, Docker isolation, OAuth support, activity logging, and web UI. Routes multiple upstream MCP servers through a single endpoint.9395MIT
- AlicenseNot gradedqualityDmaintenanceMCP proxy that reduces context usage through semantic tool routing, enabling on-demand discovery and routing of relevant tools.MIT
- AlicenseNot gradedqualityCmaintenanceIntelligent MCP proxy server that reduces context bloat by serving only the tools your AI actually needs through semantic search and a fixed two-tool surface.13MIT