Skip to content

Commit b66287a

Browse files
Synalux AIclaude
andcommitted
feat: add inference_metrics read-only MCP tool (19.2.3)
New tool returns the session's local-delegation metrics on demand. No args, read-only, reads the existing local accumulator. Description explicitly notes this tracks prism_infer delegation only, not Claude's own token spend. 2 new tests for the handler (empty + populated). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 728b99a commit b66287a

6 files changed

Lines changed: 52 additions & 1 deletion

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "prism-mcp-server",
3-
"version": "19.2.2",
3+
"version": "19.2.3",
44
"mcpName": "io.github.dcostenco/prism-coder",
55
"description": "Prism Coder — Cognitive memory + tool-calling intelligence for AI agents. Mind Palace persistent memory (BFCL Gold Certified, 100% Tool-Call Accuracy, 114 Agent Skills, PHI Guard, Tier Enforcement, Prompt-Based Skill Routing, Zero-Search HDC/HRR retrieval, HRR Semantic Drift Detection across BCBA/Coding/AAC domains, HIPAA-hardened local-first storage, SLERP-optimized GRPO alignment) plus the prism-coder 1.7B–32B open-weights LLM fleet.",
66
"module": "index.ts",

src/server.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ import { sanitizeMcpOutput } from "./utils/sanitizer.js";
100100
import { getTracer, initTelemetry } from "./utils/telemetry.js";
101101
import { context as otelContext, trace, SpanStatusCode } from "@opentelemetry/api";
102102
import { ddInfo, ddError as ddLogError } from "./utils/ddLogger.js";
103+
import { inferenceMetricsHandler } from "./utils/inferenceMetrics.js";
103104

104105
// ─── Import Tool Definitions (schemas) and Handlers (implementations) ─────
105106

@@ -171,6 +172,8 @@ import {
171172
QUERY_MEMORY_NATURAL_TOOL,
172173
// v15.5: Knowledge Ingestion
173174
KNOWLEDGE_INGEST_TOOL,
175+
// v19.2: Inference Metrics
176+
INFERENCE_METRICS_TOOL,
174177

175178
sessionSaveLedgerHandler,
176179
sessionSaveHandoffHandler,
@@ -345,6 +348,8 @@ function buildSessionMemoryTools(autoloadList: string[]): Tool[] {
345348
QUERY_MEMORY_NATURAL_TOOL, // query_memory_natural — NL → structured memory search
346349
// ─── v15.5: Knowledge Ingestion ───
347350
KNOWLEDGE_INGEST_TOOL, // knowledge_ingest — chunk code, gen Q&A, store in graph
351+
// ─── v19.2: Inference Metrics ───
352+
INFERENCE_METRICS_TOOL, // inference_metrics — read-only session delegation stats
348353
];
349354
}
350355

@@ -1078,6 +1083,9 @@ export function createServer() {
10781083
if (!SESSION_MEMORY_ENABLED) throw new Error("Session memory not configured.");
10791084
result = await knowledgeIngestHandler(args); break;
10801085

1086+
case "inference_metrics":
1087+
result = await inferenceMetricsHandler(); break;
1088+
10811089
default:
10821090
result = {
10831091
content: [{ type: "text", text: `Unknown tool: ${name}` }],

src/tools/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export { verifyBehaviorHandler } from "./behavioralVerifierHandler.js";
120120
// Chunks source code, generates Q&A via Claude Haiku, stores in knowledge graph.
121121
// Three entry points: MCP tool, REST API, GitHub webhook.
122122
export { KNOWLEDGE_INGEST_TOOL } from "./ingestDefinitions.js";
123+
export { INFERENCE_METRICS_TOOL } from "./sessionMemoryDefinitions.js";
123124
export { knowledgeIngestHandler, handleGitHubWebhook, ingestKnowledge, isIngestArgs } from "./ingestHandler.js";
124125

125126
// ── v15.4: prism_infer — local-first inference (RAM-gated cascade) ──

src/tools/sessionMemoryDefinitions.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2035,3 +2035,18 @@ export function isVerifyBehaviorArgs(a: unknown): a is {
20352035
if (typeof o.change_summary !== "string") return false;
20362036
return true;
20372037
}
2038+
2039+
// ─── v19.2: Inference Metrics Tool ──────────────────────────
2040+
2041+
export const INFERENCE_METRICS_TOOL: Tool = {
2042+
name: "inference_metrics",
2043+
description:
2044+
"Returns the current session's local-model inference metrics — call count, " +
2045+
"local vs cloud split, token totals, per-model breakdown, and average latency. " +
2046+
"Read-only, no arguments. Reflects prism_infer delegation usage only, not the " +
2047+
"host model's (Claude's) own token spend (use /cost for that).",
2048+
inputSchema: {
2049+
type: "object",
2050+
properties: {},
2051+
},
2052+
};

src/utils/inferenceMetrics.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,19 @@ export function resetInferenceMetrics(): void {
105105
debugLog("[inference-metrics] Session metrics reset");
106106
}
107107

108+
export async function inferenceMetricsHandler(): Promise<{
109+
content: Array<{ type: "text"; text: string }>;
110+
isError?: boolean;
111+
}> {
112+
const block = formatInferenceMetrics();
113+
return {
114+
content: [{
115+
type: "text",
116+
text: block || "No prism_infer calls this session. Metrics track local-model delegation only — not the host model's (Claude's) token spend.",
117+
}],
118+
};
119+
}
120+
108121
export function formatInferenceMetrics(): string {
109122
const snap = getInferenceSnapshot();
110123
if (snap.totalCalls === 0) return "";

tests/inferenceMetrics.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
getInferenceSnapshot,
55
resetInferenceMetrics,
66
formatInferenceMetrics,
7+
inferenceMetricsHandler,
78
} from "../src/utils/inferenceMetrics.js";
89

910
beforeEach(() => {
@@ -147,6 +148,19 @@ describe("formatInferenceMetrics", () => {
147148
expect(out.indexOf("prism-coder:9b")).toBeLessThan(out.indexOf("prism-coder:27b"));
148149
});
149150

151+
it("handler returns message when no calls", async () => {
152+
const result = await inferenceMetricsHandler();
153+
expect(result.content[0].text).toContain("No prism_infer calls");
154+
expect(result.content[0].text).toContain("local-model delegation");
155+
});
156+
157+
it("handler returns metrics block when calls exist", async () => {
158+
recordInference({ backend: "ollama-9b", model_picked: "prism-coder:9b", used_cloud: false, latency_ms: 100, prompt_tokens: 200, completion_tokens: 80 });
159+
const result = await inferenceMetricsHandler();
160+
expect(result.content[0].text).toContain("Total calls: 1");
161+
expect(result.content[0].text).toContain("Local: 1 (100%)");
162+
});
163+
150164
it("mixed local and cloud", () => {
151165
recordInference({ backend: "ollama-9b", model_picked: "prism-coder:9b", used_cloud: false, latency_ms: 50, prompt_tokens: 100, completion_tokens: 50 });
152166
recordInference({ backend: "synalux", model_picked: null, used_cloud: true, latency_ms: 200, prompt_tokens: 80, completion_tokens: 40 });

0 commit comments

Comments
 (0)