extract stream parser module with tests, add mcp tool call logging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 13:49:26 +00:00
co-authored by Claude Opus 4.6
parent 86ddcbfead
commit c38d5b0ea1
4 changed files with 550 additions and 133 deletions
+45 -4
View File
@@ -13,8 +13,8 @@
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import { readdirSync, existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import { readdirSync, existsSync, readFileSync, appendFileSync, mkdirSync } from 'node:fs';
import { join, dirname } from 'node:path';
// ── Types ──
@@ -186,6 +186,31 @@ function buildZodSchema(inputs: Record<string, ToolParam>): z.ZodRawShape {
return shape;
}
// ── Logging ──
const LOG_FILE = process.env.MCP_TOOLS_LOG ?? '';
function logToolCall(toolName: string, durationMs: number, success: boolean, error?: string): void {
const ts = new Date().toISOString();
const status = success ? 'ok' : 'error';
const line = error
? `${ts}\t${toolName}\t${status}\t${durationMs}ms\t${error}\n`
: `${ts}\t${toolName}\t${status}\t${durationMs}ms\n`;
// Always log to stderr for process-level visibility
console.error(`[mcp-tools] ${toolName} ${status} (${durationMs}ms)${error ? ': ' + error : ''}`);
// Write to log file if configured
if (LOG_FILE) {
try {
mkdirSync(dirname(LOG_FILE), { recursive: true });
appendFileSync(LOG_FILE, line);
} catch {
// Don't fail tool calls over logging
}
}
}
// ── Main ──
const rawDirs = process.env.PI_TOOLS_DIRS ?? '';
@@ -218,11 +243,14 @@ for (const { entryFile, meta } of tools) {
inputSchema: z.object(schema),
},
async (params) => {
const start = performance.now();
let executeFn: Function | undefined;
try {
const mod = await import(capturedEntry);
executeFn = mod.execute ?? mod.default?.execute;
} catch (err) {
const ms = Math.round(performance.now() - start);
logToolCall(toolName, ms, false, `load failed: ${String(err)}`);
return {
content: [{ type: 'text' as const, text: `Failed to load ${toolName}: ${String(err)}` }],
isError: true,
@@ -230,14 +258,27 @@ for (const { entryFile, meta } of tools) {
}
if (typeof executeFn !== 'function') {
const ms = Math.round(performance.now() - start);
logToolCall(toolName, ms, false, 'no execute function');
return {
content: [{ type: 'text' as const, text: `${toolName}/index.ts must export an "execute" function` }],
isError: true,
};
}
const result = await executeFn('mcp', params, undefined, undefined);
return result;
try {
const result = await executeFn('mcp', params, undefined, undefined);
const ms = Math.round(performance.now() - start);
logToolCall(toolName, ms, !result.isError, result.isError ? 'tool returned error' : undefined);
return result;
} catch (err) {
const ms = Math.round(performance.now() - start);
logToolCall(toolName, ms, false, String(err));
return {
content: [{ type: 'text' as const, text: `${toolName} threw: ${String(err)}` }],
isError: true,
};
}
},
);