loading…
Search for a command to run...
loading…
Exposes a Ratel tool catalog over MCP with two tools (search_tools and invoke_tool), and includes a CLI for managing multi-scope MCP configurations, OAuth flows
Exposes a Ratel tool catalog over MCP with two tools (search_tools and invoke_tool), and includes a CLI for managing multi-scope MCP configurations, OAuth flows, and telemetry.
Ratel core • Roadmap • Discord
@ratel-ai/mcp-server is two things in one package:
@modelcontextprotocol/sdk Client) sees search_capabilities + invoke_tool (plus get_skill_content when skills are configured) instead of every upstream's full tool list;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.
# 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
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.
Add the remote marketplace, then install Ratel MCP from the Ratel marketplace in Codex:
codex plugin marketplace add ratel-ai/ratel-mcp
For Codex local development from a checkout, run this from the repo root:
codex plugin marketplace add .
Add the remote marketplace and install the plugin:
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:
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 and
plugin reference.
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 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 <group> 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-compatibleratel-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).
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 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.
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.
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:
{
"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.
HTTP and SSE upstreams that require OAuth authorization run through ratel-mcp's loopback PKCE flow. From the CLI:
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.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.ratel-mcp mcp auth --check — read-only status report: tokens present, refresh availability, time-to-expiry.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.
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, OAuth event, and Ratel's upstream tool-payload token estimate 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.
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 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.
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).
buildGatewayFromConfigHigher-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.
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_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).
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 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.
ratel-mcp as the only MCP server.pnpm install
pnpm build # tsc → dist/
pnpm typecheck
pnpm lint # biome
pnpm test # vitest
CI runs all of the above on every PR.
MIT. Free to use, modify, and redistribute. See LICENSE.md.
ToolCatalog, searchCapabilitiesTool, invokeToolTool, registerMcpServer. Bundles ratel-ai-core (BM25 retrieval) via NAPI-RS.Run in your terminal:
claude mcp add ratel-ai-mcp-server -- npx Yes, @Ratel Ai/ Server MCP is free — one-click install via Unyly at no cost.
No, @Ratel Ai/ Server runs without API keys or environment variables.
A hosted option is available: Unyly runs the server in the cloud, no local setup required.
Open @Ratel Ai/ Server on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Web content fetching and conversion for efficient LLM usage.
Retrieval from AWS Knowledge Base using Bedrock Agent Runtime.
by modelcontextprotocolProvides auto-configuration for setting up an MCP server in Spring Boot applications.
A very streamlined mcp client that supports calling and monitoring stdio/sse/streamableHttp, and can also view request responses through the /logs page. It also
by xuzexin-hzNot sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All ai MCPs