/** * FusionClient — Type-Safe MCP Client (tRPC-style) * * Provides end-to-end type safety from server to client. * The server exports its router type, and the client consumes it * with full autocomplete and compile-time validation. * * @example * ```typescript * // ── SERVER (mcp-server.ts) ── * export const registry = new ToolRegistry(); * registry.register(projects); * registry.register(billing); * export type AppRouter = InferRouter; * * // ── CLIENT (agent.ts) ── * import { createFusionClient } from '@vinkius-core/mcp-fusion/client'; * import type { AppRouter } from './mcp-server'; * * const client = createFusionClient(transport); * const result = await client.execute('projects.create', { name: 'Vinkius V2' }); * // ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ * // autocomplete! typed args! * ``` * * @module */ import { type ToolResponse } from '../core/response.js'; /** * Transport interface for the fusion client. * This abstracts the MCP transport layer (stdio, HTTP, WebSocket, etc.) */ export interface FusionTransport { /** Call a tool by name with arguments */ callTool(name: string, args: Record): Promise; } /** * Router type inferred from a ToolRegistry. * * Maps tool names to their action names and argument shapes. * This type is used at compile-time only — zero runtime cost. * * @example * ```typescript * type MyRouter = { * 'projects.list': { workspace_id: string; status?: string }; * 'projects.create': { workspace_id: string; name: string }; * 'billing.refund': { invoice_id: string; amount: number }; * }; * ``` */ export type RouterMap = Record>; /** * Client-side middleware for request/response interception. * * Follows the same onion model as server-side middleware: * each middleware wraps the next, forming a pipeline. * * Use cases: authentication injection, request logging, * retry logic, timeout enforcement, response transformation. * * @example * ```typescript * const authMiddleware: ClientMiddleware = async (action, args, next) => { * const enrichedArgs = { ...args, _token: getToken() }; * return next(action, enrichedArgs); * }; * * const retryMiddleware: ClientMiddleware = async (action, args, next) => { * for (let i = 0; i < 3; i++) { * const result = await next(action, args); * if (!result.isError) return result; * } * return next(action, args); * }; * ``` */ export type ClientMiddleware = (action: string, args: Record, next: (action: string, args: Record) => Promise) => Promise; /** * Structured error parsed from a `` XML envelope. * * Provides typed access to self-healing fields so client code * can programmatically react to server errors without regex parsing. */ export declare class FusionClientError extends Error { /** Error code from the `code` attribute (e.g. `'NOT_FOUND'`). */ readonly code: string; /** Recovery suggestion from `` element. */ readonly recovery?: string | undefined; /** Available actions from `` children. */ readonly availableActions: readonly string[]; /** Error severity from the `severity` attribute. */ readonly severity: string; /** Raw ToolResponse that caused the error. */ readonly raw: ToolResponse; constructor(message: string, code: string, raw: ToolResponse, options?: { recovery?: string | undefined; availableActions?: string[] | undefined; severity?: string | undefined; }); } /** * Options for creating a FusionClient. */ export interface FusionClientOptions { /** * Client-side middleware pipeline. * * Middleware execute in registration order (first = outermost). * Each middleware can modify the request, response, or both. * * @example * ```typescript * const client = createFusionClient(transport, { * middleware: [authMiddleware, loggingMiddleware], * }); * ``` */ middleware?: ClientMiddleware[]; /** * When `true`, `execute()` throws a {@link FusionClientError} * for responses with `isError: true`. * * When `false` (default), error responses are returned normally * and the caller must check `result.isError`. * * @default false */ throwOnError?: boolean; /** * Key used as the discriminator when calling grouped tools. * * Must match the discriminator key configured on the server * (e.g. `group.discriminator('command')`). * * @default 'action' */ discriminatorKey?: string; } /** * Type-safe client that provides autocomplete and compile-time * validation for MCP tool calls. * * @typeParam TRouter - The router map inferred from the server's registry */ export interface FusionClient { /** * Execute a tool action with full type safety. * * @param action - Full action path (e.g. 'projects.create') * @param args - Typed arguments matching the action's schema * @returns The tool response */ execute(action: TAction, args: TRouter[TAction]): Promise; /** * Execute multiple tool actions concurrently. * * All calls run in parallel via `Promise.all`. * Use `{ sequential: true }` for ordered execution. * * @param calls - Array of `{ action, args }` objects * @param options - Optional execution mode * @returns Array of tool responses, one per call * * @example * ```typescript * const results = await client.executeBatch([ * { action: 'projects.list', args: { workspace_id: 'ws_1' } }, * { action: 'billing.balance', args: { account_id: 'acc_1' } }, * ]); * ``` */ executeBatch>(calls: { [K in keyof TActions]: { action: TActions[K]; args: TRouter[TActions[K] & keyof TRouter]; }; }, options?: { sequential?: boolean | undefined; } | undefined): Promise; /** * Fluent proxy for calling tools with dot-navigation. * * Builds the action path from chained property accesses, * then executes when invoked as a function. * * @example * ```typescript * // Equivalent to: client.execute('projects.create', { name: 'V2' }) * await client.proxy.projects.create({ name: 'V2' }); * * // Also works for deeper paths: * await client.proxy.platform.users.list({ limit: 10 }); * ``` */ readonly proxy: FluentProxy; } /** * Splits a dotted key `'a.b.c'` into head `'a'` and tail `'b.c'`. * @internal */ type SplitKey = K extends `${infer Head}.${infer Tail}` ? [Head, Tail] : [K, never]; /** * Collects all first segments from the router keys. * @internal */ type FirstSegments = { [K in keyof TRouter & string]: SplitKey[0]; }[keyof TRouter & string]; /** * Given a segment prefix, collects remaining tails and their arg types. * @internal */ type SubRouter = { [K in keyof TRouter & string as K extends `${Prefix}.${infer Rest}` ? Rest : never]: TRouter[K]; }; /** * Recursive proxy node type. * * - If the key resolves to a leaf action, it becomes callable. * - If it has further segments, it exposes another level of navigation. * @internal */ type ProxyNode = { [Seg in FirstSegments]: (Seg extends keyof TRouter ? ((args: TRouter[Seg]) => Promise) : unknown) & (string extends FirstSegments> ? unknown : FluentProxy>); }; /** * Top-level fluent proxy type. * * Provides dot-navigation for calling tools: * ```typescript * await client.proxy.projects.create({ name: 'V2' }); * ``` */ export type FluentProxy = ProxyNode; /** * Create a type-safe MCP client. * * The client provides full autocomplete for action names and * compile-time validation for arguments based on the server's * router type. * * @typeParam TRouter - The router map (use `InferRouter`) * @param transport - The MCP transport layer * @param options - Client options (middleware, error handling) * @returns A typed {@link FusionClient} * * @example * ```typescript * import type { AppRouter } from './mcp-server'; * * const client = createFusionClient(transport); * * // Full autocomplete + type validation: * await client.execute('projects.create', { name: 'Vinkius V2' }); * * // TS error: 'projects.nonexistent' is not a valid action * await client.execute('projects.nonexistent', {}); * * // TS error: missing required arg 'name' * await client.execute('projects.create', {}); * ``` * * @example * ```typescript * // With client middleware and throwOnError * const client = createFusionClient(transport, { * throwOnError: true, * middleware: [ * async (action, args, next) => { * console.log(`[Client] calling ${action}`); * const result = await next(action, args); * console.log(`[Client] ${action} done`); * return result; * }, * ], * }); * ``` */ export declare function createFusionClient(transport: FusionTransport, options?: FusionClientOptions): FusionClient; export {}; //# sourceMappingURL=FusionClient.d.ts.map