import { startTransition, useEffect, useMemo, useState, type ReactNode } from "react";import type { Source } from "@executor/react";import { Result, defineExecutorPluginHttpApiClient, useAtomSet, useAtomValue, useExecutorMutation, useLocalInstallation, useSource,} from "@executor/react";import { Alert, Badge, Button, IconPencil, IconSpinner, Input, Label, Select, SourceToolExplorer, Textarea, parseSourceToolExplorerSearch, type SourceToolExplorerSearch, useSourcePluginNavigation, useSourcePluginRouteParams, useSourcePluginSearch,} from "@executor/react/plugins"; import { mcpHttpApiExtension,} from "@executor/plugin-mcp-http";import { type McpConnectInput, type McpConnectionAuth, type McpDiscoverResult, type McpOAuthPopupResult, type McpStartOAuthInput,} from "@executor/plugin-mcp-shared";import { asMcpRemoteTransportValue, defaultMcpRemoteTransportFields, defaultMcpStdioTransportFields, setMcpTransportFieldsTransport, type McpTransportFields, type McpTransportValue,} from "./transport";import { parseJsonStringArray, parseJsonStringMap,} from "./json";import { buildMcpRemoteConfigKey, mcpDiscoveryRequiresOAuth,} from "./oauth-detection"; const OAUTH_STORAGE_PREFIX = "executor:mcp-oauth:";const OAUTH_TIMEOUT_MS = 2 * 60_000; const getMcpHttpClient = defineExecutorPluginHttpApiClient<"McpReactHttpClient">()( "McpReactHttpClient", [mcpHttpApiExtension] as const,); const defaultMcpInput = (): McpConnectInput => ({ name: "My MCP Source", endpoint: "https://example.com/mcp", transport: "auto", queryParams: null, headers: null, command: null, args: null, env: null, cwd: null, auth: { kind: "none", },}); const presetString = ( search: Record<string, unknown>, key: string,): string | null => { const value = search[key]; return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;}; const presetTransport = ( search: Record<string, unknown>,): McpConnectInput["transport"] => { const value = presetString(search, "presetTransport"); return value === "auto" || value === "streamable-http" || value === "sse" || value === "stdio" ? value : null;}; const presetJsonStringMap = ( search: Record<string, unknown>, key: string,): Record<string, string> | null => { const value = presetString(search, key); return value ? parseJsonStringMap(key, value) : null;}; const presetJsonStringArray = ( search: Record<string, unknown>, key: string,): Array<string> | null => { const value = presetString(search, key); return value ? parseJsonStringArray(key, value) : null;}; const mcpInputFromSearch = ( search: Record<string, unknown>,): McpConnectInput => { const defaults = defaultMcpInput(); const command = presetString(search, "presetCommand"); const args = presetJsonStringArray(search, "presetArgs"); const env = presetJsonStringMap(search, "presetEnv"); const cwd = presetString(search, "presetCwd"); const queryParams = presetJsonStringMap(search, "presetQueryParams"); const headers = presetJsonStringMap(search, "presetHeaders"); const transport = presetTransport(search) ?? (command ? "stdio" : defaults.transport); return { ...defaults, name: presetString(search, "presetName") ?? defaults.name, endpoint: command ? null : presetString(search, "presetEndpoint") ?? defaults.endpoint, transport, queryParams, headers, command, args, env, cwd, };}; const stringifyStringMap = ( value: Record<string, string> | null | undefined,): string => !value || Object.keys(value).length === 0 ? "" : JSON.stringify(value, null, 2); const stringifyStringArray = ( value: ReadonlyArray<string> | null | undefined,): string => !value || value.length === 0 ? "" : JSON.stringify(value, null, 2); const transportFieldsFromInput = (input: McpConnectInput): McpTransportFields => input.transport === "stdio" || input.command ? defaultMcpStdioTransportFields({ command: input.command ?? "", argsText: stringifyStringArray(input.args), envText: stringifyStringMap(input.env), cwd: input.cwd ?? "", }) : { ...defaultMcpRemoteTransportFields( asMcpRemoteTransportValue(input.transport), ), queryParamsText: stringifyStringMap(input.queryParams), headersText: stringifyStringMap(input.headers), }; const waitForOauthPopupResult = async ( sessionId: string,): Promise<McpOAuthPopupResult> => new Promise((resolve, reject) => { const storageKey = `${OAUTH_STORAGE_PREFIX}${sessionId}`; const startedAt = Date.now(); const cleanup = () => { window.removeEventListener("message", handleMessage); window.clearInterval(intervalId); }; const finish = (result: McpOAuthPopupResult) => { cleanup(); try { window.localStorage.removeItem(storageKey); } catch {} resolve(result); }; const handleMessage = (event: MessageEvent) => { if (event.origin !== window.location.origin) { return; } const data = event.data as McpOAuthPopupResult | undefined; if (!data || data.type !== "executor:oauth-result") { return; } if (data.ok && data.sessionId !== sessionId) { return; } finish(data); }; window.addEventListener("message", handleMessage); const intervalId = window.setInterval(() => { if (Date.now() - startedAt > OAUTH_TIMEOUT_MS) { cleanup(); reject(new Error("Timed out waiting for MCP OAuth to finish.")); return; } try { const raw = window.localStorage.getItem(storageKey); if (!raw) { return; } finish(JSON.parse(raw) as McpOAuthPopupResult); } catch { // Ignore malformed local storage and continue polling. } }, 400); }); const openOauthPopup = (): Window | null => { const popup = window.open("", "executor-mcp-oauth", "width=560,height=760"); if (!popup) { return null; } try { popup.document.title = "Connecting"; popup.document.body.innerHTML = ` <main style="font-family: system-ui, -apple-system, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; color: #333;"> <div style="width: 20px; height: 20px; border: 2px solid #e5e7eb; border-top-color: #6366f1; border-radius: 50%; animation: spin 0.7s linear infinite; margin-bottom: 16px;"></div> <p style="margin: 0; font-size: 14px; color: #888;">Redirecting to sign in…</p> <style> @keyframes spin { to { transform: rotate(360deg); } } @media (prefers-color-scheme: dark) { main { background: #09090b; color: #fafafa !important; } p { color: #a1a1aa !important; } div { border-color: #3f3f46 !important; } } </style> </main> `; } catch { // Ignore cases where the browser does not allow touching the popup document. } return popup;}; function McpSourceForm(props: { initialValue: McpConnectInput; mode: "create" | "edit"; onSubmit: (input: McpConnectInput) => Promise<void>;}) { const installation = useLocalInstallation(); const client = getMcpHttpClient(); const discoverSource = useAtomSet( client.mutation("mcp", "discoverSource"), { mode: "promise" }, ); const startOAuth = useAtomSet( client.mutation("mcp", "startOAuth"), { mode: "promise" }, ); const submitMutation = useExecutorMutation<McpConnectInput, void>(props.onSubmit); const [name, setName] = useState(props.initialValue.name); const [endpoint, setEndpoint] = useState(props.initialValue.endpoint ?? ""); const [transportFields, setTransportFields] = useState<McpTransportFields>( transportFieldsFromInput(props.initialValue), ); const [authKind, setAuthKind] = useState<McpConnectionAuth["kind"]>( props.initialValue.auth.kind, ); const [oauthAuth, setOauthAuth] = useState< Extract<McpConnectionAuth, { kind: "oauth2" }> | null >( props.initialValue.auth.kind === "oauth2" ? props.initialValue.auth : null, ); const [oauthStatus, setOauthStatus] = useState<"idle" | "pending" | "connected">( props.initialValue.auth.kind === "oauth2" ? "connected" : "idle", ); const [detectedOauthRequired, setDetectedOauthRequired] = useState( props.initialValue.auth.kind === "oauth2", ); const [lastDiscoveryKey, setLastDiscoveryKey] = useState<string | null>( props.initialValue.auth.kind === "oauth2" ? buildMcpRemoteConfigKey({ transport: transportFields.transport, endpoint: props.initialValue.endpoint ?? "", queryParams: props.initialValue.queryParams, }) : null, ); const [oauthConnectionKey, setOauthConnectionKey] = useState<string | null>( props.initialValue.auth.kind === "oauth2" ? buildMcpRemoteConfigKey({ transport: transportFields.transport, endpoint: props.initialValue.endpoint ?? "", queryParams: props.initialValue.queryParams, }) : null, ); const [error, setError] = useState<string | null>(null); const isStdio = transportFields.transport === "stdio"; const remoteQueryParamsText = transportFields.transport === "stdio" ? "" : transportFields.queryParamsText; const parsedRemoteQueryParams = (() => { if (isStdio) { return null; } try { return parseJsonStringMap("Query params", remoteQueryParamsText); } catch { return null; } })(); const remoteConfigKey = (() => { try { return buildMcpRemoteConfigKey({ transport: transportFields.transport, endpoint, queryParams: parsedRemoteQueryParams, }); } catch { return null; } })(); const runOauth = async (): Promise<Extract<McpConnectionAuth, { kind: "oauth2" }>> => { if (installation.status !== "ready") { throw new Error("Workspace is still loading."); } if (isStdio) { throw new Error("MCP OAuth is only available for remote MCP transports."); } const popup = openOauthPopup(); if (!popup) { throw new Error("Failed opening MCP OAuth popup. Allow popups for this site and try again."); } const payload: McpStartOAuthInput = { endpoint: endpoint.trim(), queryParams: parseJsonStringMap("Query params", transportFields.queryParamsText), redirectUrl: new URL( "/v1/plugins/mcp/oauth/callback", window.location.origin, ).toString(), }; let started; try { started = await startOAuth({ path: { workspaceId: installation.data.scopeId, }, payload, }); } catch (cause) { popup.close(); throw cause; } popup.location.replace(started.authorizationUrl); popup.focus(); const result = await waitForOauthPopupResult(started.sessionId); if (!result.ok) { throw new Error(result.error); } setOauthAuth(result.auth); setOauthStatus("connected"); setOauthConnectionKey(remoteConfigKey); return result.auth; }; const runDiscovery = async (input: { mode: "auto" | "manual"; }): Promise<McpDiscoverResult> => { if (installation.status !== "ready") { throw new Error("Workspace is still loading."); } if (isStdio || remoteConfigKey === null) { setDetectedOauthRequired(false); setLastDiscoveryKey(null); return null; } const payload = { endpoint: endpoint.trim(), queryParams: parseJsonStringMap("Query params", remoteQueryParamsText), }; try { const discovered = await discoverSource({ path: { workspaceId: installation.data.scopeId, }, payload, }); const oauthRequired = mcpDiscoveryRequiresOAuth(discovered); setDetectedOauthRequired(oauthRequired); setLastDiscoveryKey(remoteConfigKey); if (oauthRequired) { setAuthKind((current) => current === "oauth2" ? current : "oauth2"); } return discovered; } catch (cause) { setDetectedOauthRequired(false); setLastDiscoveryKey(remoteConfigKey); if (input.mode === "manual") { throw cause; } return null; } }; useEffect(() => { if (isStdio || remoteConfigKey === null) { setDetectedOauthRequired(false); setLastDiscoveryKey(null); return; } if (remoteConfigKey === lastDiscoveryKey) { return; } const timeout = window.setTimeout(() => { void runDiscovery({ mode: "auto" }); }, 450); return () => { window.clearTimeout(timeout); }; }, [ endpoint, installation.status, isStdio, lastDiscoveryKey, remoteConfigKey, remoteQueryParamsText, ]); useEffect(() => { if (oauthConnectionKey === null || remoteConfigKey === oauthConnectionKey) { return; } setOauthAuth(null); setOauthStatus("idle"); setOauthConnectionKey(null); }, [oauthConnectionKey, remoteConfigKey]); const buildInput = ( connectedOauthAuth: Extract<McpConnectionAuth, { kind: "oauth2" }> | null = oauthAuth, authOverride?: McpConnectionAuth, ): McpConnectInput => ({ name: name.trim(), endpoint: isStdio ? null : (endpoint.trim() || null), transport: transportFields.transport === "" ? null : transportFields.transport, queryParams: isStdio ? null : parseJsonStringMap("Query params", transportFields.queryParamsText), headers: isStdio ? null : parseJsonStringMap("Headers", transportFields.headersText), command: isStdio ? transportFields.command.trim() || null : null, args: isStdio ? parseJsonStringArray("Args", transportFields.argsText) : null, env: isStdio ? parseJsonStringMap("Environment", transportFields.envText) : null, cwd: isStdio ? transportFields.cwd.trim() || null : null, auth:Lines 1–500 of 1117. Load more
import { startTransition, useEffect, useMemo, useState, type ReactNode } from "react"; import type { Source } from "@executor/react"; import { Result, defineExecutorPluginHttpApiClient, useAtomSet, useAtomValue, useExecutorMutation, useLocalInstallation, useSource, } from "@executor/react"; import { Alert, Badge, Button, IconPencil, IconSpinner, Input, Label, Select, SourceToolExplorer, Textarea, parseSourceToolExplorerSearch, type SourceToolExplorerSearch, useSourcePluginNavigation, useSourcePluginRouteParams, useSourcePluginSearch, } from "@executor/react/plugins"; import { mcpHttpApiExtension, } from "@executor/plugin-mcp-http"; import { type McpConnectInput, type McpConnectionAuth, type McpDiscoverResult, type McpOAuthPopupResult, type McpStartOAuthInput, } from "@executor/plugin-mcp-shared"; import { asMcpRemoteTransportValue, defaultMcpRemoteTransportFields, defaultMcpStdioTransportFields, setMcpTransportFieldsTransport, type McpTransportFields, type McpTransportValue, } from "./transport"; import { parseJsonStringArray, parseJsonStringMap, } from "./json"; import { buildMcpRemoteConfigKey, mcpDiscoveryRequiresOAuth, } from "./oauth-detection"; const OAUTH_STORAGE_PREFIX = "executor:mcp-oauth:"; const OAUTH_TIMEOUT_MS = 2 * 60_000; const getMcpHttpClient = defineExecutorPluginHttpApiClient<"McpReactHttpClient">()( "McpReactHttpClient", [mcpHttpApiExtension] as const, ); const defaultMcpInput = (): McpConnectInput => ({ name: "My MCP Source", endpoint: "https://example.com/mcp", transport: "auto", queryParams: null, headers: null, command: null, args: null, env: null, cwd: null, auth: { kind: "none", }, }); const presetString = ( search: Record<string, unknown>, key: string, ): string | null => { const value = search[key]; return typeof value === "string" && value.trim().length > 0 ? value.trim() : null; }; const presetTransport = ( search: Record<string, unknown>, ): McpConnectInput["transport"] => { const value = presetString(search, "presetTransport"); return value === "auto" || value === "streamable-http" || value === "sse" || value === "stdio" ? value : null; }; const presetJsonStringMap = ( search: Record<string, unknown>, key: string, ): Record<string, string> | null => { const value = presetString(search, key); return value ? parseJsonStringMap(key, value) : null; }; const presetJsonStringArray = ( search: Record<string, unknown>, key: string, ): Array<string> | null => { const value = presetString(search, key); return value ? parseJsonStringArray(key, value) : null; }; const mcpInputFromSearch = ( search: Record<string, unknown>, ): McpConnectInput => { const defaults = defaultMcpInput(); const command = presetString(search, "presetCommand"); const args = presetJsonStringArray(search, "presetArgs"); const env = presetJsonStringMap(search, "presetEnv"); const cwd = presetString(search, "presetCwd"); const queryParams = presetJsonStringMap(search, "presetQueryParams"); const headers = presetJsonStringMap(search, "presetHeaders"); const transport = presetTransport(search) ?? (command ? "stdio" : defaults.transport); return { ...defaults, name: presetString(search, "presetName") ?? defaults.name, endpoint: command ? null : presetString(search, "presetEndpoint") ?? defaults.endpoint, transport, queryParams, headers, command, args, env, cwd, }; }; const stringifyStringMap = ( value: Record<string, string> | null | undefined, ): string => !value || Object.keys(value).length === 0 ? "" : JSON.stringify(value, null, 2); const stringifyStringArray = ( value: ReadonlyArray<string> | null | undefined, ): string => !value || value.length === 0 ? "" : JSON.stringify(value, null, 2); const transportFieldsFromInput = (input: McpConnectInput): McpTransportFields => input.transport === "stdio" || input.command ? defaultMcpStdioTransportFields({ command: input.command ?? "", argsText: stringifyStringArray(input.args), envText: stringifyStringMap(input.env), cwd: input.cwd ?? "", }) : { ...defaultMcpRemoteTransportFields( asMcpRemoteTransportValue(input.transport), ), queryParamsText: stringifyStringMap(input.queryParams), headersText: stringifyStringMap(input.headers), }; const waitForOauthPopupResult = async ( sessionId: string, ): Promise<McpOAuthPopupResult> => new Promise((resolve, reject) => { const storageKey = `${OAUTH_STORAGE_PREFIX}${sessionId}`; const startedAt = Date.now(); const cleanup = () => { window.removeEventListener("message", handleMessage); window.clearInterval(intervalId); }; const finish = (result: McpOAuthPopupResult) => { cleanup(); try { window.localStorage.removeItem(storageKey); } catch {} resolve(result); }; const handleMessage = (event: MessageEvent) => { if (event.origin !== window.location.origin) { return; } const data = event.data as McpOAuthPopupResult | undefined; if (!data || data.type !== "executor:oauth-result") { return; } if (data.ok && data.sessionId !== sessionId) { return; } finish(data); }; window.addEventListener("message", handleMessage); const intervalId = window.setInterval(() => { if (Date.now() - startedAt > OAUTH_TIMEOUT_MS) { cleanup(); reject(new Error("Timed out waiting for MCP OAuth to finish.")); return; } try { const raw = window.localStorage.getItem(storageKey); if (!raw) { return; } finish(JSON.parse(raw) as McpOAuthPopupResult); } catch { // Ignore malformed local storage and continue polling. } }, 400); }); const openOauthPopup = (): Window | null => { const popup = window.open("", "executor-mcp-oauth", "width=560,height=760"); if (!popup) { return null; } try { popup.document.title = "Connecting"; popup.document.body.innerHTML = ` <main style="font-family: system-ui, -apple-system, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; color: #333;"> <div style="width: 20px; height: 20px; border: 2px solid #e5e7eb; border-top-color: #6366f1; border-radius: 50%; animation: spin 0.7s linear infinite; margin-bottom: 16px;"></div> <p style="margin: 0; font-size: 14px; color: #888;">Redirecting to sign in…</p> <style> @keyframes spin { to { transform: rotate(360deg); } } @media (prefers-color-scheme: dark) { main { background: #09090b; color: #fafafa !important; } p { color: #a1a1aa !important; } div { border-color: #3f3f46 !important; } } </style> </main> `; } catch { // Ignore cases where the browser does not allow touching the popup document. } return popup; }; function McpSourceForm(props: { initialValue: McpConnectInput; mode: "create" | "edit"; onSubmit: (input: McpConnectInput) => Promise<void>; }) { const installation = useLocalInstallation(); const client = getMcpHttpClient(); const discoverSource = useAtomSet( client.mutation("mcp", "discoverSource"), { mode: "promise" }, ); const startOAuth = useAtomSet( client.mutation("mcp", "startOAuth"), { mode: "promise" }, ); const submitMutation = useExecutorMutation<McpConnectInput, void>(props.onSubmit); const [name, setName] = useState(props.initialValue.name); const [endpoint, setEndpoint] = useState(props.initialValue.endpoint ?? ""); const [transportFields, setTransportFields] = useState<McpTransportFields>( transportFieldsFromInput(props.initialValue), ); const [authKind, setAuthKind] = useState<McpConnectionAuth["kind"]>( props.initialValue.auth.kind, ); const [oauthAuth, setOauthAuth] = useState< Extract<McpConnectionAuth, { kind: "oauth2" }> | null >( props.initialValue.auth.kind === "oauth2" ? props.initialValue.auth : null, ); const [oauthStatus, setOauthStatus] = useState<"idle" | "pending" | "connected">( props.initialValue.auth.kind === "oauth2" ? "connected" : "idle", ); const [detectedOauthRequired, setDetectedOauthRequired] = useState( props.initialValue.auth.kind === "oauth2", ); const [lastDiscoveryKey, setLastDiscoveryKey] = useState<string | null>( props.initialValue.auth.kind === "oauth2" ? buildMcpRemoteConfigKey({ transport: transportFields.transport, endpoint: props.initialValue.endpoint ?? "", queryParams: props.initialValue.queryParams, }) : null, ); const [oauthConnectionKey, setOauthConnectionKey] = useState<string | null>( props.initialValue.auth.kind === "oauth2" ? buildMcpRemoteConfigKey({ transport: transportFields.transport, endpoint: props.initialValue.endpoint ?? "", queryParams: props.initialValue.queryParams, }) : null, ); const [error, setError] = useState<string | null>(null); const isStdio = transportFields.transport === "stdio"; const remoteQueryParamsText = transportFields.transport === "stdio" ? "" : transportFields.queryParamsText; const parsedRemoteQueryParams = (() => { if (isStdio) { return null; } try { return parseJsonStringMap("Query params", remoteQueryParamsText); } catch { return null; } })(); const remoteConfigKey = (() => { try { return buildMcpRemoteConfigKey({ transport: transportFields.transport, endpoint, queryParams: parsedRemoteQueryParams, }); } catch { return null; } })(); const runOauth = async (): Promise<Extract<McpConnectionAuth, { kind: "oauth2" }>> => { if (installation.status !== "ready") { throw new Error("Workspace is still loading."); } if (isStdio) { throw new Error("MCP OAuth is only available for remote MCP transports."); } const popup = openOauthPopup(); if (!popup) { throw new Error("Failed opening MCP OAuth popup. Allow popups for this site and try again."); } const payload: McpStartOAuthInput = { endpoint: endpoint.trim(), queryParams: parseJsonStringMap("Query params", transportFields.queryParamsText), redirectUrl: new URL( "/v1/plugins/mcp/oauth/callback", window.location.origin, ).toString(), }; let started; try { started = await startOAuth({ path: { workspaceId: installation.data.scopeId, }, payload, }); } catch (cause) { popup.close(); throw cause; } popup.location.replace(started.authorizationUrl); popup.focus(); const result = await waitForOauthPopupResult(started.sessionId); if (!result.ok) { throw new Error(result.error); } setOauthAuth(result.auth); setOauthStatus("connected"); setOauthConnectionKey(remoteConfigKey); return result.auth; }; const runDiscovery = async (input: { mode: "auto" | "manual"; }): Promise<McpDiscoverResult> => { if (installation.status !== "ready") { throw new Error("Workspace is still loading."); } if (isStdio || remoteConfigKey === null) { setDetectedOauthRequired(false); setLastDiscoveryKey(null); return null; } const payload = { endpoint: endpoint.trim(), queryParams: parseJsonStringMap("Query params", remoteQueryParamsText), }; try { const discovered = await discoverSource({ path: { workspaceId: installation.data.scopeId, }, payload, }); const oauthRequired = mcpDiscoveryRequiresOAuth(discovered); setDetectedOauthRequired(oauthRequired); setLastDiscoveryKey(remoteConfigKey); if (oauthRequired) { setAuthKind((current) => current === "oauth2" ? current : "oauth2"); } return discovered; } catch (cause) { setDetectedOauthRequired(false); setLastDiscoveryKey(remoteConfigKey); if (input.mode === "manual") { throw cause; } return null; } }; useEffect(() => { if (isStdio || remoteConfigKey === null) { setDetectedOauthRequired(false); setLastDiscoveryKey(null); return; } if (remoteConfigKey === lastDiscoveryKey) { return; } const timeout = window.setTimeout(() => { void runDiscovery({ mode: "auto" }); }, 450); return () => { window.clearTimeout(timeout); }; }, [ endpoint, installation.status, isStdio, lastDiscoveryKey, remoteConfigKey, remoteQueryParamsText, ]); useEffect(() => { if (oauthConnectionKey === null || remoteConfigKey === oauthConnectionKey) { return; } setOauthAuth(null); setOauthStatus("idle"); setOauthConnectionKey(null); }, [oauthConnectionKey, remoteConfigKey]); const buildInput = ( connectedOauthAuth: Extract<McpConnectionAuth, { kind: "oauth2" }> | null = oauthAuth, authOverride?: McpConnectionAuth, ): McpConnectInput => ({ name: name.trim(), endpoint: isStdio ? null : (endpoint.trim() || null), transport: transportFields.transport === "" ? null : transportFields.transport, queryParams: isStdio ? null : parseJsonStringMap("Query params", transportFields.queryParamsText), headers: isStdio ? null : parseJsonStringMap("Headers", transportFields.headersText), command: isStdio ? transportFields.command.trim() || null : null, args: isStdio ? parseJsonStringArray("Args", transportFields.argsText) : null, env: isStdio ? parseJsonStringMap("Environment", transportFields.envText) : null, cwd: isStdio ? transportFields.cwd.trim() || null : null, auth: