/** * `qodo-skills` MCP server implementation. * * Three tools, all `autoApproved: true`: `list_skills`, `get_skill`, * `get_skill_file`. The server is a thin facade over a `SkillsManager` * — the heavy lifting (catalog walk, parsing, validation, rendering) lives * in the manager. The server owns: * * - Tool-routing (which handler runs for which `tool_name`). * - In-memory caches keyed by `` + ` + `. * - Path validation for `get_skill_file` (see `path.ts`). * - Telemetry emission via the manager's `forwardEvents` sink. * - Structured error responses on the `isError: true` envelope. * * Integration: the SDK has no `InternalMCPBaseServer` interface. The * integration shape is `McpInProcessHandler` from `QarRegistryClient` * (`(req: { tool_name, arguments }) => Promise<...>`); the * `QodoSkillsMcpServer` class is invoked via the {@link asMcpHandler} * adapter. The SDK ships zero built-in MCPs by default — this server * auto-registers via `client.qar.registerMcp('qodo-skills', * { transport: 'sdk', handler })` when `ClientOptions.skills` is * provided. Reach the registered config via * `client.qar.getMcp('qodo-skills')`; the server instance itself is * owned by `QodoClient`. * * Caches are cleared on {@link QodoSkillsMcpServer.dispose}. */ import type { McpInProcessHandler } from '../../client/QarRegistryClient.js'; import type { SkillsManager } from '../manager.js'; import type { SubstitutionContext } from '../substitute.js'; import type { McpToolResult, QodoSkillsMcpEvent } from './types.js'; /** The MCP tool names the server routes. */ export declare const QODO_SKILLS_TOOL_NAMES: readonly ["list_skills", "get_skill", "get_skill_file"]; export type QodoSkillsToolName = (typeof QODO_SKILLS_TOOL_NAMES)[number]; export { qodoSkillsFunctionToolDefs } from './wireDefs.js'; /** * Server class for the three `qodo-skills` MCP tools. Construct one per * `QodoClient` instance; the SDK auto-wires it through * {@link QodoClient.constructor} when `ClientOptions.skills` is configured. */ export declare class QodoSkillsMcpServer { private readonly skills; private readonly bodyCache; private readonly fileCache; /** * Shared with `SkillsManager.internalActiveFqns` so caller-pin * activation and LLM-driven activation surface in one place * (`client.skills.getActiveSkills()` / * `client.skillsMcpServer.loadedSkillFqns`). The MCP server still owns * the body / file caches it manages, but it does NOT own the active * set — the manager does. */ private readonly activeFqns; private readonly emit; /** * Resolver for ambient substitution context. Wired in by `QodoClient` so * each `get_skill` invocation sees the consumer's session ID, the active * effort level, and the current `${QODO_SKILL_DIR}` (= the skill's own * canonical path). When undefined, those envvar substitutions stay literal. */ private readonly resolveContext; constructor(skills: SkillsManager, opts?: { readonly emit?: (event: QodoSkillsMcpEvent) => void; /** * Lazy resolver for runtime context (session ID, effort). Re-evaluated * on every `get_skill` call so a long-lived MCP server tracks the * active task's identity. */ readonly resolveContext?: () => Partial; }); /** * The set of skill FQNs whose body has been loaded into the current run. * Surfaces for testing + for forward-looking trust-tier middleware that * needs to know which skills' `allowed-tools` should be in scope. */ get loadedSkillFqns(): readonly string[]; /** Bytes currently held in the body cache. Useful for memory-pressure tests. */ get cachedBodyBytes(): number; /** * Drop the in-memory caches and active-skill set. Idempotent. Called by * `QodoClient.disconnect()` so the next `connect()` starts cold. * * The active set is shared with `SkillsManager.internalActiveFqns`; * clearing here implicitly clears the manager's surface too — both * `client.skills.getActiveSkills()` and `client.skillsMcpServer.loadedSkillFqns` * return empty after dispose. */ dispose(): void; /** * Route a call by `tool_name` to the matching handler. Unknown names * return an MCP error envelope. The handler signature matches the * `McpInProcessHandler` contract so the same instance is registered via * `client.qar.registerMcp(name, { transport: 'sdk', handler })`. */ callTool(toolName: string, args: Record): Promise; private handleListSkills; private handleGetSkill; /** * Convert an activation-resolver failure into the matching telemetry * event(s). Cycles + unresolved-dep failures piggyback on the existing * `sdk.skill.error` channel; trust violations and cap breaches have * dedicated event kinds. */ private emitResolverFailure; /** * Emit the `allowed-tools` telemetry shape (`applied` for trusted, * `advisory` for third-party). Same logic as the SDK's caller-pin * activation path; kept in sync intentionally. */ private emitAllowedToolsEvents; private handleGetSkillFile; /** * Render the file envelope, applying substitutions to markdown / text * content. Cache hits go through the same path so substitutions apply * even on cached reads. */ private renderFileEnvelope; /** * Forward substitution warnings from `substitute(...)` through the * manager's telemetry sink. Each `SubstitutionWarning` becomes one * `sdk.skill.substitution_warn` event keyed to the skill that emitted it. */ private emitSubstitutionWarnings; /** * Forward MCP-specific telemetry through the manager's sink. The base * `SkillEvent` union doesn't include these MCP-only kinds; the * manager's sink accepts a superset, so we cast through `unknown` at * the boundary. Consumers reading `SkillEvent` discriminate by `kind` * literal. */ private emitTel; } /** * Adapt a {@link QodoSkillsMcpServer} to the {@link McpInProcessHandler} * shape expected by `client.qar.registerMcp('qodo-skills', { transport: * 'sdk', handler })`. The handler unwraps the wire `{ tool_name, arguments }` * shape and delegates to {@link QodoSkillsMcpServer.callTool}. * * The returned value is the MCP envelope `{ content, isError? }`. QAR's * agent loop reads `content[0].text` (or `content[0].data` for images) as * the tool call's response payload. */ export declare function asMcpHandler(server: QodoSkillsMcpServer): McpInProcessHandler; //# sourceMappingURL=server.d.ts.map