Smart Proxy
No description available
Ask AI about Smart Proxy
Powered by Claude · Grounded in docs
I know everything about Smart Proxy. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
MSP (MCP Smart Proxy)
msp is a small Rust CLI that lets an AI work with many MCP servers through one proxy server.
Instead of exposing every downstream MCP tool directly, msp exposes a small built-in proxy toolset. This keeps the upstream tool list small, reduces prompt noise, and avoids wasting tokens on tools the agent will never use.
The installed binary name is msp. Running msp without arguments shows the top-level help.
Why use it
- Reduce the number of tools your agent sees (reduce the token cost without losing any tool).
- Cache downstream MCP tool metadata and summaries.
- Proxy both local stdio MCP servers and remote Streamable HTTP MCP servers.
- Reuse your existing Codex, OpenCode, Claude Code, or GitHub Copilot CLI MCP setup instead of rebuilding everything from scratch.
How it works
msp does three things:
- Connects to each configured MCP server and caches its tool metadata.
- Generates a short summary for each server by using a configured provider:
codex,opencode, orclaude, or uses a manually configured server description. - Starts a stdio MCP proxy that exposes these proxy tools:
activate_additional_mcpsactivate_tools_in_additional_mcpcall_tool_in_additional_mcpeval_lua_script
Agents first inspect the cached server index, optionally inspect one tool definition, and then call the downstream tool through the proxy.
The built-in eval_lua_script tool runs Lua 5.5 scripts inside the proxy through mlua. Scripts can call any configured downstream MCP tool through call_mcp_tool(mcp_name, tool_name, args), where args is a Lua table that maps to a JSON object.
When a host starts msp mcp, msp auto-starts one background daemon for that config file. That daemon owns downstream MCP communication and periodic self-update checks. Later msp mcp processes that use the same config reuse the same Unix socket daemon, even when they pass different --provider values. The daemon exits after 1 hour with no requests.
The default daemon socket lives under ~/.cache/mcp-smart-proxy/ and uses a short hash of the config path so it stays within Unix socket path limits on macOS and Linux.
Requirements
curlorwget, plustar, for installation- The
codexCLI when using--provider codex - The
opencodeCLI when using--provider opencode - The
claudeCLI when using--provider claude - The
copilotCLI when using--provider copilot - A browser session for remote MCP servers that require OAuth login
Install
Install the latest release:
curl -fsSL https://raw.githubusercontent.com/cybershape/mcp-smart-proxy/master/install.sh | bash
Install to a custom directory:
curl -fsSL https://raw.githubusercontent.com/cybershape/mcp-smart-proxy/master/install.sh | INSTALL_DIR=/tmp/msp/bin bash
Install a specific version:
curl -fsSL https://raw.githubusercontent.com/cybershape/mcp-smart-proxy/master/install.sh | VERSION=v0.0.19 bash
By default the installer writes msp to:
- macOS on Apple Silicon:
/opt/homebrew/bin - macOS on Intel:
/usr/local/bin - Linux as root:
/usr/local/bin - Linux as a regular user:
~/.local/bin
After installation:
msp
Quick Start
Fastest path for Codex users
Import your existing Codex MCP servers into msp, replace Codex's MCP entries with the proxy, and keep a backup:
msp import codex --replace
If you want to restore the original Codex MCP servers later:
msp restore codex
To add a new server after that:
msp add --provider codex github npx -y @modelcontextprotocol/server-github
To add a new server with a manual summary and no AI provider:
msp add --description "Use this for GitHub workflows." github npx -y @modelcontextprotocol/server-github
To add a remote server that needs headers up front:
msp add --provider codex --url https://example.com/mcp --header Authorization='Bearer ${DEMO_TOKEN}' remote-demo
Fastest path for OpenCode, Claude Code, or GitHub Copilot CLI
Import existing servers:
msp import opencode
msp import claude
msp import copilot
Install the proxy into the host:
msp install opencode
msp install claude
msp install copilot
Install the bundled pi extension globally:
msp install pi
Replace existing host MCP entries and keep a backup:
msp install opencode --replace
msp install claude --replace
msp install copilot --replace
Restore the original host config later if needed:
msp restore opencode
msp restore claude
msp restore copilot
Remove the global pi extension later if needed:
msp restore pi
Start from scratch
Add a server:
msp add --provider codex github npx -y @modelcontextprotocol/server-github
Install msp into your host:
msp install codex
From that point, the host launches msp mcp as its MCP server entrypoint. Pass --provider <provider> when you want msp to regenerate server summaries with that provider.
The first launch starts the shared daemon automatically. Later launches for the same config reuse it.
You can inspect or control that shared process directly:
msp daemon status
msp daemon stop
msp daemon restart
Common Tasks
Add a server
msp add --provider codex github npx -y @modelcontextprotocol/server-github
Or skip AI summarization and persist a manual description instead:
msp add --description "Use this for GitHub workflows." github npx -y @modelcontextprotocol/server-github
If the command is a single http:// or https:// URL, msp stores it as a native remote server:
msp add --provider codex remote-demo https://example.com/mcp
If the server needs headers, env values, forwarded env vars, or an initial enabled state, pass them during add so the first cache refresh uses the final connection settings:
msp add --provider codex --url https://example.com/mcp --header Authorization='Bearer ${DEMO_TOKEN}' --env DEMO_REGION=global --env-var DEMO_TOKEN --enabled false remote-demo
add requires either --provider or --description. With --provider, msp summarizes the fetched tools immediately. With --description, msp stores that text in the local config and uses it as the cached server summary without calling any AI provider. If both are passed, the manual description wins. The command succeeds only when both config persistence and the initial cache refresh succeed; if cache generation fails, msp rolls back the new server entry instead of leaving partial config behind. When you use config flags with add, place them before the server name so the trailing command remains untouched. You can still refresh later with msp reload, or let the shared msp mcp daemon refresh enabled servers in the background after startup.
List servers
msp list
msp list shows each configured server, whether it is enabled, and when its cache was last refreshed.
Enable or disable a server
msp disable github
msp enable github
Disabled servers stay in the config and keep their cache files, but bulk reload and daemon-managed mcp startup skip them.
Show or update one server
Show current config:
msp config github
Update a stdio server:
msp config github --cmd uvx --clear-args --arg demo-server --env DEMO_REGION=global --env-var DEMO_TOKEN --enabled false
Update a remote server:
msp config remote-demo --url https://example.com/mcp --clear-headers --header Authorization='Bearer ${DEMO_TOKEN}'
Forward one shell variable into a remote server config:
msp config remote-demo --env-var DEMO_TOKEN
msp config can update transport, enabled, command or URL fields, headers, static env values, and forwarded env var names.
Reload cached tools
Reload one server:
msp reload --provider codex github
Reload every enabled server:
msp reload --provider codex
reload fetches the downstream tool list, compares it to the cache, and only regenerates the summary when the tool list changed. If you omit --provider, reload still updates tools and keeps the existing cached summary unless the server has a manual description in config.
Run Lua automation through the proxy
The built-in eval_lua_script MCP tool is useful when the host wants lightweight programmable orchestration without exposing every downstream tool directly in the prompt.
Example Lua script:
local docs = call_mcp_tool("context7", "query-docs", {
libraryId = "/websites/rs_crate_mlua",
query = "create_async_function examples",
})
return {
isError = docs.isError,
structured = docs.structuredContent,
}
eval_lua_script returns structured JSON. A script that returns no values produces null, one value produces that value, and multiple return values are encoded as a JSON array.
Log in or out of a remote server
Start OAuth login:
msp login remote-demo
Clear cached OAuth credentials:
msp logout remote-demo
OAuth metadata is discovered from the remote MCP server at runtime. Credentials are cached under ~/.cache/mcp-smart-proxy/oauth/.
Unsupported Figma remote MCP server
msp does not support Figma's hosted MCP endpoint at https://mcp.figma.com/mcp.
The proxy rejects that URL during msp add ..., msp config --url, and local config load with a clear error instead of letting setup continue into a broken OAuth flow.
During msp import ... and msp install ... --replace, Figma hosted MCP entries are skipped for import and left in the original host config instead of being deleted.
msp add --provider codex figma https://mcp.figma.com/mcp
Expected result:
server `figma` uses unsupported remote MCP URL `https://mcp.figma.com/mcp`; msp does not support Figma's hosted MCP endpoint
Remove a server
msp remove github
This removes the server from the config and deletes its cached tool file if one exists.
Update msp itself
msp update
This checks GitHub Releases, downloads the newest build for the current platform, and replaces the current executable in place when a newer release exists.
When the daemon is running, it is also responsible for periodic background self-update checks for msp mcp traffic.
Manage the shared daemon
Check whether the daemon for the current config is running:
msp daemon status
Stop the daemon and remove its socket and pid state:
msp daemon stop
Restart the daemon for the current config:
msp daemon restart
All three commands also accept --socket <path> when you need to target a custom daemon socket.
Keep custom socket paths short enough for Unix domain socket limits.
The daemon writes a runtime log next to its socket, for example ~/.cache/mcp-smart-proxy/msp-<scope>.sock.log.
Detached daemon startup also writes ~/.cache/mcp-smart-proxy/msp-<scope>.sock.startup.log until the new daemon answers a status probe. If startup fails or the new daemon becomes unresponsive before it can serve status, that startup log is kept for diagnosis.
If the daemon socket accepts a connection but never replies, msp daemon status, stop, and restart fail quickly instead of hanging indefinitely. stop and restart also fall back to force-stopping the unresponsive daemon by pid-file state so the socket can recover without manual cleanup.
Concurrent daemon refresh requests for the same provider are coalesced into one shared reload. Slow cache-lock waits, MCP tool discovery, and summary subprocesses also fail with timeouts instead of blocking the daemon forever.
Install Into a Host
Install the proxy into Codex, OpenCode, Claude Code, or GitHub Copilot CLI:
msp install codex
msp install opencode
msp install claude
msp install copilot
Install the bundled global pi extension:
msp install pi
This writes the embedded extension source to ~/.pi/agent/extensions/msp.ts without leaving a sibling lock file in that directory.
With --replace, msp first imports the host's current MCP servers into msp, backs them up, removes them from the host config, and then installs the proxy:
msp install codex --replace
msp install opencode --replace
msp install claude --replace
msp install copilot --replace
Backup files:
- Codex:
$CODEX_HOME/config.msp-backup.tomlor~/.codex/config.msp-backup.toml - OpenCode:
~/.config/opencode/opencode.msp-backup.json - Claude Code:
~/.claude.msp-backup.json
Restore host config from backup:
msp restore codex
msp restore opencode
msp restore claude
msp restore copilot
Remove the bundled global pi extension:
msp restore pi
Import Existing Servers
msp can import MCP servers from:
- Codex:
msp import codex - OpenCode:
msp import opencode - Claude Code:
msp import claude
Provider selection works like this:
import codexdefaults to providercodeximport opencodedefaults to provideropencodeimport claudedefaults to providerclaude--provider ...overrides the default summary provider
Examples:
msp import codex
msp import --provider opencode codex
msp import opencode
msp import claude
Import behavior:
- Existing names are skipped after normalization.
- Entries that launch
msp mcpare skipped. - Only supported MCP config shapes are imported.
- If refresh fails during the import batch,
msprolls back the servers added in that run.
Configuration
Default config path:
~/.config/mcp-smart-proxy/config.toml
Override it with --config <PATH>.
The default daemon socket path is derived from this config path with a short stable hash, so different config files still get distinct daemons without exceeding Unix socket length limits.
Example:
[servers.github]
transport = "stdio"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
description = "Use this for GitHub workflows."
enabled = true
env_vars = ["GITHUB_TOKEN"]
[servers.github.env]
GITHUB_API_URL = "https://api.github.com"
GITHUB_ENTERPRISE_MODE = "false"
[servers.test]
transport = "remote"
url = "https://example.com/mcp"
enabled = false
env_vars = ["DEMO_TOKEN", "DEMO_REGION"]
[servers.test.headers]
Authorization = "Bearer ${DEMO_TOKEN}"
X-Region = "${DEMO_REGION:-global}"
[servers.filesystem]
transport = "stdio"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
Notes:
- The config file stores managed
serversonly. - Servers are enabled by default unless
enabled = false. transportis optional. If omitted,mspinfers it fromcommandorurl.stdioservers usecommandplusargs.remoteservers use a Streamable HTTPurlplus optionalheaders.descriptionis an optional manual summary that overrides AI-generated summaries for that server.envstores static variables for the downstream server.env_varslists variables thatmspforwards from its own environment.
Run the Proxy
msp mcp --provider codex
msp mcp is a stdio MCP server entrypoint, not an interactive shell command. Start it from an MCP host such as Codex, OpenCode, or Claude Code, or install it with msp install ....
If you want downstream tools that return structuredContent to expose compact TOON text instead, start the proxy with --output-toon. In that mode, call_tool_in_additional_mcp converts the downstream structured JSON to TOON text, replaces the text content with that TOON payload, and clears structuredContent before returning the result upstream.
When the proxy starts, msp mcp serves the currently cached toolsets immediately and asks the shared daemon to refresh every enabled configured server in the background. If you pass --provider ..., that refresh also regenerates summaries with the selected provider. If you omit --provider, the daemon still refreshes tools and keeps each server's manual description or existing cached summary. The current stdio session keeps using the startup cache snapshot; refreshed cache is used by later sessions or explicit reloads. Background refresh failures are logged by the daemon and do not block MCP readiness.
Inspect and Call Cached MCP Tools from the Terminal
msp cli gives you an interactive terminal view over the same cached MCP inventory that the proxy uses.
List cached MCP servers and their summaries:
msp cli -h
List cached tools for one MCP server:
msp cli github -h
Show one tool's argument help:
msp cli github search_repositories -h
Call one downstream MCP tool through the shared daemon:
msp cli github search_repositories --query rust --page 1
When a tool returns a single plain text content item and no structuredContent, msp cli prints that text directly.
If you want terminal output in TOON when a downstream tool returns structuredContent, add --output-toon before the MCP name:
msp cli --output-toon github search_repositories --query rust --page 1
With --output-toon, msp cli renders structuredContent as TOON. Without --output-toon, the same structured payload is printed as pretty JSON. Mixed or non-text content still falls back to JSON.
msp cli starts or reuses the shared daemon, loads the current cached MCP snapshot through the daemon protocol, and routes the final downstream tool call through the daemon as well.
Use the Bundled pi Extension
This repository also includes a project-local pi extension in ./pi-extension.
It detects whether msp is available on the local system, runs msp cli -h once per pi session, caches that output, and appends the cached inventory plus short MSP usage guidance to pi's systemPrompt. That gives pi a lightweight snapshot of which cached MCP servers are currently reachable through msp cli.
The same extension source is embedded in the msp binary, so you can install it globally without copying files out of this repository:
msp install pi
That writes ~/.pi/agent/extensions/msp.ts. If that file already exists, msp install pi overwrites it, without leaving a sibling lock file in that directory.
Try the project-local copy without installing anything:
pi -e ./pi-extension/msp.ts
You can still copy or symlink ./pi-extension into .pi/extensions/ or ~/.pi/agent/extensions/ for local development if you want pi to auto-discover it directly from the repository.
If msp is missing or msp cli -h fails, the extension shows one warning and then skips prompt injection for that session.
When your MSP inventory changes, run /reload or start a new pi session so the extension refreshes its cached msp cli -h snapshot. If you installed the global extension with msp install pi, later msp self-updates also refresh that installed ~/.pi/agent/extensions/msp.ts automatically when the bundled extension changes.
Background Self-Update
When msp mcp is running, it checks GitHub Releases every 30 minutes.
- If a newer build exists for the current platform, it downloads and atomically replaces the current
mspbinary. - It writes a sibling latest-version record next to the binary.
- If the running process sees that it is older than that record, it restarts itself into the updated binary.
- If
~/.pi/agent/extensions/msp.tsalready exists, the restarted binary refreshes that installed global pi extension automatically when the bundled extension changed. - Lock files prevent concurrent updates from racing on the same executable path.
Background self-update requires write access to the installed msp binary path.
Proxy Tool Contract
activate_additional_mcps
Input:
{
"external_mcp_names": ["github", "filesystem"]
}
Output:
[github]
example_tool: Example description
another_tool: Another description that is longer but still fits in the preview
[filesystem]
read_file: Read the contents of a file
Each section starts with [mcp_name]. Within a section, each output line is tool_name: description-preview. If a tool has no description, the line is just tool_name.
activate_tools_in_additional_mcp
Input:
{
"external_mcp_name": "github",
"tool_names": ["example_tool", "another_tool"]
}
Output:
{
"tools": [
{
"name": "example_tool",
"title": "Example Tool",
"description": "Example description",
"input_schema": {}
},
{
"name": "another_tool",
"title": "Another Tool",
"description": "Another example",
"input_schema": {}
}
]
}
call_tool_in_additional_mcp
Input:
{
"external_mcp_name": "github",
"tool_name": "example_tool",
"args_in_json": "{\"owner\":\"octo-org\",\"repo\":\"demo\"}"
}
args_in_json must decode to a JSON object or null.
Limitations
- Downstream MCP servers must use either stdio or Streamable HTTP transport.
- Remote OAuth currently assumes an interactive browser-based authorization code flow.
- Tool discovery depends on metadata cached by
reload. - The proxy exposes a fixed activation-and-call interface instead of dynamically re-exporting downstream tools as first-class proxy tools.
