mcp-winnow

mcp-winnow

Enables agents to use many MCP servers without context bloat by exposing meta-tools (search, load, call, run_code) that reduce token usage via progressive disclosure and result trimming.

Category
Visit Server

README

Winnow

Keep the grain, drop the bloat.

CI License: MIT Node MCP

Winnow (mcp-winnow) is an embedded TypeScript SDK that lets an agent use many MCP servers without context bloat — it winnows away both tool-definition bloat and tool-result bloat, and works the same attended or headless.

Full design + decision log: docs/DESIGN.md. Validation numbers: bench/RESULTS.md (reference surface) and bench/REAL-RESULTS.md (real MCP servers over stdio); reproduce the real hosted MCPs over HTTP with bench/README.md (npm run bench:public).

The idea

  • Definition bloat → the model sees ~4 meta-tools (search_tools, load_tool, call_tool, run_code) instead of hundreds of full schemas. Full defs stay inside the SDK; the agent searches, loads only what it needs, then calls.
  • Result bloat → every result is trimmed by a JMESPath projection and a hard token cap the agent can only lower — so even a forgotten projection can't leak an unbounded blob.

Measured on a representative surface: ~10× (call) / 26× (exec) end-to-end token reduction; hybrid search 100% recall@8. Against real servers (server-everything + server-filesystem, 27 tools): 90.4% fewer tool-definition tokens, 60–80% off real tool results via the cap — see bench/REAL-RESULTS.md (cd bench && npm i && npm run bench:real). And against real public/hosted MCPs over HTTP — DeepWiki, GitMCP, plus GitHub's hosted MCP folded in: transport + search + auth validated end to end, and ~75% off a real remote doc payload via the cap. In one gh-authed example run (numbers vary with the token's visible toolset and the network), the GitHub catalog pushed the surface to 52 tools across 3 servers for a 97.7% tool-definition reduction (11,137 → 259). Reproduce with npm run bench:public — it folds in GitHub via a gh login or a PAT; see bench/README.md.

Quickstart

npm install
npm run demo        # end-to-end: search -> loadTool -> call, with token savings
npm test            # core unit + integration tests
npm run typecheck
import { Winnow } from "mcp-winnow";

const client = new Winnow({ upstreams: [/* your MCP server connections */] });
await client.init();

const hits = await client.searchTools("list open pull requests"); // minimal entries + score
const [def] = client.loadTool(hits[0].id);                          // full schema on demand
const res  = await client.call(hits[0].id, { state: "open" }, {
  project: "[].{number: number, title: title}",                     // trim before it hits context
});

Documentation

  • Usage guide — task recipes: connecting servers, auth, result-filter projections, exec, agent integration, the gateway, cache & watch, troubleshooting.
  • API reference — the Winnow class, options, and types.
  • Config reference — the full winnow.config.json schema.
  • Examples — what each runnable example demonstrates.
  • Design spec — architecture and the why behind every decision.

Status

Area State
Catalog / progressive disclosure (C1) ✅ implemented
Hybrid search: Orama BM25 + optional embedder + RRF (S1) ✅ implemented
Result-filter: JMESPath + hard cap + base64 stubbing (F1) ✅ implemented
Config + ${ENV} interpolation, zod fail-fast (G1) ✅ implemented
Public Winnow facade + 4 meta-tool adapter (A2) ✅ implemented
Pluggable upstream + in-memory mock ✅ implemented
Real stdio transport (buildUpstreams) ✅ implemented — verified against the reference server-everything (see examples/real-stdio.ts)
Real Streamable-HTTP transport + bearer auth ✅ implemented + verified live against a local server, incl. 401 on bad token (examples/real-http.ts)
Winnow.fromConfig() ✅ implemented
Code-exec sandbox: sync QuickJS-WASM in a worker + Atomics bridge (X1) ✅ implemented — npx tsx examples/exec-demo.ts (30 fat PRs → 117 tok, 74×)
Persistent catalog cache: disk-keyed by upstream identity, zero-connection warm start, refresh() (P1) ✅ implemented (cache/cacheDir/cacheTtlMs; default on)
Live tools/list_changed watch mode: auto-refresh catalog on a server change (P6) ✅ implemented (opt-in watch: true)
HTTP auth: static bearer / pre-provisioned OAuth / client_credentials grant (P2) ✅ implemented — all browserless, live-verified (test/http-auth.test.ts)
Gateway: run Winnow as an MCP server, stdio + HTTP (P4) ✅ implemented — npx tsx examples/gateway-demo.ts (host → gateway → real upstream)
Packaged for publish: dist build, types, mcp-winnow bin (P3) ✅ npm run build; verified via npm pack → clean install → bin runs

Every part of the spec is implemented, plus the gateway that makes it installable into any MCP host, packaged so npx -y mcp-winnow works.

Install into any MCP host (gateway)

Winnow can run as an MCP server exposing just the 4 meta-tools — so a host connects to ONE server and sees FOUR tools while Winnow hides N upstream servers behind search/load/call/run_code. run_code runs server-side in Winnow's sandbox, so hosts that can't import TS still get the full composition win.

Winnow speaks standard MCP over stdio (the host spawns mcp-winnow gateway) or Streamable HTTP (--http, for hosts/remotes that connect to a URL). winnow.config.json lists the upstream servers to aggregate (same schema as Winnow.fromConfig). Build the bin with npm run build, or run from source: npx tsx src/gateway/cli.ts --config winnow.config.json. Copy-paste the snippet for your agent:

Claude Desktop / Cursor — stdio

// claude_desktop_config.json  or  .cursor/mcp.json
"mcpServers": {
  "winnow": { "command": "npx", "args": ["-y", "mcp-winnow", "gateway", "--config", "winnow.config.json"] }
}

Claude Code — plugin

Winnow ships as a Claude Code plugin (plugin/, listed in .claude-plugin/marketplace.json):

/plugin marketplace add Cambrionic/winnow
/plugin install winnow@winnow

Then drop a winnow.config.json in your project root. (Requires mcp-winnow on npm, or a local npm link — see plugin/README.md.)

Codex CLI — stdio

~/.codex/config.toml (or project-scoped .codex/config.toml):

[mcp_servers.winnow]
command = "npx"
args = ["-y", "mcp-winnow", "gateway", "--config", "winnow.config.json"]

Or one-shot: codex mcp add winnow -- npx -y mcp-winnow gateway --config winnow.config.json. For a remote/hosted gateway, swap command/args for url = "https://…" + bearer_token_env_var = "WINNOW_TOKEN".

Pi — Streamable HTTP

Pi connects to MCP over HTTP, so run the gateway with --http and register the URL in Pi (via its /mcp extension; stored in ~/.pi/agent/mcp/servers.json):

# 1. start the gateway over HTTP (bearer optional via WINNOW_GATEWAY_TOKEN)
npx -y mcp-winnow gateway --config winnow.config.json --http --port 8080
# 2. in Pi:
/mcp add winnow http://localhost:8080

With a bearer: set WINNOW_GATEWAY_TOKEN on the gateway, then /mcp add winnow http://localhost:8080 Authorization=Bearer <token> in Pi.

Layout

src/        SDK: client, catalog, search, filter, config, adapter, sandbox, upstream/, gateway/
examples/   runnable demos + mock servers (see examples/README.md)
test/       unit + integration tests (offline)
bench/      validation benchmarks (token reduction + search recall)
docs/       USAGE, API, CONFIG guides + DESIGN.md (the build-ready spec)
plugin/     the Claude Code plugin package
wayfinder/  the decision map this project was designed through

Contributing

Contributions are welcome! See CONTRIBUTING.md for dev setup and workflow, and please follow the Code of Conduct. New to the code? docs/DESIGN.md explains every decision, and wayfinder/ records how they were reached.

  • 🐛 Bugs & ✨ features: open an issue
  • 🔒 Security: see SECURITY.md — please report privately

License

MIT © Cambrionic — see LICENSE.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured