{
  "markdown": "<!-- mcp-name: io.github.kaiser-data/kitsune-mcp -->\n<div align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/kaiser-data/kitsune-mcp/main/kitsune-logo.png\" alt=\"Kitsune MCP\" width=\"160\" />\n  <h1>🦊 Kitsune MCP</h1>\n  <p><strong>The agent harness for MCP.</strong><br/>\n  One config entry. Borrow any of 130,000+ servers <em>mid-session</em> — develop live, reach the long tail, try community code contained — then shift back.<br/>\n  <em>Session survives.</em></p>\n</div>\n\n[![PyPI](https://img.shields.io/pypi/v/kitsune-mcp?color=blue&label=pypi)](https://pypi.org/project/kitsune-mcp/)\n[![npm](https://img.shields.io/npm/v/kitsune-mcp?color=cb3837&label=npm&logo=npm)](https://www.npmjs.com/package/kitsune-mcp)\n[![MCP Registry](https://img.shields.io/badge/MCP%20Registry-listed-8a2be2)](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.kaiser-data%2Fkitsune-mcp)\n[![Python](https://img.shields.io/pypi/pyversions/kitsune-mcp)](https://pypi.org/project/kitsune-mcp/)\n[![CI](https://github.com/kaiser-data/kitsune-mcp/actions/workflows/test.yml/badge.svg)](https://github.com/kaiser-data/kitsune-mcp/actions)\n[![Coverage](https://codecov.io/gh/kaiser-data/kitsune-mcp/branch/main/graph/badge.svg)](https://codecov.io/gh/kaiser-data/kitsune-mcp)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n[![Smithery](https://smithery.ai/badge/@kaiser-data/kitsune-mcp)](https://smithery.ai/server/@kaiser-data/kitsune-mcp)\n[![Glama](https://glama.ai/mcp/servers/kaiser-data/kitsune-mcp/badges/score.svg)](https://glama.ai/mcp/servers/kaiser-data/kitsune-mcp)\n[![Discord](https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white)](https://discord.gg/EYgcf7EX)\n\n---\n\nKitsune is a **runtime MCP proxy**: one always-on gateway your agent uses to reach the rest of the ecosystem. `search` finds a server across 7 registries. `shapeshift(id)` mounts its tools in the current turn. `shapeshift()` drops them. No config edit. No client restart.\n\n```text\nsearch → shapeshift → call → shapeshift()       # reach, use, release\nconnect → shapeshift → edit → reload → call     # MCP REPL (default install)\n```\n\n**Install for reach and live execution — not for token savings.** Native Tool Search already defers schemas for servers you've configured. Kitsune covers what Tool Search cannot: servers you've never set up, servers you're writing right now, and community packages you want to try without wiring them into `mcp.json` forever.\n\n| | Loop | Why it wins |\n|---|---|---|\n| **MCP REPL** | edit → `reload` → `call` | Iterate on your own server without killing the session |\n| **Long-tail reach** | `search` → `shapeshift` → `call` | One-offs and obscure APIs with no pre-install |\n| **Try-before-you-trust** | `confirm=True` + Docker cage on by default + TOFU pins | Community catalog without blind always-on installs |\n\n| Use Kitsune when… | Skip it when… |\n|---|---|\n| You're building an MCP and need an edit/reload loop | You only need 1–3 trusted servers (configure them natively) |\n| A task needs a server that isn't in your config | Every turn hits the same server (keep it always-on) |\n| CLI flag-guessing on a long-tail API is too risky | You want cheaper tokens — floor is **~1,774 tokens/turn**, additive on modern clients |\n| You want to evaluate community MCP code safely | Unattended prod admin/billing/security keys ([Safety](#safety-model)) |\n| You're consolidating a crowded MCP config ([GATEWAY](#gateway-consolidate-always-on-servers)) | You need sub-second first call (cold mount ~1–15s — `prewarm` or always-on) |\n\nWorked high-stakes flows (IAM, IR, audits): [`examples/scenarios/`](./examples/scenarios/). CLI vs MCP accuracy argument lives there too — short version: models nail common CLI commands and fail on the long tail; Kitsune mounts schemas only while you need them.\n\n---\n\n## Contents\n\n- [Installation](#installation)\n- [Quick start](#quick-start)\n- [Developing an MCP server live](#developing-an-mcp-server-live)\n- [How it works](#how-it-works)\n- [Tool reference](#tool-reference)\n- [Server sources](#server-sources)\n- [Safety model](#safety-model)\n- [GATEWAY: consolidate always-on servers](#gateway-consolidate-always-on-servers)\n- [Performance](#performance)\n- [Configuration](#configuration)\n- [Mount patterns](#mount-patterns)\n- [For MCP developers](#for-mcp-developers)\n- [Why Kitsune?](#why-kitsune)\n- [Contributing](#contributing)\n\n---\n\n## Installation\n\n```bash\npip install kitsune-mcp      # recommended\n# or\nuvx kitsune-mcp              # isolated env via uv, no venv setup\n# or\nnpx kitsune-mcp              # npm (delegates to uvx internally)\n```\n\n**Requirements:** Python 3.12+ · `node`/`npx` for npm-based servers · `uvx` from [uv](https://github.com/astral-sh/uv) for PyPI-based servers · Docker optional (sandbox)\n\nAdd once to your MCP client config:\n\n```json\n{\n  \"mcpServers\": {\n    \"kitsune\": { \"command\": \"kitsune-mcp\" }\n  }\n}\n```\n\n| Client | Config file |\n|---|---|\n| Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |\n| Claude Desktop (Windows) | `%APPDATA%\\Claude\\claude_desktop_config.json` |\n| Claude Code | `~/.claude/mcp.json` |\n| Cursor / Windsurf | `~/.cursor/mcp.json` |\n| Cline / Continue.dev | VS Code settings / `~/.continue/config.json` |\n\nAlso works with OpenClaw, Zed, and any MCP-compatible client.\n\nLean profile at rest: **9 tools · ~1,774 tokens/turn** (`status`, `search`, `auth`, `shapeshift`, `call`, `auto`, plus the `connect` / `release` / `reload` REPL trio) — measured via `python examples/benchmark.py`.\n\n---\n\n## Quick start\n\n**Borrow a server you never configured:**\n\n```python\nsearch(\"web scraping\")\nshapeshift(\"firecrawl\", tools=[\"scrape_url\"])  # surgical: one tool, not the whole surface\ncall(\"scrape_url\", arguments={\"url\": \"https://example.com\"})\nshapeshift()  # drop form — session stays up\n```\n\n**Community / long-tail (confirm; caged by default):**\n\n```python\nsearch(\"pdf\", registry=\"glama\")\nshapeshift(\"mcp-pdf-tools\", confirm=True)  # npm/PyPI caged in Docker by default (when available)\ncall(\"extract_text\", arguments={\"path\": \"report.pdf\"})\nshapeshift(\"mcp-pdf-tools\", confirm=True, sandbox=False)  # opt out of the cage\nshapeshift()\n```\n\n**Hosted (Smithery HTTP — needs a free `SMITHERY_API_KEY`):**\n\n```python\nsearch(\"exa\", registry=\"smithery\")\nshapeshift(\"exa\")\ncall(\"web_search_exa\", arguments={\"query\": \"MCP registry growth 2026\"})\nshapeshift()\n```\n\n**Credentials mid-session:**\n\n```python\nauth(\"BRAVE_API_KEY\", \"sk-...\")\nshapeshift(\"brave\", tools=[\"brave_web_search\"])\ncall(\"brave_web_search\", arguments={\"query\": \"MCP protocol 2026\"})\nshapeshift()\n```\n\n**One-shot** — pass `server_hint` when you know the id (`auto` without it is best-effort and can misfire):\n\n```python\nauto(\"current time in Tokyo\", server_hint=\"mcp-server-time\")\n```\n\nFull live walkthrough: [`docs/demo-realtime.md`](docs/demo-realtime.md).\n\n---\n\n## Developing an MCP server live\n\nBuilding an MCP normally means: edit → restart client → lose session → re-test. Kitsune turns that into an **MCP REPL** in one session — and `connect` / `release` / `reload` are in the **default lean profile**, so this works on a plain `pip install` with no `KITSUNE_TOOLS=all`.\n\n```python\nconnect(\"uvx --from . my-mcp-server\", name=\"dev\")  # start child process\nshapeshift(\"dev\")  # mount tools → client sees them\ncall(\"summarize\", arguments={\"url\": \"https://example.com\"})\n\n# … edit the tool in your editor …\n\nreload(\"dev\")  # release → restart fresh code → remount, one call\ncall(\"summarize\", arguments={\"url\": \"https://example.com\"})\n```\n\n`reload(\"dev\")` folds the whole cycle — kill the stale process, start your edited code, remount so the client sees the new schemas — into a single call. It also removes the classic footgun: calling `connect()` again after an edit *without* releasing first hands you back the **old** process; `reload` always releases first.\n\nLocal `connect()` targets are untrusted (`confirm` / `KITSUNE_TRUST` apply). Process isolation ≠ security sandbox — see [Safety model](#safety-model). Companion skill: `kitsune-dev`.\n\n---\n\n## How it works\n\n`shapeshift(server_id)` picks a transport (stdio / HTTP+SSE / WebSocket / Docker), connects, fetches `tools/list`, and registers each tool as a native FastMCP tool with the server's real schema. The client gets `notifications/tools/list_changed` and sees first-class tools — no wrapper indirection.\n\n`shapeshift()` with no args deregisters proxies, closes the connection, and returns to the lean baseline.\n\n<div align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\"\n            srcset=\"https://raw.githubusercontent.com/kaiser-data/kitsune-mcp/main/docs/architecture-dark.svg\"/>\n    <img src=\"https://raw.githubusercontent.com/kaiser-data/kitsune-mcp/main/docs/architecture-light.svg\"\n         alt=\"Kitsune MCP architecture\" width=\"700\"/>\n  </picture>\n</div>\n\n**Mental model — tool-schema RAG:** index the ecosystem → `search` retrieves candidates → `shapeshift(..., tools=[…])` injects only what's needed → agent calls natively → `shapeshift()` evicts.\n\n| Source | Transport |\n|---|---|\n| npm | `npx <package>` (local; optional Docker sandbox) |\n| PyPI | `uvx <package>` (local; optional Docker sandbox) |\n| GitHub | `npx github:user/repo` or `uvx --from git+…` |\n| Smithery hosted | HTTP + SSE (`SMITHERY_API_KEY`) |\n| WebSocket | `ws://` / `wss://` |\n| Docker image | `docker run …` hardened profile |\n\n---\n\n## Tool reference\n\n**Lean (default)**\n\n| Tool | Signature | Role |\n|---|---|---|\n| `status()` | — | Current form, pool, GATEWAY scan, session stats |\n| `search()` | `query, registry?, compare?` | Fan-out across 7 registries |\n| `auth()` | `server_or_var, value?` | Env keys + OAuth 2.1 browser flow / logout |\n| `shapeshift()` | `server_id?, tools=[], …` | Mount / unmount; `tools=[…]` surgical; `confirm=True`; caged by default (`sandbox=False` opts out) |\n| `call()` | `tool_name, arguments` | Invoke; server inferred when mounted |\n| `auto()` | `task, server_hint=, arguments=` | search → mount → call (prefer `server_hint`) |\n\n**Forge** (`KITSUNE_TOOLS=all` or `kitsune-forge`): `connect`, `release`, `prewarm`, `inspect`, `test`, `bench`, `compare`, `craft`, `run`, `fetch`, `setup`, `skill`, `shiftback`, … — see [For MCP developers](#for-mcp-developers).\n\n---\n\n## Server sources\n\n| Registry | Auth | `registry=` |\n|---|---|---|\n| [modelcontextprotocol/servers](https://github.com/modelcontextprotocol/servers) | — | `official` |\n| [registry.modelcontextprotocol.io](https://registry.modelcontextprotocol.io) | — | `mcpregistry` |\n| [Glama](https://glama.ai/mcp/servers) | — | `glama` |\n| npm | — | `npm` |\n| PyPI | — | `pypi` |\n| GitHub | — | `github:owner/repo` |\n| [Smithery](https://smithery.ai) | Free API key | `smithery` |\n\n`search()` fans out across no-auth registries by default. Add `SMITHERY_API_KEY` for hosted HTTP servers (no local install).\n\n---\n\n## Safety model\n\nReach into 130k community servers only works if unknown code can be **contained**. Consent, sandbox, and pins are product features — not footnotes.\n\n**Headline controls**\n\n- `confirm=True` (or `KITSUNE_TRUST`) before community / local mounts\n- **Community npm/PyPI mounts cage in hardened Docker by default** (when Docker is present); `sandbox=False` or `KITSUNE_SANDBOX=off` opts out, `sandbox=True` forces it, `KITSUNE_SANDBOX=all` cages every local mount\n- TOFU pins in `~/.kitsune/pins.json` — later malicious publishes don't silently replace what you already ran\n\n### What it protects against\n\n**1. Unverified code without consent**\n\n| Tier | Sources | On mount |\n|---|---|---|\n| High | `official` | runs directly |\n| Medium | `mcpregistry`, `glama`, `smithery` | runs directly |\n| Community | `npm`, `pypi`, `github`, local `connect()` | **requires `confirm=True`** |\n\n`KITSUNE_TRUST=community` waives the gate; `status()` warns when that override is active.\n\n> **`confirm=True` is not a human-approval boundary.** The model can set it. Real approval belongs in your client's tool-approval UI.\n\n**2. Shell injection at spawn.** Install commands are validated (no `& ; | $ \\` `\\n` / `../`) and launched with `create_subprocess_exec` — no shell. Vets the launch line, not what the package does once running.\n\n**3. SSRF.** `fetch()` and registry HTTP are HTTPS-only; private/loopback/non-global hosts blocked; **every redirect hop re-validated** (`KITSUNE_ALLOW_LOCAL_FETCH=1` to opt out).\n\n**4. Credential exposure.** `~/.kitsune/.env` and `oauth/` at mode `0600`; OAuth 2.1 + PKCE S256 + DCR (RFC 7591); missing-cred warnings before calls; `auth(id, \"logout\")` clears tokens (RFC 7009 where available).\n\n**5. Docker sandbox for untrusted local servers — on by default.** Community `npm`/`pypi`/`github` mounts (and the `auto()`/`call()`/`run()` exec paths) cage automatically when Docker is on `PATH`; no host FS, `--cap-drop ALL`, read-only rootfs, RAM/PID caps. Cred env vars forwarded by **name** only (`docker -e KEY`) — never in argv, `ps`, or the pool key. First sandboxed mount pulls `node:22-slim` / `uv:python3.13-bookworm-slim`. Best-effort: no Docker → runs uncaged with a nudge (an explicit `sandbox=True` hard-fails instead). Opt out per-call with `sandbox=False` or session-wide with `KITSUNE_SANDBOX=off`. Filesystem-style servers need host paths and don't fit the sandbox.\n\n### What it does NOT do\n\n- **Cage needs Docker + opt-in-trusted sources.** Community mounts cage by default *only when Docker is present*; without it (or with `sandbox=False`/`KITSUNE_SANDBOX=off`, or for medium/high-trust sources) local stdio runs as your user — full FS, network, inherited env. Process isolation ≠ a security boundary.\n- **Docker ≠ kernel boundary.** Hardened flags blunt escalation / fork bombs / FS tampering; not a guarantee against container escape. No default non-root / `--network none` (most servers need egress).\n- **TOFU ≠ digest pin.** Pins a version, not a content hash. `github:` / `git+` / hand-written `connect()` commands aren't pinned. High assurance: pin by digest or vendor.\n- **Tools first.** Resource/prompt proxying is narrower (URI templates skipped; HTTP path differs). \"Any server\" means tool execution.\n\n**Bottom line:** strong for supervised developer and personal use. **Do not run unattended with production admin, billing, or security credentials in default local mode.** Keep Docker installed so the default cage engages, and prefer client approval for untrusted packages.\n\nSee guards live: [`docs/demo-realtime.md`](docs/demo-realtime.md#act-3).\n\n---\n\n## GATEWAY: consolidate always-on servers\n\nOptional. Keep daily drivers (GitHub, filesystem, …) native if you prefer. When a config is crowded, `status()` flags other always-on servers so you can collapse to one Kitsune entry and reach them via `shapeshift`:\n\n```\nGATEWAY\n  ⚠  1 other server(s) active in claude-desktop (~8 extra tools in context)\n     Run setup() to harvest their credentials and reduce bloat\n```\n\n```python\nsetup()  # preview\nsetup(action=\"harvest\")  # keys → ~/.kitsune/.env (non-destructive)\nsetup(action=\"absorb\")  # register for shapeshift()\nsetup(project=True)  # project mcp.json with only Kitsune\n```\n\nNever modifies existing configs without explicit confirmation. (`setup` is forge-profile.)\n\n---\n\n## Performance\n\n### Connection latency (what you feel)\n\nWarm pool re-attach within a session: **0 ms**.\n\n| Transport | Cold start | Warm |\n|---|---|---|\n| HTTP / Smithery | 0–1.4 s | 0.0 s |\n| Local `npx` | 1.7–6.3 s | 0.0 s |\n| Local `uvx` | 1.0–5.2 s | 0.0 s |\n\nUse `prewarm` (forge) when you know you'll need a server soon.\n\n### Token overhead (secondary)\n\n> Real vs **fully-mounted always-on** or clients **without** Tool Search. On Claude Code 2.1.7+ with native deferral, this is mostly not a Kitsune-specific win. Product pitch is reach + REPL above — not this table.\n\nEvery Kitsune figure **includes** the ~1,774 floor. Reproduce: `python examples/benchmark.py`. Methodology: [`docs/benchmarks.md`](docs/benchmarks.md).\n\n<div align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\"\n            srcset=\"https://raw.githubusercontent.com/kaiser-data/kitsune-mcp/main/docs/token-cost-dark.svg\"/>\n    <img src=\"https://raw.githubusercontent.com/kaiser-data/kitsune-mcp/main/docs/token-cost-light.svg\"\n         alt=\"Token cost comparison: always-on vs Kitsune\" width=\"700\"/>\n  </picture>\n</div>\n\n| Server | Always-on | Surgical + floor | vs always-on |\n|---|---:|---:|---:|\n| `mcp-server-time` | 261 | ~2,035 | always-on cheaper ¹ |\n| `mcp-server-git` | 1,242 | ~2,084 | always-on cheaper ¹ |\n| `server-memory` | 2,615 | ~2,354 | 10% |\n| `server-filesystem` | 3,207 | ~2,464 | 23% |\n| `brave` | 3,612 | ~2,224 | 38% |\n| `server-github` | 4,229 | ~2,074 | 51% |\n| `notion-hosted` | 13,707 | ~3,724 | 73% |\n\n¹ Break-even: Kitsune pays off past one medium server, or two-plus small ones sharing the single floor. Multi-server stack (GitHub+fs+git → Notion suite): **~72–85%** vs fully-mounted always-on — same caveat as above.\n\nFewer visible tools also helps selection reliability (Gorilla / ToolBench); on modern clients Tool Search delivers much of that focus for *configured* servers. Kitsune-specific accuracy bench: not yet — contributions welcome.\n\n---\n\n## Configuration\n\n### Env and `.env`\n\nRe-read on every `shapeshift` / `call` — add keys mid-session, no restart.\n\nSearch order: `CWD/.env` → `~/.env` → `~/.kitsune/.env` (last wins).\n\n```bash\nauth(\"BRAVE_API_KEY\", \"sk-...\")    # → ~/.kitsune/.env\n```\n\n### Tool surface\n\n```json\n{ \"env\": { \"KITSUNE_TOOLS\": \"shapeshift,call,auth\" } }   # subset\n{ \"env\": { \"KITSUNE_TOOLS\": \"all\" } }                    # forge\n```\n\n### State directory\n\nDefault `~/.kitsune/` (credentials, pins, OAuth, session). Relocate with `KITSUNE_HOME=/tmp/kitsune-iso`.\n\n### Sandbox / trust policy\n\n```bash\nKITSUNE_SANDBOX=community   # Docker-cage community npm/PyPI mounts\nKITSUNE_SANDBOX=all         # cage every local mount\nKITSUNE_TRUST=community     # waive confirm gate (status warns)\nKITSUNE_REPIN=1             # adopt newer pinned version\n```\n\n### Smithery\n\n```json\n{ \"env\": { \"SMITHERY_API_KEY\": \"your-key\" } }\n```\n\nFree key: [smithery.ai/account/api-keys](https://smithery.ai/account/api-keys). Without it, npm / PyPI / official / GitHub still work.\n\n---\n\n## Mount patterns\n\nSwitch forms mid-session — take only the slice you need:\n\n```python\n# Research\nshapeshift(\"brave\", tools=[\"brave_web_search\"])\nshapeshift(\"mcp-server-fetch\")\nshapeshift(\"@modelcontextprotocol/server-memory\", tools=[\"read_graph\", \"search_nodes\"])\n\n# Code\nshapeshift(\n    \"@modelcontextprotocol/server-filesystem\",\n    tools=[\"read_file\", \"write_file\", \"edit_file\"],\n    server_args=[\"/path/to/project\"],\n)\nshapeshift(\"mcp-server-git\", tools=[\"git_status\", \"git_diff\", \"git_log\"])\n\n# Notes\nshapeshift(\"notion-hosted\", tools=[\"notion-search\", \"notion-append-block-children\"])\nshapeshift(\"@modelcontextprotocol/server-memory\", tools=[\"add_memory\", \"search_nodes\"])\n\nshapeshift()  # always drop when the task is done\n```\n\n---\n\n## For MCP developers\n\n```json\n{ \"command\": \"kitsune-mcp\", \"env\": { \"KITSUNE_TOOLS\": \"all\" } }\n```\n\n| Tool | Role |\n|---|---|\n| `connect` / `release` / `prewarm` | MCP REPL + warm pool |\n| `inspect(server_id)` | Schemas, live cred check, measured cost |\n| `test(server_id)` | Quality score 0–100 |\n| `bench(server_id, tool, args)` | Latency p50 / p95 / min / max |\n| `compare(query)` | Side-by-side cost, tools, trust, creds |\n| `craft(name, description, params, url)` | Register a live HTTP-backed tool |\n\nTest inside real Claude / Cursor sessions — not only an inspector UI. Companion skills: `kitsune-dev`, `kitsune-improve`.\n\n---\n\n## Why Kitsune?\n\nIn Japanese folklore the Kitsune (狐) is known for what it can *become*: borrow a form, use that power, cast it off, return to itself.\n\nThat is the product loop — reach, use, release; or edit, reload, re-test. One config entry. Long tail one call away. Session intact.\n\n`shapeshift()` is a literal mid-session mount, not a metaphor. Durable advantages: **reach, live development, contained try-before-trust** — not a smaller token bill on clients that already defer schemas.\n\n> *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.*\n\n---\n\n## Contributing\n\n```bash\nmake dev     # install with dev dependencies\nmake test    # pytest\nmake lint    # ruff\n```\n\nIssues and PRs: [github.com/kaiser-data/kitsune-mcp](https://github.com/kaiser-data/kitsune-mcp) · [CHANGELOG.md](CHANGELOG.md)\n\n---\n\n*MIT License · Python 3.12+ · Built on [FastMCP](https://github.com/jlowin/fastmcp)*\n",
  "bytes": 20651,
  "sha": "879cf55fc295d7810665aa55d377d58ce0eab655dec8962be9bec09e53e5dc63",
  "repo_slug": "kaiser-data/kitsune-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_kaiser_data_kitsune_mcp_83573f53/readme"
}