MCP Servers

A collection of Model Context Protocol servers, templates, tools and more.

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.

Created 5/12/2026
Updated about 10 hours ago
Repository documentation and setup instructions

@ratel-ai/mcp-server

Expose a Ratel catalog over MCP — and manage your MCP scopes from one CLI.

Ratel coreRoadmapDiscord

npm GitHub stars Discord license

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

  • a library that takes a Ratel ToolCatalog and exposes it as a Model Context Protocol server — the MCP client (Claude Desktop, an agent framework, an @modelcontextprotocol/sdk Client) sees search_tools + invoke_tool 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, which ingests an upstream MCP server's tools into a catalog. createMcpServer exposes a catalog as an MCP server.

Install

# 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:

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

CLI quickstart

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

# 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 Claude Code MCP setup into ratel-mcp's scopes, then point Claude at ratel-mcp
ratel-mcp mcp import

# Start the gateway over stdio (this is what Claude Code spawns after `import`)
ratel-mcp serve --config ~/.ratel/config.json

Run ratel-mcp <group> for the verbs in a group:

| Group | Verbs | |---|---| | mcp | add, remove, list, get, edit, import, link, auth | | backup | list, undo | | (top-level) | serve |

ratel-mcp mcp add — Claude-compatible

ratel-mcp mcp add [flags] <name> -- <command> [args...]      # stdio
ratel-mcp mcp add [flags] <name> <url>                       # 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 <id> / --client-secret <s> / --callback-port <n> / --oauth-scope <s> | OAuth client config for http/sse entries. DCR is preferred — pass --client-id only when the upstream doesn't support it. | | --description <text> | 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/<name>.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 | <root>/.ratel/config.json | Committed alongside the repo. | | local | <root>/.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 import wizard wires the right --config chain into Claude Code at each scope.

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 <id>] [--callback-port <n>] [--oauth-scope "<s>"] — 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/<project-slug>/<ISO-ts>-<short>.jsonl by default — every search, invoke, gateway call, upstream MCP call, and OAuth event flows through the same JSONL (ADR 0009). 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 <path> | — | 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 — it shares the on-disk format.

Backups & undo

Every import, link, add, edit, and remove snapshots the files it touches into ~/.ratel/backups/<ISO>/ with a manifest.json. ratel-mcp backup list shows what's available; ratel-mcp backup undo (deliberately hidden from --help) restores the most recent set.

Library quickstart

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 exactly two tools: search_tools and invoke_tool. The catalog's tools are reachable through invoke_tool, never listed directly — that's the whole point (see ADR 0003 in ratel-ai/ratel).

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.

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:

{
  "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_tools'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:

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

Arrays (e.g. the hits returned by search_tools) 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.

invokeToolTool's wrapped error payload ({ error: "..." } for unknown ids or executor throws) flows through as an ordinary structured result rather than an MCP isError: true — clients can branch on the field.

Examples

Build & test

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

CI runs all of the above on every PR.

License

Elastic License 2.0, with a grant making it free for OSI-approved open-source projects. Non-OSS / commercial production use requires a commercial license. See LICENSE.md.

Related

  • @ratel-ai/sdk — the TypeScript SDK with ToolCatalog, searchToolsTool, invokeToolTool, registerMcpServer. Bundles ratel-ai-core (BM25 retrieval) via NAPI-RS.
  • @ratel-ai/cli — the long-term Ratel artifacts CLI (telemetry inspection today).
  • ratel-ai/ratel — overview, roadmap, ADRs, benchmark links.
Quick Setup
Installation guide for this server

Install Package (if required)

npx @modelcontextprotocol/server-ratel-mcp

Cursor configuration (mcp.json)

{ "mcpServers": { "ratel-ai-ratel-mcp": { "command": "npx", "args": [ "ratel-ai-ratel-mcp" ] } } }