@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.
150 lines (149 loc) • 5.12 kB
TypeScript
import { AuthInfo } from '@modelcontextprotocol/server';
/**
* One question for the user.
* `message` is the text the user sees.
*/
export type ToolInputRequest = {
message: string;
};
/**
* The prompt for `ctx.context.sample`.
* `messages` is the list the model reads.
* Each item has `role` and `content`.
*/
export type SampleRequest = {
messages: ReadonlyArray<{
role: string;
content: string;
}>;
};
type WaitForInput<TAnswer> = (request: ToolInputRequest) => Promise<TAnswer>;
type SampleModel<TSample> = (request: SampleRequest) => Promise<TSample>;
/** The error a tool gets when the user declines or cancels an input request. */
export declare const inputDeclinedMessage = "The user did not accept the input request.";
/**
* Callbacks for one tool call.
*
* Era `2025` needs `waitForInput` and `clientSample`.
* Era `2026` uses `sample` for the model result.
* On a protocol 2026 retry, `inputAnswer` is the answer.
* `inputDeclined` is true when the user declined or cancelled the request.
*/
export type ServerToolContextOptions<TAnswer, TSample> = {
era: '2025';
waitForInput: WaitForInput<TAnswer>;
clientSample: SampleModel<TSample>;
sample?: SampleModel<TSample>;
authInfo?: AuthInfo;
} | {
era: '2026';
inputAnswer?: TAnswer;
inputDeclined?: boolean;
sample?: SampleModel<TSample>;
authInfo?: AuthInfo;
};
/**
* The tool stopped because it needs user input.
*
* `resultType` is `input_required`.
* `request` is the object passed to `ctx.context.requestInput`.
* Catch this error, then run the tool again with `inputAnswer`.
* `ctx.context.requestInput` then returns that answer.
*
* @param request - The input request from the tool
*
* @example
* try {
* await tool(ctx)
* } catch (error) {
* if (error instanceof ToolInputRequiredError) {
* error.request
* }
* }
*/
export declare class ToolInputRequiredError extends Error {
readonly request: ToolInputRequest;
readonly resultType: "input_required";
constructor(request: ToolInputRequest);
}
/**
* The runtime context that `createMCPServer` gives a tool on `ctx.context`.
*
* Pass it as the context type of `.server()`, so `ctx.context.requestInput`
* and `ctx.context.sample` type-check.
*
* @example
* ```ts
* const askCity = toolDefinition({
* name: 'ask_city',
* description: 'Ask which city to use',
* inputSchema: z.object({}),
* }).server<MCPToolContext>(async (_args, ctx) => {
* const city = await ctx.context.requestInput({ message: 'Which city?' })
* return { city }
* })
* ```
*/
export type MCPToolContext<TContext extends Record<string, unknown> = Record<never, never>> = ReturnType<typeof createServerToolContext> & TContext;
/**
* Builds the context for one tool call.
*
* On era `2025`, `requestInput` waits on `waitForInput`.
* The same tool call then continues with that answer.
* If `inputAnswer` is absent on era `2026`, `requestInput` throws
* {@link ToolInputRequiredError}.
* If you pass `inputAnswer`, the tool runs again.
* Then `requestInput` returns that answer.
* If you pass `inputDeclined`, `requestInput` throws an Error instead.
* Code before `requestInput` runs on both calls.
*
* On era `2025`, `sample` calls `clientSample`.
* It does not call the `sample` adapter.
* On era `2026`, `sample` calls the `sample` adapter.
* It does not call `clientSample`.
* If `sample` is absent on era `2026`, `ctx.sample` throws an Error.
* The error message contains `sample`.
*
* @param options - The protocol era and the callbacks for that era
*
* @example
* const ctx = createServerToolContext({
* era: '2025',
* waitForInput: async () => 'Paris',
* clientSample: async () => 'A short draft',
* })
* await ctx.requestInput({ message: 'Which city?' })
*/
export declare function createServerToolContext<TAnswer = unknown, TSample = unknown>(options: ServerToolContextOptions<TAnswer, TSample>): {
/**
* The verified token of the caller, from the server `auth` option.
* `undefined` when the server has no `auth`, or for
* `createMCPClient({ server })`.
*/
authInfo: AuthInfo | undefined;
/**
* Asks the user for a value.
*
* On era `2025`, this waits on `waitForInput` and returns that answer.
* If `inputAnswer` is absent on era `2026`, this throws
* {@link ToolInputRequiredError}.
* If `inputAnswer` is present, this returns that answer.
* If the user declined or cancelled, this throws an Error.
* On era `2026`, a second call in the same tool call throws an Error.
*
* @param request - The question for the user
*/
requestInput(request: ToolInputRequest): Promise<TAnswer>;
/**
* Asks for a model result.
*
* On era `2025`, this calls `clientSample`.
* On era `2026`, this calls the `sample` adapter.
* If that adapter is absent, this throws an Error.
* The message contains `sample`.
*
* @param request - The prompt for the model
*/
sample(request: SampleRequest): Promise<TSample>;
};
export {};