diff --git a/apps/server/src/mcp/McpHttpServer.test.ts b/apps/server/src/mcp/McpHttpServer.test.ts index 27e93f2ecefdb..8b73483380e2c 100644 --- a/apps/server/src/mcp/McpHttpServer.test.ts +++ b/apps/server/src/mcp/McpHttpServer.test.ts @@ -420,15 +420,13 @@ it.effect.each(["limit", "runLimit", "maxCharsPerItem"])( }, ); -it("routes same-child follow-ups to t3_thread_send and independent reviews to delegate_task", () => { +it("keeps cross-tool routing out of the delegate_task description", () => { const description = OrchestratorToolkit.tools.delegate_task.description ?? ""; - expect(description).toContain( - "For independent T3 delegated review rounds, call delegate_task again", - ); - expect(description).toContain( - "For a follow-up that needs the same child's conversation, use t3_thread_send on its childThreadId.", - ); - expect(description).not.toContain("not the target for starting another"); + expect(description).toContain("Start one child agent with only `task`"); + // Review rounds, follow-ups, and polling are routed in the system text. + expect(description).not.toContain("t3_thread_send"); + expect(description).not.toContain("Do not poll"); + expect(description).not.toContain("Prefer native"); }); it.effect( @@ -447,7 +445,8 @@ it.effect( const linkTool = server.tools.find(({ tool }) => tool.name === "link_pull_request"); expect(linkTool?.tool.annotations?.idempotentHint).toBe(true); expect(linkTool?.tool.annotations?.openWorldHint).toBe(false); - expect(linkTool?.tool.description).toContain("Register every pull request you open"); + expect(linkTool?.tool.description).toContain("Link a PR to this thread"); + expect(linkTool?.tool.description).toContain("settles the thread on merge"); const denied = yield* server .callTool({ name: "list_thread_pull_requests", arguments: {} }) diff --git a/apps/server/src/mcp/toolkits/attachment/tools.ts b/apps/server/src/mcp/toolkits/attachment/tools.ts index 62c44c9363abe..567de103e95f3 100644 --- a/apps/server/src/mcp/toolkits/attachment/tools.ts +++ b/apps/server/src/mcp/toolkits/attachment/tools.ts @@ -33,21 +33,20 @@ const shared = { const AttachmentUploadTool = Tool.make("t3_attachment_prepare_upload", { ...shared, description: - "Create the app's signed upload URL. POST the exact bytes to relativeUrl on this MCP server's HTTP origin, then pass the attachment metadata and returned ID to t3_thread_send_attachments. Upload is separate from sending; provider attachment support is decided by its adapter.", + "Get an upload URL. POST the file's exact bytes to `relativeUrl` on this MCP server's HTTP origin, then send via `t3_thread_send_attachments` with the returned `attachmentId`.", parameters: Schema.Struct({ upload: AttachmentCreateUploadUrlInput }), success: AttachmentCreateUploadUrlResult, }).annotate(Tool.Destructive, true); const AttachmentDiscardTool = Tool.make("t3_attachment_discard", { ...shared, - description: - "Discard a pending upload. Already-delivered thread attachments are never deleted by this operation.", + description: "Discard a pending upload (not a sent attachment).", parameters: AttachmentDeleteInput, success: Schema.Struct({}), }).annotate(Tool.Destructive, true); const AttachmentSendTool = Tool.make("t3_thread_send_attachments", { ...shared, description: - "Send uploaded attachments to this thread or any other thread in this environment. Each call is a new message, without a retry key. Acceptance does not mean the provider can consume the attachment or has finished the turn. The target cannot have broader permission modes than the caller; failures retain claimed files when dispatch outcome is uncertain.", + "Send uploaded attachments as a new message to a thread. No retry key. Target modes cannot exceed yours. Success does not mean the provider read them or finished.", parameters: Schema.Struct({ threadId: Schema.optional(ThreadId), message: Schema.optional(Schema.String.check(Schema.isMaxLength(120000))), diff --git a/apps/server/src/mcp/toolkits/device/tools.ts b/apps/server/src/mcp/toolkits/device/tools.ts index 36a91b844ade5..f02f39b32462a 100644 --- a/apps/server/src/mcp/toolkits/device/tools.ts +++ b/apps/server/src/mcp/toolkits/device/tools.ts @@ -29,8 +29,9 @@ const DeviceToolFailure = Schema.Union([DeviceToolError, OrchestratorMcpFailure] const DeviceListTool = Tool.make("device_list", { description: - "List iOS Simulators and Android Emulators on this environment's device hosts, which platforms each host can run, and which devices are already open in this thread's Device panel. Call this before device_open when you do not know a device id.", - // Empty Struct emits object|array, which can make providers reject every server tool. + "List iOS Simulators and Android Emulators here, what each host can run, and what is open in this thread's Device panel. Call before `device_open` when you do not know a device id.", + // An empty struct serializes as `anyOf [object, array]`, which some + // providers reject and then drop every tool on the server with it. parameters: Schema.Struct({ hostId: Schema.optional( Schema.String.annotate({ description: "Limit to one device host. Defaults to all hosts." }), @@ -48,7 +49,7 @@ const DeviceListTool = Tool.make("device_list", { const DeviceOpenTool = Tool.make("device_open", { description: - "Open a simulator or emulator for this thread: boots it if needed, starts its live stream, and shows it in the user's Device panel so they can watch. Returns the agent-device CLI invocation pinned to the device; drive the device with that CLI afterwards.", + "Open a simulator or emulator for this thread. Boots it if needed and shows it in the user's Device panel. Returns the `agent-device` CLI call pinned to that device. Drive the device with that CLI.", parameters: DeviceToolOpenInput, success: DeviceToolOpenResult, failure: DeviceToolFailure, @@ -69,7 +70,7 @@ const DeviceOpenTool = Tool.make("device_open", { export const DeviceScreenshotTool = Tool.make("device_screenshot", { description: - "Capture the current screen of an open device as a PNG image. Use it to see what the user sees; for taps and text use the agent-device CLI.", + "Get a PNG of an open device's screen. For taps and text, use the `agent-device` CLI.", parameters: DeviceToolTargetInput, success: DeviceToolScreenshotResult, failure: DeviceToolFailure, @@ -83,11 +84,9 @@ export const DeviceScreenshotTool = Tool.make("device_screenshot", { const DeviceCloseTool = Tool.make("device_close", { description: - "Remove a device from this thread's Device panel. Pass shutdown=true to also power the simulator or emulator off.", + "Remove a device from this thread's Device panel. `shutdown=true` also turns it off.", parameters: DeviceToolCloseInput, - success: Schema.Record(Schema.String, Schema.Never).annotate({ - description: "The device was closed.", - }), + success: Schema.Record(Schema.String, Schema.Never), failure: DeviceToolFailure, dependencies, }) diff --git a/apps/server/src/mcp/toolkits/environment/tools.ts b/apps/server/src/mcp/toolkits/environment/tools.ts index e441174900ce1..d684ff02e3402 100644 --- a/apps/server/src/mcp/toolkits/environment/tools.ts +++ b/apps/server/src/mcp/toolkits/environment/tools.ts @@ -40,7 +40,7 @@ const shared = { const EnvironmentReadTool = Tool.make("t3_environment_read", { ...shared, description: - "Read this server's identity and selected environment preferences. Provider/model availability is exposed by orchestrator_capabilities. Writing instructions are limited to 4,000 characters.", + "Read this server's identity and preferences. Providers and models: `orchestrator_capabilities`.", success: Schema.Struct({ environmentId: ExecutionEnvironmentDescriptor.fields.environmentId, label: Schema.String, @@ -54,7 +54,7 @@ const EnvironmentReadTool = Tool.make("t3_environment_read", { const EnvironmentPreferencesTool = Tool.make("t3_environment_preferences_update", { ...shared, description: - "Update selected environment-wide preferences through normal settings persistence and notifications. Requires a live full-access/default calling thread. Omitted fields are preserved; empty customInstructions clears them.", + "Update environment-wide preferences. Live full-access or default caller thread only. Omitted fields are unchanged; `customInstructions: ''` clears it.", parameters: Schema.Struct({ defaultThreadEnvMode: ServerSettingsPatch.fields.defaultThreadEnvMode, newWorktreesStartFromOrigin: ServerSettingsPatch.fields.newWorktreesStartFromOrigin, diff --git a/apps/server/src/mcp/toolkits/html/tools.ts b/apps/server/src/mcp/toolkits/html/tools.ts index f061923993b84..6f67d58a8a38c 100644 --- a/apps/server/src/mcp/toolkits/html/tools.ts +++ b/apps/server/src/mcp/toolkits/html/tools.ts @@ -21,20 +21,20 @@ const Html = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(512_0 }); const PAGE_RULES = - 'Write one self-contained document with inline