Files
reverse-engineering-anything/src/server/registerSessionStatusTool.ts
T
Kaoru0822andClaude Opus 5.5 b8312fa8d1 fix(server): report 2025-era client identity and features in binary_session
binary_session read the client's identity, protocol revision, and
capabilities only from the 2026-era per-request envelope. A 2025-era
connection sends none, so every such client was reported as `client: null`
with no negotiated protocol and no features, although it declared them in
`initialize`.

When a request carries no envelope, read the values the SDK keeps from the
connection's initialize handshake. An envelope still takes precedence.

Fixes #1167

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 03:20:43 +00:00

119 lines
4.1 KiB
TypeScript

import type { McpServer } from "@modelcontextprotocol/server";
import type { BinarySessionPort } from "../application/binary/BinarySession.js";
import type { ProviderAvailability } from "../application/AnalysisProvider.js";
import { AnalysisCancelledError } from "../domain/analysisErrorCore.js";
import { buildCapabilityInventory } from "../application/CapabilityInventory.js";
import { toolContract } from "../contracts/toolContracts.js";
import type { ClientFeatureAvailability } from "../contracts/toolOutputSchemaPrimitives.js";
import { jsonObjectSchema } from "../domain/jsonValue.js";
import { createServerIdentity } from "../serverIdentity.js";
import { mcpClientMetadata } from "./mcpClientMetadata.js";
import type { SessionAvailability } from "./sessionAvailabilityPolicy.js";
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
import { toCallToolResult } from "./toolResult.js";
import { err } from "../domain/result.js";
type ToolAvailability = ReturnType<typeof buildCapabilityInventory>[number];
/** Inputs required to register the binary session status tool. */
export interface SessionStatusToolOptions {
readonly server: McpServer;
readonly session: BinarySessionPort;
readonly contract: ReturnType<typeof toolContract<"binary_session">>;
readonly startedAt: string;
readonly availabilityPolicy: () => SessionAvailability;
readonly androidAnalysisAvailability: (
signal: AbortSignal,
) => Promise<ProviderAvailability>;
}
/** Register the read-only provider and target status operation. */
export const registerSessionStatusTool = (
options: SessionStatusToolOptions,
): void => {
const {
server,
session,
contract,
startedAt,
availabilityPolicy,
androidAnalysisAvailability,
} = options;
server.registerTool(
contract.name,
toolRegistrationOptions(contract),
async (input, context) => {
const { client, clientFeatures, protocolVersion } = mcpClientMetadata(
context,
server.server,
);
let androidAvailability: ProviderAvailability;
try {
androidAvailability = await androidAnalysisAvailability(
context.mcpReq.signal,
);
} catch (cause) {
if (!context.mcpReq.signal.aborted) throw cause;
return toCallToolResult(
err(new AnalysisCancelledError("binary_session")),
contract,
);
}
const status = session.status();
const statusObject = jsonObjectSchema.parse(status);
const toolAvailability = buildCapabilityInventory(
status,
{
...availabilityPolicy(),
androidAnalysisAvailability: androidAvailability,
},
clientFeatures,
);
const serverIdentity = createServerIdentity({
startedAt,
expected: {
...(input.expected_package_version === undefined
? {}
: { package_version: input.expected_package_version }),
...(input.expected_catalog_digest === undefined
? {}
: { catalog_digest: input.expected_catalog_digest }),
...(input.expected_server_path === undefined
? {}
: { server_path: input.expected_server_path }),
},
...(client === undefined ? {} : { client }),
...(protocolVersion === undefined ? {} : { protocolVersion }),
});
return toCallToolResult(
{
ok: true,
value: projectSessionStatus({
status: statusObject,
toolAvailability,
serverIdentity,
clientFeatures,
}),
},
contract,
);
},
);
};
const projectSessionStatus = (options: {
readonly status: Readonly<
Record<string, import("../domain/jsonValue.js").JsonValue>
>;
readonly toolAvailability: readonly ToolAvailability[];
readonly serverIdentity: ReturnType<typeof createServerIdentity>;
readonly clientFeatures: ClientFeatureAvailability;
}) =>
jsonObjectSchema.parse({
...options.status,
tool_availability: options.toolAvailability,
client_features: options.clientFeatures,
server_identity: options.serverIdentity,
});