mirror of
https://github.com/morluto/rea.git
synced 2026-11-04 13:56:06 +00:00
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>
119 lines
4.1 KiB
TypeScript
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,
|
|
});
|