You have a GraphQL API. You want Claude (or any AI) to use it. The usual path: write wrapper functions, map types, handle auth, deal with pagination...
Or: one command.
npx graphql-to-mcp https://countries.trevorblades.com/graphql
That's it. Every query and mutation becomes an MCP tool. Claude can now search countries, filter by continent, look up languages — all from the schema it just introspected.
The Problem
GraphQL is great for developers. Typed schemas, introspection, nested queries — powerful stuff.
But LLMs hate nested input objects. Give Claude a mutation like this:
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) { id name }
}
Where CreateUserInput is:
input CreateUserInput {
name: String!
email: String!
address: AddressInput
}
input AddressInput {
street: String
city: String
country: String
}
The LLM has to construct:
{
"input": {
"name": "Alice",
"email": "alice@example.com",
"address": {
"street": "123 Main St",
"city": "Portland",
"country": "US"
}
}
}
This fails constantly. LLMs hallucinate extra nesting, miss required fields, or get the structure wrong. Deeply nested JSON is the #1 cause of tool-calling failures.
The Solution: Flat Schemas
graphql-to-mcp flattens every InputObject into simple key-value parameters:
| Parameter | Type | Required |
|---|---|---|
input_name |
string | yes |
input_email |
string | yes |
input_address_street |
string | no |
input_address_city |
string | no |
input_address_country |
string | no |
The LLM fills flat fields. The tool reconstructs the nested GraphQL variables automatically.
Result: near-perfect accuracy, even with complex mutations.
Setup in 30 Seconds
Claude Desktop / Cursor
Add to your MCP config:
{
"mcpServers": {
"my-api": {
"command": "npx",
"args": ["-y", "graphql-to-mcp", "https://your-api.com/graphql"]
}
}
}
With Auth
{
"mcpServers": {
"github": {
"command": "npx",
"args": [
"-y", "graphql-to-mcp",
"https://api.github.com/graphql",
"--bearer", "ghp_your_token",
"--prefix", "github"
]
}
}
}
Filter Operations
Only expose what you need:
{
"args": [
"-y", "graphql-to-mcp",
"https://api.example.com/graphql",
"--include", "get*",
"--exclude", "internal*"
]
}
What Happens Under the Hood
- Introspect — Fetches the full GraphQL schema via introspection query
-
Flatten — Every
InputObjecttype gets flattened into simple parameters - Generate — Each query/mutation becomes an MCP tool with a JSON Schema
- Execute — When the LLM calls a tool, flat args → nested GraphQL variables → POST to your endpoint
- Truncate — Large responses get smart-truncated (arrays sliced to 20, depth pruned to 5 levels)
Smart Truncation
GraphQL can return massive payloads. A listUsers with 10K results would blow up any LLM context window.
graphql-to-mcp handles this automatically:
- Arrays → sliced to 20 items, with
_meta: "… 9980 more items (10000 total)" - Deep nesting → pruned at 5 levels, with
"[Object(12 keys)]"summaries - Hard safety net at 50K characters
No configuration needed. The LLM gets enough data to understand the response without drowning in tokens.
Built-in Resilience
- Retry logic — automatic retries on 429/5xx with exponential backoff
- Timeout — configurable request timeout (default 30s)
- Partial errors — GraphQL partial errors are surfaced alongside data
Why Not Just Use [existing-tool]?
There are a few GraphQL MCP tools out there. Here's why this one is different:
| Feature | graphql-to-mcp | Others |
|---|---|---|
| Flat schemas | Yes — nested inputs flattened | No — raw nested JSON |
| Auto-introspection | Yes | Some |
| Smart truncation | Yes — array + depth pruning | No — raw or hard cut |
| Include/exclude filters | Yes — glob patterns | Rare |
| Retry on 429/5xx | Yes — exponential backoff | No |
| Zero config | Yes — just an endpoint URL | Most need config |
Try It Now
npx graphql-to-mcp https://countries.trevorblades.com/graphql
Then ask Claude: "What countries are in Europe?"
It just works.
Links:
- npm: graphql-to-mcp
- GitHub: Docat0209/mcp-graphql
- Also: mcp-openapi — same approach for REST/OpenAPI APIs
Top comments (0)