graphql-mcp-bridge
Version:
A bridge implementation connecting GraphQL APIs with the Model Context Protocol (MCP), enabling seamless integration between GraphQL services and MCP-compatible AI systems
1,377 lines (1,074 loc) • 38.8 kB
Markdown
# GraphQL MCP Bridge
<!-- markdownlint-disable MD033 -->
[](https://github.com/pshaddel/graphql-mcp-bridge/actions/workflows/ci.yml)
[](https://opensource.org/licenses/MIT)
[](https://codecov.io/github/pshaddel/graphql-mcp-bridge)
[](https://nodejs.org/)
[](https://www.typescriptlang.org/)
[](https://www.npmjs.com/package/graphql-mcp-bridge)
<div align="center">
<img width="200" height="200" alt="logo-light" src="https://github.com/user-attachments/assets/fd1c1676-2486-41c1-bd8b-ba4250549ba8">
</div>
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.
<div align="center">
<img width="80%" alt="Screenshot 2025-09-14 at 01 04 08" src="https://github.com/user-attachments/assets/33b5f7d5-e93e-4ed3-b3e8-472f2441b95b#gh-dark-mode-only" />
<img width="80%" alt="Screenshot 2025-09-14 at 01 02 40" src="https://github.com/user-attachments/assets/836dc4a4-cedd-4b3c-abcd-8eb4d35dadd4#gh-light-mode-only" />
</div>
## 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
```bash
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
```typescript
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:
```typescript
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
```typescript
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:
```typescript
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.
```typescript
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:
```typescript
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:
```typescript
// 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
```typescript
```
## 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
```typescript
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
```typescript
// 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
```typescript
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
```typescript
// 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:
```typescript
// 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:
```typescript
// 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:
```typescript
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:
```typescript
// 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
```typescript
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:
```typescript
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
```typescript
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
```typescript
// 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
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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:**
```typescript
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:
```typescript
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
```typescript
// 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
```typescript
// 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:
```typescript
// 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:
```typescript
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
```bash
# 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:
```bash
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**:
```bash
npm cache clean --force
# or
pnpm store prune
```
2. **Update npm/pnpm**:
```bash
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](https://github.com/pshaddel/graphql-mcp-bridge/issues) 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](https://github.com/pshaddel/graphql-mcp-bridge/releases) for version history and changes.