Skip to content

Commit 801af66

Browse files
committed
release: v2.7.236 runtime auto-load decision telemetry
1 parent ccc36e2 commit 801af66

File tree

4 files changed

+128
-17
lines changed

4 files changed

+128
-17
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44

55
All notable changes to this project will be documented in this file.
66

7+
## [2.7.236] — 2026-03-15
8+
9+
- feat(core/mcp): `packages/core/src/routers/mcpRouter.ts` now applies confidence-based auto-load evaluation telemetry for runtime `search_tools` responses too (not just cached ranking), including outcome, skip reason, execution status, and threshold fields in emitted search events.
10+
- fix(core/mcp-search): runtime search profile reranking now preserves runtime `autoLoaded` state by tool name so operator telemetry and loaded-state badges no longer silently drop auto-load attribution when profile ranking is active.
11+
- feat(core/mcp): auto-load `load` telemetry emitted from both runtime-search and cached-ranking paths now records working-set pressure snapshots when available (`loaded/hydrated` counts, caps, utilization, idle threshold), improving triage under capacity pressure.
12+
- test(validation): reran focused MCP suites (`toolSearchRanking`, `metamcp-session-working-set`) with `16` tests passing and revalidated web TypeScript gate with explicit `WEB_TSC_OK`.
13+
714
## [2.7.235] — 2026-03-15
815

916
- feat(core/mcp): `packages/core/src/routers/mcpRouter.ts` now records working-set pressure snapshot fields (`loadedToolCount`, `hydratedSchemaCount`, configured caps, utilization percentages, idle-eviction threshold) on load/unload/hydrate telemetry events.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.7.235
1+
2.7.236

VERSION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
# Borg Project Version: 2.7.235
1+
# Borg Project Version: 2.7.236

packages/core/src/routers/mcpRouter.ts

Lines changed: 119 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,6 @@ function collectServerProbeTrafficEvents(options: {
196196
});
197197
}
198198

199-
function recordAutoLoadTelemetry(toolName: string, message: string): void {
200-
toolSelectionTelemetry.record({
201-
type: 'load',
202-
toolName,
203-
status: 'success',
204-
message,
205-
evictedTools: parseEvictedToolsFromMessage(message),
206-
});
207-
}
208-
209199
function toLatencyMs(startedAt: number): number {
210200
return Math.max(0, Date.now() - startedAt);
211201
}
@@ -424,6 +414,8 @@ export const mcpRouter = t.router({
424414
inputSchema: null,
425415
}));
426416

417+
const runtimeAutoLoadedToolName = runtimeMappedResults.find((tool) => tool.autoLoaded)?.name;
418+
427419
const mappedResults = selectedProfile
428420
? rankToolSearchCandidates(
429421
runtimeMappedResults.map((tool) => ({
@@ -466,20 +458,122 @@ export const mcpRouter = t.router({
466458
matchReason: tool.matchReason ?? 'matched available metadata',
467459
score: tool.score ?? 0,
468460
rank: index + 1,
469-
autoLoaded: false,
461+
autoLoaded: tool.name === runtimeAutoLoadedToolName,
462+
loaded: Boolean(tool.loaded) || tool.name === runtimeAutoLoadedToolName,
470463
inputSchema: null,
471464
}))
472465
: runtimeMappedResults;
473466

474-
const autoLoadedResult = mappedResults.find((tool) => tool.autoLoaded);
475-
if (autoLoadedResult) {
476-
recordAutoLoadTelemetry(autoLoadedResult.name, `Tool '${autoLoadedResult.name}' auto-loaded from search.`);
467+
const autoLoadEvaluation = evaluateAutoLoadCandidate(
468+
mappedResults.map((tool) => ({
469+
name: tool.name,
470+
description: tool.description,
471+
serverName: tool.server,
472+
serverDisplayName: tool.serverDisplayName,
473+
advertisedName: tool.advertisedName,
474+
originalName: tool.originalName ?? undefined,
475+
serverTags: tool.serverTags,
476+
toolTags: tool.toolTags,
477+
semanticGroup: tool.semanticGroup,
478+
semanticGroupLabel: tool.semanticGroupLabel,
479+
keywords: tool.keywords,
480+
alwaysOn: tool.alwaysOn,
481+
loaded: tool.loaded,
482+
hydrated: tool.hydrated,
483+
deferred: tool.deferred,
484+
})),
485+
normalizedQuery,
486+
{
487+
minConfidence: preferences.autoLoadMinConfidence,
488+
},
489+
);
490+
491+
const runtimeAutoLoadedResult = mappedResults.find((tool) => tool.autoLoaded);
492+
let autoLoadDecision = runtimeAutoLoadedResult
493+
? {
494+
toolName: runtimeAutoLoadedResult.name,
495+
reason: 'runtime search_tools returned auto-loaded result',
496+
confidence: 0,
497+
scoreGap: 0,
498+
topScore: runtimeAutoLoadedResult.score ?? 0,
499+
secondScore: mappedResults[1]?.score ?? 0,
500+
}
501+
: autoLoadEvaluation.decision;
502+
let autoLoadExecutionStatus: 'success' | 'error' | 'not-attempted' = runtimeAutoLoadedResult
503+
? 'success'
504+
: 'not-attempted';
505+
let autoLoadExecutionError: string | undefined;
506+
507+
if (runtimeAutoLoadedResult && server) {
508+
let pressure: Awaited<ReturnType<typeof readWorkingSetSnapshot>> | undefined;
509+
try {
510+
pressure = await readWorkingSetSnapshot(server);
511+
} catch {
512+
pressure = undefined;
513+
}
514+
515+
toolSelectionTelemetry.record({
516+
type: 'load',
517+
toolName: runtimeAutoLoadedResult.name,
518+
status: 'success',
519+
message: `Tool '${runtimeAutoLoadedResult.name}' auto-loaded by runtime search.`,
520+
autoLoadReason: autoLoadDecision.reason,
521+
autoLoadConfidence: autoLoadDecision.confidence,
522+
scoreGap: autoLoadDecision.scoreGap,
523+
topScore: autoLoadDecision.topScore,
524+
...pressure,
525+
});
526+
} else if (!runtimeAutoLoadedResult && autoLoadDecision && server) {
527+
const autoLoadStartedAt = Date.now();
528+
try {
529+
const loadResult = await server.executeTool('load_tool', { name: autoLoadDecision.toolName });
530+
autoLoadExecutionStatus = 'success';
531+
const loadMessage = getToolTextContent(loadResult);
532+
const pressure = await readWorkingSetSnapshot(server);
533+
toolSelectionTelemetry.record({
534+
type: 'load',
535+
toolName: autoLoadDecision.toolName,
536+
status: 'success',
537+
message: loadMessage,
538+
evictedTools: parseEvictedToolsFromMessage(loadMessage),
539+
latencyMs: toLatencyMs(autoLoadStartedAt),
540+
autoLoadReason: autoLoadDecision.reason,
541+
autoLoadConfidence: autoLoadDecision.confidence,
542+
scoreGap: autoLoadDecision.scoreGap,
543+
topScore: autoLoadDecision.topScore,
544+
...pressure,
545+
});
546+
} catch (error) {
547+
autoLoadExecutionStatus = 'error';
548+
autoLoadExecutionError = error instanceof Error ? error.message : String(error);
549+
toolSelectionTelemetry.record({
550+
type: 'load',
551+
toolName: autoLoadDecision.toolName,
552+
status: 'error',
553+
message: autoLoadExecutionError,
554+
latencyMs: toLatencyMs(autoLoadStartedAt),
555+
autoLoadReason: autoLoadDecision.reason,
556+
autoLoadConfidence: autoLoadDecision.confidence,
557+
scoreGap: autoLoadDecision.scoreGap,
558+
topScore: autoLoadDecision.topScore,
559+
});
560+
}
561+
}
562+
563+
if (!runtimeAutoLoadedResult && autoLoadExecutionStatus === 'success' && autoLoadDecision) {
564+
mappedResults.forEach((tool) => {
565+
if (tool.name === autoLoadDecision?.toolName) {
566+
tool.autoLoaded = true;
567+
tool.loaded = true;
568+
tool.matchReason = `${tool.matchReason}; ${autoLoadDecision.reason}`;
569+
}
570+
});
477571
}
478572

479573
const topScore = typeof mappedResults[0]?.score === 'number' ? mappedResults[0].score : undefined;
480574
const secondScore = typeof mappedResults[1]?.score === 'number' ? mappedResults[1].score : 0;
481575
const scoreGap = typeof topScore === 'number' ? topScore - secondScore : undefined;
482-
const ignoredSearchTelemetry = buildIgnoredSearchResultTelemetry(mappedResults, autoLoadedResult?.name);
576+
const ignoredSearchTelemetry = buildIgnoredSearchResultTelemetry(mappedResults, autoLoadDecision?.toolName);
483577

484578
toolSelectionTelemetry.record({
485579
type: 'search',
@@ -497,6 +591,14 @@ export const mcpRouter = t.router({
497591
ignoredResultCount: ignoredSearchTelemetry.ignoredResultCount,
498592
ignoredResultNames: ignoredSearchTelemetry.ignoredResultNames,
499593
latencyMs: toLatencyMs(searchStartedAt),
594+
autoLoadReason: autoLoadDecision?.reason,
595+
autoLoadConfidence: autoLoadDecision?.confidence || undefined,
596+
autoLoadEvaluated: runtimeAutoLoadedResult ? true : autoLoadEvaluation.evaluated,
597+
autoLoadOutcome: runtimeAutoLoadedResult ? 'loaded' : autoLoadEvaluation.outcome,
598+
autoLoadSkipReason: runtimeAutoLoadedResult ? undefined : autoLoadEvaluation.skipReason,
599+
autoLoadMinConfidence: runtimeAutoLoadedResult ? preferences.autoLoadMinConfidence : autoLoadEvaluation.minConfidence,
600+
autoLoadExecutionStatus,
601+
autoLoadExecutionError,
500602
status: 'success',
501603
});
502604

@@ -550,6 +652,7 @@ export const mcpRouter = t.router({
550652
try {
551653
const loadResult = await server.executeTool('load_tool', { name: autoLoadDecision.toolName });
552654
autoLoadExecutionStatus = 'success';
655+
const pressure = await readWorkingSetSnapshot(server);
553656
toolSelectionTelemetry.record({
554657
type: 'load',
555658
toolName: autoLoadDecision.toolName,
@@ -561,6 +664,7 @@ export const mcpRouter = t.router({
561664
autoLoadConfidence: autoLoadDecision.confidence,
562665
scoreGap: autoLoadDecision.scoreGap,
563666
topScore: autoLoadDecision.topScore,
667+
...pressure,
564668
});
565669
} catch (error) {
566670
autoLoadExecutionStatus = 'error';

0 commit comments

Comments
 (0)