UNPKG

@tanstack/ai-mcp

Version:

Host-side Model Context Protocol client for TanStack AI: discover and run MCP server tools, resources, and prompts in any adapter's chat() loop, with generated end-to-end types.

123 lines (122 loc) • 6.62 kB
import { Client, ClientOptions, GetPromptResult, Prompt, ReadResourceResult, Resource, ResourceTemplateType, Transport } from '@modelcontextprotocol/client'; import { DescriptorFromServer, DirectClientOptions, DirectMCPClient } from './direct-client.js'; import { MCPServer } from './server/create-server.js'; import { TransportConfig } from './transport.js'; import { AnyToolDefinition, AutomaticDescriptor, DescriptorTools, MCPClientOptions, MappedServerTools, ServerDescriptor, ToolsOptions } from './types.js'; type CallToolResult = Awaited<ReturnType<Client['callTool']>>; /** * The raw MCP result of `callTool`. When the tool output type is known, * `structuredContent` has that type. An untyped tool keeps the SDK type. */ export type TypedCallToolResult<TOutput> = unknown extends TOutput ? CallToolResult : Omit<CallToolResult, 'structuredContent'> & { structuredContent?: TOutput; }; export interface MCPClient<TServer extends ServerDescriptor = AutomaticDescriptor> { readonly capabilities: TServer['capabilities']; /** * Auto-discovery: every server tool as a ServerTool. With a generated * descriptor, tool names are typed as the descriptor's name literals; * args/results stay untyped — use the `tools(defs)` overload for typed args. * * Both overloads yield {@link McpServerTool}s, so `tool.metadata.mcp` (the * server's title / annotations) is typed without an annotation or a cast. */ tools: { (options?: ToolsOptions): Promise<DescriptorTools<TServer>>; /** * Explicit: bind these TanStack toolDefinitions to the server (typed + * validated, allowlist). Note: when the client has a `prefix`, the * runtime tool name is `${prefix}_${def.name}` while the static `TName` * stays the unprefixed definition name. */ <const TDefs extends ReadonlyArray<AnyToolDefinition>>(defs: TDefs, options?: ToolsOptions): Promise<MappedServerTools<TDefs>>; }; resources: () => Promise<Array<Resource>>; /** * Reads one resource. With a typed server, `uri` is one of its resource URIs. */ readResource: (uri: TServer['resources'][keyof TServer['resources']]['uri']) => Promise<ReadResourceResult>; resourceTemplates: () => Promise<Array<ResourceTemplateType>>; prompts: () => Promise<Array<Prompt>>; /** * Renders one prompt. With a typed server, `name` is one of its prompt * names and `args` has that prompt's argument type. MCP sends each * argument as a string. */ getPrompt: <TName extends keyof TServer['prompts'] & string>(name: TName, args?: TServer['prompts'][TName]['args']) => Promise<GetPromptResult>; /** * Call a tool directly and return its raw MCP result. Tools declaring * `execution.taskSupport: 'required'` automatically use task execution when * the server declares the tasks capability for tools/call. Pass * `options.signal` to abort — an in-flight task is best-effort cancelled on * the server. * * With a typed server, `name` is one of its tool names and `args` has * that tool's input type. The result is the raw MCP result. For a tool * with an output schema, `structuredContent` has the tool output type. */ callTool: <TName extends keyof TServer['tools'] & string>(name: TName, args?: TServer['tools'][TName]['input'], options?: { signal?: AbortSignal; }) => Promise<TypedCallToolResult<TServer['tools'][TName]['output']>>; /** * The ORIGINAL connection descriptor this client was created from — the * `transport` input and `prefix` passed to `createMCPClient`. Used by * `createMcpAppCallHandler` to reconnect per-call (serverless-safe) without * a separate transport-config map. * * `transport` is `undefined` when the client was built from a ready-made * `Transport` instance rather than a serializable config — either via * `createMCPClientFromTransport` (test-only) or `createMCPClient({ transport: * <instance> })`. A live `Transport` instance is single-use and cannot be * reconnected, so only serializable `TransportConfig`s are retained here. */ getInfo: () => { transport: TransportConfig | undefined; prefix: string | undefined; /** * The options this client was built with, so a caller that reconstructs it * from this descriptor keeps them. Without it a rebuilt client silently * reverts to the SDK defaults — including the AJV validator that edge * runtimes cannot compile. * * Optional so an existing hand-rolled `MCPClient` keeps compiling. */ clientOptions?: ClientOptions; toolFilter?: MCPClientOptions['toolFilter']; needsApproval?: MCPClientOptions['needsApproval']; }; close: () => Promise<void>; [Symbol.asyncDispose]: () => Promise<void>; } /** * Connects to an MCP server. * * Pass `transport` for a server at a URL, on SSE, or on stdio. * The client speaks MCP over that transport, so server auth applies. * * To type a transport client from a TanStack server, pass `typeof server` * as the type argument. Import the server with `import type`. * * Pass `server` to call a `createMCPServer` result in this process. * That client calls the tool functions directly. It opens no connection, * and the server `auth` option does not run. * * @param options - A transport, or a TanStack MCP server in this process * * @example * ```ts * const remote = await createMCPClient<typeof server>({ * transport: { type: 'http', url: 'https://mcp.example.com/mcp' }, * }) * await remote.callTool('get_weather', { city: 'Paris' }) * * const local = await createMCPClient({ server }) * await local.callTool('get_weather', { city: 'Paris' }) * ``` */ export declare function createMCPClient<TDescriptor extends ServerDescriptor = AutomaticDescriptor>(options: MCPClientOptions): Promise<MCPClient<TDescriptor>>; export declare function createMCPClient<TServer extends MCPServer>(options: MCPClientOptions): Promise<MCPClient<DescriptorFromServer<TServer>>>; export declare function createMCPClient<TServer extends MCPServer>(options: DirectClientOptions<TServer>): Promise<DirectMCPClient<TServer>>; /** Test-only: connect directly from a transport instance (skips resolveTransport). */ export declare function createMCPClientFromTransport<TServer extends ServerDescriptor = AutomaticDescriptor>(transport: Transport, prefix?: string, clientOptions?: ClientOptions): Promise<MCPClient<TServer>>; export {};