graphql
Execute GraphQL queries and mutations or introspect schemas with support for variables, custom headers, and request configuration.
Instructions
Execute GraphQL queries and mutations with support for variables and custom headers
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | The action to perform with GraphQL |
Implementation Reference
- src/tools/graphql.ts:65-100 (handler)Main GraphQL tool handler function that switches between 'execute' and 'introspect' actions, delegating to respective helper functions with comprehensive error handling.export default async function graphql({ action }: InferSchema<typeof schema>) { try { switch (action.type) { case "execute": return await executeGraphQL( action.endpoint, action.query, action.variables, action.headers, action.operationName, action.timeout ) case "introspect": return await introspectGraphQL( action.endpoint, action.headers || {}, action.action, action.typeName, action.useCache, action.cacheTTL ) default: return JSON.stringify({ success: false, error: "Invalid action type", }, null, 2) } } catch (error) { return JSON.stringify({ success: false, error: error instanceof Error ? error.message : "Unknown error occurred", }, null, 2) } }
- src/tools/graphql.ts:7-50 (schema)Zod schema defining discriminated union for 'execute' (query/mutation with vars, headers) and 'introspect' (schema actions with cache) parameters.export const schema = { action: z.discriminatedUnion("type", [ // GraphQL execution action z.object({ type: z.literal("execute"), endpoint: z.string().describe("The GraphQL endpoint URL"), query: z.string().describe("The GraphQL query or mutation string"), variables: z.record(z.any()) .optional() .describe("Variables to pass to the GraphQL query/mutation"), headers: z.record(z.string()) .optional() .describe("HTTP headers to include in the request (e.g., authorization tokens)"), operationName: z.string() .optional() .describe("Name of the operation to execute (useful when query contains multiple operations)"), timeout: z.number() .optional() .default(30000) .describe("Request timeout in milliseconds. Defaults to 30 seconds"), }), // GraphQL introspection action z.object({ type: z.literal("introspect"), endpoint: z.string().describe("The GraphQL endpoint URL to introspect"), headers: z.record(z.string()) .optional() .describe("HTTP headers to include in the request (e.g., authorization tokens)"), action: z.enum(["full-schema", "list-operations", "get-type"]) .describe("What to fetch: full-schema gets the complete schema, list-operations lists available queries/mutations, get-type gets a specific type definition"), typeName: z.string() .optional() .describe("The name of the type to fetch (required when action is 'get-type')"), useCache: z.boolean() .optional() .default(true) .describe("Whether to use cached schema if available"), cacheTTL: z.number() .optional() .default(300000) .describe("Cache time-to-live in milliseconds. Defaults to 5 minutes"), }), ]).describe("The action to perform with GraphQL"), }
- src/tools/graphql.ts:53-62 (registration)Tool metadata registration exporting name 'graphql', description, and annotations.export const metadata: ToolMetadata = { name: "graphql", description: "Execute GraphQL queries and mutations with support for variables and custom headers", annotations: { title: "GraphQL Client", readOnlyHint: false, destructiveHint: false, idempotentHint: false, }, }
- src/utils/graphql/execute.ts:4-106 (helper)Core helper for executing GraphQL queries/mutations using graphql-request, with timeout, detailed errors, operation type detection, and sensitive header masking.export async function executeGraphQL( endpoint: string, query: string, variables?: Record<string, any>, headers?: Record<string, string>, operationName?: string, timeout: number = 30000 ): Promise<string> { try { // Validate query syntax const document = gql`${query}` // Create GraphQL client with configuration const client = new GraphQLClient(endpoint, { headers: headers || {}, }) // Execute the GraphQL request const startTime = Date.now() // Set up timeout if specified let timeoutId: NodeJS.Timeout | undefined const timeoutPromise = timeout ? new Promise((_, reject) => { timeoutId = setTimeout(() => reject(new Error(`Request timed out after ${timeout}ms`)), timeout) }) : null // Race between request and timeout const data = await (timeoutPromise ? Promise.race([ client.request(document, variables || {}), timeoutPromise ]) : client.request(document, variables || {})) const responseTime = Date.now() - startTime // Clear timeout if set if (timeoutId) clearTimeout(timeoutId) // Parse operation type from query const operationType = detectOperationType(query) return JSON.stringify({ success: true, data, operationType, responseTime: `${responseTime}ms`, request: { endpoint, operationName, variables: variables || {}, headers: maskSensitiveHeaders(headers) } }, null, 2) } catch (error) { // Handle different error types let errorDetails: any = { success: false, error: "Unknown error occurred", errorType: "UnknownError", } if (error instanceof Error) { // GraphQL errors from graphql-request if ('response' in error && error.response) { const graphqlError = error as any errorDetails = { success: false, errorType: "GraphQLError", errors: graphqlError.response.errors || [], data: graphqlError.response.data || null, status: graphqlError.response.status, headers: graphqlError.response.headers, } } // Network or other errors else { errorDetails.error = error.message if (error.message.includes('timeout')) { errorDetails.errorType = "TimeoutError" errorDetails.error = `Request timed out after ${timeout}ms` } else if (error.message.includes('Network') || error.message.includes('fetch')) { errorDetails.errorType = "NetworkError" } else if (error.message.includes('Syntax Error')) { errorDetails.errorType = "SyntaxError" errorDetails.error = "Invalid GraphQL query syntax" } } } // Add request details to error response errorDetails.request = { endpoint, operationName, variables: variables || {}, headers: maskSensitiveHeaders(headers) } return JSON.stringify(errorDetails, null, 2) } }
- Core helper for GraphQL introspection supporting full schema, operation lists, type details with schema caching.export async function introspectGraphQL( endpoint: string, headers: Record<string, string> = {}, action: string, typeName?: string, useCache: boolean = true, cacheTTL: number = 300000 ): Promise<string> { try { // Get schema (from cache or fetch) const introspectionData = await getSchema(endpoint, headers, useCache, cacheTTL) // Build schema from introspection result const schema = buildClientSchema(introspectionData) switch (action) { case "full-schema": return handleFullSchema(schema) case "list-operations": return handleListOperations(schema) case "get-type": if (!typeName) { return JSON.stringify({ success: false, error: "typeName is required when action is 'get-type'", }, null, 2) } return handleGetType(schema, typeName) default: return JSON.stringify({ success: false, error: `Unknown action: ${action}`, }, null, 2) } } catch (error) { return JSON.stringify({ success: false, error: error instanceof Error ? error.message : "Unknown error occurred", errorType: error instanceof Error && error.message.includes("Network") ? "NetworkError" : "IntrospectionError", endpoint, }, null, 2) } }