Hot-reload for MCP servers + mount any community server mid-session, Docker-caged. No restarts.
Let your agent test the MCP server it's building.
It edits the tool, calls reload("dev"), calls it again and sees the result.
No client restart, no config edit, no human in the loop.
Plus: mount any of 130,000+ community servers mid-session, caged in Docker.
When an agent writes an MCP server, it can't test its own change. The client still runs the old process, so after every edit a person has to restart the client or reconnect the server (and in some clients, register it in the config first) before the agent can call the tool and see whether the fix worked.
Kitsune hands that loop to the agent. connect, call and reload are tool calls, so the agent can run the tool, see it fail, fix the code, reload and check again on its own, in any MCP client.
Kitsune is one MCP server that sits in your client config and loads other servers while the session is running. Your own server under development, or any server from 7 registries. Their tools show up as native tools. Drop them and the session carries on.
connect("uvx --from . my-mcp-server", name="dev") # start your server
shapeshift("dev") # its tools appear in the client
call("summarize", arguments={"url": "https://example.com"})
# … the agent (or you) edits the tool …
reload("dev") # restart on fresh code, remount
call("summarize", arguments={"url": "https://example.com"}) # new code, same session
Try it in 30 seconds with the planted-bug demo server: examples/demo-hot-reload/.
Claude Code
claude mcp add -s user kitsune -- uvx kitsune-mcp
Any other client (Claude Desktop, Cursor, Windsurf, Cline, Zed, …):
{
"mcpServers": {
"kitsune": { "command": "uvx", "args": ["kitsune-mcp"] }
}
}
Also on PyPI (pip install kitsune-mcp, then "command": "kitsune-mcp") and npm (npx kitsune-mcp, delegates to uvx).
Requirements: Python 3.12+ · uv · node/npx for npm-based servers · Docker optional, used to cage community servers.
| Loop | What you get | |
|---|---|---|
| MCP REPL | connect → shapeshift → edit → reload → call | Iterate on your own server without restarting the client |
| Long-tail reach | search → shapeshift → call → shapeshift() | Use a server once without adding it to your config forever |
| Try before you trust | confirm=True + Docker cage + version pins | Run community npm/PyPI code contained, not as your user |
search("web scraping")
shapeshift("firecrawl", tools=["scrape_url"]) # mount one tool, not the whole server
call("scrape_url", arguments={"url": "https://example.com"})
shapeshift() # drop it, session stays up
search("pdf", registry="glama")
shapeshift("mcp-pdf-tools", confirm=True) # npm/PyPI run in hardened Docker when available
call("extract_text", arguments={"path": "report.pdf"})
shapeshift()
auth("BRAVE_API_KEY", "sk-...") # saved to ~/.kitsune/.env, no restart
shapeshift("brave", tools=["brave_web_search"])
Full live walkthrough: docs/demo-realtime.md.
| Use Kitsune when… | Skip it when… |
|---|---|
| You're building an MCP server and want an edit/reload loop | You only use 1–3 trusted servers (configure them natively) |
| A task needs a server that isn't in your config | Every turn hits the same server (keep it always-on) |
| You want to evaluate community MCP code without installing it for good | You want lower token use: the base cost is ~1,774 tokens/turn, and modern clients already defer tool schemas |
| Your MCP config is crowded (GATEWAY) | You need a sub-second first call (cold mount takes ~1–15 s) |
shapeshift(server_id) picks a transport (stdio, HTTP+SSE, WebSocket, Docker), connects, reads tools/list, and registers each tool as a native tool with the server's real schema. The client receives notifications/tools/list_changed and sees first-class tools, with no wrapper in between. shapeshift() with no argument removes them and closes the connection.
search() covers the official servers repo, the MCP Registry, Glama, npm, PyPI, GitHub (github:owner/repo) and Smithery (hosted servers, free API key).
Default profile: 9 tools, ~1,774 tokens/turn (measured with python examples/benchmark.py).
| Tool | Role |
|---|---|
connect(command, name) | Start a persistent local server, e.g. the one you're developing |
reload(name) | Release, restart on fresh code, remount, in one call |
release(name) | Stop a persistent server |
search(query, registry?) | Search 7 registries |
shapeshift(server_id?, tools=[…]) | Mount a server's tools (all or a subset); no argument drops them |
call(tool_name, arguments) | Call a tool; the server is inferred when mounted |
auth(var_or_server, value?) | Set an env key or run an OAuth 2.1 browser flow |
auto(task, server_hint=) | Search, mount and call in one step |
status() | Current form, connection pool, session stats |
KITSUNE_TOOLS=all (or the kitsune-forge command) adds developer tools: inspect (schemas, credential check, token cost), test (quality score 0–100), bench (p50/p95 latency), compare, prewarm, craft, setup, and more. See docs/tools.md. Companion skills for Claude Code: kitsune-dev, kitsune-improve.
confirm=True. The model can set that flag itself, so real approval belongs in your client's tool-approval UI.connect() runs the command it is given, as your user, with no consent gate and no cage. It is meant for your own code. Approve connect calls in your client before the agent runs them.--cap-drop ALL, read-only root, memory and PID limits. Credentials are passed by name, never in argv.~/.kitsune/policy.json checks every downstream call against allow/ask/deny rules, path roots and a host allowlist. ask goes to you through MCP elicitation.fetch() blocks private hosts and re-checks every redirect.Don't run it unattended with production admin, billing or security credentials. The full model, including what it does not protect against: docs/safety.md.
.env lookup, tool profiles, sandbox and trust settings, Smithery, GATEWAY, mount patternsIn Japanese folklore the kitsune (狐) is known for what it can become: it borrows a form, uses that power, casts it off and returns to itself. That is the product loop.
I am not Japanese, and I use this name with the highest respect for the mythology and culture it comes from. The parallel felt too precise to ignore.
make dev # install with dev dependencies
make test # pytest
make lint # ruff
Issues and PRs welcome: github.com/kaiser-data/kitsune-mcp · CHANGELOG.md
MIT License · Python 3.12+ · Built on FastMCP
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
npx -y kitsune-mcpMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-kaiser-data-kitsune-mcp": {
"command": "npx",
"args": [
"-y",
"kitsune-mcp"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.
Claude Desktop setup referencekitsune-mcpnpmio.github.kaiser-data/kitsune-mcp works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.