| @@ -1,8 +1,9 @@ |
| 1 | 1 | import { useReducer, useCallback, useEffect, useRef, useState, type ReactNode } from "react"; |
| 2 | 2 | import { useAtomSet } from "@effect/atom-react"; |
| 3 | 3 | import * as Exit from "effect/Exit"; |
| 4 | +import * as Option from "effect/Option"; |
| 5 | +import * as Schema from "effect/Schema"; |
| 4 | 6 | |
| 5 | | −import { useErrorMessageFromExit } from "@executor-js/react/api/error-reporting"; |
| 6 | 7 | import { useScope } from "@executor-js/react/api/scope-context"; |
| 7 | 8 | import { Button } from "@executor-js/react/components/button"; |
| 8 | 9 | import { |
| @@ -59,6 +60,15 @@ import { probeMcpEndpoint, addMcpSourceOptimistic } from "./atoms"; · 50 unmodified lines |
| 59 | 60 | import { mcpPresets, type McpPreset } from "../sdk/presets"; |
| 60 | 61 | import { MCP_OAUTH_CONNECTION_SLOT, type McpCredentialInput } from "../sdk/types"; |
| 61 | 62 | |
| 63 | +const ErrorMessage = Schema.Struct({ message: Schema.String }); |
| 64 | +const decodeErrorMessage = Schema.decodeUnknownOption(ErrorMessage); |
| 65 | + |
| 66 | +const errorMessageFromExit = (exit: Exit.Exit<unknown, unknown>, fallback: string): string => |
| 67 | + Option.match(Option.flatMap(Exit.findErrorOption(exit), decodeErrorMessage), { |
| 68 | + onNone: () => fallback, |
| 69 | + onSome: ({ message }) => message, |
| 70 | + }); |
| 71 | + |
| 62 | 72 | // --------------------------------------------------------------------------- |
| 63 | 73 | // Preset lookup |
| 64 | 74 | // --------------------------------------------------------------------------- |
| @@ -291,7 +301,6 @@ export default function AddMcpSource(props: { · 226 unmodified lines |
| 291 | 301 | const doAdd = useAtomSet(addMcpSourceOptimistic(scopeId), { |
| 292 | 302 | mode: "promiseExit", |
| 293 | 303 | }); |
| 294 | | − const errorMessageFromExit = useErrorMessageFromExit(); |
| 295 | 304 | const secretList = useSecretPickerSecrets(); |
| 296 | 305 | const oauth = useOAuthPopupFlow<OAuthCompletionPayload>({ |
| 297 | 306 | popupName: "mcp-oauth", |
| @@ -348,16 +357,13 @@ export default function AddMcpSource(props: { · 50 unmodified lines |
| 348 | 357 | if (Exit.isFailure(exit)) { |
| 349 | 358 | dispatch({ |
| 350 | 359 | type: "probe-fail", |
| 351 | | − error: errorMessageFromExit(exit, "Failed to connect", { |
| 352 | | − surface: "mcp_source", |
| 353 | | − action: "probe_remote_source", |
| 354 | | − }), |
| 360 | + error: errorMessageFromExit(exit, "Failed to connect"), |
| 355 | 361 | }); |
| 356 | 362 | return; |
| 357 | 363 | } |
| 358 | 364 | setRemoteAuthMode(exit.value.requiresOAuth ? "oauth2" : "none"); |
| 359 | 365 | dispatch({ type: "probe-ok", probe: exit.value }); |
| 360 | | − }, [state.url, scopeId, doProbe, remoteCredentials, errorMessageFromExit]); |
| 366 | + }, [state.url, scopeId, doProbe, remoteCredentials]); |
| 361 | 367 | |
| 362 | 368 | // Keep the latest handleProbe in a ref so the debounced effect can call it |
| 363 | 369 | // without depending on its identity (which changes every render). |
| @@ -477,10 +483,7 @@ export default function AddMcpSource(props: { · 113 unmodified lines |
| 477 | 483 | if (Exit.isFailure(exit)) { |
| 478 | 484 | dispatch({ |
| 479 | 485 | type: "add-fail", |
| 480 | | − error: errorMessageFromExit(exit, "Failed to add source", { |
| 481 | | − surface: "mcp_source", |
| 482 | | − action: "add_remote_source", |
| 483 | | − }), |
| 486 | + error: errorMessageFromExit(exit, "Failed to add source"), |
| 484 | 487 | }); |
| 485 | 488 | return; |
| 486 | 489 | } |
| @@ -496,7 +499,6 @@ export default function AddMcpSource(props: { · 9 unmodified lines |
| 496 | 499 | doAdd, |
| 497 | 500 | props, |
| 498 | 501 | scopeId, |
| 499 | | − errorMessageFromExit, |
| 500 | 502 | requestCredentialTargetScope, |
| 501 | 503 | oauthCredentialTargetScope, |
| 502 | 504 | ]); |
| @@ -547,26 +549,12 @@ export default function AddMcpSource(props: { · 44 unmodified lines |
| 547 | 549 | reactivityKeys: sourceWriteKeys, |
| 548 | 550 | }); |
| 549 | 551 | if (Exit.isFailure(exit)) { |
| 550 | | − setStdioError( |
| 551 | | − errorMessageFromExit(exit, "Failed to add source", { |
| 552 | | − surface: "mcp_source", |
| 553 | | − action: "add_stdio_source", |
| 554 | | − }), |
| 555 | | − ); |
| 552 | + setStdioError(errorMessageFromExit(exit, "Failed to add source")); |
| 556 | 553 | setStdioAdding(false); |
| 557 | 554 | return; |
| 558 | 555 | } |
| 559 | 556 | props.onComplete(); |
| 560 | | − }, [ |
| 561 | | − stdioCommand, |
| 562 | | − stdioArgs, |
| 563 | | − stdioEnv, |
| 564 | | − stdioIdentity, |
| 565 | | − doAdd, |
| 566 | | − scopeId, |
| 567 | | − props, |
| 568 | | − errorMessageFromExit, |
| 569 | | − ]); |
| 557 | + }, [stdioCommand, stdioArgs, stdioEnv, stdioIdentity, doAdd, scopeId, props]); |
| 570 | 558 | |
| 571 | 559 | // ---- Render ---- |
| 572 | 560 | |