Skip to main content
Glama
ehewes

mcp-store

by ehewes
README.md
# mcp-store

**One MCP endpoint for all your agents' tools.** mcp-store is a gateway that aggregates any number
of existing MCP servers *and* your own Python tools behind a single connection — with a context
layer that tells agents what's available and when to use it.

Three ways to put a tool on it:

- **Route an existing MCP server** — paste its standard `mcpServers` JSON snippet into
  `config.json`. stdio, Streamable HTTP, and legacy SSE upstreams all work, and you can rename,
  redescribe, or hide their tools at the gateway without touching the upstream.
- **Write your own MCP in Python** — one folder under `plugins/` is one MCP. Drop in `@tool`
  functions (zero boilerplate) or a full FastMCP sub-server; the folder name becomes the tool
  namespace. `mcp-store new-plugin <name>` scaffolds a working one.
- **Ship it with the gateway** — built-in tools like `echo` and `catalog` live in the codebase
  itself.

Agents connect once and get everything, namespaced by source (`fs_read_file`, `weather_get_forecast`),
described by an auto-generated routing map, and — for large catalogs — discoverable through an
opt-in search interface instead of a context-crushing tool list.

## How it works

```mermaid
%%{init: {"flowchart": {"curve": "basis"}}}%%
flowchart TB
    INF["Infisical<br/>secret store"]
    A1["Claude Code"]
    A2["OpenClaw agents"]
    A3["any MCP client"]

    subgraph gw["mcp-store gateway — one process"]
        direction TB
        EP["<b>one MCP endpoint</b><br/>Streamable HTTP<br/>:8000/mcp/ · stdio"]

        PRES["<b>what agents see</b><br/>curation — rename · hide<br/>context — instructions<br/>catalog · opt-in search"]

        BI["<b>built-ins</b><br/>echo · catalog<br/><i>ship with the gateway</i>"]
        PL["<b>plugins — your Python</b><br/>one folder = one MCP<br/>weather/ → weather_*<br/><i>drop a folder · restart</i>"]
        PX["<b>upstream proxies</b><br/>fs_* · gh_* from config.json<br/><i>paste any mcpServers entry</i>"]

        EP --> PRES
        PRES --> BI
        PRES --> PL
        PRES --> PX
    end

    A1 --> EP
    A2 -->|"one connection each"| EP
    A3 --> EP

    INF -. "secrets → env at runtime<br/>never in repo or image" .-> gw

    U1["npx MCP server<br/>stdio subprocess"]
    U2["remote MCP server<br/>Streamable HTTP"]
    U3["legacy MCP server<br/>SSE"]

    PX --> U1
    PX --> U2
    PX --> U3

    %% theme-safe styling: explicit fills + dark text on nodes; the one
    %% cluster stays transparent so its title keeps GitHub's light/dark contrast
    classDef client fill:#dbeafe,stroke:#60a5fa,color:#0f172a
    classDef endpoint fill:#ede9fe,stroke:#8b5cf6,stroke-width:2px,color:#0f172a
    classDef shaping fill:#fef3c7,stroke:#f59e0b,color:#0f172a
    classDef builtin fill:#f1f5f9,stroke:#94a3b8,color:#0f172a
    classDef plugin fill:#dcfce7,stroke:#22c55e,stroke-width:2px,color:#0f172a
    classDef proxy fill:#ccfbf1,stroke:#14b8a6,color:#0f172a
    classDef upstream fill:#f8fafc,stroke:#cbd5e1,color:#334155
    classDef secret fill:#ffe4e6,stroke:#f43f5e,color:#0f172a

    class A1,A2,A3 client
    class EP endpoint
    class PRES shaping
    class BI builtin
    class PL plugin
    class PX proxy
    class U1,U2,U3 upstream
    class INF secret

    style gw fill:transparent,stroke:#8b5cf6,stroke-width:2px

    %% secrets edge red; proxy→upstream edges teal to tie them to their tile
    linkStyle 7 stroke:#f43f5e
    linkStyle 8,9,10 stroke:#14b8a6,stroke-width:2px
```

**Reading it top to bottom, the way a call flows:** agents hold one connection each to the
endpoint; the presentation layer decides what they see (curation reshapes tools, the context layer
explains them); below it sit the three tool sources. **Green is where your code goes.** The purple
border is the process boundary — plugins run inside it with full privileges, while the teal arrows
are the proxies' live sessions out to upstream servers running in their own processes. The dashed
red edge is the only way secrets enter: injected into the environment at runtime, never committed,
never baked into the image.

A request flows agent → endpoint → transforms (curation, optional tool search) → the owning
source: built-in and plugin tools execute in-process; upstream tools route through a live proxy to
the real server. Secrets never live in the repo or the image — [Infisical](https://infisical.com)
injects them into the environment at runtime, and `config.json` carries only `${VAR}` references.

Built on [FastMCP 3](https://gofastmcp.com). The design bias throughout is *adopt, don't build* —
proxying, mounting, transforms, and directory loading are framework primitives, composed rather
than reimplemented.

## Quickstart

```bash
uv sync
cp config.example.json config.json       # edit to taste
uv run mcp-store serve --config config.json
```

The gateway is now serving at **`http://127.0.0.1:8000/mcp/`**. Point an agent at it:

```bash
claude mcp add --transport http mcp-store http://127.0.0.1:8000/mcp/
```

See what it exposes, or run the preflight:

```bash
uv run mcp-store tools --config config.json
uv run mcp-store doctor --config config.json
```

## Documentation

Full documentation lives in [`docs/`](docs/README.md).

> 🤖 **AI agents:** follow **[docs/AGENTGUIDE.md](docs/AGENTGUIDE.md)** — a task-router with exact
> recipes and verification steps for working on this repo. Humans: start with the guides below.

| Guide | What it covers |
|---|---|
| [Getting started](docs/getting-started.md) | Install, first run, connecting agents, your first plugin |
| [Configuration reference](docs/configuration.md) | Every config key, default, and failure behaviour |
| [Routing upstream servers](docs/upstreams.md) | Adding existing MCP servers and curating their tools |
| [Writing your own MCP](docs/plugins.md) | The folder-per-MCP plugin model, manifest, trust model |
| [The context layer](docs/context-layer.md) | Instructions, the `catalog` tool, tool search |
| [Secrets](docs/secrets.md) | The Infisical model — runtime injection, `${VAR}` references |
| [Deployment](docs/deployment.md) | Docker, compose, container paths, `doctor` as a CI gate |
| [CLI reference](docs/cli.md) | `serve` · `tools` · `doctor` · `new-plugin` |
| [Architecture](docs/architecture.md) | Internals, request/startup flow diagrams, design decisions |

Engineering history — research, verified-fact spikes against FastMCP 3.4.4, decision records — is
in [`context/`](context/README.md).

## Project layout

```
mcp-store/
├── config.example.json    # copy to config.json; servers + plugins + gateway settings
├── plugins/               # your MCPs, one folder each (weather/ and notes/ are examples)
├── src/mcp_store/         # the gateway: config, plugins, gateway, curation, doctor, cli
├── tests/                 # 83 tests, all in-memory, sub-second
├── docs/                  # user documentation (start at docs/README.md)
└── context/               # internal engineering docs: plans, research, decisions
```

## Status

Python 3.11+ · [uv](https://docs.astral.sh/uv/) · FastMCP `>=3.4,<4` · 83 tests passing ·
verified end-to-end over HTTP, stdio, and in Docker against real upstream servers. No auth in v1 —
bind to localhost or a trusted network ([details](docs/deployment.md#network-exposure)).