Skip to content

Library Mode

The @graphql2mcp/lib package lets you register GraphQL tools on an existing MCP server instance. This is useful when you want to combine GraphQL tools with your own custom tools in a single server.

Installation

bash
npm install @graphql2mcp/lib

Basic Usage

typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { registerGraphQLTools } from '@graphql2mcp/lib';

const server = new McpServer({ name: 'my-server', version: '1.0.0' });

const result = registerGraphQLTools(server, {
    source: './schema.graphql',
    endpoint: 'https://api.example.com/graphql'
});

console.error(`Registered ${result.count} tools`);

const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
await server.connect(transport);

registerGraphQLTools(server, options)

Registers GraphQL tools on an MCP server instance and returns metadata about the registered tools. Does not create or manage transports -- the caller controls the server lifecycle.

Parameters:

  • server -- an MCP server instance (or any object with a compatible registerTool method)
  • options -- a GraphQLToolsOptions object

Returns: RegisterGraphQLToolsResult

getGraphQLTools(options)

Generates tools with bound execution handlers without registering them on a server. Use this when you need full control over how tools are registered, or when integrating with a non-standard MCP server.

typescript
import { getGraphQLTools } from '@graphql2mcp/lib';

const { tools, count } = getGraphQLTools({
    source: './schema.graphql',
    endpoint: 'https://api.example.com/graphql',
    mutations: 'all'
});

for (const tool of tools) {
    // Each tool has: name, title, description, inputSchema, annotations,
    // handler, operationType, fieldName, queryDocument
    server.registerTool(
        tool.name,
        {
            title: tool.title,
            description: tool.description,
            inputSchema: tool.inputSchema,
            annotations: tool.annotations
        },
        tool.handler
    );
}

Parameters:

Returns: GetGraphQLToolsResult with tools: GraphQLToolEntry[] and count: number

loadSchemaFromUrl(options)

Introspect a live GraphQL endpoint and return the schema. Re-exported from @graphql2mcp/core for convenience.

typescript
import { loadSchemaFromUrl, registerGraphQLTools } from '@graphql2mcp/lib';

const schema = await loadSchemaFromUrl({
    url: 'https://api.example.com/graphql',
    headers: { Authorization: 'Bearer YOUR_TOKEN' }
});

registerGraphQLTools(server, {
    schema,
    endpoint: 'https://api.example.com/graphql',
    headers: { Authorization: 'Bearer YOUR_TOKEN' }
});

Options Reference

PropertyTypeDefaultDescription
sourcestringSDL string, file path, glob, or introspection JSON path
schemaGraphQLSchemaPre-built GraphQL schema object (alternative to source)
endpointstring(required)GraphQL execution endpoint URL
headersRecord<string, string>{}HTTP headers for runtime execution
mutationsMutationMode'none'Mutation exposure mode ('all', 'none', or { whitelist: [...] })
depthnumber3Field selection depth for return types
includestring[]Only include these operation names
excludestring[]Exclude these operation names
queryPrefixstring'query_'Prefix for query tool names
mutationPrefixstring'mutation_'Prefix for mutation tool names
customScalarsRecord<string, z.ZodType>Custom scalar-to-Zod mappings
timeoutnumber30000Request timeout in milliseconds

Either source or schema must be provided. If both are given, schema takes precedence.

Result Type

typescript
interface RegisterGraphQLToolsResult {
    tools: RegisteredToolInfo[];
    count: number;
}

interface RegisteredToolInfo {
    name: string;
    title: string;
    operationType: 'query' | 'mutation';
    fieldName: string;
}

The result tells you which tools were registered and how many. This is useful for logging or validation.

Combining with Custom Tools

A common pattern is to register your own tools alongside GraphQL tools:

typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { z } from 'zod';
import { registerGraphQLTools } from '@graphql2mcp/lib';

const server = new McpServer({ name: 'my-hybrid-server', version: '1.0.0' });

// Register custom tools
server.registerTool(
    'ping',
    {
        title: 'Ping',
        description: 'Simple health check tool',
        inputSchema: {
            message: z.string().optional().describe('Optional message to echo back')
        }
    },
    ({ message }) => ({
        content: [{ type: 'text', text: `pong: ${message ?? 'no message'}` }]
    })
);

// Register GraphQL tools alongside custom tools
const result = registerGraphQLTools(server, {
    source: `
        type Query {
            "Get a list of countries"
            countries(filter: CountryFilter): [Country!]!
            "Get a specific country by code"
            country(code: ID!): Country
        }

        type Country {
            code: ID!
            name: String!
            capital: String
            currency: String
        }

        input CountryFilter {
            code: String
            continent: String
        }
    `,
    endpoint: 'https://countries.trevorblades.com/graphql'
});

console.error(`Registered ${result.count} GraphQL tools`);

const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
await server.connect(transport);

Using a Pre-built Schema

If you already have a GraphQLSchema object (e.g., from graphql-js or a code-first framework), pass it directly:

typescript
import { buildSchema } from 'graphql';
import { registerGraphQLTools } from '@graphql2mcp/lib';

const schema = buildSchema(`
    type Query {
        hello: String
    }
`);

registerGraphQLTools(server, {
    schema,
    endpoint: 'https://api.example.com/graphql'
});

Custom Scalars

Map custom GraphQL scalars to Zod schemas for proper input validation:

typescript
import { z } from 'zod';
import { registerGraphQLTools } from '@graphql2mcp/lib';

registerGraphQLTools(server, {
    source: './schema.graphql',
    endpoint: 'https://api.example.com/graphql',
    customScalars: {
        DateTime: z.string().datetime(),
        JSON: z.record(z.string(), z.unknown()),
        BigInt: z.number().int()
    }
});

Without custom scalar mappings, unknown scalars default to z.string().

With Mutations

See the Mutations guide for a detailed explanation of mutation modes. Here is a quick example:

typescript
registerGraphQLTools(server, {
    source: './schema.graphql',
    endpoint: 'https://api.example.com/graphql',
    mutations: { whitelist: ['createUser', 'updateUser'] }
});

Type Reference

GraphQLToolEntry

Each tool returned by getGraphQLTools:

typescript
interface GraphQLToolEntry {
    name: string; // e.g. "query_users"
    title: string; // e.g. "Query: users"
    description: string;
    inputSchema: Record<string, z.ZodType>;
    annotations: ToolAnnotations;
    handler: (args: Record<string, unknown>) => Promise<CallToolResult>;
    operationType: 'query' | 'mutation';
    fieldName: string; // original GraphQL field name
    queryDocument: string; // the GraphQL document sent at runtime
}

MutationMode

typescript
type MutationMode = 'none' | 'all' | { whitelist: string[] };
  • 'none' -- only queries are converted to tools (default)
  • 'all' -- all queries and mutations are converted
  • { whitelist: ['name1', 'name2'] } -- only the named mutations are converted

ToolAnnotations

Re-exported from @modelcontextprotocol/sdk. Queries get readOnlyHint: true, destructiveHint: false. Mutations get readOnlyHint: false, destructiveHint: true.

McpServerLike

Structural interface for the server parameter. Both McpServer from the MCP SDK and custom implementations satisfy this:

typescript
interface McpServerLike {
    registerTool(
        name: string,
        config: {
            title?: string;
            description?: string;
            inputSchema?: Record<string, z.ZodType>;
            annotations?: ToolAnnotations;
        },
        handler: (args: Record<string, unknown>) => Promise<CallToolResult>
    ): unknown;
}

Released under the MIT License.