agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
104 lines • 4.54 kB
TypeScript
/**
* mockMcpClient — in-memory MCP client for development and tests.
*
* const slack = mockMcpClient({
* tools: [
* {
* name: 'send_message',
* description: 'Post a message to a channel',
* inputSchema: { type: 'object' },
* handler: async ({ text }) => `Posted: ${text}`,
* },
* ],
* });
*
* const agent = Agent.create({ provider: mock({ reply: 'ok' }) })
* .tools(await slack.tools())
* .build();
*
* Pattern: Adapter (GoF) — produces an `McpClient` with the same shape
* as `mcpClient(opts)` but driven by an in-memory tool table
* instead of the MCP SDK + transport. Drop-in for development:
* start with `mockMcpClient`, swap to `mcpClient` once the
* real server is ready.
*
* Why public: `mcpClient`'s `_client` injection is `@internal` because
* the SDK shape isn't a stable public surface. `mockMcpClient` exposes
* a curated tool-handler shape that's tied to OUR Tool contract instead
* — stable, documented, and the right level of abstraction for
* mock-first development.
*/
import type { McpClient, McpClientOptions, McpCallToolResult } from './types.js';
/** A scripted tool exposed by the mock MCP server. */
export interface MockMcpTool {
/** Tool name as the LLM sees it. */
readonly name: string;
/** Description surfaced to the LLM via the tool schema. */
readonly description?: string;
/**
* JSON-schema-like input schema. Passed through to the agent's tool
* registry verbatim — same as a real MCP server's `listTools()`.
*/
readonly inputSchema: Readonly<Record<string, unknown>>;
/**
* MCP's extensibility bag, exactly as a real server would send it (9.71.0).
*
* agentfootprint's tool DECLARATIONS — `argumentsFrom`, `resultKind`,
* `owner`, `resultClass`, `resultCeiling` — ride under the `agentfootprint`
* key here, and this mock reads them through the SAME defensive reader
* `mcpClient` uses. So a rail armed by a remotely-declared tool can be
* developed and tested before the real server exists, and so can the
* unhappy path: put a malformed declaration in and watch the field get
* dropped, the warning name the rule, and the tool register anyway.
*
* ```ts
* mockMcpClient({
* name: 'fleet-mcp',
* tools: [{
* name: 'backup_status',
* inputSchema: { type: 'object', properties: { machine: { type: 'string' } } },
* _meta: { agentfootprint: { argumentsFrom: ['fleet_report'] } },
* }],
* });
* ```
*
* Omitted → the registered `Tool` carries no declarations, exactly as
* before this existed.
*/
readonly _meta?: Readonly<Record<string, unknown>>;
/**
* Async handler that runs when the agent calls this tool. Receives
* the args the LLM produced; returns a string or an MCP result envelope.
* Strings retain the existing behavior in text mode; envelopes use the
* same result decoder as the real client.
*
* Defaults to `async () => '[mock result]'` when omitted — useful
* when the consumer cares about wiring not behavior.
*/
readonly handler?: (args: Record<string, unknown>,
/**
* The request's `_meta` bag, exactly as the real client would send it —
* `{ agentfootprint: { time } }` on a call handed `ctx.time` (the time
* layer), absent otherwise. A handler that ignores it behaves as before.
*/
request: {
readonly _meta?: Readonly<Record<string, unknown>>;
}) => Promise<string | McpCallToolResult>;
}
export interface MockMcpClientOptions {
/** Logical server name. Surfaces in observability + error messages. */
readonly name?: string;
/** Tools exposed by the mock server. */
readonly tools: readonly MockMcpTool[];
/** Same result decoding as mcpClient. A string handler becomes one text
* block; return McpCallToolResult to exercise structuredContent or isError. */
readonly resultMode?: McpClientOptions['resultMode'];
}
/**
* Build an in-memory `McpClient`. Useful when you want to develop
* against MCP semantics without spawning subprocesses, hitting the
* network, or installing `@modelcontextprotocol/sdk`. Same `McpClient`
* shape as `mcpClient(opts)` — code that consumes one accepts the other.
*/
export declare function mockMcpClient(options: MockMcpClientOptions): McpClient;
//# sourceMappingURL=mockMcpClient.d.ts.map