Skip to main content
Glama
mcp-tool-search

mcp-tool-search

by ericnunes30
README.md
# mcp-tool-search

A standalone MCP (Model Context Protocol) gateway/proxy server that lets any MCP-compatible client (Cursor, Claude Code, Codex, VSCode, Windsurf, Claude Desktop, Pi) search and call tools across multiple downstream MCP servers through a single `mcp` proxy tool.

## Features

- Single `mcp` proxy tool (~200 tokens) for on-demand tool search/describe/call
- Connects to multiple downstream MCP servers (stdio, HTTP, SSE, StreamableHTTP)
- Direct tool mode: expose individual MCP tools as native tools
- Metadata caching with content-based invalidation
- OAuth authentication support (authorization_code + client_credentials)
- npx resolution (skip ~143MB npm parent process)
- Output guarding (50KiB / 2000 line truncation)
- Config compatible with standard MCP `mcpServers` format
- stdio transport only (all clients supported)
- Windows installer, standalone `.exe` (no Node.js), and portable zip
- Builds with [tsup](https://github.com/egoist/tsup) (esbuild) — keeps `.ts` import extensions in source, compiles to single-file `.js` bundle

## Quick Start

### Installation

**Windows, no Node.js (recommended)** — standalone executable from GitHub Releases:

```powershell
irm https://github.com/ericnunes30/mcp-gateway-proxy/releases/latest/download/install.ps1 | iex
```

This installs to `%LOCALAPPDATA%\mcp-tool-search\`, creates
`%USERPROFILE%\.config\mcp-tool-search\mcp.json` if it does not exist, and prints ready-to-paste
client snippets. It does **not** modify existing MCP client configs.

**Portable zip, Node.js >= 20** — download `mcp-tool-search.zip` from
[Releases](https://github.com/ericnunes30/mcp-gateway-proxy/releases) and run the setup once:

```bash
node dist/cli.cjs setup
```

**From source**:

```bash
git clone https://github.com/ericnunes30/mcp-gateway-proxy.git
cd mcp-gateway-proxy
npm install
npm run build      # dist/cli.js (needs node_modules)
npm run dist:win   # release/ (standalone exe + zip + install.ps1)
```

> The `mcp-tool-search` package on npm is a different project. Install this repository from
> Releases or build it from source.

See [INSTALL.md](INSTALL.md) for config locations, client snippets, update and uninstall.

### Configuration

Create `~/.config/mcp-tool-search/mcp.json` (the tool default; `setup` creates it for you):

```json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]
    },
    "github": {
      "url": "https://api.github.com/mcp",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}
```

`--mcp-config <path>` overrides this default. Multiple sources are merged — see
[INSTALL.md](INSTALL.md#configuration-file).

### CLI Usage

```powershell
# Standalone executable (installed by setup; not added to PATH)
& "$env:LOCALAPPDATA\mcp-tool-search\mcp-tool-search.exe" --version
& "$env:LOCALAPPDATA\mcp-tool-search\mcp-tool-search.exe" setup --print
& "$env:LOCALAPPDATA\mcp-tool-search\mcp-tool-search.exe" --mcp-config "$env:USERPROFILE\.config\mcp-tool-search\mcp.json"
```

```bash
# From source
node dist/cli.js --mcp-config ~/.config/mcp-tool-search/mcp.json

# From the zip bundle
node dist/cli.cjs --mcp-config ~/.config/mcp-tool-search/mcp.json

# --config also works as an alias
node dist/cli.js --config ~/.config/mcp-tool-search/mcp.json
```

### Update / Uninstall

```powershell
# update: re-run the installer (replaces the exe, keeps the config)
irm https://github.com/ericnunes30/mcp-gateway-proxy/releases/latest/download/install.ps1 | iex

# uninstall (keeps ~/.config/mcp-tool-search)
& "$env:LOCALAPPDATA\mcp-tool-search\mcp-tool-search.exe" uninstall

# uninstall and remove the config directory too
& "$env:LOCALAPPDATA\mcp-tool-search\mcp-tool-search.exe" uninstall --purge
```

### Client Configuration

`setup` installs the executable at
`%LOCALAPPDATA%\mcp-tool-search\mcp-tool-search.exe` and creates
`%USERPROFILE%\.config\mcp-tool-search\mcp.json`. It prints a ready-to-paste block for the clients
below (`setup --print` shows them again without changing anything).

Replace `C:\Users\YOU` with your user folder. Restart the client (or start a new session) after
editing.

#### Claude Code — `%USERPROFILE%\.claude.json`

Add under `"mcpServers"`:

```json
{
  "mcpServers": {
    "mcp-tool-search": {
      "type": "stdio",
      "command": "C:\\Users\\YOU\\AppData\\Local\\mcp-tool-search\\mcp-tool-search.exe",
      "args": ["--mcp-config", "C:\\Users\\YOU\\.config\\mcp-tool-search\\mcp.json"]
    }
  }
}
```

#### Claude Desktop — `%APPDATA%\Claude\claude_desktop_config.json`

Same `mcpServers` block as Claude Code.

#### Cursor — `%USERPROFILE%\.cursor\mcp.json`

Same `mcpServers` block as Claude Code.

#### OpenCode — `%USERPROFILE%\.config\opencode\opencode.json`

OpenCode uses `"mcp"` (not `"mcpServers"`) and `"type": "local"`:

```json
{
  "mcp": {
    "mcp-tool-search": {
      "type": "local",
      "command": [
        "C:\\Users\\YOU\\AppData\\Local\\mcp-tool-search\\mcp-tool-search.exe",
        "--mcp-config",
        "C:\\Users\\YOU\\.config\\mcp-tool-search\\mcp.json"
      ],
      "enabled": true
    }
  }
}
```

#### Codex — `%USERPROFILE%\.codex\config.toml`

```toml
[mcp_servers.mcp-tool-search]
command = "C:/Users/YOU/AppData/Local/mcp-tool-search/mcp-tool-search.exe"
args = [ "--mcp-config", "C:/Users/YOU/.config/mcp-tool-search/mcp.json" ]
```

Forward slashes work in the TOML variant.

#### Pi (with [pi-mcp-extension](https://github.com/irahardianto/pi-mcp-extension)) — `%USERPROFILE%\.pi\agent\mcp.json`

Same `mcpServers` block as Claude Code.

#### Portable zip / Node.js variant

For the zip bundle, replace the `command`/`args` with Node.js:

```json
{
  "command": "node",
  "args": [
    "C:\\path\\to\\mcp-tool-search\\dist\\cli.cjs",
    "--mcp-config",
    "C:\\Users\\YOU\\.config\\mcp-tool-search\\mcp.json"
  ]
}
```

The proxy connects to all your downstream MCP servers. The client only needs to know about the
proxy — no duplication of tools.

```
Client → mcp-tool-search (proxy) → N downstream servers
```

## Proxy Tool Usage

The `mcp` tool accepts these parameters:

| Parameter | Type | Description |
|-----------|------|-------------|
| `tool` | string | Tool name to call |
| `args` | string | Arguments as JSON string |
| `connect` | string | Server name to connect |
| `describe` | string | Tool name to describe |
| `search` | string | Search query |
| `regex` | boolean | Treat search as regex |
| `includeSchemas` | boolean | Include schemas in search |
| `server` | string | Filter to server |
| `action` | string | "ui-messages", "auth-start", "auth-complete" |

### Examples

```
mcp({})                                              → Show server status
mcp({ server: "filesystem" })                        → List tools from server
mcp({ search: "file" })                              → Search tools
mcp({ describe: "read_file" })                        → Show tool details
mcp({ connect: "github" })                            → Connect to server
mcp({ tool: "read_file", args: '{"path":"/foo"}' })   → Call a tool
mcp({ action: "auth-start", server: "linear" })      → Start OAuth
mcp({ action: "auth-complete", server: "linear", args: '{"redirectUrl":"..."}' }) → Complete OAuth
```

### Example Output

```
mcp({}) →
MCP: 2/5 servers, 38 tools

✓ chrome-devtools (29 tools)
✓ pencil (9 tools)
○ stitch (not connected)
○ tavily (not connected)
○ computer-use (not connected)
```

```
mcp({ search: "screenshot" }) →
Found 3 tools matching "screenshot":

chrome_devtools_take_screenshot
  Take a screenshot of the page or element.
  Parameters: format, quality, uid, fullPage, filePath

pencil_get_screenshot
  Returns a screenshot of a node in a .pen file.
  Parameters: filePath, nodeId
```

## Configuration Reference

### Server Entry

| Field | Type | Description |
|-------|------|-------------|
| `command` | string | Command to run (stdio) |
| `args` | string[] | Arguments for command |
| `env` | Record<string,string> | Environment variables |
| `url` | string | HTTP/SSE/StreamableHTTP URL |
| `headers` | Record<string,string> | HTTP headers |
| `transport` | "stdio" \| "http" \| "sse" \| "streamable-http" | Transport type |
| `type` | "http" | Alias for transport (standard MCP compatibility) |
| `oauth` | object | OAuth configuration |
| `lifecycle` | "lazy" \| "eager" \| "keep-alive" | Connection mode |
| `idleTimeout` | number | Idle timeout (seconds) |
| `directTools` | boolean \| string[] | Expose as direct tools |
| `excludeTools` | string[] | Tools to exclude |
| `exposeResources` | boolean | Expose resources as tools |
| `toolPrefix` | "server" \| "short" \| "none" | Tool name prefix |
| `requestTimeoutMs` | number | Request timeout |
| `autoAuth` | boolean | Auto-start OAuth |
| `bearerToken` | string | Bearer token |
| `bearerTokenEnv` | string | Env var name for bearer token |

### Settings (top-level)

| Field | Type | Description |
|-------|------|-------------|
| `toolPrefix` | "server" \| "short" \| "none" | Global tool prefix |
| `directTools` | boolean \| string[] | Global direct tools |
| `autoAuth` | boolean | Global auto-auth |
| `idleTimeout` | number | Global idle timeout |
| `requestTimeoutMs` | number | Global request timeout |
| `disableProxyTool` | boolean | Disable proxy tool |
| `authRequiredMessage` | string | Custom auth message |

### Environment Variables

| Variable | Description |
|----------|-------------|
| `MCP_TOOL_SEARCH_DEBUG` | Enable debug logging to stderr |
| `MCP_TOOL_SEARCH_DATA_DIR` | Data directory override (default: `~/.config/mcp-tool-search/`) |
| `MCP_DIRECT_TOOLS` | Direct tools filter (comma-separated, `server/tool` or `server`) |
| `MCP_TOOL_SEARCH_NPX_CACHE_TTL` | npx cache TTL in ms (default: 24h) |

## Architecture

```
┌──────────────────┐     stdio      ┌──────────────────┐
│  MCP Client      │◄──────────────►│  mcp-tool-search │
│  (Cursor, etc.)  │   JSON-RPC     │   (this server)  │
└──────────────────┘                └────────┬─────────┘
                                               │
                    ┌──────────────────────────┼──────────────────────────┐
                    │                          │                          │
              ┌─────┴─────┐            ┌──────┴──────┐           ┌──────┴──────┐
              │  stdio    │            │   HTTP/SSE  │           │ Streamable  │
              │  server   │            │   server    │           │   HTTP      │
              └───────────┘            └─────────────┘           └─────────────┘
```

## Development

```bash
npm install       # Install dependencies
npm run build     # Build with tsup → dist/cli.js
npm run typecheck # Type-check with tsc (no emit)
npm test          # Run vitest test suite (89 tests)
npm run dist:win  # Build release/ (standalone exe, zip, install.ps1, checksums)
```

### Build System

The project uses [tsup](https://github.com/egoist/tsup) (powered by esbuild) for compilation:

- **Source code** uses `.ts` import extensions (e.g., `import { foo } from "./types.ts"`)
- **tsconfig.json** has `noEmit: true` for type-checking only
- **tsup** bundles everything into a single `dist/cli.js` file (~150KB)
- Dependencies (`@modelcontextprotocol/sdk`, `open`, `recheck`, `typebox`, `zod`) are externalized in the normal build
- **`tsup.sea.config.ts`** produces a self-contained CommonJS bundle (`dist-sea/cli.cjs`, no
  `node_modules`) used by the portable zip and the Windows executable
- **`scripts/build-release.mjs`** wraps the SEA bundle into a Node single executable
  (`node --experimental-sea-config` + `postject`) and assembles `release/`

### Release

Push a `v*` tag to run `.github/workflows/release.yml`, which builds and attaches to the GitHub
release:

| Asset | Description |
|---|---|
| `mcp-tool-search-windows-amd64.exe` | Standalone executable (Node SEA, no Node.js required) |
| `mcp-tool-search.zip` | Self-contained CJS bundle + launcher (Node.js >= 20) |
| `install.ps1` | One-liner installer used by `irm ... \| iex` |
| `SHA256SUMS.txt` | Checksums for the artifacts |

### Project Structure

```
src/
├── cli.ts                    # CLI entry point (server + setup/uninstall commands)
├── server.ts                 # MCP server setup + stdio transport
├── state.ts                  # Gateway state factory
├── version.ts                # Runtime version
├── install/
│   └── setup.ts              # Installer commands (setup/uninstall, client snippets)
├── config/
│   ├── types.ts              # Config types (McpConfig, ServerEntry, etc.)
│   ├── config.ts             # Config loader + merge logic
│   ├── imports.ts            # Config imports/expand logic
│   └── paths.ts              # Config path resolution
├── cache/
│   ├── metadata-cache.ts     # Metadata cache (hash-based invalidation)
│   └── tool-metadata.ts      # Tool metadata builder + search
├── auth/
│   ├── auth-store.ts         # Token storage (secure file permissions)
│   ├── oauth-provider.ts     # OAuth client provider
│   ├── oauth-flow.ts         # OAuth flow (start, complete, authenticate)
│   └── callback-server.ts    # OAuth callback HTTP server
├── mcp/
│   ├── server-manager.ts     # MCP server connection manager
│   ├── transport.ts          # Transport creation (stdio/HTTP/SSE/StreamableHTTP)
│   └── npx-resolver.ts       # npx package resolution (skip npm parent)
├── lifecycle/
│   ├── lifecycle.ts          # Lifecycle manager (lazy/eager/keep-alive)
│   └── lazy-connect.ts       # Lazy connect with 60s failure backoff
├── guard/
│   └── output-guard.ts       # Output truncation (50KiB / 2000 lines)
├── tools/
│   ├── proxy.ts              # Proxy tool definition + dispatch
│   ├── proxy-actions.ts      # Proxy action handlers (search/describe/call/etc.)
│   ├── direct-tools.ts       # Direct tool resolution + executor
│   └── tool-registrar.ts     # MCP content transformation
├── handlers/
│   ├── list-tools.ts         # ListToolsRequest handler
│   └── call-tool.ts          # CallToolRequest handler
└── utils/
    ├── abort.ts              # AbortSignal utilities
    ├── logger.ts             # stderr-only logger
    ├── env.ts                # Environment variable interpolation
    └── utils.ts              # General utilities (parallelLimit, etc.)
```

## License

MIT