Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/server/src/mcp/toolkits/project/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
ProjectCreatePayload,
ProjectUpdatePayload,
ProjectId,
OrchestratorMcpClientRequestId,
OrchestratorMcpOptionalClientRequestId,
OrchestratorMcpEnvironmentTarget,
OrchestratorMcpFailure,
SourceControlCloneRepositoryInput,
Expand Down Expand Up @@ -150,7 +150,7 @@ const ThreadLaunchTool = Tool.make("t3_thread_launch", {
}),
),
attachments: Schema.optional(Schema.Array(McpAttachmentInput).check(Schema.isMaxLength(8))),
clientRequestId: Schema.optional(OrchestratorMcpClientRequestId),
clientRequestId: OrchestratorMcpOptionalClientRequestId,
/** Set by a linked environment launching its agent's delegated task here; ignored otherwise. */
delegatedFrom: Schema.optional(OrchestrationV2DelegatedFrom),
}),
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/provider/agentContextBudget.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"claudeSystemAppendBytes": 5597,
"codexAdditionalContextBytes": 7327,
"mcpToolListJsonBytes": 48130
"mcpToolListJsonBytes": 48895
}
13 changes: 13 additions & 0 deletions packages/contracts/src/orchestratorMcp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as Schema from "effect/Schema";
import {
OrchestratorMcpDelegateTaskInput,
OrchestratorMcpDelegateTaskResult,
OrchestratorMcpThreadSendInput,
} from "./orchestratorMcp.ts";

const decodeDelegateTaskInput = Schema.decodeUnknownSync(OrchestratorMcpDelegateTaskInput);
Expand Down Expand Up @@ -66,4 +67,16 @@ describe("orchestrator MCP contracts", () => {
}),
).toThrow();
});

it.each([OrchestratorMcpDelegateTaskInput, OrchestratorMcpThreadSendInput])(
"advertises the clientRequestId description in the encoded JSON schema",
(input) => {
const { properties } = Schema.toJsonSchemaDocument(input).schema as {
properties: Record<string, { description?: string }>;
};
expect(properties.clientRequestId?.description).toBe(
"Reuse it to retry the same call. A new call gets a new key.",
);
},
);
});
36 changes: 20 additions & 16 deletions packages/contracts/src/orchestratorMcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,19 @@ import { ProviderDriverKind, ProviderInstanceId } from "./providerInstance.ts";

const OrchestratorMcpPrompt = TrimmedNonEmptyString.check(Schema.isMaxLength(120_000));
const OrchestratorMcpTitle = TrimmedNonEmptyString.check(Schema.isMaxLength(512));
export const OrchestratorMcpClientRequestId = TrimmedNonEmptyString.check(Schema.isMaxLength(256))
.check(
Schema.makeFilter((value) =>
hasOnlyPairedSurrogates(value)
? true
: "clientRequestId must contain valid paired Unicode surrogates.",
),
)
.annotate({ description: "Reuse it to retry the same call. A new call gets a new key." });
export const OrchestratorMcpClientRequestId = TrimmedNonEmptyString.check(
Schema.isMaxLength(256),
).check(
Schema.makeFilter((value) =>
hasOnlyPairedSurrogates(value)
? true
: "clientRequestId must contain valid paired Unicode surrogates.",
),
);
// Described on the optional field: a description on a trimmed string misses its encoded JSON schema.
export const OrchestratorMcpOptionalClientRequestId = Schema.optional(
OrchestratorMcpClientRequestId,
).annotate({ description: "Reuse it to retry the same call. A new call gets a new key." });

// OpenCode 1.15 serializes nested MCP union objects as JSON strings; decode that form at the boundary.
const OrchestratorMcpScheduleFromJsonString = Schema.fromJsonString(
Expand Down Expand Up @@ -186,7 +190,7 @@ export const OrchestratorMcpDelegateTaskInput = Schema.Struct({
timeoutMs: Schema.optional(Schema.Finite).annotate({
description: "Mode 'wait' only. Default 4 min. A timeout does not cancel the child.",
}),
clientRequestId: Schema.optional(OrchestratorMcpClientRequestId),
clientRequestId: OrchestratorMcpOptionalClientRequestId,
runtimeMode: Schema.optional(
OrchestratorMcpRuntimeMode.annotate({
description:
Expand Down Expand Up @@ -256,7 +260,7 @@ export type OrchestratorMcpTaskStatusInput = typeof OrchestratorMcpTaskStatusInp
export const OrchestratorMcpTaskCancelInput = Schema.Struct({
taskId: NodeId,
reason: Schema.optional(Schema.String.check(Schema.isMaxLength(2_000))),
clientRequestId: Schema.optional(OrchestratorMcpClientRequestId),
clientRequestId: OrchestratorMcpOptionalClientRequestId,
});
export type OrchestratorMcpTaskCancelInput = typeof OrchestratorMcpTaskCancelInput.Type;

Expand All @@ -280,7 +284,7 @@ export const OrchestratorMcpCreateThreadsInput = Schema.Struct({
Schema.isMinLength(1),
Schema.isMaxLength(20),
),
clientRequestId: Schema.optional(OrchestratorMcpClientRequestId),
clientRequestId: OrchestratorMcpOptionalClientRequestId,
});
export type OrchestratorMcpCreateThreadsInput = typeof OrchestratorMcpCreateThreadsInput.Type;

Expand Down Expand Up @@ -524,7 +528,7 @@ export const OrchestratorMcpThreadSendInput = Schema.Struct({
"Start a new provider session without the thread's earlier context, like /clear. Never steers.",
}),
),
clientRequestId: Schema.optional(OrchestratorMcpClientRequestId),
clientRequestId: OrchestratorMcpOptionalClientRequestId,
});
export type OrchestratorMcpThreadSendInput = typeof OrchestratorMcpThreadSendInput.Type;

Expand Down Expand Up @@ -575,7 +579,7 @@ export const OrchestratorMcpThreadInterruptInput = Schema.Struct({
* watches and stop every task it delegated. `runId` is then not used.
*/
stop: Schema.optional(Schema.Boolean),
clientRequestId: Schema.optional(OrchestratorMcpClientRequestId),
clientRequestId: OrchestratorMcpOptionalClientRequestId,
});
export type OrchestratorMcpThreadInterruptInput = typeof OrchestratorMcpThreadInterruptInput.Type;

Expand Down Expand Up @@ -688,7 +692,7 @@ export const OrchestratorMcpScheduleTaskInput = Schema.Struct({
"Runs share one thread but each starts a fresh session without earlier runs' context. Unbound: the first run makes the thread.",
}),
),
clientRequestId: Schema.optional(OrchestratorMcpClientRequestId),
clientRequestId: OrchestratorMcpOptionalClientRequestId,
});
export type OrchestratorMcpScheduleTaskInput = typeof OrchestratorMcpScheduleTaskInput.Type;

Expand Down Expand Up @@ -837,7 +841,7 @@ export const OrchestratorMcpEnvironmentLinkInput = Schema.Struct({
requestedAccess: Schema.optional(AuthMcpClientAccess).annotate({
description: "Suggested access. The user picks; default approval-required.",
}),
clientRequestId: Schema.optional(OrchestratorMcpClientRequestId),
clientRequestId: OrchestratorMcpOptionalClientRequestId,
});
export type OrchestratorMcpEnvironmentLinkInput = typeof OrchestratorMcpEnvironmentLinkInput.Type;

Expand Down
14 changes: 14 additions & 0 deletions packages/contracts/src/threadMetadataMcp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,18 @@ describe("ThreadMetadataMcpUpdateInput", () => {
decodeUpdate({ action: "regenerate_title", clientRequestId: "metadata-\udc00" }),
);
});

it("advertises its parameter descriptions in the encoded JSON schema", () => {
const { properties } = Schema.toJsonSchemaDocument(ThreadMetadataMcpUpdateInput).schema as {
properties: Record<string, { description?: string }>;
};
assert.equal(
properties.title?.description,
"New concise display title. Required only when action is rename.",
);
assert.equal(
properties.clientRequestId?.description,
"Stable idempotency key to reuse when retrying this mutation.",
);
});
});
29 changes: 15 additions & 14 deletions packages/contracts/src/threadMetadataMcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,29 @@ export function hasOnlyPairedSurrogates(value: string): boolean {
return true;
}

const ThreadMetadataTitle = TrimmedNonEmptyString.check(Schema.isMaxLength(512)).annotate({
description: "New concise display title. Required only when action is rename.",
});
const ThreadMetadataTitle = TrimmedNonEmptyString.check(Schema.isMaxLength(512));

const ThreadMetadataClientRequestId = TrimmedNonEmptyString.check(Schema.isMaxLength(256))
.check(
Schema.makeFilter((value) =>
hasOnlyPairedSurrogates(value)
? true
: "clientRequestId must contain valid paired Unicode surrogates.",
),
)
.annotate({ description: "Stable idempotency key to reuse when retrying this mutation." });
const ThreadMetadataClientRequestId = TrimmedNonEmptyString.check(Schema.isMaxLength(256)).check(
Schema.makeFilter((value) =>
hasOnlyPairedSurrogates(value)
? true
: "clientRequestId must contain valid paired Unicode surrogates.",
),
);

export const ThreadMetadataMcpAction = Schema.Literals(["rename", "regenerate_title"]);
export type ThreadMetadataMcpAction = typeof ThreadMetadataMcpAction.Type;

export const ThreadMetadataMcpUpdateInput = Schema.Struct({
threadId: Schema.optional(ThreadId),
action: ThreadMetadataMcpAction,
title: Schema.optional(ThreadMetadataTitle),
clientRequestId: Schema.optional(ThreadMetadataClientRequestId),
// Described on the optional fields: a description on a trimmed string misses its encoded JSON schema.
title: Schema.optional(ThreadMetadataTitle).annotate({
description: "New concise display title. Required only when action is rename.",
}),
clientRequestId: Schema.optional(ThreadMetadataClientRequestId).annotate({
description: "Stable idempotency key to reuse when retrying this mutation.",
}),
}).check(
Schema.makeFilter((input) =>
(input.action === "rename") === (input.title !== undefined)
Expand Down
Loading