JSPM

graphql-mcp-bridge

0.0.2
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 7
  • Score
    100M100P100Q32616F
  • License ISC

A bridge implementation connecting GraphQL APIs with the Model Context Protocol (MCP), enabling seamless integration between GraphQL services and MCP-compatible AI systems

Package Exports

    This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (graphql-mcp-bridge) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

    Readme

    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: "john@example.com" } }, // ✅ 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: "john@example.com" } },
      { 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.