Skip to content

A bridge implementation connecting GraphQL APIs with the Model Context Protocol (MCP), enabling seamless integration between GraphQL services and MCP-compatible AI systems. This tool facilitates data exchange and API communication by translating GraphQL operations into MCP-compatible formats.

License

Notifications You must be signed in to change notification settings

pshaddel/graphql-mcp-bridge

Repository files navigation

GraphQL MCP Bridge

CI License: MIT codecov Node.js Version TypeScript npm version

logo-light
A powerful bridge implementation connecting GraphQL APIs with the Model Context Protocol (MCP), enabling seamless integration between GraphQL services and MCP-compatible AI systems. Transform any GraphQL schema into type-safe, validated MCP tools with intelligent field selection and comprehensive runtime validation powered by Zod.
Screenshot 2025-09-14 at 01 04 08 Screenshot 2025-09-14 at 01 02 40

Features

  • 🔗 GraphQL to MCP Bridge: Convert GraphQL schemas to MCP-compatible function definitions
  • 📝 Description Preservation: Automatically preserves GraphQL field descriptions as tool descriptions
  • ⚙️ Flexible Configuration: Selective operation generation with customizable naming patterns
    • Operation Type Control: Choose which operation types to include (queries, mutations, subscriptions)
    • Custom Prefixes: Add prefixes to operation names for better organization
    • Granular Control: Fine-tune which operations are exposed as MCP tools
    • Selective Tool Generation: Skip operations using ignore phrases in descriptions
  • 🛡️ Comprehensive Zod Validation:
    • Input Validation: Automatic validation of operation arguments with type-safe Zod schemas
    • Output Selection Validation: Validate field selection objects for GraphQL queries and mutations
    • Nested Type Support: Handle complex nested inputs, enums, interfaces, and union types
    • Circular Reference Protection: Safe handling of self-referencing types
  • 🎯 Smart Field Selection:
    • Dynamic field selection with support for nested objects, unions, and interfaces
    • Default Selection Generation: Automatically select scalar and enum fields when no selection provided
    • Strict Schema Validation: Prevent selection of non-existent fields
  • 🚀 Query Generation: Automatic GraphQL query string generation with variable handling
  • 📝 TypeScript Support: Full TypeScript support with comprehensive type definitions
  • ⚙️ Advanced Schema Support: Handles enums, interfaces, unions, complex inputs, and nested types
  • Runtime Safety:
    • Built-in validation for all operations before execution
    • User-friendly error messages for validation failures
    • Fallback handling for edge cases and malformed data

Installation

From npm

npm install graphql-mcp-bridge
# or
pnpm install graphql-mcp-bridge
# or
yarn add graphql-mcp-bridge

That's it! No authentication or special configuration required.

Quick Start

import { schemaParser } from 'graphql-mcp-bridge';

// Define your GraphQL schema
const schema = `
  type User {
    id: ID!
    username: String!
    email: String!
    posts: [Post!]!
  }

  type Post {
    id: ID!
    title: String!
    content: String!
  }

  type Query {
    """
    Retrieves a user by their unique identifier
    """
    user(id: ID!): User

    """
    Fetches a list of posts with optional limit
    """
    posts(limit: Int): [Post!]!
  }

  type Mutation {
    """
    Creates a new user account
    """
    createUser(input: CreateUserInput!): User
  }

  input CreateUserInput {
    username: String!
    email: String!
  }
`;

// Convert to MCP tools (queries only by default)
const tools = await schemaParser(schema);

// Or with custom configuration to include mutations and subscriptions
const toolsWithMutations = await schemaParser(schema, {
  query: true,
  mutation: true,
  subscription: false,
  queryPrefix: 'get_',
  mutationPrefix: 'do_'
});

// Use the generated tools
const userTool = tools.find(tool => tool.name === 'user');

// Access the preserved description from GraphQL schema
console.log(userTool.description);
// Output: "Retrieves a user by their unique identifier"

// The tool automatically validates inputs and field selections
const result = await userTool.execution(
  { id: "123" }, // Variables - validated against input schema
  { id: true, username: true, posts: { title: true } } // Field selection - validated against output schema
);

console.log(result.query);
// Output: query user($id: ID!) { user(id: $id) { id username posts { title } } }

// Access the validation schemas directly
console.log('Input schema:', userTool.inputSchema);
console.log('Output schema:', userTool.outputSchema);
console.log('Description:', userTool.description); // GraphQL field description

Integration Example

Here's a simplified example of how to integrate the generated tools with an MCP server:

import { schemaParser } from 'graphql-mcp-bridge';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp';

export async function registerSchemaTools(
  parsedSchema: Tool[],
  mcpServer: McpServer,
) {
  for (const tool of parsedSchema) {
    mcpServer.registerTool(
      tool.name,
      {
        description: tool.description || "No description provided",
        inputSchema: {
          input: tool.inputSchema,
          output: tool.outputSchema,
        },
      },
      async ({ input, output }) => {
        const res = await tool.execution(input, output);
        const { data, error } = await queryRunner(res.query, res.variables);

        if (error) {
          return {
            content: [
              {
                type: "text",
                text: `Error from GraphQL API: ${JSON.stringify(error, null, 2)}`,
              },
            ],
          };
        }

        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(data, null, 2),
            },
          ],
        };
      },
    );
  }

  console.info(
    "Registered Tools:",
    parsedSchema.map((s) => s.name),
  );
}

// Usage
const schema = `/* your GraphQL schema */`;
const tools = await schemaParser(schema);
await registerSchemaTools(tools, mcpServer);

Description Preservation

GraphQL MCP Bridge automatically preserves GraphQL field descriptions and exposes them as tool descriptions. This feature ensures that documentation from your GraphQL schema is carried over to the generated MCP tools.

How It Works

const schema = `
  type Query {
    """
    Retrieves user information by ID
    Supports fetching related posts and profile data
    """
    getUser(id: ID!): User

    """
    Lists all users with pagination support
    """
    getUsers(limit: Int, offset: Int): [User!]!

    # This field has no description
    healthCheck: String
  }

  type User {
    id: ID!
    username: String!
  }
`;

const tools = await schemaParser(schema);

// Description is preserved from GraphQL schema
const getUserTool = tools.find(tool => tool.name === 'getUser');
console.log(getUserTool.description);
// Output: "Retrieves user information by ID\nSupports fetching related posts and profile data"

const getUsersTool = tools.find(tool => tool.name === 'getUsers');
console.log(getUsersTool.description);
// Output: "Lists all users with pagination support"

// Fallback description for fields without documentation
const healthCheckTool = tools.find(tool => tool.name === 'healthCheck');
console.log(healthCheckTool.description);
// Output: "GraphQL query operation: healthCheck"

Description Fallbacks

When a GraphQL field doesn't have a description, the system provides a meaningful fallback:

  • Queries: "GraphQL query operation: {fieldName}"
  • Mutations: "GraphQL mutation operation: {fieldName}"
  • Subscriptions: "GraphQL subscription operation: {fieldName}"

Integration with MCP Servers

The preserved descriptions integrate seamlessly with MCP servers:

mcpServer.registerTool(
  tool.name,
  {
    description: tool.description, // Uses GraphQL field description or fallback
    inputSchema: tool.inputSchema,
    outputSchema: tool.outputSchema,
  },
  async ({ input, output }) => {
    // Tool execution logic
  }
);

This ensures that AI systems and other consumers of your MCP tools have access to the original documentation from your GraphQL schema.

Selective Tool Generation

GraphQL MCP Bridge provides flexible ways to control which operations are converted to MCP tools, allowing you to exclude specific operations from tool generation.

Using Ignore Phrases

You can prevent specific GraphQL operations from being converted to MCP tools by including a special phrase in their description. By default, any operation containing NO_MPC_TOOL in its description will be skipped.

const schema = `
  type Query {
    getUser(id: ID!): User

    # This operation will be ignored and no tool will be generated
    internalHealthCheck: String @deprecated(reason: "NO_MPC_TOOL - Internal use only")

    # This will also be ignored
    """
    Administrative function for internal monitoring
    NO_MPC_TOOL
    """
    adminDashboard: AdminStats

    # This operation will be included as it doesn't contain the ignore phrase
    getUsers: [User!]!
  }
`;

const tools = await schemaParser(schema);
// Only generates tools for: getUser, getUsers
// Skips: internalHealthCheck, adminDashboard

Custom Ignore Phrases

You can customize the ignore phrase to match your naming conventions:

const tools = await schemaParser(schema, {
  ignorePhrase: 'INTERNAL_ONLY'
});

// Now any operation with 'INTERNAL_ONLY' in its description will be skipped
const schemaWithCustomIgnore = `
  type Query {
    getUser(id: ID!): User

    # This will be ignored
    debugQuery: String # INTERNAL_ONLY - Debug purposes
  }
`;

Alternative Approach: Schema Preprocessing

Instead of using ignore phrases, you can preprocess your GraphQL schema to remove unwanted operations before passing it to the library:

// Remove specific operations from schema string
function removeInternalOperations(schemaString: string): string {
  // Custom logic to filter out operations
  // This approach gives you complete control over schema modification
  return filteredSchema;
}

const cleanedSchema = removeInternalOperations(originalSchema);
const tools = await schemaParser(cleanedSchema);

Best Practices

  1. Use Descriptive Ignore Phrases: Choose phrases that clearly indicate why an operation should be ignored
  2. Document Your Convention: If using custom ignore phrases, document them in your team's GraphQL schema guidelines
  3. Consider Schema Preprocessing: For complex filtering logic, schema preprocessing might be more maintainable than ignore phrases

Advanced Configuration Examples

Selective Operation Types

Configuration

GraphQL MCP Bridge supports configurable operation generation through the Config type. This allows you to control which types of operations are included and customize their naming.

Configuration Options

export type Config = {
  /**
   * Include mutation operations
   * Default: false
   */
  mutation?: boolean;

  /**
   * Include subscription operations
   * Default: false
   * Note: Subscriptions are not yet fully supported. You can generate
   * the functions, but need to handle subscription logic yourself.
   */
  subscription?: boolean;

  /**
   * Include query operations
   * Default: true
   */
  query?: boolean;

  /**
   * Prefix for query operation names
   * Example: 'get_' would turn 'user' into 'get_user'
   * Default: ''
   */
  queryPrefix?: string;

  /**
   * Prefix for mutation operation names
   * Example: 'do_' would turn 'createUser' into 'do_createUser'
   * Default: ''
   */
  mutationPrefix?: string;

  /**
   * Prefix for subscription operation names
   * Example: 'sub_' would turn 'userUpdated' into 'sub_userUpdated'
   * Default: ''
   */
  subscriptionPrefix?: string;

  /**
   * If a query or mutation has this phrase in its description, it will be ignored and no tool will be generated for it.
   * Default: 'NO_MPC_TOOL'
   */
  ignorePhrase?: string;

  /**
   * Maximum number of operations to process to prevent memory exhaustion.
   * Useful for very large GraphQL schemas like GitHub's.
   * Default: 200
   */
  maxOperations?: number;

  /**
   * Maximum number of arguments per operation to process.
   * Limits processing of operations with excessive arguments.
   * Default: 50
   */
  maxOperationArgs?: number;

  /**
   * Maximum schema processing depth to prevent stack overflow.
   * Controls how deeply nested types are processed.
   * Default: 10
   */
  maxSchemaDepth?: number;

  /**
   * Maximum number of fields per type to process.
   * Limits processing of types with excessive fields.
   * Default: 100
   */
  maxFields?: number;
};

Configuration Examples

// Default configuration - queries only
const tools = await schemaParser(schema);

// Include mutations with custom prefixes
const toolsWithMutations = await schemaParser(schema, {
  query: true,
  mutation: true,
  queryPrefix: 'fetch_',
  mutationPrefix: 'execute_'
});

// All operation types with subscription prefix
const allTools = await schemaParser(schema, {
  query: true,
  mutation: true,
  subscription: true,
  subscriptionPrefix: 'listen_'
});

// Only mutations
const mutationTools = await schemaParser(schema, {
  query: false,
  mutation: true
});

// Custom ignore phrase for selective tool generation
const selectiveTools = await schemaParser(schema, {
  query: true,
  mutation: true,
  ignorePhrase: 'INTERNAL_ONLY'
});

// Memory optimization for large schemas
const optimizedTools = await schemaParser(schema, {
  query: true,
  mutation: true,
  maxOperations: 100,        // Process only first 100 operations
  maxOperationArgs: 25,      // Limit to 25 arguments per operation
  maxSchemaDepth: 5,         // Limit nested type depth
  maxFields: 50              // Limit fields per type
});

// Configuration for very large schemas (like GitHub GraphQL API)
const githubOptimized = await schemaParser(githubSchema, {
  query: true,
  mutation: false,           // Skip mutations for faster processing
  maxOperations: 50,         // Very conservative limit
  maxSchemaDepth: 3,         // Shallow processing
  maxFields: 20,             // Conservative field limit
  ignorePhrase: 'DEPRECATED' // Skip deprecated operations
});

Practical Configuration Examples

Operation Type Selection

const schema = `
  type Query {
    getUser(id: ID!): User
    getUsers: [User!]!
  }

  type Mutation {
    createUser(input: CreateUserInput!): User
    updateUser(id: ID!, input: UpdateUserInput!): User
  }

  type Subscription {
    userUpdated: User
  }

  type User {
    id: ID!
    username: String!
  }

  input CreateUserInput {
    username: String!
  }

  input UpdateUserInput {
    username: String
  }
`;

// Only queries (default behavior)
const queryTools = await schemaParser(schema);
console.log(queryTools.map(t => t.name));
// Output: ['getUser', 'getUsers']

// Include mutations with prefix
const mutationTools = await schemaParser(schema, {
  query: true,
  mutation: true,
  mutationPrefix: 'execute_'
});
console.log(mutationTools.map(t => t.name));
// Output: ['getUser', 'getUsers', 'execute_createUser', 'execute_updateUser']

// All operations with custom prefixes
const allTools = await schemaParser(schema, {
  query: true,
  mutation: true,
  subscription: true,
  queryPrefix: 'fetch_',
  mutationPrefix: 'do_',
  subscriptionPrefix: 'listen_'
});
console.log(allTools.map(t => t.name));
// Output: ['fetch_getUser', 'fetch_getUsers', 'do_createUser', 'do_updateUser', 'listen_userUpdated']

Custom Naming Patterns

// API-style naming
const apiTools = await schemaParser(schema, {
  query: true,
  mutation: true,
  queryPrefix: 'api_get_',
  mutationPrefix: 'api_post_'
});

// GraphQL operation type prefixes
const typedTools = await schemaParser(schema, {
  query: true,
  mutation: true,
  subscription: true,
  queryPrefix: 'QUERY_',
  mutationPrefix: 'MUTATION_',
  subscriptionPrefix: 'SUBSCRIPTION_'
});

Validation System

GraphQL MCP Bridge includes a comprehensive validation system powered by Zod that ensures type safety at runtime. The validation system works on two levels:

Input Validation

All operation arguments are automatically validated against Zod schemas generated from the GraphQL schema:

// The tool automatically validates input arguments
const result = await createUserTool.execution(
  { input: { username: "john", email: "[email protected]" } }, // ✅ Valid
  { id: true, username: true }
);

// This will throw a validation error
try {
  await createUserTool.execution(
    { input: { username: "john" } }, // ❌ Missing required 'email' field
    { id: true }
  );
} catch (error) {
  console.error(error.message); // "Validation failed for createUser: ..."
}

Output Selection Validation

Field selection objects are validated to ensure you only select existing fields:

// Valid field selection
const result = await getUserTool.execution(
  { id: "123" },
  {
    id: true,
    username: true,
    posts: { id: true, title: true } // ✅ Valid nested selection
  }
);

// This will throw a validation error
try {
  await getUserTool.execution(
    { id: "123" },
    { id: true, nonExistentField: true } // ❌ Field doesn't exist
  );
} catch (error) {
  console.error(error.message); // Output selection validation failed
}

Manual Validation

You can also use the validation functions directly:

import {
  validateOperationArguments,
  validateOutputSelection,
  generateValidationSchemas,
  generateOutputSelectionSchemas
} from 'graphql-mcp-bridge';

const tools = await schemaParser(schema);
const userTool = tools.find(tool => tool.name === 'user');

// Validate arguments manually
try {
  const validatedArgs = userTool.inputSchema.parse({ id: "123" });
  console.log('Arguments are valid:', validatedArgs);
} catch (error) {
  console.error('Invalid arguments:', error.message);
}

// Validate output selection manually
try {
  const validatedSelection = userTool.outputSchema.parse({
    id: true,
    username: true
  });
  console.log('Selection is valid:', validatedSelection);
} catch (error) {
  console.error('Invalid selection:', error.message);
}

Default Field Selection

When no field selection is provided or an empty object is passed, the system automatically selects all scalar and enum fields at the first level:

// These are equivalent:
await getUserTool.execution({ id: "123" }, {});
await getUserTool.execution({ id: "123" }, {
  id: true,
  username: true,
  email: true,
  createdAt: true
  // Complex fields like 'posts' are not auto-selected
});

Supported Validation Features

  • Scalar Types: String, Int, Float, Boolean, ID with proper type checking
  • Enum Validation: Ensures only valid enum values are accepted
  • Complex Input Objects: Nested input validation with proper type checking
  • List Types: Array validation with item type checking
  • Non-null Types: Required field validation
  • Optional Fields: Proper handling of nullable fields
  • Circular References: Safe handling without infinite recursion
  • Union Types: Validation for fragment selections
  • Interface Types: Validation for interface implementations

Memory Optimization for Large Schemas

When working with very large GraphQL schemas (like GitHub's 70K+ line schema), memory optimization becomes crucial. The library provides several configuration options to prevent JavaScript heap out of memory errors:

Memory Optimization Options

  • maxOperations: Limits the number of operations processed (default: 200)
  • maxOperationArgs: Limits arguments per operation (default: 50)
  • maxSchemaDepth: Prevents deep recursion in nested types (default: 10)
  • maxFields: Limits fields processed per type (default: 100)

Example: GitHub GraphQL API

import { schemaParser } from 'graphql-mcp-bridge';

// Optimized configuration for GitHub's large schema
const tools = await schemaParser(githubSchema, {
  query: true,
  mutation: false,           // Skip mutations for faster processing
  maxOperations: 100,        // Process only essential operations
  maxSchemaDepth: 5,         // Limit depth to prevent stack overflow
  maxFields: 30,             // Conservative field limit
  maxOperationArgs: 20,      // Limit complex operation arguments
  ignorePhrase: 'DEPRECATED' // Skip deprecated operations
});

console.log(`Generated ${tools.length} tools from large schema`);

Memory-Efficient Batch Processing

For extremely large schemas, use the memory-efficient batch parser:

import { parseSchemaInBatches } from 'graphql-mcp-bridge';

// Process in small batches with memory cleanup
const tools = await parseSchemaInBatches(massiveSchema, {
  query: true,
  batchSize: 25,              // Process 25 operations at a time
  clearCacheInterval: 50,     // Clear cache every 50 operations
  maxOperations: 200,         // Total operations limit
  maxSchemaDepth: 3           // Very shallow processing
});

Memory Management Utilities

import { clearTypeSchemaCache, getTypeSchemaCacheSize } from 'graphql-mcp-bridge';

// Monitor cache size
console.log(`Cache contains ${getTypeSchemaCacheSize()} schemas`);

// Clear cache to free memory
clearTypeSchemaCache();

Performance Tips

  1. Start Conservative: Begin with low limits and increase as needed
  2. Skip Complex Operations: Use ignorePhrase to skip complex or deprecated operations
  3. Limit Operation Types: Process only queries for read-only use cases
  4. Monitor Memory: Use Node.js --max-old-space-size flag if needed
  5. Batch Processing: Use parseSchemaInBatches for schemas over 10K lines

Advanced Usage Examples

Basic Query with Field Selection

// Simple query without arguments
const schema = `
  type Query {
    hello: String
  }
`;

const tools = await schemaParser(schema);
const helloTool = tools[0];
const result = await helloTool.execution({}, {});
// Output: query hello { hello }

Mutations with Input Validation

const schema = `
  type Mutation {
    createUser(input: CreateUserInput!): User
  }

  input CreateUserInput {
    username: String!
    email: String!
  }

  type User {
    id: ID!
    username: String!
    email: String!
  }
`;

// Enable mutations in configuration
const tools = await schemaParser(schema, {
  query: true,
  mutation: true
});
const createUserTool = tools.find(tool => tool.name === 'createUser');

// Valid input
const result = await createUserTool.execution(
  { input: { username: "john_doe", email: "[email protected]" } },
  { id: true, username: true }
);
// Output: mutation createUser($input: CreateUserInput!) { createUser(input: $input) { id username } }

// Invalid input throws validation error
try {
  await createUserTool.execution(
    { input: { username: "john_doe" } }, // Missing email
    { id: true }
  );
} catch (error) {
  console.error(error.message); // Validation error for missing email
}

Working with Enums

const schema = `
  enum Role {
    ADMIN
    USER
    GUEST
  }

  type User {
    id: ID!
    username: String!
    role: Role!
  }

  type Query {
    getUsersByRole(role: Role!): [User!]
  }
`;

const tools = await schemaParser(schema);
const getUsersByRoleTool = tools[0];

const result = await getUsersByRoleTool.execution(
  { role: "ADMIN" },
  { id: true, username: true, role: true }
);
// Output: query getUsersByRole($role: Role!) { getUsersByRole(role: $role) { id username role } }

Complex Nested Selections

const schema = `
  type Comment {
    id: ID!
    content: String!
  }

  type Post {
    id: ID!
    title: String!
    comments: [Comment!]!
  }

  type User {
    id: ID!
    username: String!
    posts: [Post!]!
  }

  type Query {
    getUser(id: ID!): User
  }
`;

const tools = await schemaParser(schema);
const getUserTool = tools[0];

const result = await getUserTool.execution(
  { id: "123" },
  {
    id: true,
    username: true,
    posts: {
      title: true,
      comments: {
        content: true
      }
    }
  }
);
// Output: query getUser($id: ID!) { getUser(id: $id) { id username posts { title comments { content } } } }

Union and Interface Types

const schema = `
  interface Animal {
    id: ID!
    name: String!
  }

  type Dog implements Animal {
    id: ID!
    name: String!
    breed: String!
  }

  type Cat implements Animal {
    id: ID!
    name: String!
    color: String!
  }

  union Pet = Dog | Cat

  type Query {
    getPet(id: ID!): Pet
    getAnimal(id: ID!): Animal
  }
`;

const tools = await schemaParser(schema);

// Union type selection
const getPetTool = tools.find(tool => tool.name === 'getPet');
const petResult = await getPetTool.execution(
  { id: "123" },
  {
    __typename: true,
    Dog: { id: true, name: true, breed: true },
    Cat: { id: true, name: true, color: true }
  }
);
// Output: query getPet($id: ID!) { getPet(id: $id) { __typename ... on Dog { id name breed } ... on Cat { id name color } } }

// Interface type selection
const getAnimalTool = tools.find(tool => tool.name === 'getAnimal');
const animalResult = await getAnimalTool.execution(
  { id: "456" },
  {
    id: true,
    name: true,
    Dog: { breed: true },
    Cat: { color: true }
  }
);
// Output: query getAnimal($id: ID!) { getAnimal(id: $id) { id name ... on Dog { breed } ... on Cat { color } } }

Complex Input Types with Validation

const schema = `
  enum Status {
    ACTIVE
    INACTIVE
    PENDING
  }

  input RangeInput {
    start: Int!
    end: Int!
  }

  input FilterInput {
    status: Status
    tags: [String!]
    range: RangeInput
  }

  type Item {
    id: ID!
    name: String!
    status: Status!
    tags: [String!]!
    details: Details
  }

  type Details {
    description: String
    createdAt: String!
    updatedAt: String
  }

  type Query {
    getItems(filter: FilterInput): [Item!]!
  }
`;

const tools = await schemaParser(schema);
const getItemsTool = tools[0];

// Valid complex input
const result = await getItemsTool.execution(
  {
    filter: {
      status: "ACTIVE",
      tags: ["tag1", "tag2"],
      range: { start: 10, end: 50 }
    }
  },
  {
    id: true,
    name: true,
    status: true,
    details: {
      description: true,
      createdAt: true
    }
  }
);

// Access validation schemas directly
const inputSchema = getItemsTool.inputSchema;
const outputSchema = getItemsTool.outputSchema;

// Validate input manually
try {
  inputSchema.parse({ filter: { status: "INVALID_STATUS" } });
} catch (error) {
  console.error("Invalid input:", error.message);
}

// Validate output selection manually
try {
  outputSchema.parse({ id: true, nonExistentField: true });
} catch (error) {
  console.error("Invalid field selection:", error.message);
}

API Reference

Core Functions

schemaParser(graphqlSchema: string, config?: Config): Promise<Tool[]>

Parses a GraphQL schema string and returns an array of MCP-compatible tools with built-in validation and field selection.

Parameters:

  • graphqlSchema (string): A valid GraphQL schema definition
  • config (Config, optional): Configuration object to control operation generation

Returns:

  • Promise<Tool[]>: Array of MCP tools, each containing:
    • name: Operation name (with optional prefix applied)
    • execution(variables, selectedFields): Async function that returns { query, variables }
    • description: GraphQL field description or auto-generated fallback description
    • inputSchema: Zod schema for input validation
    • outputSchema: Zod schema for output field selection validation

Config Type:

type Config = {
  mutation?: boolean;      // Include mutations (default: false)
  subscription?: boolean;  // Include subscriptions (default: false)
  query?: boolean;         // Include queries (default: true)
  queryPrefix?: string;    // Prefix for query names (default: '')
  mutationPrefix?: string; // Prefix for mutation names (default: '')
  subscriptionPrefix?: string; // Prefix for subscription names (default: '')
  ignorePhrase?: string;   // Ignore operations with this phrase in description (default: 'NO_MPC_TOOL')
};

generateQueryString(operation, variables?, selectedFields?): string

Generates a GraphQL query string from an operation definition with optional variables and field selection.

Validation Functions

generateValidationSchemas(operations, schema): Record<string, z.ZodSchema>

Generates Zod validation schemas for GraphQL operations input arguments.

Parameters:

  • operations: Array of GraphQL operations extracted from schema
  • schema: GraphQL schema object

Returns:

  • Object mapping operation names to their input validation schemas

generateOutputSelectionSchemas(operations, schema): Record<string, z.ZodSchema>

Generates Zod schemas for validating output field selections, supporting nested objects, unions, and interfaces.

Parameters:

  • operations: Array of GraphQL operations extracted from schema
  • schema: GraphQL schema object

Returns:

  • Object mapping operation names to their output selection validation schemas

validateOperationArguments(operationName, variables, validationSchemas): any

Validates operation arguments against generated Zod schemas.

Parameters:

  • operationName (string): Name of the GraphQL operation
  • variables (any): Input variables to validate
  • validationSchemas (Record<string, z.ZodSchema>): Generated validation schemas

Returns:

  • Validated and parsed variables object

Throws:

  • Error if validation fails with detailed error message

validateOutputSelection(operationName, selection, outputSchemas): any

Validates output field selection against generated output schemas.

Parameters:

  • operationName (string): Name of the GraphQL operation
  • selection (any): Field selection object to validate
  • outputSchemas (Record<string, z.ZodSchema>): Generated output selection schemas

Returns:

  • Validated and parsed selection object (with defaults applied if empty)

Throws:

  • Error if validation fails with detailed error message

Tool Structure

Each generated tool from schemaParser has the following structure:

type Tool = {
  name: string;
  execution: (variables: any, selectedFields: any) => Promise<{
    query: string;
    variables: any;
  }>;
  description: string; // GraphQL field description or auto-generated fallback
  inputSchema: z.ZodTypeAny;
  outputSchema: z.ZodTypeAny;
};

Field Selection Syntax

The field selection parameter supports:

  • Simple fields: { id: true, name: true }
  • Nested objects: { user: { id: true, posts: { title: true } } }
  • Union types: { __typename: true, Dog: { breed: true }, Cat: { color: true } }
  • Interface types: { id: true, Dog: { breed: true }, Cat: { color: true } }
  • Arrays: Automatically handled for list types

Error Handling

The library provides detailed error messages for validation issues, with comprehensive coverage of GraphQL type validation:

Input Validation Errors

// Missing required field
throw new Error('Validation failed for createUser: Required at "input.email"');

// Wrong type
throw new Error('Validation failed for updateUser: Expected string, received number at "input.username"');

// Invalid enum value
throw new Error('Validation failed for updatePost: Invalid enum value. Expected DRAFT | PUBLISHED, received INVALID at "input.status"');

// Array validation
throw new Error('Validation failed for createPost: Expected array, received string at "input.tags"');

// Nested object validation
throw new Error('Validation failed for createUser: Required at "input.profile.firstName"');

Output Selection Validation Errors

// Non-existent field
throw new Error('Output selection validation failed for getUser: Unrecognized key(s) in object: "nonExistentField"');

// Wrong selection type
throw new Error('Output selection validation failed for getUser: Expected boolean, received string at "id"');

// Invalid nested selection
throw new Error('Output selection validation failed for getUser: Unrecognized key(s) in object: "invalidNestedField" at "posts"');

Circular Reference Handling

The validation system safely handles circular references in GraphQL schemas:

// For schemas with circular references like User -> Post -> User
// The system provides fallback schemas to prevent infinite recursion
const tools = await schemaParser(schemaWithCircularRefs); // ✅ Works safely

Validation Error Structure

All validation errors follow a consistent structure:

try {
  await tool.execution(invalidInput, invalidSelection);
} catch (error) {
  console.log(error.message); // User-friendly error message
  console.log(error.name);    // 'Error'
  // Original Zod error details are parsed into readable format
}

Supported GraphQL Features

Core Operations

  • Queries and Mutations: Full support for Query and Mutation operations with validation
  • Subscriptions: Configurable subscription support (function generation only - WebSocket handling required)
  • Operation Selection: Choose which operation types to include via configuration
  • Custom Naming: Configurable prefixes for operation names
  • Description Preservation: Automatically preserves GraphQL field descriptions as tool descriptions

Type System

  • Scalar Types: String, Int, Float, Boolean, ID with proper Zod validation
  • Object Types: Complex nested object structures with recursive validation
  • Input Types: Complex input arguments with comprehensive validation
  • Enums: Enumeration types with strict value validation
  • Lists: Arrays of any supported type with item validation
  • Non-null Types: Required field validation with proper error messages
  • Interfaces: Interface types with fragment selection validation
  • Union Types: Union types with fragment selection validation
  • ⚠️ Custom Scalars: Limited support (treated as strings with basic validation)

Advanced Features

  • Configurable Operation Generation: Selective inclusion of queries, mutations, and subscriptions
  • Custom Operation Naming: Configurable prefixes for operation names
  • Nested Field Selection: Deep object field selection with validation
  • Circular References: Safe handling without infinite recursion
  • Default Selections: Automatic selection of scalar/enum fields when no selection provided
  • Fragment Validation: Proper validation for union and interface fragment selections
  • Input Object Nesting: Deep nested input validation with type checking
  • Optional Field Handling: Proper nullable field validation

Validation Features

  • Runtime Type Checking: All inputs validated at runtime before execution
  • Schema-based Validation: Generated from actual GraphQL schema definition
  • Detailed Error Messages: User-friendly error reporting with field paths
  • Fallback Handling: Graceful degradation for edge cases
  • Circular Reference Protection: Prevents infinite loops in recursive types

Development

# Install dependencies
pnpm install

# Run tests
pnpm test

# Run tests with coverage
pnpm run test:coverage

# Run tests in watch mode
pnpm test:watch

# Build the package
pnpm run build

# Type checking
pnpm run type-check

# Format code
pnpm run format

# Check formatting
pnpm run format:check

Testing

The project includes comprehensive test coverage for all features:

  • Basic query and mutation generation
  • Complex input type validation
  • Enum type handling
  • Interface and union type support
  • Nested field selection
  • Error handling and validation
  • Edge cases and error conditions

Run tests with:

pnpm test

Publishing

This package is published to npm. To publish a new version:

  1. Update the version in package.json
  2. Run npm run build to build the package
  3. Run npm publish to publish to npm
  4. Create a new release on GitHub for documentation

Requirements

  • Node.js: >= 24.0.0
  • Dependencies: graphql, zod

Troubleshooting

Common Issues

If you encounter any installation issues, try:

  1. Clear npm cache:

    npm cache clean --force
    # or
    pnpm store prune
  2. Update npm/pnpm:

    npm install -g npm@latest
    # or
    npm install -g pnpm@latest
  3. Check Node.js version: This package requires Node.js >= 24.0.0

If you're still having issues, please open an issue on GitHub.

License

ISC License - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Guidelines

  1. Follow the existing code style (Biome formatting)
  2. Add tests for any new features
  3. Update documentation as needed
  4. Ensure all tests pass before submitting

Changelog

See GitHub Releases for version history and changes.

About

A bridge implementation connecting GraphQL APIs with the Model Context Protocol (MCP), enabling seamless integration between GraphQL services and MCP-compatible AI systems. This tool facilitates data exchange and API communication by translating GraphQL operations into MCP-compatible formats.

Topics

Resources

License

Stars

Watchers

Forks