# Ratel Mcp

> MCP server library + ratel-mcp CLI: expose a Ratel tool catalog as a Model Context Protocol server. Manage upstream MCP servers, OAuth, and Claude Code import from one binary.

- **Type:** MCP server
- **Install:** `agentstack add mcp-ratel-ai-ratel-mcp`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [ratel-ai](https://agentstack.voostack.com/s/ratel-ai)
- **Installs:** 0
- **Category:** [Security](https://agentstack.voostack.com/c/security)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [ratel-ai](https://github.com/ratel-ai)
- **Source:** https://github.com/ratel-ai/ratel-mcp

## Install

```sh
agentstack add mcp-ratel-ai-ratel-mcp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

@ratel-ai/mcp-server
  Expose a Ratel catalog over MCP — and manage your MCP scopes from one CLI.

  
    Ratel core •
    Roadmap •
    Discord
  

  
    
    
    
    
  

`@ratel-ai/mcp-server` is two things in one package:

- a **library** that takes a Ratel [`ToolCatalog`](https://github.com/ratel-ai/ratel) and exposes it as a Model Context Protocol server — the MCP client (Claude Desktop, an agent framework, an `@modelcontextprotocol/sdk` `Client`) sees `search_capabilities` + `invoke_tool` (plus `get_skill_content` when skills are configured) instead of every upstream's full tool list;
- a **CLI** (`ratel-mcp`) that drops the gateway between an MCP host (Claude Code, Cursor, ChatGPT) and an arbitrary set of upstream MCP servers — with Claude-compatible config UX, three-scope hierarchy, OAuth 2.1 / PKCE for HTTP+SSE upstreams, and a one-shot `mcp import` wizard for migrating an existing Claude Code MCP setup.

This is the inverse of `@ratel-ai/sdk`'s [`registerMcpServer`](https://github.com/ratel-ai/ratel/blob/main/src/sdk/ts/README.md#registermcpserver--index-an-mcp-servers-tools-into-the-catalog), which ingests an upstream MCP server's tools *into* a catalog. `createMcpServer` exposes a catalog *as* an MCP server.

## Install

```bash
# CLI (global install)
pnpm add -g @ratel-ai/mcp-server

# Library (in a TS/Node project)
pnpm add @ratel-ai/mcp-server @ratel-ai/sdk @modelcontextprotocol/sdk
```

Or skip the install and run the CLI on-the-fly:

```bash
npx -y @ratel-ai/mcp-server --help
```

### Agent plugin marketplaces

The repo also ships a shared Ratel MCP plugin for Codex and Claude Code. It
starts the gateway over stdio with `npx -y @ratel-ai/mcp-server@latest serve
--auto-config` and bundles skills for setup, debugging, and tool-usage review.

#### Codex

Add the remote marketplace, then install **Ratel MCP** from the **Ratel**
marketplace in Codex:

```bash
codex plugin marketplace add ratel-ai/ratel-mcp
```

For Codex local development from a checkout, run this from the repo root:

```bash
codex plugin marketplace add .
```

#### Claude Code

Add the remote marketplace and install the plugin:

```bash
claude plugin marketplace add ratel-ai/ratel-mcp
claude plugin install ratel-mcp@ratel
```

If Claude Code is already running, restart it or run `/reload-plugins` inside
the session.

For Claude Code local development from a checkout, use `.` as the marketplace:

```bash
claude plugin marketplace add .
claude plugin install ratel-mcp@ratel
```

Claude Code plugins cannot currently set a top-level `statusLine` default.
Install the Ratel statusline separately with `ratel-mcp statusline install`
or from the Claude Code agent page in `ratel-mcp ui`. See Claude's
[statusline docs](https://code.claude.com/docs/en/statusline) and
[plugin reference](https://code.claude.com/docs/en/plugins-reference).

## CLI quickstart

`ratel-mcp` mirrors `claude mcp add`'s flag layout — any invocation that works against Claude Code's CLI works here unchanged.

```bash
# Add an upstream (stdio)
ratel-mcp mcp add --scope user airtable -e API_KEY=xyz -- npx -y airtable-mcp-server

# Add an upstream (HTTP, with OAuth)
ratel-mcp mcp add --scope user stripe https://mcp.stripe.com --transport http

# List what's configured
ratel-mcp mcp list

# Import your existing agent MCP setup into ratel-mcp's scopes
ratel-mcp mcp import
ratel-mcp mcp import --agent codex

# Point an agent at the Ratel gateway without removing native MCP entries
ratel-mcp mcp link
ratel-mcp mcp link --agent claude-code

# Install the Claude Code statusline
ratel-mcp statusline install

# Start the gateway over stdio (this is what linked agents spawn)
ratel-mcp serve --config ~/.ratel/config.json
```

Run `ratel-mcp ` for the verbs in a group:

| Group | Verbs |
|---|---|
| `mcp` | `add`, `remove`, `list`, `get`, `edit`, `import`, `link`, `auth` |
| `backup` | `list` |
| `statusline` | render from stdin, `install`, `uninstall` |
| (top-level) | `serve`, `ui` |

### `ratel-mcp mcp add` — Claude-compatible

```
ratel-mcp mcp add [flags]  --  [args...]      # stdio
ratel-mcp mcp add [flags]                         # http / sse
```

| Flag | Meaning |
|---|---|
| `--transport stdio\|http\|sse` | Force a transport. Inferred otherwise (URL → http, `--` → stdio). |
| `--scope user\|project\|local` | Which scope to write to. Defaults to `user`. |
| `--env KEY=VALUE` / `-e KEY=VALUE` | Env var for stdio entries. Repeatable. |
| `--header "Name: Value"` | HTTP header for http/sse entries. Repeatable. |
| `--client-id ` / `--client-secret ` / `--callback-port ` / `--oauth-scope ` | OAuth client config for http/sse entries. DCR is preferred — pass `--client-id` only when the upstream doesn't support it. |
| `--description ` | Human description of the server. Wins over the auto-fetched upstream `instructions`. |
| `--no-fetch-description` | Skip the auto-probe — no connect, no description fetch, no OAuth flow. |
| `--force` | Overwrite an existing entry of the same name in the chosen scope. |

By default, `mcp add` connects to the upstream and stores its server-level `instructions` (per the MCP spec) as the entry's `description`. For http/sse upstreams it drives the OAuth 2.1 / PKCE flow inline (browser opens, tokens persist at `~/.ratel/oauth/.json`).

### Three-scope hierarchy

`ratel-mcp` mirrors Claude Code's MCP scoping with three logical configs:

| Scope | Path | Notes |
|---|---|---|
| user | `~/.ratel/config.json` | Per-user, applies everywhere. |
| project | `/.ratel/config.json` | Committed alongside the repo. |
| local | `/.ratel/config.local.json` | Per-user-per-project; add to your project's `.gitignore`. |

When you run `ratel-mcp serve --config a.json --config b.json --config c.json`, the configs are merged in order — last wins on `mcpServers` key collisions. The `link` command wires the right `--config` chain into Claude Code at each scope. The `import` wizard migrates selected native MCP entries into Ratel and can clean those imported entries out of the agent config as its second stage.

### Claude Code statusline

`ratel-mcp statusline` is a Claude Code statusline command. Claude passes a
JSON payload on stdin; the command writes two statusline rows to stdout and
fails open with a loading/no-telemetry line if the payload or telemetry is
missing.

```bash
ratel-mcp statusline install          # write ~/.claude/settings.json
ratel-mcp statusline install --force  # replace another configured statusLine
ratel-mcp statusline uninstall        # remove only a Ratel-owned statusLine
```

Install writes a user-scoped Claude Code setting:

```json
{
  "statusLine": {
    "type": "command",
    "command": "ratel-mcp statusline",
    "padding": 0,
    "refreshInterval": 30
  }
}
```

The statusline reports Ratel as enabled when Claude Code is configured to start
Ratel through a linked MCP entry or an enabled `ratel-mcp@...` plugin. It does
not install or enable the plugin itself.

### OAuth flow

HTTP and SSE upstreams that require OAuth authorization run through `ratel-mcp`'s loopback PKCE flow. From the CLI:

1. `ratel-mcp mcp add --scope user my-upstream https://mcp.example/mcp [--client-id ] [--callback-port ] [--oauth-scope ""]` — records the entry **and** drives the OAuth flow inline.
2. `ratel-mcp mcp auth my-upstream` — refresh-first. If a `refresh_token` is on disk, rotates silently (no browser). Falls back to PKCE only when refresh fails.
3. `ratel-mcp mcp auth --check` — read-only status report: tokens present, refresh availability, time-to-expiry.
4. `ratel-mcp mcp list` — shows a single-line auth column per entry: `ok` / `expired` / `needs auth` / `n/a`.

When the gateway boots, every HTTP/SSE upstream with stored tokens runs through a proactive refresh. A 401 during a live `invoke_tool` returns `{ error: "needs_auth", upstream }` so the agent can branch and call the `auth` MCP tool to recover.

### Telemetry

`ratel-mcp serve` writes one JSON line per event to `~/.ratel/telemetry//-.jsonl` by default — every search, invoke, gateway call, upstream MCP call, OAuth event, and Ratel's upstream tool-payload token estimate flows through the same JSONL ([ADR 0009](https://github.com/ratel-ai/ratel/blob/main/docs/adr/0009-trace-events-core-owned-schema.md)). Best-effort, sampleable, lossy on backpressure — query-log shaped, not oplog.

| Flag | Env | Purpose |
|---|---|---|
| `--telemetry off` | `RATEL_TELEMETRY=off` | Disable telemetry for this run. |
| `--telemetry-file ` | — | Override the JSONL path verbatim (no slugging). |
| — | `RATEL_TELEMETRY_DIR` | Override the default telemetry root. |

For summarizing the resulting JSONL stream, see [`@ratel-ai/cli`'s `ratel inspect`](https://github.com/ratel-ai/ratel/tree/main/src/integrations/cli) — it shares the on-disk format.

### Backups

Every `import`, `link`, `add`, `edit`, and `remove` snapshots the files it touches into `~/.ratel/backups//` with a `manifest.json`. `ratel-mcp backup list` shows what's available.

### Browser UI

```bash
ratel-mcp ui              # starts a local UI on an ephemeral 127.0.0.1 port, opens your browser
ratel-mcp ui --port 5731  # bind a specific port
ratel-mcp ui --no-open    # print the URL without launching a browser
```

The UI mirrors the CLI verbs across all three scopes: view/add/edit/remove servers, drive OAuth, import/link from Claude Code, and inspect backups. The server binds to `127.0.0.1` only and gates every request on a single-use session token printed in the launch URL. Stop it with `Ctrl-C`.

## Library quickstart

```ts
import { ToolCatalog } from "@ratel-ai/sdk";
import { createMcpServer } from "@ratel-ai/mcp-server";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const catalog = new ToolCatalog();
catalog.register({
  id: "read_file",
  name: "read_file",
  description: "Read a file from local disk.",
  inputSchema: { type: "object", properties: { path: { type: "string" } } },
  outputSchema: { type: "object", properties: { contents: { type: "string" } } },
  execute: async ({ path }) => ({ contents: await fs.readFile(path, "utf8") }),
});

const handle = await createMcpServer(catalog, {
  name: "my-gateway",
  version: "0.1.0",
  transport: new StdioServerTransport(),
});

// later, on shutdown:
await handle.close();
```

The MCP client connected to the other end will see `search_capabilities` and `invoke_tool` (and `get_skill_content` when skills are present). `search_capabilities` returns a `tools` bucket and a `skills` bucket. For backward compatibility the server also advertises the deprecated `search_tools` (its pre-0.2.0 tools-only result), so clients pinned to that name keep working; new clients should use `search_capabilities`. The catalog's tools are reachable through `invoke_tool`, never listed directly — that's the whole point (see [ADR 0003 in `ratel-ai/ratel`](https://github.com/ratel-ai/ratel/blob/main/docs/adr/0003-tool-selection-replace-vs-suggest.md)).

### `buildGatewayFromConfig`

Higher-level entrypoint that takes a parsed Ratel config (an `mcpServers` map mirroring Claude Code's shape) and spins up an upstream MCP `Client` per entry, registers each upstream's tools into a fresh catalog, and returns the catalog plus per-upstream metadata.

```ts
import { buildGatewayFromConfig, parseConfig } from "@ratel-ai/mcp-server";

const config = parseConfig(JSON.parse(await fs.readFile("./ratel-config.json", "utf8")));
const gateway = await buildGatewayFromConfig(config, {
  logger: (m) => console.error(m),
});

// gateway.catalog       -> ToolCatalog with every upstream tool registered
// gateway.upstreamServers -> [{ name, description?, toolCount }] for the search-tools description block
// await gateway.close() -> tears down every upstream client
```

If any single upstream fails to start, `buildGatewayFromConfig` logs the failure and the rest still register — the gateway stays available. The handle exposes `runAuthFlow()` (refresh-first; PKCE fallback) for HTTP/SSE upstreams marked `needsAuth`, and `setListChangedNotifier()` so the MCP server can re-list after a successful flow.

## Config shape

The config mirrors Claude Code's `.claude.json` `mcpServers` shape:

```json
{
  "mcpServers": {
    "ev": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-everything"],
      "description": "filesystem & shell utilities"
    },
    "remote": {
      "type": "http",
      "url": "https://example.com/mcp",
      "headers": { "Authorization": "Bearer xyz" }
    }
  }
}
```

`type` defaults to `"stdio"` when absent. `description` is optional metadata — used to seed the agent's awareness of each upstream via `search_capabilities`'s description, never sent over the upstream transport. `stdio` and `http` are wired up by `defaultTransportFactory`; `sse` and unknown types are accepted by `parseConfig` but skipped at runtime by the default factory (provide your own factory for sse).

## Result wrapping

Every `tools/call` response carries the gateway's return value as a JSON-serialized text block; plain-object returns are also surfaced as `structuredContent`:

```json
{
  "content": [{ "type": "text", "text": "{\"foo\":1}" }],
  "structuredContent": { "foo": 1 }
}
```

Arrays (e.g. the tool hits returned by `search_capabilities`) only travel in `content[0].text`, since MCP requires `structuredContent` to be a JSON object.

When `invoke_tool` drives a tool that was itself registered via `registerMcpServer`, the upstream's MCP-shaped result (`{ content, structuredContent }`) is nested inside our `structuredContent` one level deeper.

`invoke_tool`'s and `get_skill_content`'s error payloads (`{ error: "...", isError: true }` for unknown ids, bad args, or executor throws) are promoted to an MCP `isError: true` result by the server, so the host and model can tell a failed call from real content — the `error` field still carries the reason.

## Examples

- [`examples/claude-with-ratel/`](examples/claude-with-ratel/README.md) — Claude Code session fronted by `ratel-mcp` as the only MCP server.

## Build & test

```bash
pnpm install
pnpm build        # tsc → dist/
pnpm typecheck
pnpm lint         # biome
pnpm test         # vitest
```

CI runs all of the above on every PR.

## License

**MIT**. Free to use, modify, and redistribute. See [LICENSE.md](LICENSE.md).

## Related

- [`@ratel-ai/sdk`](https://github.com/ratel-ai/ratel/blob/main/src/sdk/ts/README.md) — the TypeScript SDK with `ToolCatalog`, `searchCapabilitiesTool`, `invokeToolTool`, `registerMcpServer`. Bundles `ratel-ai-core` (BM25 retrieval) via NAPI-RS.
- [`@ratel-ai/cli`](https://github.com/ratel-ai/ratel/tree/main/src/integrations/cli) — the long-term Ratel artifacts CLI (telemetry inspection today).
- [`ratel-ai/ratel`](https://github.com/ratel-ai/ratel) — overview, roadmap, ADRs, benchmark links.

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [ratel-ai](https://github.com/ratel-ai)
- **Source:** [ratel-ai/ratel-mcp](https://github.com/ratel-ai/ratel-mcp)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-ratel-ai-ratel-mcp
- Seller: https://agentstack.voostack.com/s/ratel-ai
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
