| ... | ... | @@ -88,6 +88,37 @@ const McpAuthMiddlewareLive = HttpRouter.middleware<{ |
| 88 | 88 | provides: McpInvocationContext.McpInvocationContext; |
| 89 | 89 | }>()(makeMcpAuthMiddleware).layer; |
| 90 | 90 | |
| 91 | const previewSnapshotFailure = <E>(cause: Cause.Cause<E>) => { |
| 92 | if (Cause.hasInterrupts(cause) || cause.reasons.some(Cause.isDieReason)) { |
| 93 | return Effect.failCause(cause).pipe(Effect.orDie); |
| 94 | } |
| 95 | const failures = cause.reasons.filter(Cause.isFailReason); |
| 96 | const firstFailure = failures[0]?.error; |
| 97 | const errorTag = |
| 98 | typeof firstFailure === "object" && |
| 99 | firstFailure !== null && |
| 100 | "_tag" in firstFailure && |
| 101 | typeof firstFailure._tag === "string" |
| 102 | ? firstFailure._tag |
| 103 | : "PreviewSnapshotError"; |
| 104 | const result = new McpSchema.CallToolResult({ |
| 105 | isError: true, |
| 106 | structuredContent: { |
| 107 | error: { |
| 108 | _tag: errorTag, |
| 109 | operation: "snapshot", |
| 110 | failureCount: failures.length, |
| 111 | }, |
| 112 | }, |
| 113 | content: [{ type: "text", text: "Preview snapshot failed." }], |
| 114 | }); |
| 115 | return Effect.logWarning("preview snapshot failed", { |
| 116 | operation: "snapshot", |
| 117 | errorTag, |
| 118 | failureCount: failures.length, |
| 119 | }).pipe(Effect.as(result)); |
| 120 | }; |
| 121 | |
| 91 | 122 | const registerPreviewSnapshot = Effect.fn("McpHttpServer.registerPreviewSnapshot")(function* () { |
| 92 | 123 | const server = yield* McpServer.McpServer; |
| 93 | 124 | const broker = yield* PreviewAutomationBroker.PreviewAutomationBroker; |
| ... | ... | @@ -122,12 +153,8 @@ const registerPreviewSnapshot = Effect.fn("McpHttpServer.registerPreviewSnapshot |
| 122 | 153 | Effect.flatMap(Effect.fromOption), |
| 123 | 154 | Effect.provideService(PreviewAutomationBroker.PreviewAutomationBroker, broker), |
| 124 | 155 | Effect.provideService(McpInvocationContext.McpInvocationContext, invocation), |
| 125 | | Effect.matchCause({ |
| 126 | | onFailure: (cause) => |
| 127 | | new McpSchema.CallToolResult({ |
| 128 | | isError: true, |
| 129 | | content: [{ type: "text", text: Cause.pretty(cause) }], |
| 130 | | }), |
| 156 | Effect.matchCauseEffect({ |
| 157 | onFailure: previewSnapshotFailure, |
| 131 | 158 | onSuccess: ({ encodedResult }) => { |
| 132 | 159 | const snapshot = encodedResult as { |
| 133 | 160 | readonly screenshot: { |
| ... | ... | @@ -147,18 +174,20 @@ const registerPreviewSnapshot = Effect.fn("McpHttpServer.registerPreviewSnapshot |
| 147 | 174 | height: screenshot.height, |
| 148 | 175 | }, |
| 149 | 176 | }; |
| 150 | | return new McpSchema.CallToolResult({ |
| 151 | | isError: false, |
| 152 | | structuredContent: metadata, |
| 153 | | content: [ |
| 154 | | { type: "text", text: JSON.stringify(metadata) }, |
| 155 | | { |
| 156 | | type: "image", |
| 157 | | data: new Uint8Array(Buffer.from(screenshot.data, "base64")), |
| 158 | | mimeType: screenshot.mimeType, |
| 159 | | }, |
| 160 | | ], |
| 161 | | }); |
| 177 | return Effect.succeed( |
| 178 | new McpSchema.CallToolResult({ |
| 179 | isError: false, |
| 180 | structuredContent: metadata, |
| 181 | content: [ |
| 182 | { type: "text", text: JSON.stringify(metadata) }, |
| 183 | { |
| 184 | type: "image", |
| 185 | data: new Uint8Array(Buffer.from(screenshot.data, "base64")), |
| 186 | mimeType: screenshot.mimeType, |
| 187 | }, |
| 188 | ], |
| 189 | }), |
| 190 | ); |
| 162 | 191 | }, |
| 163 | 192 | }), |
| 164 | 193 | ); |