npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

toolmux

v0.1.0

Published

Smart MCP proxy with code execution — connect N MCP servers, expose 2 tools (execute + search). O(1) token cost.

Readme

toolmux

Smart MCP proxy with code execution. Connect N MCP servers, expose one endpoint with 2 tools.

Instead of dumping 50+ tool definitions into your agent's context window, toolmux gives the agent 2 tools — execute and search — and lets it write code against a typed tool surface.

The problem

When you connect multiple MCP servers to an agent, every tool gets injected into the context window. 5 servers with 20 tools each = 100 tool definitions the model has to parse on every turn. This wastes tokens, degrades tool selection accuracy, and hits context limits fast.

Even worse: when an agent needs to chain 5 API calls, each call round-trips through the LLM. The model reads the result, decides the next call, outputs it, reads that result... burning tokens and time on what should be a simple script.

How toolmux solves it

Agent ←→ toolmux (2 tools) ←→ GitHub MCP (28 tools)
                              ←→ Slack MCP (15 tools)
                              ←→ Filesystem MCP (14 tools)
                              ←→ Linear MCP (22 tools)

The agent sees 2 tools instead of 79. The token cost is O(1) — it stays the same regardless of how many upstream tools exist:

Scenario                      Direct      Toolmux    Reduction
1 server × 14 tools             2229        1474       34%
3 servers × 45 tools            8859        1474       83%
5 servers × 100 tools          19125        1474       92%
10 servers × 200 tools         37925        1474       96%

How the agent uses it

The agent writes JavaScript that calls tools.* directly. Multiple calls execute in one shot — no round trips through the LLM between each call:

// Agent writes this, toolmux executes it in a V8 sandbox
const repos = await tools.github__list_repos({ owner: "octocat" });
const issues = await Promise.all(
  repos.slice(0, 3).map(r =>
    tools.github__list_issues({ owner: r.owner, repo: r.name })
  )
);
return { repoCount: repos.length, issues };

The execute tool's description includes auto-generated TypeScript type declarations for every connected tool, so the LLM knows exactly what arguments to pass.

When the agent doesn't know what tools are available, it uses search:

search({ query: "create github issue", include_schema: true })

Quick start

Install from npm

npm install -g toolmux

Or use directly with npx:

npx toolmux --help

Create a config

cat > toolmux.json << 'EOF'
{
  "servers": [
    {
      "name": "filesystem",
      "transport": {
        "type": "stdio",
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/projects"]
      }
    }
  ]
}
EOF

From source

git clone https://github.com/tylergibbs1/toolmux
cd toolmux
bun install
bun run src/cli.ts --help

Add to your agent

Claude Code

claude mcp add toolmux -- npx toolmux

If your toolmux.json is in a specific directory:

claude mcp add toolmux -- npx toolmux --config /path/to/toolmux.json

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "toolmux": {
      "command": "npx",
      "args": ["toolmux"]
    }
  }
}

Cursor

Add to .cursor/mcp.json:

{
  "mcpServers": {
    "toolmux": {
      "command": "npx",
      "args": ["toolmux"]
    }
  }
}

Config

toolmux looks for config in this order:

  1. --config <path> or positional arg
  2. ./toolmux.json
  3. ./.toolmux.json
  4. ~/.config/toolmux/config.json
  5. ~/toolmux.json

Config format

{
  "servers": [
    {
      "name": "github",
      "transport": {
        "type": "stdio",
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-github"],
        "env": {
          "GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_TOKEN"
        }
      }
    },
    {
      "name": "slack",
      "transport": {
        "type": "http",
        "url": "https://mcp.slack.com/sse",
        "headers": {
          "Authorization": "Bearer $SLACK_TOKEN"
        }
      }
    },
    {
      "name": "old-server",
      "transport": {
        "type": "sse",
        "url": "https://legacy.example.com/mcp/sse"
      },
      "disabled": true
    }
  ]
}

Transport types

| Type | Use case | |------|----------| | stdio | Local MCP servers (most common). Spawns a process. | | http | Remote MCP servers using Streamable HTTP transport. | | sse | Legacy MCP servers using SSE transport. |

Environment variables

All string values in config support $VAR and ${VAR} expansion:

{
  "env": { "API_KEY": "$MY_API_KEY" }
}

Tools exposed to the agent

execute

Write and run JavaScript in a sandboxed V8 context. Call any connected tool via await tools.qualified_name(args). Auto-generated TypeScript type declarations are included in the tool description so the LLM knows exact signatures.

const weather = await tools.weather__get_current({ location: "Austin, TX" });
if (weather.temperature > 90) {
  await tools.slack__post_message({ channel: "#team", text: "It's hot in Austin!" });
}
return weather;

The sandbox:

  • Runs in a forked Node.js process with vm.createContext (V8 context isolation)
  • No access to fetch, require, process, fs, or the network
  • Only tools.* calls can reach external systems
  • 30 second timeout
  • console.log() output is captured and returned

search

Find available tools by intent. Use before execute when you don't know the tool name or need its input schema.

search({ query: "send a message" })                    // concise list
search({ query: "github issues", include_schema: true }) // with full JSON schemas
search({ query: "" })                                   // list all tools

The include_schema flag lets the agent get tool names + full input schemas in one round trip (replaces the old discover → describe two-step).

How tool names work

Each tool gets a qualified name: {server}__{original_name}.

  • Server name is lowercased and sanitized (non-alphanumeric → _)
  • Original tool name is preserved as-is
  • Example: server "GitHub" + tool "create_issue"github__create_issue

Architecture

┌─────────┐     stdio      ┌──────────┐     stdio/http/sse     ┌───────────┐
│  Agent   │◄──────────────►│ toolmux  │◄──────────────────────►│ MCP Srv 1 │
│ (Claude, │                │          │◄──────────────────────►│ MCP Srv 2 │
│  Cursor) │  2 meta-tools  │  Pool +  │◄──────────────────────►│ MCP Srv 3 │
│          │                │  Index   │    N upstream servers   │    ...    │
└─────────┘                 │  V8 VM   │                        └───────────┘
                            └──────────┘

When the agent uses execute:

1. Agent writes code using tools.* calls
2. toolmux spawns a forked process with a V8 sandbox
3. Code runs — tools.* calls are proxied via IPC to the parent
4. Parent dispatches each call to the correct upstream MCP server
5. Results flow back through IPC → sandbox continues execution
6. Final result + console logs returned to agent
  • No daemon — single process, starts and stops with the agent
  • No database — tool index lives in memory
  • No auth layer — credentials pass through to upstream servers via config
  • V8 isolation — sandboxed code can't access filesystem, network, or process

Examples

# Run the agent test (Claude + toolmux + filesystem MCP)
ANTHROPIC_API_KEY=sk-... npx tsx examples/agent-test.ts

# Force code execution mode
ANTHROPIC_API_KEY=sk-... npx tsx examples/agent-test.ts --execute

# Token efficiency benchmark
ANTHROPIC_API_KEY=sk-... npx tsx examples/token-benchmark.ts

Inspired by

License

MIT