import { guideFor } from '../../instructions.ts' import { defineTool, type RegisteredTool } from '../../server.ts' /** * Serves the provider-authoring guide on demand, ONE TOPIC AT A TIME. The * server's always-on `instructions` are a terse tool-flow reference; this tool * is the deep version, so any MCP client — not just Claude Code, which alone * auto-loads skills — can pull it. * * Topic-scoped rather than all-at-once because the full guide is ~7k tokens: * paying that to answer "how do I mark a header secret?" crowds out the actual * task, which is exactly what makes an agent slow and prone to inventing * details. Every response carries the topic index, so a caller that guessed * wrong (or called with no topic at all) always learns what else is available * without a second round-trip. * * `oldMode` selects the backend-specific guide, so the agent is never told to * call auth/publish tools that aren't registered in its mode. Stateless: * callable before `attach_browser`. */ export function howItWorksTool(oldMode: boolean): RegisteredTool { const guide = guideFor(oldMode) const names = [...guide.topics.keys()] return defineTool<{ topic?: string }>( { name: 'how_it_works', description: 'Reclaim provider-authoring guide for THIS server, by topic. Call it ' + 'with no arguments for the overview plus the topic index, then again ' + 'for whichever topic you need. Topics: ' + names.join(', ') + '. Use `all` for the entire guide at once (large — prefer a topic). ' + 'Read the relevant topic BEFORE acting unless you are already fluent ' + 'in that part of the flow.', inputSchema: { type: 'object', properties: { topic: { type: 'string', // Deliberately NOT an `enum`: schema validation runs before the // handler, so an enum turns a mistyped topic into a validation // error instead of a response carrying the real topic list. A // plain string lets the tool answer the mistake usefully. description: 'Which section to return: ' + names.join(', ') + ', or `all`. Omit for the overview + topic index. An ' + 'unrecognized value comes back with the list, not an error.', }, }, }, }, async({ topic }) => { if(topic === 'all') { return { topic: 'all', topics: guide.index, guide: guide.full } } const requested = topic ?? 'overview' const section = guide.topics.get(requested) if(!section) { // Not an error: hand back the index so the caller self-corrects in // the same turn rather than retrying blind. return { error: `Unknown topic "${requested}".`, topics: guide.index, guide: guide.topics.get('overview'), } } return { topic: requested, topics: guide.index, guide: section } }, ) }