mcpInstructionsDelta.ts
utils/mcpInstructionsDelta.ts
131
Lines
4752
Bytes
4
Exports
5
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is one piece of the larger system. Its name, directory, imports, and exports show where it fits. Start by reading the exports and related files first.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with mcp. It contains 131 lines, 5 detected imports, and 4 detected exports.
Important relationships
Detected exports
McpInstructionsDeltaClientSideInstructionisMcpInstructionsDeltaEnabledgetMcpInstructionsDelta
Keywords
nameinstructionsserverblockannouncedconnectedaddedblocksremovedlength
Detected imports
../services/analytics/growthbook.js../services/analytics/index.js../services/mcp/types.js../types/message.js./envUtils.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js'
import { logEvent } from '../services/analytics/index.js'
import type {
ConnectedMCPServer,
MCPServerConnection,
} from '../services/mcp/types.js'
import type { Message } from '../types/message.js'
import { isEnvDefinedFalsy, isEnvTruthy } from './envUtils.js'
export type McpInstructionsDelta = {
/** Server names — for stateless-scan reconstruction. */
addedNames: string[]
/** Rendered "## {name}\n{instructions}" blocks for addedNames. */
addedBlocks: string[]
removedNames: string[]
}
/**
* Client-authored instruction block to announce when a server connects,
* in addition to (or instead of) the server's own `InitializeResult.instructions`.
* Lets first-party servers (e.g., claude-in-chrome) carry client-side
* context the server itself doesn't know about.
*/
export type ClientSideInstruction = {
serverName: string
block: string
}
/**
* True → announce MCP server instructions via persisted delta attachments.
* False → prompts.ts keeps its DANGEROUS_uncachedSystemPromptSection
* (rebuilt every turn; cache-busts on late connect).
*
* Env override for local testing: CLAUDE_CODE_MCP_INSTR_DELTA=true/false
* wins over both ant bypass and the GrowthBook gate.
*/
export function isMcpInstructionsDeltaEnabled(): boolean {
if (isEnvTruthy(process.env.CLAUDE_CODE_MCP_INSTR_DELTA)) return true
if (isEnvDefinedFalsy(process.env.CLAUDE_CODE_MCP_INSTR_DELTA)) return false
return (
process.env.USER_TYPE === 'ant' ||
getFeatureValue_CACHED_MAY_BE_STALE('tengu_basalt_3kr', false)
)
}
/**
* Diff the current set of connected MCP servers that have instructions
* (server-authored via InitializeResult, or client-side synthesized)
* against what's already been announced in this conversation. Null if
* nothing changed.
*
* Instructions are immutable for the life of a connection (set once at
* handshake), so the scan diffs on server NAME, not on content.
*/
export function getMcpInstructionsDelta(
mcpClients: MCPServerConnection[],
messages: Message[],
clientSideInstructions: ClientSideInstruction[],
): McpInstructionsDelta | null {
const announced = new Set<string>()
let attachmentCount = 0
let midCount = 0
for (const msg of messages) {
if (msg.type !== 'attachment') continue
attachmentCount++
if (msg.attachment.type !== 'mcp_instructions_delta') continue
midCount++
for (const n of msg.attachment.addedNames) announced.add(n)
for (const n of msg.attachment.removedNames) announced.delete(n)
}
const connected = mcpClients.filter(
(c): c is ConnectedMCPServer => c.type === 'connected',
)
const connectedNames = new Set(connected.map(c => c.name))
// Servers with instructions to announce (either channel). A server can
// have both: server-authored instructions + a client-side block appended.
const blocks = new Map<string, string>()
for (const c of connected) {
if (c.instructions) blocks.set(c.name, `## ${c.name}\n${c.instructions}`)
}
for (const ci of clientSideInstructions) {
if (!connectedNames.has(ci.serverName)) continue
const existing = blocks.get(ci.serverName)
blocks.set(
ci.serverName,
existing
? `${existing}\n\n${ci.block}`
: `## ${ci.serverName}\n${ci.block}`,
)
}
const added: Array<{ name: string; block: string }> = []
for (const [name, block] of blocks) {
if (!announced.has(name)) added.push({ name, block })
}
// A previously-announced server that is no longer connected → removed.
// There is no "announced but now has no instructions" case for a still-
// connected server: InitializeResult is immutable, and client-side
// instruction gates are session-stable in practice. (/model can flip
// the model gate, but deferred_tools_delta has the same property and
// we treat history as historical — no retroactive retractions.)
const removed: string[] = []
for (const n of announced) {
if (!connectedNames.has(n)) removed.push(n)
}
if (added.length === 0 && removed.length === 0) return null
// Same diagnostic fields as tengu_deferred_tools_pool_change — same
// scan-fails-in-prod bug, same attachment persistence path.
logEvent('tengu_mcp_instructions_pool_change', {
addedCount: added.length,
removedCount: removed.length,
priorAnnouncedCount: announced.size,
clientSideCount: clientSideInstructions.length,
messagesLength: messages.length,
attachmentCount,
midCount,
})
added.sort((a, b) => a.name.localeCompare(b.name))
return {
addedNames: added.map(a => a.name),
addedBlocks: added.map(a => a.block),
removedNames: removed.sort(),
}
}