nyk
feat(refactor): ready for manual QA after main sync (#274)
b6ecafa unverified
Raw
History Blame Contribute Delete
2.94 kB
/**
* MCP Audit — logs and analyzes MCP tool calls per agent.
*
* Tracks every tool invocation with success/failure, duration, and error detail.
* Provides aggregated stats for efficiency dashboards.
*/
import { getDatabase } from '@/lib/db'
export interface McpCallInput {
agentName?: string
mcpServer?: string
toolName?: string
success?: boolean
durationMs?: number
error?: string
workspaceId?: number
}
export interface McpCallStats {
totalCalls: number
successCount: number
failureCount: number
successRate: number
avgDurationMs: number
toolBreakdown: Array<{
toolName: string
mcpServer: string
calls: number
successes: number
failures: number
avgDurationMs: number
}>
}
export function logMcpCall(input: McpCallInput): number {
const db = getDatabase()
const result = db.prepare(`
INSERT INTO mcp_call_log (agent_name, mcp_server, tool_name, success, duration_ms, error, workspace_id)
VALUES (?, ?, ?, ?, ?, ?, ?)
`).run(
input.agentName ?? null,
input.mcpServer ?? null,
input.toolName ?? null,
input.success !== false ? 1 : 0,
input.durationMs ?? null,
input.error ?? null,
input.workspaceId ?? 1,
)
return result.lastInsertRowid as number
}
export function getMcpCallStats(
agentName: string,
hours: number = 24,
workspaceId: number = 1,
): McpCallStats {
const db = getDatabase()
const since = Math.floor(Date.now() / 1000) - hours * 3600
const totals = db.prepare(`
SELECT
COUNT(*) as total,
SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successes,
SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) as failures,
AVG(duration_ms) as avg_duration
FROM mcp_call_log
WHERE agent_name = ? AND workspace_id = ? AND created_at > ?
`).get(agentName, workspaceId, since) as any
const breakdown = db.prepare(`
SELECT
tool_name,
mcp_server,
COUNT(*) as calls,
SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successes,
SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) as failures,
AVG(duration_ms) as avg_duration
FROM mcp_call_log
WHERE agent_name = ? AND workspace_id = ? AND created_at > ?
GROUP BY tool_name, mcp_server
ORDER BY calls DESC
`).all(agentName, workspaceId, since) as any[]
const total = totals?.total ?? 0
const successCount = totals?.successes ?? 0
const failureCount = totals?.failures ?? 0
return {
totalCalls: total,
successCount,
failureCount,
successRate: total > 0 ? Math.round((successCount / total) * 10000) / 100 : 100,
avgDurationMs: Math.round(totals?.avg_duration ?? 0),
toolBreakdown: breakdown.map((row: any) => ({
toolName: row.tool_name ?? 'unknown',
mcpServer: row.mcp_server ?? 'unknown',
calls: row.calls,
successes: row.successes,
failures: row.failures,
avgDurationMs: Math.round(row.avg_duration ?? 0),
})),
}
}