1import * as Schema from "effect/Schema";
2
3import { ThreadId, TrimmedNonEmptyString, TurnItemId } from "./baseSchemas.ts";
4
5/**
6 * Requests an MCP App makes of its own MCP server, through the environment.
7 * Each names the tool call that produced the app; the environment resolves the
8 * app's server from that item, so an app can only reach the server it came from.
9 */
10const McpAppTarget = {
11 threadId: ThreadId,
12 itemId: TurnItemId,
13};
14
15export const McpAppCallToolInput = Schema.Struct({
16 ...McpAppTarget,
17 name: TrimmedNonEmptyString,
18 arguments: Schema.Record(Schema.String, Schema.Unknown),
19});
20export type McpAppCallToolInput = typeof McpAppCallToolInput.Type;
21
22/** MCP `CallToolResult`, passed through to the app unchanged. */
23export const McpAppCallToolResult = Schema.Struct({
24 content: Schema.Array(Schema.Unknown),
25 structuredContent: Schema.optional(Schema.Unknown),
26 isError: Schema.optional(Schema.Boolean),
27 _meta: Schema.optional(Schema.Unknown),
28});
29export type McpAppCallToolResult = typeof McpAppCallToolResult.Type;
30
31export const McpAppToolInfoInput = Schema.Struct({
32 ...McpAppTarget,
33 name: TrimmedNonEmptyString,
34});
35export type McpAppToolInfoInput = typeof McpAppToolInfoInput.Type;
36
37/**
38 * What a client needs to decide whether to ask before a call: whether the tool
39 * exists for apps and whether its server declares it read-only.
40 */
41export const McpAppToolInfo = Schema.Struct({
42 callable: Schema.Boolean,
43 readOnly: Schema.Boolean,
44 title: Schema.optional(Schema.String),
45 /** The server's MCP `Tool` definition, which the host passes to the app as `toolInfo`. */
46 tool: Schema.optional(Schema.Unknown),
47});
48export type McpAppToolInfo = typeof McpAppToolInfo.Type;
49
50export const McpAppReadResourceInput = Schema.Struct({
51 ...McpAppTarget,
52 uri: TrimmedNonEmptyString,
53});
54export type McpAppReadResourceInput = typeof McpAppReadResourceInput.Type;
55
56/** MCP `ReadResourceResult`, passed through to the app unchanged. */
57export const McpAppReadResourceResult = Schema.Struct({
58 contents: Schema.Array(Schema.Unknown),
59});
60export type McpAppReadResourceResult = typeof McpAppReadResourceResult.Type;
61
62/**
63 * What an app wants the agent to know on its next turn (`ui/update-model-context`).
64 * Each update replaces the app's previous one; no content clears it.
65 */
66export const McpAppUpdateModelContextInput = Schema.Struct({
67 ...McpAppTarget,
68 /**
69 * The thread whose next turn gets the context: the one on screen, which is
70 * the app's own thread or a fork that shows it.
71 */
72 conversationThreadId: ThreadId,
73 content: Schema.optional(Schema.Array(Schema.Unknown)),
74 structuredContent: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
75});
76export type McpAppUpdateModelContextInput = typeof McpAppUpdateModelContextInput.Type;
77
78export const McpAppRequestErrorReason = Schema.Literals([
79 "not-an-app",
80 "provider-unsupported",
81 "session-stopped",
82 "tool-not-callable",
83 "unsupported-content",
84 "request-failed",
85]);
86export type McpAppRequestErrorReason = typeof McpAppRequestErrorReason.Type;
87
88export class McpAppRequestError extends Schema.TaggedError<McpAppRequestError>()(
89 "McpAppRequestError",
90 {
91 threadId: ThreadId,
92 reason: McpAppRequestErrorReason,
93 cause: Schema.optional(Schema.Defect()),
94 },
95) {
96 override get message(): string {
97 switch (this.reason) {
98 case "not-an-app":
99 return "This tool call has no MCP app.";
100 case "provider-unsupported":
101 return "This thread's provider cannot run MCP app requests.";
102 case "session-stopped":
103 // A forked thread shows its source's apps, so this names the thread
104 // that made the app rather than the one on screen.
105 return "The app's thread is not running. Send a message in the thread that created it to use the app again.";
106 case "tool-not-callable":
107 return "This app cannot call that tool.";
108 case "unsupported-content":
109 return "Only text and structured content are supported.";
110 case "request-failed":
111 return "The app's MCP server request failed.";
112 }
113 }
114}