Shared boocontext MCP client (boocontext_client.ts) wrapping the existing mcp-client.ts callTool() infrastructure with 32KB truncation and error handling. Used by get_code_health. 4 new first-class agent tools backed by the boocontext MCP server: - get_code_health — A-F grades per file across 7 dimensions, project health summary, refactoring candidates (wraps boocontext_health) - get_code_impact — merged symbol trace + blast radius in one call (wraps boocontext_impact, replaces two-step get_symbol_info+get_blast_radius) - get_type_info — TypeScript type recovery via type-inject MCP (wraps boocontext_types, returns signatures, interfaces, generics, JSDoc) - get_code_map — DCP-compressed context map with compress toggle (wraps boocontext_map, 10x token reduction vs full scan) All 4 registered in ALL_TOOLS as read-only tools.
111 lines
4.0 KiB
TypeScript
111 lines
4.0 KiB
TypeScript
/**
|
|
* v2.7.18: shared MCP client wrapper for the boocontext sidecar.
|
|
*
|
|
* Calls into the existing multi-server MCP client infrastructure
|
|
* (services/mcp-client.ts) which connects to boocontext as a stdio
|
|
* MCP process defined in data/mcp.json (server name "boocontext",
|
|
* command: `node /opt/forks/boocontext/dist/standalone.js`).
|
|
*
|
|
* The boocontext MCP server is initialized once at app boot in
|
|
* index.ts via initMcp() and the actual MCP tool call routing is
|
|
* handled by mcp-client.ts:callTool() — this module is a thin
|
|
* convenience wrapper that prepends the "boocontext_" server prefix,
|
|
* normalises the response, and applies inline truncation matching
|
|
* the same pattern as codecontext_client.ts.
|
|
*
|
|
* Usage:
|
|
* import { callBoocontext } from './services/boocontext_client.js';
|
|
* const resp = await callBoocontext({
|
|
* toolName: 'codesight_get_summary',
|
|
* args: { directory: '/opt/boocode' },
|
|
* });
|
|
*/
|
|
|
|
import { callTool } from './mcp-client.js';
|
|
import { truncateIfNeeded } from './truncate.js';
|
|
|
|
// ---- Exported types ----
|
|
|
|
export interface BoocontextRequest {
|
|
/** Unprefixed tool name as defined on the boocontext MCP server
|
|
* (e.g. "codesight_scan", "boocontext_overview", "codesight_get_summary"). */
|
|
toolName: string;
|
|
/** Arguments to pass to the tool. */
|
|
args: Record<string, unknown>;
|
|
}
|
|
|
|
export interface BoocontextResponse {
|
|
/** The tool output text. */
|
|
result: string;
|
|
/** Whether the result was truncated to fit the inline limit. */
|
|
truncated: boolean;
|
|
/** Opaque id pointing at the full pre-slice content on tmpfs, set when
|
|
* truncated=true and storage succeeded. */
|
|
outputPath?: string;
|
|
}
|
|
|
|
// ---- Constants ----
|
|
|
|
/** Must match the server name in data/mcp.json. */
|
|
const BOOCONTEXT_SERVER_NAME = 'boocontext';
|
|
|
|
/** Inline truncation limit, matching codecontext_client.ts. */
|
|
const TRUNCATION_LIMIT = 32_000;
|
|
|
|
// ---- Public API ----
|
|
|
|
/**
|
|
* Call a boocontext MCP tool by its unprefixed name.
|
|
*
|
|
* Prepends the "boocontext_" server prefix, delegates to the
|
|
* multi-server MCP client's callTool(), and normalises the response
|
|
* into a BoocontextResponse with inline truncation.
|
|
*
|
|
* @param req The tool name and arguments.
|
|
* @param log Optional Fastify-compatible logger (for debug traces).
|
|
* @returns The tool result, possibly truncated.
|
|
* @throws If the boocontext server is not connected or the tool
|
|
* returns an MCP-level error.
|
|
*/
|
|
export async function callBoocontext(
|
|
req: BoocontextRequest,
|
|
log?: { debug?: (obj: object, msg: string) => void; warn?: (obj: object, msg: string) => void },
|
|
): Promise<BoocontextResponse> {
|
|
const prefixedName = `${BOOCONTEXT_SERVER_NAME}_${req.toolName}`;
|
|
|
|
log?.debug?.({ tool: prefixedName }, 'boocontext: calling tool');
|
|
|
|
const raw = await callTool(prefixedName, req.args);
|
|
|
|
// callTool returns { error: true, output: string } on failure (both
|
|
// for MCP-level isError and for network/protocol exceptions).
|
|
if (typeof raw === 'object' && raw !== null && (raw as Record<string, unknown>).error === true) {
|
|
const errOutput = (raw as Record<string, unknown>).output ?? 'Unknown MCP error';
|
|
throw new Error(`boocontext error: ${String(errOutput)}`);
|
|
}
|
|
|
|
const result = typeof raw === 'string' ? raw : JSON.stringify(raw);
|
|
|
|
// Inline truncation at 32 kB, matching codecontext_client.ts.
|
|
// The model gets a clear hint about how to narrow the next call
|
|
// rather than a silent cut.
|
|
if (result.length > TRUNCATION_LIMIT) {
|
|
const truncated = result.slice(0, TRUNCATION_LIMIT);
|
|
const omitted = result.length - TRUNCATION_LIMIT;
|
|
const slicedWithMarker =
|
|
`${truncated}\n\n[truncated, ${omitted} chars omitted; narrow with additional filters]`;
|
|
const wrapped = await truncateIfNeeded({
|
|
fullContent: result,
|
|
slicedContent: slicedWithMarker,
|
|
wasTruncated: true,
|
|
});
|
|
return {
|
|
result: wrapped.content,
|
|
truncated: wrapped.truncated,
|
|
...(wrapped.outputPath ? { outputPath: wrapped.outputPath } : {}),
|
|
};
|
|
}
|
|
|
|
return { result, truncated: false };
|
|
}
|