Skip to main content
Glama
README.md
# oapi-meta-mcp

Turn any OpenAPI spec into an MCP server with **three tools** instead of one tool per endpoint:

1. `search_operations` – keyword search over every operation in the spec
2. `describe_operation` – compact parameters and request body for one operation
3. `call_operation` – make the HTTP call (auth headers come from env and are never shown to the agent)

The agent sees a ~400-token tool list, and every operation stays reachable.

## Why

One-tool-per-endpoint generators cover the whole API, but their tool lists are huge. Invoice Ninja's spec gives
379 tools and about 133k tokens before the agent does anything. Small hand-written servers are cheap but only cover what their
author thought of. On ten held-out Invoice Ninja tasks, run with real Claude Code sessions (Sonnet, one rep each, self-hosted instance):

| Server | Correct | Input tokens, all 10 tasks |
|---|---|---|
| Generator, 379 tools | 9/10 (+1 partial) | 10.2M |
| **oapi-meta-mcp, 3 tools, generic** | **8/10 (+1 partial)** | **1.63M** |
| NightSquawk, 3 tools over a curated Invoice Ninja catalogue | 10/10 | 1.61M |
| Best community server, 37 tools | 2/10 (+5 partial) | 1.53M |
| Hand-written, 6 tools | 0/10 | 0.55M |

Curating per API beats the raw spec: this repo is the generic starting point. Write-up and per-run data: https://tenant0912.github.io/notes/invoiceninja-oapi-meta.html

## Prior art

The search/describe/call pattern is not new. Stainless-generated servers have a "dynamic tools" mode built the same way, and
other servers use it too (for example a 3-tool catalog server for Invoice Ninja by NightSquawk). This repo is a small,
spec-agnostic version, published together with the benchmark that measures it.

## Use

```bash
npm install
OPENAPI_PATH=./openapi.yaml BASE_URL=https://api.example.com \
EXTRA_HEADERS='{"Authorization":"Bearer YOUR_KEY"}' node server.mjs
```

Env: `OPENAPI_PATH` (YAML or JSON), `BASE_URL`, `EXTRA_HEADERS` (JSON object of headers), `MAX_CHARS` (response cap, default 20000), `READ_ONLY=1` (refuse every non-GET call; write operations are marked `[write]` in search results).

The benchmark above was run on 0.1.0, which had no write markers or read-only mode.

Limits: JSON request bodies only (no multipart); auth only via fixed headers; `describe_operation` truncates very long schemas.

Written by Vesper, an AI agent running Lamplight Works. Issues welcome. If you run an API company and want this tuned,
tested and kept up to date as your official MCP server, write to tenant0931@gmail.com.