Skip to content

Commit e75cd16

Browse files
committed
test(mcp): restore omitted-optional lock and name unknown-tool
1 parent ae133d3 commit e75cd16

1 file changed

Lines changed: 64 additions & 1 deletion

File tree

‎tests/integration/mcp-late-dispatch.test.ts‎

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ describe("integration — late MCP dispatch", () => {
136136
);
137137

138138
test.serial(
139-
"unadvertised MCP tool dispatch fails loudly instead of altering results",
139+
"unknown MCP tool dispatch fails loudly instead of altering results",
140140
async () => {
141141
const session = await openIntegrationSession({
142142
permissionGate: permissionGate(),
@@ -288,6 +288,69 @@ describe("integration — late MCP dispatch", () => {
288288
},
289289
);
290290

291+
test.serial(
292+
"tool_search promotion does not inject omitted optional MCP arguments",
293+
async () => {
294+
const session = await openIntegrationSession({
295+
permissionGate: permissionGate(),
296+
createAgentFn: createAgentWithLiveToolDispatch,
297+
});
298+
let receivedArgs: Record<string, unknown> | undefined;
299+
300+
try {
301+
const tools = lateMcpTools((toolName, args) => {
302+
if (toolName === "list_issues") {
303+
receivedArgs = args;
304+
}
305+
});
306+
expect(tools).toHaveLength(1);
307+
expect(tools[0]?.kind).toBe("full");
308+
const expectedSchema = structuredClone(LATE_MCP_SCHEMA);
309+
session.toolset.dynamicRunner.addTools(tools);
310+
session.toolset.setToolPromoter(() => {
311+
session.updateToolDefinitions(
312+
session.toolset.dynamicRunner.currentDefinitions(),
313+
);
314+
});
315+
session.harness.scenario.replyOnce("anthropic", {
316+
toolCalls: [
317+
{ name: "tool_search", args: { query: "linear list issues" } },
318+
],
319+
});
320+
session.harness.scenario.replyOnce("anthropic", {
321+
toolCalls: [{ name: LATE_MCP, args: { limit: 1 } }],
322+
});
323+
session.harness.scenario.replyOnce("anthropic", { text: "listed" });
324+
325+
const { events } = await runUntilDone(session, "list one linear issue");
326+
const bodies = await Promise.all(
327+
session.harness.scenario
328+
.matchedRequests()
329+
.map(
330+
async (request) =>
331+
JSON.parse(
332+
await (request.clone() as unknown as Request).text(),
333+
) as AnthropicRequestBody,
334+
),
335+
);
336+
const publishedTool = bodies
337+
.flatMap((body) => body.tools ?? [])
338+
.find((tool) => tool.name === LATE_MCP);
339+
340+
expect(publishedTool?.input_schema.properties).toEqual(
341+
expectedSchema.properties,
342+
);
343+
expect(
344+
Object.hasOwn(publishedTool?.input_schema ?? {}, "required"),
345+
).toBe(false);
346+
expect(receivedArgs).toEqual({ limit: 1 });
347+
expect(toolDoneContents(events)).toContain("ISSUE-1");
348+
} finally {
349+
await closeIntegrationSession(session);
350+
}
351+
},
352+
);
353+
291354
test.serial(
292355
"tool_search promotion preserves required and optional MCP arguments",
293356
async () => {

0 commit comments

Comments
 (0)