UNPKG

@graphql-hive/plugin-mcp

Version:
279 lines (272 loc) 10.8 kB
import { Logger } from '@graphql-hive/logger'; import { DisposableSymbols } from '@whatwg-node/disposablestack'; import { MaybePromise } from '@whatwg-node/promise-helpers'; import { UnifiedGraphPlugin, Instrumentation as Instrumentation$2 } from '@graphql-mesh/fusion-runtime'; import { Plugin, YogaInitialContext, Instrumentation as Instrumentation$1 } from 'graphql-yoga'; import { MeshFetch, KeyValueCache, MeshFetchRequestInit, Logger as Logger$1 } from '@graphql-mesh/types'; import { FetchInstrumentation } from '@graphql-mesh/utils'; import { ExecutionRequest, MaybePromise as MaybePromise$1 } from '@graphql-tools/utils'; import { GraphQLResolveInfo } from 'graphql/type'; import { Langfuse, LangfuseOptions } from 'langfuse'; type TopicDataMap = { [topic: string]: any; }; type PubSubListener<Data extends TopicDataMap, Topic extends keyof Data> = (data: Data[Topic]) => void; interface PubSub<M extends TopicDataMap = TopicDataMap> { /** * Publish {@link data} for a {@link topic}. * @returns `void` or a `Promise` that resolves when the data has been successfully published */ publish<Topic extends keyof M>(topic: Topic, data: M[Topic]): MaybePromise<void>; /** * A distinct list of all topics that are currently subscribed to. * Can be a promise to accomodate distributed systems where subscribers exist on other * locations and we need to know about all of them. */ subscribedTopics(): MaybePromise<Iterable<keyof M>>; /** * Subscribe and listen to a {@link topic} receiving its data. * * If the {@link listener} is provided, it will be called whenever data is emitted for the {@link topic}, * * @returns an unsubscribe function or a `Promise<unsubscribe function>` that resolves when the subscription is successfully established. the unsubscribe function returns `void` or a `Promise` that resolves on successful unsubscribe and subscription cleanup * * If the {@link listener} is not provided, * * @returns an `AsyncIterable` that yields data for the given {@link topic} */ subscribe<Topic extends keyof M>(topic: Topic): AsyncIterable<M[Topic]>; subscribe<Topic extends keyof M>(topic: Topic, listener: PubSubListener<M, Topic>): MaybePromise<() => MaybePromise<void>>; /** * Closes active subscriptions and disposes of all resources. Publishing and subscribing after disposal * is not possible and will throw an error if attempted. */ dispose(): MaybePromise<void>; /** @see {@link dispose} */ [DisposableSymbols.asyncDispose](): Promise<void>; } interface GatewayConfigContext { /** * WHATWG compatible Fetch implementation. */ fetch: MeshFetch; /** * The logger to use throught Hive and its plugins. */ log: Logger; /** * Current working directory. * Note that working directory does not exist in serverless environments and will therefore be empty. */ cwd: string; /** * Event bus for pub/sub. */ pubsub?: PubSub; /** * Cache Storage */ cache?: KeyValueCache; } interface GatewayContext extends GatewayConfigContext, YogaInitialContext { /** * Environment agnostic HTTP headers provided with the request. */ headers: Record<string, string>; /** * Runtime context available within WebSocket connections. */ connectionParams?: Record<string, string>; } type GatewayPlugin<TPluginContext extends Record<string, any> = Record<string, any>, TContext extends Record<string, any> = Record<string, any>> = Plugin<Partial<TPluginContext> & GatewayContext & TContext, GatewayConfigContext> & UnifiedGraphPlugin<Partial<TPluginContext> & GatewayContext & TContext> & { onFetch?: OnFetchHook<Partial<TPluginContext> & TContext>; onCacheGet?: OnCacheGetHook; onCacheSet?: OnCacheSetHook; onCacheDelete?: OnCacheDeleteHook; /** * An Instrumentation instance that will wrap each phases of the request pipeline. * This should be used primarily as an observability tool (for monitoring, tracing, etc...). * * Note: The wrapped functions in instrumentation should always be called. Use hooks to * conditionally skip a phase. */ instrumentation?: Instrumentation<TPluginContext & TContext & GatewayContext>; }; interface OnFetchHookPayload<TContext> { url: string; setURL(url: URL | string): void; options: MeshFetchRequestInit; setOptions(options: MeshFetchRequestInit): void; /** * The context is not available in cases where "fetch" is done in * order to pull a supergraph or do some internal work. * * The logger will be available in all cases. */ context: (GatewayContext & TContext) | { log: Logger; }; /** @deprecated Please use `log` from the {@link context} instead. */ logger: Logger$1; info: GraphQLResolveInfo; fetchFn: MeshFetch; setFetchFn: (fetchFn: MeshFetch) => void; executionRequest?: ExecutionRequest; endResponse: (response$: MaybePromise$1<Response>) => void; } interface OnFetchHookDonePayload { response: Response; setResponse: (response: Response) => void; } type OnFetchHookDone = (payload: OnFetchHookDonePayload) => MaybePromise$1<void>; type OnFetchHook<TContext> = (payload: OnFetchHookPayload<TContext>) => MaybePromise$1<void | OnFetchHookDone>; type OnCacheGetHook = (payload: OnCacheGetHookEventPayload) => MaybePromise$1<OnCacheGetHookResult | void>; interface OnCacheGetHookEventPayload { cache: KeyValueCache; key: string; ttl?: number; } interface OnCacheGetHookResult { onCacheHit?: OnCacheHitHook; onCacheMiss?: OnCacheMissHook; onCacheGetError?: OnCacheErrorHook; } type OnCacheErrorHook = (payload: OnCacheErrorHookPayload) => void; interface OnCacheErrorHookPayload { error: Error; } type OnCacheHitHook = (payload: OnCacheHitHookEventPayload) => void; interface OnCacheHitHookEventPayload { value: any; } type OnCacheMissHook = () => void; type OnCacheSetHook = (payload: OnCacheSetHookEventPayload) => MaybePromise$1<OnCacheSetHookResult | void>; interface OnCacheSetHookResult { onCacheSetDone?: () => void; onCacheSetError?: OnCacheErrorHook; } interface OnCacheSetHookEventPayload { cache: KeyValueCache; key: string; value: any; ttl?: number; } type OnCacheDeleteHook = (payload: OnCacheDeleteHookEventPayload) => MaybePromise$1<OnCacheDeleteHookResult | void>; interface OnCacheDeleteHookResult { onCacheDeleteDone?: () => void; onCacheDeleteError?: OnCacheErrorHook; } interface OnCacheDeleteHookEventPayload { cache: KeyValueCache; key: string; } type Instrumentation<TContext extends Record<string, any>> = Instrumentation$1<TContext> & Instrumentation$2 & FetchInstrumentation; interface DescriptionProviderConfig { type: string & {}; [key: string]: unknown; } interface DescriptionProviderContext { label?: string; } interface DescriptionProvider { fetchDescription(toolName: string, config: DescriptionProviderConfig, context?: DescriptionProviderContext): Promise<string>; } type LangfuseGetPromptOptions = NonNullable<Parameters<Langfuse['getPrompt']>[2]>; declare function createLangfuseProvider(client: Langfuse, defaults?: Partial<LangfuseGetPromptOptions>): DescriptionProvider; declare module '@graphql-hive/gateway-runtime' { interface GatewayConfigContext { dispatchRequest?: (req: Request) => Response | Promise<Response>; } } type MCPToolSource = { type: 'inline'; query: string; } | { type: 'graphql'; operationName: string; operationType: 'query' | 'mutation'; file?: string; }; interface MCPToolOverrides { title?: string; description?: string; descriptionProvider?: { type: 'langfuse'; prompt: string; version?: number; options?: LangfuseGetPromptOptions; } | DescriptionProviderConfig; } interface MCPInputOverrides { schema?: { properties?: Record<string, { description?: string; examples?: unknown[]; default?: unknown; alias?: string; descriptionProvider?: DescriptionProviderConfig; }>; }; } interface MCPOutputOverrides { /** Dot-notation path to extract from the GraphQL response data, e.g. "search.items" */ path: string; } interface ToolHookContext { toolName: string; headers: Record<string, string>; query: string; } interface MCPToolHooks { /** * Called before GraphQL execution. Receives de-aliased arguments * (original GraphQL variable names, not MCP alias names). * Return a non-undefined value to short-circuit execution and use that value as the tool result. * Return undefined (or void) to continue with normal GraphQL execution. * When preprocess short-circuits, postprocess is NOT called. */ preprocess?: (args: Record<string, unknown>, context: ToolHookContext) => unknown | Promise<unknown>; /** * Called after GraphQL execution (and output.path extraction) to transform the result. * Not called when preprocess short-circuits. * When a postprocess hook is registered, the response uses text content * instead of structuredContent since the hook may change the result shape. */ postprocess?: (result: unknown, args: Record<string, unknown>, context: ToolHookContext) => unknown | Promise<unknown>; } interface MCPToolConfig { name: string; source: MCPToolSource; tool?: MCPToolOverrides; input?: MCPInputOverrides; output?: MCPOutputOverrides; hooks?: MCPToolHooks; } interface MCPConfig { name: string; version?: string; path?: string; graphqlPath?: string; operationsPath?: string; operationsStr?: string; tools: MCPToolConfig[]; providers?: { langfuse?: LangfuseOptions & { defaults?: Partial<LangfuseGetPromptOptions>; }; [key: string]: DescriptionProvider | Record<string, unknown> | undefined; }; disableGraphQLEndpoint?: boolean; } interface ResolvedToolConfig { name: string; query: string; tool?: MCPToolOverrides; input?: MCPInputOverrides; output?: MCPOutputOverrides; hooks?: MCPToolHooks; directiveDescription?: string; providerDescription?: string; } declare function useMCP(config: MCPConfig): GatewayPlugin; export { type DescriptionProvider, type DescriptionProviderConfig, type DescriptionProviderContext, type MCPConfig, type MCPInputOverrides, type MCPOutputOverrides, type MCPToolConfig, type MCPToolHooks, type MCPToolOverrides, type MCPToolSource, type ResolvedToolConfig, type ToolHookContext, createLangfuseProvider, useMCP };