graphql-mcp
by simoncrypta
README.md
**Experimental PoC**
# graphql-mcp
An MCP (Model Context Protocol) server that exposes GraphQL operations as
tools for AI models. Point it at any GraphQL endpoint and a schema, and it
turns your operations into typed MCP tools — with schema exploration tools,
hot reload, OAuth resource-server auth, MCP prompts, MCP Apps widgets, and
OpenTelemetry built in.
Configuration is plain TypeScript — no YAML, no env-var templating; read
`process.env` directly in your config file.
## Quick start
```bash
npm install graphql-mcp # or pnpm add / vp add
```
Create `graphql-mcp.config.ts`:
```ts
import { defineConfig } from "graphql-mcp";
export default defineConfig({
endpoint: "http://localhost:4000/graphql",
schema: { source: "local", path: "./schema.graphql" },
operations: { source: "local", paths: ["./operations"] },
tools: {
introspect: { enabled: true },
search: { enabled: true },
validate: { enabled: true },
execute: { enabled: true },
},
});
```
Run it:
```bash
graphql-mcp # stdio transport (default)
graphql-mcp path/to/config.ts # explicit config path
```
Local schema files, operation files, prompt files, and the config file itself
are watched: edits hot reload without dropping connected sessions (config
changes trigger a validated in-process restart; `SIGHUP` forces one).
## How operations become tools
Every named GraphQL operation in your operation files becomes one MCP tool:
- The tool name is the operation name; leading `#` comments become the tool
description (otherwise one is generated from schema descriptions plus the
tree-shaken SDL of the types the operation touches).
- The input schema is derived from the operation's variables (non-null
variables are required; input objects, enums, lists, and custom scalars are
fully expanded). `#` comments before individual variables override their
descriptions.
- Queries are annotated read-only and idempotent; mutations destructive.
Mutations are skipped unless `overrides.mutationMode` is `explicit` or
`all`.
- Fields marked `@private` are stripped from the query sent downstream and
filtered out of the structured result the model sees (the full response is
preserved in `_meta.structuredContent` for the host client).
Operation sources: `local` (watched `.graphql` files/directories), `manifest`
(a local `apollo-persisted-query-manifest` v1 JSON file), or `none`.
## Schema tools
Four individually toggleable tools under `tools`:
| Tool | What it does |
| ------------ | --------------------------------------------------------------------------------------------------------------------------- |
| `introspect` | Returns the SDL for a named type, recursing to a `depth`; optional token-cheap minified encoding. |
| `search` | Full-text search over type names, descriptions, and fields; returns tree-shaken SDL along scored paths from the root types. |
| `validate` | Validates an operation against the schema without executing it. |
| `execute` | Executes an ad hoc operation written by the model (mutations only when `mutationMode: "all"`). |
Each accepts an optional `hint` appended to its description.
## Transports
- `stdio` (default) — for local MCP clients.
- `http` — streamable HTTP on `/mcp` with per-session state (or stateless via
`statefulMode: false`), CORS, Host/Origin validation against DNS rebinding,
a `/health` endpoint (`?live` / `?ready`), and graceful shutdown.
### OAuth 2.1 resource server
Nest `auth` under the HTTP transport to require bearer tokens:
```ts
transport: {
type: "http",
port: 8000,
auth: {
servers: ["https://auth.example.com"], // OIDC/OAuth discovery + JWKS
audiences: ["my-api"],
scopes: ["read:graphql"],
scopeMode: "require_all",
},
},
overrides: {
requiredScopes: { DeleteUser: ["admin"] }, // per-operation step-up
},
```
The server publishes RFC 9728 protected-resource metadata at
`/.well-known/oauth-protected-resource`, answers challenges with
`WWW-Authenticate` (including `insufficient_scope`), and passes validated
tokens through to the GraphQL endpoint (disable with
`disableAuthTokenPassthrough`).
## Prompts and MCP Apps
- `prompts: { directory: "./prompts" }` serves Markdown files with YAML
frontmatter (`name`, `description`, `arguments`) as MCP prompts, with
`{{arg}}` substitution and hot reload.
- `apps: { directory: "./apps" }` serves MCP Apps: each subdirectory with an
`.application-manifest.json` contributes a `ui://widget/...` HTML resource
and entrypoint tools backed by GraphQL operations, including prefetch
operations, extra inputs/outputs, CSP and widget settings, and
OpenAI-Apps-SDK/MCP-Apps targeting via `?appTarget=` or client capabilities.
## Telemetry
```ts
telemetry: {
serviceName: "my-graphql-mcp",
exporters: {
metrics: { otlp: { protocol: "http/protobuf" } },
tracing: { otlp: { endpoint: "http://collector:4318/v1/traces" } },
},
},
```
Emits `graphql_mcp.operation.count` / `graphql_mcp.operation.duration`
metrics and per-tool-call spans, with configurable attribute omission,
export interval, and sampler.
## Config reference
Top-level keys (all optional except `schema`):
| Key | Purpose |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `endpoint` | Target GraphQL URL (default `http://localhost:4000/graphql`). |
| `schema` | `{ source: "local", path, watch? }` — the SDL file to serve from. |
| `operations` | `local` paths, `manifest` file, or `none`. |
| `transport` | `{ type: "stdio" }` or `{ type: "http", address?, port?, statefulMode?, hostValidation?, auth? }`. |
| `tools` | Enable/configure `introspect`, `search`, `validate`, `execute`. |
| `overrides` | `mutationMode`, `descriptions`, `annotations`, `requiredScopes`, `enableOutputSchema`, `disableTypeDescription`, `disableSchemaDescription`. |
| `headers` / `forwardHeaders` | Static headers and client headers forwarded to GraphQL. |
| `cors`, `healthCheck` | HTTP transport policies. |
| `serverInfo`, `instructions` | MCP identity and initialize instructions. |
| `customScalars` | Inline map of scalar name to JSON Schema, or a path to a JSON file. |
| `prompts`, `apps` | Prompt and MCP App directories. |
| `logging` | `level`, optional `path` (rotating file logs), `rotation`. |
| `telemetry` | OpenTelemetry metrics/tracing via OTLP. |
The config is validated with helpful errors; unknown keys are rejected. See
[examples/graphql-mcp.config.ts](examples/graphql-mcp.config.ts) for a
commented full example.
## Development
```bash
vp install # install dependencies
vp check # format, lint, type check
vp test # run the test suite
vp pack # build dist/ (library + CLI)
```
## Acknowledgements
The behavior of this server is a TypeScript reimplementation inspired by the
MIT-licensed [Apollo MCP Server](https://github.com/apollographql/apollo-mcp-server)
(Rust), excluding its GraphOS-specific functionality.
## License
MIT
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues