Skip to content

A Go-based MCP (Model Context Protocol) server that exposes tools allowing an LLM to generate and execute valid GraphQL queries via HTTP.

Notifications You must be signed in to change notification settings

edouard-claude/mcp-graphql

Repository files navigation

MCP GraphQL Server

A Go-based MCP (Model Context Protocol) server that exposes tools allowing an LLM to generate and execute valid GraphQL queries via HTTP.

πŸš€ Features

  • Standard MCP transport: JSON-RPC 2.0 over stdio
  • GraphQL HTTP client: Uses only Go standard library
  • 5 MCP tools:
    • graphql.introspectSchema β€” GraphQL schema introspection
    • graphql.printSDL β€” SDL generation from introspection
    • graphql.execute β€” Query/mutation execution with variables
    • graphql.validate β€” Query validation against schema
    • graphql.listOperations β€” Operations and variables extraction
  • Header management: Automatic injection of x-api-key, optional Authorization
  • Security: Secret redaction in logs, timeouts, exponential retries
  • Schema cache: Configurable TTL with forceRefresh option

πŸ“¦ Installation

Pre-compiled binaries

Download the binary for your platform from releases.

Build from source

git clone https://github.com/edouard-claude/mcp-graphql.git
cd mcp-graphql-server
make build

Docker

docker build -t mcp-graphql-server .
docker run -e GRAPHQL_ENDPOINT=https://api.example.com/graphql mcp-graphql-server

βš™οΈ Configuration

Environment variables

Variable Description Default Required
GRAPHQL_ENDPOINT GraphQL endpoint URL - βœ…
GRAPHQL_X_API_KEY API key for x-api-key header - ❌
GRAPHQL_AUTH_BEARER Default Bearer token - ❌
HTTP_TIMEOUT_SECONDS HTTP timeout in seconds 30 ❌
HTTP_MAX_RETRIES Maximum number of retries 2 ❌
LOG_LEVEL Log level (info, debug, error) info ❌
HTTP_MAX_PAYLOAD_MB Maximum payload size in MB 2 ❌
SCHEMA_CACHE_TTL_MINUTES Schema cache TTL in minutes 10 ❌

Configuration file (optional)

Create a config.yaml or config.json file:

endpoint: "https://api.example.com/graphql"
defaultHeaders:
  x-api-key: "${GRAPHQL_X_API_KEY}"
retry:
  maxRetries: 2
  backoffMs: 300
httpTimeout: 30
httpMaxRetries: 2
logLevel: "info"
maxPayloadSize: 2097152
schemaCacheTTL: 10

Run with: ./mcp-graphql-server --config=config.yaml

πŸ› οΈ MCP Tools

1. graphql.introspectSchema

Introspects the GraphQL schema and returns the introspection result.

Parameters:

{
  "headers": { "Authorization": "Bearer xxx" },
  "withDescriptions": true,
  "forceRefresh": false
}

Example call:

echo '{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "graphql.introspectSchema",
    "arguments": { "withDescriptions": true }
  }
}' | ./mcp-graphql-server

2. graphql.printSDL

Generates the GraphQL schema in SDL format.

Parameters:

{
  "headers": {},
  "preferFederation": false,
  "forceRefresh": false
}

Example call:

echo '{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "graphql.printSDL",
    "arguments": { "preferFederation": false }
  }
}' | ./mcp-graphql-server

3. graphql.execute

Executes a GraphQL query or mutation.

Parameters:

{
  "operationName": "GetUser",
  "query": "query GetUser($id: ID!) { user(id: $id) { id name } }",
  "variables": { "id": "123" },
  "headers": { "Authorization": "Bearer xxx" },
  "timeoutSeconds": 30
}

Example call:

echo '{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "graphql.execute",
    "arguments": {
      "operationName": "GetUser",
      "query": "query GetUser($id: ID!) { user(id: $id) { id name } }",
      "variables": { "id": "123" },
      "headers": { "Authorization": "Bearer XYZ" }
    }
  }
}' | ./mcp-graphql-server

4. graphql.validate

Validates a GraphQL query against the schema.

Parameters:

{
  "query": "query GetUser($id: ID!) { user(id: $id) { id name } }",
  "operationName": "GetUser",
  "headers": {}
}

Example call:

echo '{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "graphql.validate",
    "arguments": {
      "query": "query GetUser($id: ID!) { user(id: $id) { id name } }",
      "operationName": "GetUser"
    }
  }
}' | ./mcp-graphql-server

5. graphql.listOperations

Extracts all operations and their variables from a GraphQL document.

Parameters:

{
  "query": "query GetUser($id: ID!) { user(id: $id) { id name } } mutation CreateUser($name: String!) { createUser(name: $name) { id } }"
}

Example call:

echo '{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "tools/call",
  "params": {
    "name": "graphql.listOperations",
    "arguments": {
      "query": "query GetUser($id: ID!) { user(id: $id) { id name } } mutation CreateUser($name: String!) { createUser(name: $name) { id } }"
    }
  }
}' | ./mcp-graphql-server

πŸ”’ Security and Headers

Header merge order (increasing priority)

  1. Default headers (env/config): x-api-key, Authorization (if GRAPHQL_AUTH_BEARER)
  2. Tool parameter headers: Override defaults

Allowed headers

Only these headers can be passed via the headers parameter:

  • Authorization
  • x-api-key
  • x-request-id
  • x-tenant-id
  • content-type

Secure configuration example

export GRAPHQL_ENDPOINT="https://api.example.com/graphql"
export GRAPHQL_X_API_KEY="your-api-key"
export GRAPHQL_AUTH_BEARER="your-default-token"
export LOG_LEVEL="info"

πŸ“ Complete examples

Complete workflow with jq

#!/bin/bash

# Configuration
ENDPOINT="https://api.example.com/graphql"
API_KEY="your-api-key"

# 1. List available tools
echo "=== Available tools ==="
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | \
  ./mcp-graphql-server | jq '.result.tools[].name'

# 2. Introspect schema
echo -e "\n=== Schema introspection ==="
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"graphql.introspectSchema","arguments":{"withDescriptions":true}}}' | \
  ./mcp-graphql-server | jq '.result.content[0].text' | jq '.introspection.queryType.name'

# 3. Generate SDL
echo -e "\n=== SDL generation ==="
echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"graphql.printSDL","arguments":{}}}' | \
  ./mcp-graphql-server | jq -r '.result.content[0].text' | jq -r '.sdl' | head -20

# 4. List operations from a query
echo -e "\n=== Operations extraction ==="
QUERY='query GetUser($id: ID!) { user(id: $id) { id name email } } mutation CreateUser($name: String!, $email: String!) { createUser(name: $name, email: $email) { id } }'
echo "{\"jsonrpc\":\"2.0\",\"id\":4,\"method\":\"tools/call\",\"params\":{\"name\":\"graphql.listOperations\",\"arguments\":{\"query\":\"$QUERY\"}}}" | \
  ./mcp-graphql-server | jq '.result.content[0].text' | jq '.operations[]'

# 5. Validate a query
echo -e "\n=== Query validation ==="
echo "{\"jsonrpc\":\"2.0\",\"id\":5,\"method\":\"tools/call\",\"params\":{\"name\":\"graphql.validate\",\"arguments\":{\"query\":\"$QUERY\",\"operationName\":\"GetUser\"}}}" | \
  ./mcp-graphql-server | jq '.result.content[0].text' | jq '.isValid'

# 6. Execute a query
echo -e "\n=== Query execution ==="
echo "{\"jsonrpc\":\"2.0\",\"id\":6,\"method\":\"tools/call\",\"params\":{\"name\":\"graphql.execute\",\"arguments\":{\"operationName\":\"GetUser\",\"query\":\"query GetUser(\$id: ID!) { user(id: \$id) { id name } }\",\"variables\":{\"id\":\"123\"}}}}}" | \
  ./mcp-graphql-server | jq '.result.content[0].text'

Example with curl and a public GraphQL server

# Use Rick and Morty GraphQL API as example
export GRAPHQL_ENDPOINT="https://rickandmortyapi.com/graphql"

# Introspection
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"graphql.introspectSchema","arguments":{"withDescriptions":true}}}' | \
  ./mcp-graphql-server | jq '.result.content[0].text' | jq '.introspection.queryType.name'

# Query execution
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"graphql.execute","arguments":{"query":"{ characters { results { name } } }"}}}' | \
  ./mcp-graphql-server | jq '.result.content[0].text'

# SDL generation
echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"graphql.printSDL","arguments":{}}}' | \
  ./mcp-graphql-server | jq -r '.result.content[0].text' | jq -r '.sdl' | head -20

πŸ§ͺ Tests

# Run all tests
make test

# Tests with coverage
make test-coverage

# Specific tests
go test -v ./internal/graphql/...
go test -v ./internal/util/...

πŸ—οΈ Development

Project structure

/cmd/mcp-graphql-server
  main.go                    # Entry point
/internal/mcp
  server.go                  # JSON-RPC server, tool registry
  tools.go                   # Tool definitions and handlers
  types.go                   # MCP request/response structures
/internal/graphql
  client.go                  # HTTP client, execution
  introspection.go           # Introspection request and cache
  sdl.go                     # SDL generation
  validate.go                # Basic schema validation
  ops.go                     # Operation and variable extraction
/internal/config
  config.go                  # Environment + file + flag configuration
/internal/logging
  logger.go                  # Logger with redaction
/internal/util
  redact.go                  # Secret redaction
  retry.go                   # Backoff/retry

Development commands

# Formatting and linting
make fmt vet

# Build and test
make all

# Run in development mode
make run-example

# Build for all platforms
make build-all

# Create a release
make release

πŸ“Š Monitoring and Logs

Log levels

  • debug: HTTP request details, cache, validation
  • info: Important operations, recoverable errors
  • error: Critical errors only

Example logs

2024-01-15T10:30:00Z INFO Starting MCP GraphQL server endpoint=https://api.example.com/graphql
2024-01-15T10:30:01Z DEBUG Executing GraphQL request endpoint=https://api.example.com/graphql operationName=GetUser headers=map[Authorization:**** x-api-key:****]
2024-01-15T10:30:01Z DEBUG GraphQL response received hasData=true errorCount=0

πŸ› Troubleshooting

Common issues

  1. Error "GRAPHQL_ENDPOINT is required"

    export GRAPHQL_ENDPOINT="https://your-api.com/graphql"
  2. Timeout on requests

    export HTTP_TIMEOUT_SECONDS=60
  3. Authentication errors

    export GRAPHQL_X_API_KEY="your-api-key"
    # or
    export GRAPHQL_AUTH_BEARER="your-token"
  4. Stale schema cache Use "forceRefresh": true in tool parameters.

Debugging

# Enable detailed logs
export LOG_LEVEL=debug
./mcp-graphql-server

# Test connectivity
curl -X POST https://your-api.com/graphql \
  -H "Content-Type: application/json" \
  -H "x-api-key: your-key" \
  -d '{"query":"{ __schema { queryType { name } } }"}'

πŸ“„ License

MIT License - see the LICENSE file for details.

🀝 Contributing

  1. Fork the project
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“ž Support

About

A Go-based MCP (Model Context Protocol) server that exposes tools allowing an LLM to generate and execute valid GraphQL queries via HTTP.

Resources

Stars

Watchers

Forks