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.

110 lines (109 loc) • 3.89 kB
//#region src/server/context.ts /** The error a tool gets when the user declines or cancels an input request. */ var inputDeclinedMessage = "The user did not accept the input request."; /** * 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 * } * } */ var ToolInputRequiredError = class extends Error { request; resultType = "input_required"; constructor(request) { super("The tool stopped because it needs input. Run the tool again with inputAnswer."); this.request = request; this.name = "ToolInputRequiredError"; } }; /** * 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?' }) */ function createServerToolContext(options) { let asked = false; return { /** * The verified token of the caller, from the server `auth` option. * `undefined` when the server has no `auth`, or for * `createMCPClient({ server })`. */ authInfo: options.authInfo, /** * 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 */ async requestInput(request) { if (options.era === "2025") return options.waitForInput(request); if (asked) throw new Error("ctx.context.requestInput can ask only one question per tool call on protocol 2026. Split the questions into separate tools."); asked = true; if (options.inputDeclined === true) throw new Error(inputDeclinedMessage); if (options.inputAnswer === void 0) throw new ToolInputRequiredError(request); return options.inputAnswer; }, /** * 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 */ async sample(request) { if (options.era === "2025") return options.clientSample(request); if (options.sample === void 0) throw new Error("ctx.context.sample needs the sample adapter on protocol 2026. Pass sample to createServerToolContext."); return options.sample(request); } }; } //#endregion export { ToolInputRequiredError, createServerToolContext, inputDeclinedMessage }; //# sourceMappingURL=context.js.map