mcp-use 1.2.1 → 1.2.2-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/chunk-3RJENWH4.js +248 -0
- package/dist/{chunk-MGUO7HXB.js → chunk-7UX634PO.js} +307 -1066
- package/dist/chunk-KLIBVJ3Z.js +759 -0
- package/dist/chunk-MZLETWQQ.js +250 -0
- package/dist/chunk-RSGKBEHH.js +1411 -0
- package/dist/index.cjs +880 -541
- package/dist/index.d.ts +24 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +92 -33
- package/dist/{langfuse-6AJGHMAV.js → langfuse-LCJ6VJEP.js} +2 -1
- package/dist/src/adapters/base.d.ts +2 -2
- package/dist/src/adapters/base.d.ts.map +1 -1
- package/dist/src/adapters/index.d.ts +2 -2
- package/dist/src/adapters/index.d.ts.map +1 -1
- package/dist/src/adapters/langchain_adapter.d.ts +4 -4
- package/dist/src/adapters/langchain_adapter.d.ts.map +1 -1
- package/dist/src/agents/base.d.ts +1 -1
- package/dist/src/agents/base.d.ts.map +1 -1
- package/dist/src/agents/index.d.ts +3 -3
- package/dist/src/agents/index.d.ts.map +1 -1
- package/dist/src/agents/mcp_agent.d.ts +12 -12
- package/dist/src/agents/mcp_agent.d.ts.map +1 -1
- package/dist/src/agents/prompts/system_prompt_builder.d.ts +2 -2
- package/dist/src/agents/prompts/system_prompt_builder.d.ts.map +1 -1
- package/dist/src/agents/prompts/templates.d.ts.map +1 -1
- package/dist/src/agents/remote.d.ts +2 -2
- package/dist/src/agents/remote.d.ts.map +1 -1
- package/dist/src/agents/types.d.ts +1 -1
- package/dist/src/agents/types.d.ts.map +1 -1
- package/dist/src/agents/utils/ai_sdk.d.ts +1 -1
- package/dist/src/agents/utils/ai_sdk.d.ts.map +1 -1
- package/dist/src/agents/utils/index.d.ts +1 -1
- package/dist/src/agents/utils/index.d.ts.map +1 -1
- package/dist/src/auth/browser-provider.d.ts +2 -2
- package/dist/src/auth/browser-provider.d.ts.map +1 -1
- package/dist/src/auth/callback.d.ts.map +1 -1
- package/dist/src/auth/index.d.ts +3 -3
- package/dist/src/auth/index.d.ts.map +1 -1
- package/dist/src/auth/types.d.ts +1 -1
- package/dist/src/auth/types.d.ts.map +1 -1
- package/dist/src/browser.cjs +573 -219
- package/dist/src/browser.d.ts +17 -17
- package/dist/src/browser.d.ts.map +1 -1
- package/dist/src/browser.js +17 -46
- package/dist/src/client/base.d.ts +2 -2
- package/dist/src/client/base.d.ts.map +1 -1
- package/dist/src/client/browser.d.ts +2 -2
- package/dist/src/client/browser.d.ts.map +1 -1
- package/dist/src/client.d.ts +2 -2
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/config.d.ts +1 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/connectors/base.d.ts +51 -9
- package/dist/src/connectors/base.d.ts.map +1 -1
- package/dist/src/connectors/http.d.ts +3 -3
- package/dist/src/connectors/http.d.ts.map +1 -1
- package/dist/src/connectors/index.d.ts +4 -4
- package/dist/src/connectors/index.d.ts.map +1 -1
- package/dist/src/connectors/stdio.d.ts +3 -3
- package/dist/src/connectors/stdio.d.ts.map +1 -1
- package/dist/src/connectors/websocket.d.ts +4 -7
- package/dist/src/connectors/websocket.d.ts.map +1 -1
- package/dist/src/logging.d.ts +4 -4
- package/dist/src/logging.d.ts.map +1 -1
- package/dist/src/managers/index.d.ts +2 -2
- package/dist/src/managers/index.d.ts.map +1 -1
- package/dist/src/managers/server_manager.d.ts +4 -4
- package/dist/src/managers/server_manager.d.ts.map +1 -1
- package/dist/src/managers/tools/acquire_active_mcp_server.d.ts +3 -3
- package/dist/src/managers/tools/acquire_active_mcp_server.d.ts.map +1 -1
- package/dist/src/managers/tools/add_server_from_config.d.ts +3 -3
- package/dist/src/managers/tools/add_server_from_config.d.ts.map +1 -1
- package/dist/src/managers/tools/base.d.ts +6 -6
- package/dist/src/managers/tools/base.d.ts.map +1 -1
- package/dist/src/managers/tools/connect_mcp_server.d.ts +4 -4
- package/dist/src/managers/tools/connect_mcp_server.d.ts.map +1 -1
- package/dist/src/managers/tools/index.d.ts +5 -5
- package/dist/src/managers/tools/index.d.ts.map +1 -1
- package/dist/src/managers/tools/list_mcp_servers.d.ts +3 -3
- package/dist/src/managers/tools/list_mcp_servers.d.ts.map +1 -1
- package/dist/src/managers/tools/release_mcp_server_connection.d.ts +3 -3
- package/dist/src/managers/tools/release_mcp_server_connection.d.ts.map +1 -1
- package/dist/src/managers/types.d.ts +3 -3
- package/dist/src/managers/types.d.ts.map +1 -1
- package/dist/src/oauth-helper.d.ts.map +1 -1
- package/dist/src/observability/index.d.ts +3 -3
- package/dist/src/observability/index.d.ts.map +1 -1
- package/dist/src/observability/langfuse.d.ts +1 -1
- package/dist/src/observability/langfuse.d.ts.map +1 -1
- package/dist/src/observability/manager.d.ts +1 -1
- package/dist/src/observability/manager.d.ts.map +1 -1
- package/dist/src/observability/types.d.ts +1 -1
- package/dist/src/observability/types.d.ts.map +1 -1
- package/dist/src/react/index.cjs +1571 -321
- package/dist/src/react/index.d.ts +6 -6
- package/dist/src/react/index.d.ts.map +1 -1
- package/dist/src/react/index.js +3 -2
- package/dist/src/react/types.d.ts +27 -5
- package/dist/src/react/types.d.ts.map +1 -1
- package/dist/src/react/useMcp.d.ts +32 -1
- package/dist/src/react/useMcp.d.ts.map +1 -1
- package/dist/src/react/useWidget.d.ts +5 -2
- package/dist/src/react/useWidget.d.ts.map +1 -1
- package/dist/src/react/widget-types.d.ts +3 -3
- package/dist/src/react/widget-types.d.ts.map +1 -1
- package/dist/src/server/adapters/mcp-ui-adapter.d.ts +3 -3
- package/dist/src/server/adapters/mcp-ui-adapter.d.ts.map +1 -1
- package/dist/src/server/index.cjs +170 -50
- package/dist/src/server/index.d.ts +4 -4
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/server/index.js +174 -51
- package/dist/src/server/logging.d.ts +1 -1
- package/dist/src/server/logging.d.ts.map +1 -1
- package/dist/src/server/mcp-server.d.ts +29 -29
- package/dist/src/server/mcp-server.d.ts.map +1 -1
- package/dist/src/server/types/common.d.ts +2 -2
- package/dist/src/server/types/common.d.ts.map +1 -1
- package/dist/src/server/types/index.d.ts +4 -4
- package/dist/src/server/types/index.d.ts.map +1 -1
- package/dist/src/server/types/prompt.d.ts +2 -2
- package/dist/src/server/types/prompt.d.ts.map +1 -1
- package/dist/src/server/types/resource.d.ts +24 -24
- package/dist/src/server/types/resource.d.ts.map +1 -1
- package/dist/src/server/types/tool.d.ts +3 -3
- package/dist/src/server/types/tool.d.ts.map +1 -1
- package/dist/src/server/types/widget.d.ts +1 -1
- package/dist/src/server/types.d.ts +1 -1
- package/dist/src/server/types.d.ts.map +1 -1
- package/dist/src/session.d.ts +1 -1
- package/dist/src/session.d.ts.map +1 -1
- package/dist/src/task_managers/base.d.ts.map +1 -1
- package/dist/src/task_managers/index.d.ts +5 -5
- package/dist/src/task_managers/index.d.ts.map +1 -1
- package/dist/src/task_managers/sse.d.ts +3 -3
- package/dist/src/task_managers/sse.d.ts.map +1 -1
- package/dist/src/task_managers/stdio.d.ts +4 -4
- package/dist/src/task_managers/stdio.d.ts.map +1 -1
- package/dist/src/task_managers/streamable_http.d.ts +3 -3
- package/dist/src/task_managers/streamable_http.d.ts.map +1 -1
- package/dist/src/task_managers/websocket.d.ts +2 -2
- package/dist/src/task_managers/websocket.d.ts.map +1 -1
- package/dist/src/telemetry/events.d.ts.map +1 -1
- package/dist/src/telemetry/index.d.ts +4 -4
- package/dist/src/telemetry/index.d.ts.map +1 -1
- package/dist/src/telemetry/telemetry.d.ts +1 -1
- package/dist/src/telemetry/telemetry.d.ts.map +1 -1
- package/dist/src/telemetry/utils.d.ts +1 -1
- package/dist/src/telemetry/utils.d.ts.map +1 -1
- package/dist/tsup.config.d.ts.map +1 -1
- package/dist/vitest.config.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-62GFHYCL.js +0 -300
- package/dist/chunk-JV7HAYUT.js +0 -860
- package/dist/chunk-ZUEQQ6YK.js +0 -444
package/dist/chunk-JV7HAYUT.js
DELETED
|
@@ -1,860 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BrowserOAuthClientProvider
|
|
3
|
-
} from "./chunk-62GFHYCL.js";
|
|
4
|
-
import {
|
|
5
|
-
__name
|
|
6
|
-
} from "./chunk-SHUYVCID.js";
|
|
7
|
-
|
|
8
|
-
// src/react/useMcp.ts
|
|
9
|
-
import {
|
|
10
|
-
CallToolResultSchema,
|
|
11
|
-
GetPromptResultSchema,
|
|
12
|
-
ListPromptsResultSchema,
|
|
13
|
-
ListResourcesResultSchema,
|
|
14
|
-
ListToolsResultSchema,
|
|
15
|
-
ReadResourceResultSchema
|
|
16
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
17
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
18
|
-
import { auth, UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
19
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
20
|
-
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
21
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
22
|
-
import { sanitizeUrl } from "strict-url-sanitise";
|
|
23
|
-
|
|
24
|
-
// src/utils/assert.ts
|
|
25
|
-
function assert(condition, message) {
|
|
26
|
-
if (!condition) {
|
|
27
|
-
throw new Error(message);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
__name(assert, "assert");
|
|
31
|
-
|
|
32
|
-
// src/react/useMcp.ts
|
|
33
|
-
var DEFAULT_RECONNECT_DELAY = 3e3;
|
|
34
|
-
var DEFAULT_RETRY_DELAY = 5e3;
|
|
35
|
-
var AUTH_TIMEOUT = 5 * 60 * 1e3;
|
|
36
|
-
function useMcp(options) {
|
|
37
|
-
const {
|
|
38
|
-
url,
|
|
39
|
-
enabled = true,
|
|
40
|
-
clientName,
|
|
41
|
-
clientUri,
|
|
42
|
-
callbackUrl = typeof window !== "undefined" ? sanitizeUrl(new URL("/oauth/callback", window.location.origin).toString()) : "/oauth/callback",
|
|
43
|
-
storageKeyPrefix = "mcp:auth",
|
|
44
|
-
clientConfig = {},
|
|
45
|
-
customHeaders = {},
|
|
46
|
-
debug: _debug = false,
|
|
47
|
-
autoRetry = false,
|
|
48
|
-
autoReconnect = DEFAULT_RECONNECT_DELAY,
|
|
49
|
-
transportType = "auto",
|
|
50
|
-
preventAutoAuth = false,
|
|
51
|
-
onPopupWindow,
|
|
52
|
-
timeout = 3e4,
|
|
53
|
-
// 30 seconds default for connection timeout
|
|
54
|
-
sseReadTimeout = 3e5
|
|
55
|
-
// 5 minutes default for SSE read timeout
|
|
56
|
-
} = options;
|
|
57
|
-
const [state, setState] = useState("discovering");
|
|
58
|
-
const [tools, setTools] = useState([]);
|
|
59
|
-
const [resources, setResources] = useState([]);
|
|
60
|
-
const [resourceTemplates, setResourceTemplates] = useState([]);
|
|
61
|
-
const [prompts, setPrompts] = useState([]);
|
|
62
|
-
const [error, setError] = useState(void 0);
|
|
63
|
-
const [log, setLog] = useState([]);
|
|
64
|
-
const [authUrl, setAuthUrl] = useState(void 0);
|
|
65
|
-
const clientRef = useRef(null);
|
|
66
|
-
const transportRef = useRef(null);
|
|
67
|
-
const authProviderRef = useRef(null);
|
|
68
|
-
const connectingRef = useRef(false);
|
|
69
|
-
const isMountedRef = useRef(true);
|
|
70
|
-
const connectAttemptRef = useRef(0);
|
|
71
|
-
const authTimeoutRef = useRef(null);
|
|
72
|
-
const stateRef = useRef(state);
|
|
73
|
-
const autoReconnectRef = useRef(autoReconnect);
|
|
74
|
-
const successfulTransportRef = useRef(null);
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
stateRef.current = state;
|
|
77
|
-
autoReconnectRef.current = autoReconnect;
|
|
78
|
-
}, [state, autoReconnect]);
|
|
79
|
-
const addLog = useCallback(
|
|
80
|
-
(level, message, ...args) => {
|
|
81
|
-
const fullMessage = args.length > 0 ? `${message} ${args.map((arg) => JSON.stringify(arg)).join(" ")}` : message;
|
|
82
|
-
console[level](`[useMcp] ${fullMessage}`);
|
|
83
|
-
if (isMountedRef.current) {
|
|
84
|
-
setLog((prevLog) => [...prevLog.slice(-100), { level, message: fullMessage, timestamp: Date.now() }]);
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
[]
|
|
88
|
-
);
|
|
89
|
-
const disconnect = useCallback(
|
|
90
|
-
async (quiet = false) => {
|
|
91
|
-
if (!quiet) addLog("info", "Disconnecting...");
|
|
92
|
-
connectingRef.current = false;
|
|
93
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
94
|
-
authTimeoutRef.current = null;
|
|
95
|
-
const transport = transportRef.current;
|
|
96
|
-
clientRef.current = null;
|
|
97
|
-
transportRef.current = null;
|
|
98
|
-
if (isMountedRef.current && !quiet) {
|
|
99
|
-
setState("discovering");
|
|
100
|
-
setTools([]);
|
|
101
|
-
setResources([]);
|
|
102
|
-
setResourceTemplates([]);
|
|
103
|
-
setPrompts([]);
|
|
104
|
-
setError(void 0);
|
|
105
|
-
setAuthUrl(void 0);
|
|
106
|
-
}
|
|
107
|
-
if (transport) {
|
|
108
|
-
try {
|
|
109
|
-
await transport.close();
|
|
110
|
-
if (!quiet) addLog("debug", "Transport closed");
|
|
111
|
-
} catch (err) {
|
|
112
|
-
if (!quiet) addLog("warn", "Error closing transport:", err);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
[addLog]
|
|
117
|
-
);
|
|
118
|
-
const failConnection = useCallback(
|
|
119
|
-
(errorMessage, connectionError) => {
|
|
120
|
-
addLog("error", errorMessage, connectionError ?? "");
|
|
121
|
-
if (isMountedRef.current) {
|
|
122
|
-
setState("failed");
|
|
123
|
-
setError(errorMessage);
|
|
124
|
-
const manualUrl = authProviderRef.current?.getLastAttemptedAuthUrl();
|
|
125
|
-
if (manualUrl) {
|
|
126
|
-
setAuthUrl(manualUrl);
|
|
127
|
-
addLog("info", "Manual authentication URL may be available.", manualUrl);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
connectingRef.current = false;
|
|
131
|
-
},
|
|
132
|
-
[addLog]
|
|
133
|
-
);
|
|
134
|
-
const connect = useCallback(async () => {
|
|
135
|
-
if (!enabled || !url) {
|
|
136
|
-
addLog("debug", enabled ? "No server URL provided, skipping connection." : "Connection disabled via enabled flag.");
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
if (connectingRef.current) {
|
|
140
|
-
addLog("debug", "Connection attempt already in progress.");
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
if (!isMountedRef.current) {
|
|
144
|
-
addLog("debug", "Connect called after unmount, aborting.");
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
connectingRef.current = true;
|
|
148
|
-
connectAttemptRef.current += 1;
|
|
149
|
-
setError(void 0);
|
|
150
|
-
setAuthUrl(void 0);
|
|
151
|
-
successfulTransportRef.current = null;
|
|
152
|
-
setState("discovering");
|
|
153
|
-
addLog("info", `Connecting attempt #${connectAttemptRef.current} to ${url}...`);
|
|
154
|
-
if (!authProviderRef.current) {
|
|
155
|
-
authProviderRef.current = new BrowserOAuthClientProvider(url, {
|
|
156
|
-
storageKeyPrefix,
|
|
157
|
-
clientName,
|
|
158
|
-
clientUri,
|
|
159
|
-
callbackUrl,
|
|
160
|
-
preventAutoAuth,
|
|
161
|
-
onPopupWindow
|
|
162
|
-
});
|
|
163
|
-
addLog("debug", "BrowserOAuthClientProvider initialized in connect.");
|
|
164
|
-
}
|
|
165
|
-
if (!clientRef.current) {
|
|
166
|
-
clientRef.current = new Client(
|
|
167
|
-
{ name: clientConfig.name || "mcp-use", version: clientConfig.version || "0.1.0" },
|
|
168
|
-
{ capabilities: {} }
|
|
169
|
-
);
|
|
170
|
-
addLog("debug", "MCP Client initialized in connect.");
|
|
171
|
-
}
|
|
172
|
-
const tryConnectWithTransport = /* @__PURE__ */ __name(async (transportTypeParam, isAuthRetry = false) => {
|
|
173
|
-
addLog("info", `Attempting connection with ${transportTypeParam.toUpperCase()} transport${isAuthRetry ? " (after auth)" : ""}...`);
|
|
174
|
-
if (stateRef.current !== "authenticating") {
|
|
175
|
-
setState("connecting");
|
|
176
|
-
}
|
|
177
|
-
let transportInstance;
|
|
178
|
-
try {
|
|
179
|
-
assert(authProviderRef.current, "Auth Provider must be initialized");
|
|
180
|
-
assert(clientRef.current, "Client must be initialized");
|
|
181
|
-
if (transportRef.current) {
|
|
182
|
-
await transportRef.current.close().catch((e) => addLog("warn", `Error closing previous transport: ${e.message}`));
|
|
183
|
-
transportRef.current = null;
|
|
184
|
-
}
|
|
185
|
-
const commonOptions = {
|
|
186
|
-
authProvider: authProviderRef.current,
|
|
187
|
-
requestInit: {
|
|
188
|
-
headers: {
|
|
189
|
-
Accept: "application/json, text/event-stream",
|
|
190
|
-
...customHeaders
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
// Note: The MCP SDK's SSEClientTransport doesn't expose timeout configuration directly
|
|
194
|
-
// Timeout handling is managed by the underlying EventSource and browser/Node.js fetch implementations
|
|
195
|
-
// The timeout and sseReadTimeout options are preserved for future use or custom implementations
|
|
196
|
-
};
|
|
197
|
-
const sanitizedUrl = sanitizeUrl(url);
|
|
198
|
-
const targetUrl = new URL(sanitizedUrl);
|
|
199
|
-
addLog("debug", `Creating ${transportTypeParam.toUpperCase()} transport for URL: ${targetUrl.toString()}`);
|
|
200
|
-
if (transportTypeParam === "http") {
|
|
201
|
-
addLog("debug", "Creating StreamableHTTPClientTransport...");
|
|
202
|
-
transportInstance = new StreamableHTTPClientTransport(targetUrl, commonOptions);
|
|
203
|
-
addLog("debug", "StreamableHTTPClientTransport created successfully");
|
|
204
|
-
} else {
|
|
205
|
-
addLog("debug", "Creating SSEClientTransport...");
|
|
206
|
-
transportInstance = new SSEClientTransport(targetUrl, commonOptions);
|
|
207
|
-
addLog("debug", "SSEClientTransport created successfully");
|
|
208
|
-
}
|
|
209
|
-
transportRef.current = transportInstance;
|
|
210
|
-
addLog("debug", `${transportTypeParam.toUpperCase()} transport created and assigned to ref.`);
|
|
211
|
-
} catch (err) {
|
|
212
|
-
failConnection(
|
|
213
|
-
`Failed to create ${transportTypeParam.toUpperCase()} transport: ${err instanceof Error ? err.message : String(err)}`,
|
|
214
|
-
err instanceof Error ? err : void 0
|
|
215
|
-
);
|
|
216
|
-
return "failed";
|
|
217
|
-
}
|
|
218
|
-
transportInstance.onmessage = (message) => {
|
|
219
|
-
addLog("debug", `[Transport] Received: ${JSON.stringify(message)}`);
|
|
220
|
-
clientRef.current?.handleMessage?.(message);
|
|
221
|
-
};
|
|
222
|
-
transportInstance.onerror = (err) => {
|
|
223
|
-
addLog("warn", `Transport error event (${transportTypeParam.toUpperCase()}):`, err);
|
|
224
|
-
failConnection(`Transport error (${transportTypeParam.toUpperCase()}): ${err.message}`, err);
|
|
225
|
-
};
|
|
226
|
-
transportInstance.onclose = () => {
|
|
227
|
-
if (!isMountedRef.current || connectingRef.current) return;
|
|
228
|
-
addLog("info", `Transport connection closed (${successfulTransportRef.current || "unknown"} type).`);
|
|
229
|
-
const currentState = stateRef.current;
|
|
230
|
-
const currentAutoReconnect = autoReconnectRef.current;
|
|
231
|
-
if (currentState === "ready" && currentAutoReconnect) {
|
|
232
|
-
const delay = typeof currentAutoReconnect === "number" ? currentAutoReconnect : DEFAULT_RECONNECT_DELAY;
|
|
233
|
-
addLog("info", `Attempting to reconnect in ${delay}ms...`);
|
|
234
|
-
setState("connecting");
|
|
235
|
-
setTimeout(() => {
|
|
236
|
-
if (isMountedRef.current) {
|
|
237
|
-
connect();
|
|
238
|
-
}
|
|
239
|
-
}, delay);
|
|
240
|
-
} else if (currentState !== "failed" && currentState !== "authenticating") {
|
|
241
|
-
failConnection("Cannot connect to server");
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
try {
|
|
245
|
-
addLog("info", `Connecting client via ${transportTypeParam.toUpperCase()}...`);
|
|
246
|
-
await clientRef.current.connect(transportInstance);
|
|
247
|
-
addLog("info", `Client connected via ${transportTypeParam.toUpperCase()}. Loading tools, resources, and prompts...`);
|
|
248
|
-
successfulTransportRef.current = transportTypeParam;
|
|
249
|
-
setState("loading");
|
|
250
|
-
const toolsResponse = await clientRef.current.request({ method: "tools/list" }, ListToolsResultSchema);
|
|
251
|
-
let resourcesResponse = { resources: [], resourceTemplates: [] };
|
|
252
|
-
try {
|
|
253
|
-
resourcesResponse = await clientRef.current.request({ method: "resources/list" }, ListResourcesResultSchema);
|
|
254
|
-
} catch (err) {
|
|
255
|
-
addLog("debug", "Server does not support resources/list method", err);
|
|
256
|
-
}
|
|
257
|
-
let promptsResponse = { prompts: [] };
|
|
258
|
-
try {
|
|
259
|
-
promptsResponse = await clientRef.current.request({ method: "prompts/list" }, ListPromptsResultSchema);
|
|
260
|
-
} catch (err) {
|
|
261
|
-
addLog("debug", "Server does not support prompts/list method", err);
|
|
262
|
-
}
|
|
263
|
-
if (isMountedRef.current) {
|
|
264
|
-
setTools(toolsResponse.tools);
|
|
265
|
-
setResources(resourcesResponse.resources);
|
|
266
|
-
setResourceTemplates(Array.isArray(resourcesResponse.resourceTemplates) ? resourcesResponse.resourceTemplates : []);
|
|
267
|
-
setPrompts(promptsResponse.prompts);
|
|
268
|
-
const summary = [`Loaded ${toolsResponse.tools.length} tools`];
|
|
269
|
-
if (resourcesResponse.resources.length > 0 || resourcesResponse.resourceTemplates && resourcesResponse.resourceTemplates.length > 0) {
|
|
270
|
-
summary.push(`${resourcesResponse.resources.length} resources`);
|
|
271
|
-
if (Array.isArray(resourcesResponse.resourceTemplates) && resourcesResponse.resourceTemplates.length > 0) {
|
|
272
|
-
summary.push(`${resourcesResponse.resourceTemplates.length} resource templates`);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
if (promptsResponse.prompts.length > 0) {
|
|
276
|
-
summary.push(`${promptsResponse.prompts.length} prompts`);
|
|
277
|
-
}
|
|
278
|
-
addLog("info", summary.join(", ") + ".");
|
|
279
|
-
setState("ready");
|
|
280
|
-
connectAttemptRef.current = 0;
|
|
281
|
-
return "success";
|
|
282
|
-
} else {
|
|
283
|
-
return "failed";
|
|
284
|
-
}
|
|
285
|
-
} catch (connectErr) {
|
|
286
|
-
addLog("debug", `Client connect error via ${transportTypeParam.toUpperCase()}:`, connectErr);
|
|
287
|
-
const errorInstance = connectErr instanceof Error ? connectErr : new Error(String(connectErr));
|
|
288
|
-
const errorMessage = errorInstance.message;
|
|
289
|
-
const is404 = errorMessage.includes("404") || errorMessage.includes("Not Found");
|
|
290
|
-
const is405 = errorMessage.includes("405") || errorMessage.includes("Method Not Allowed");
|
|
291
|
-
const isLikelyCors = errorMessage === "Failed to fetch" || errorMessage === "NetworkError when attempting to fetch resource." || errorMessage === "Load failed";
|
|
292
|
-
if (transportTypeParam === "http" && (is404 || is405 || isLikelyCors)) {
|
|
293
|
-
addLog("warn", `HTTP transport failed (${isLikelyCors ? "CORS" : is404 ? "404" : "405"}), will try fallback.`);
|
|
294
|
-
return "fallback";
|
|
295
|
-
}
|
|
296
|
-
if (errorInstance instanceof UnauthorizedError || errorMessage.includes("Unauthorized") || errorMessage.includes("401")) {
|
|
297
|
-
if (isAuthRetry) {
|
|
298
|
-
addLog("error", "Authentication failed even after successful token refresh. This may indicate a server issue.");
|
|
299
|
-
failConnection("Authentication loop detected - auth succeeded but connection still unauthorized.");
|
|
300
|
-
return "failed";
|
|
301
|
-
}
|
|
302
|
-
addLog("info", "Authentication required.");
|
|
303
|
-
assert(authProviderRef.current, "Auth Provider not available for auth flow");
|
|
304
|
-
const existingTokens = await authProviderRef.current.tokens();
|
|
305
|
-
if (preventAutoAuth && !existingTokens) {
|
|
306
|
-
addLog("info", "Authentication required but auto-auth prevented. User action needed.");
|
|
307
|
-
setState("pending_auth");
|
|
308
|
-
return "auth_redirect";
|
|
309
|
-
}
|
|
310
|
-
if (stateRef.current !== "authenticating" && stateRef.current !== "pending_auth") {
|
|
311
|
-
setState("authenticating");
|
|
312
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
313
|
-
authTimeoutRef.current = setTimeout(() => {
|
|
314
|
-
if (isMountedRef.current) {
|
|
315
|
-
const currentState = stateRef.current;
|
|
316
|
-
if (currentState === "authenticating") {
|
|
317
|
-
failConnection("Authentication timed out. Please try again.");
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}, AUTH_TIMEOUT);
|
|
321
|
-
}
|
|
322
|
-
try {
|
|
323
|
-
assert(url, "Server URL is required for authentication");
|
|
324
|
-
const baseUrl = new URL(url).origin;
|
|
325
|
-
const authResult = await auth(authProviderRef.current, { serverUrl: baseUrl });
|
|
326
|
-
if (!isMountedRef.current) return "failed";
|
|
327
|
-
if (authResult === "AUTHORIZED") {
|
|
328
|
-
addLog("info", "Authentication successful via existing token or refresh. Retrying transport connection...");
|
|
329
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
330
|
-
authTimeoutRef.current = null;
|
|
331
|
-
return await tryConnectWithTransport(transportTypeParam, true);
|
|
332
|
-
} else if (authResult === "REDIRECT") {
|
|
333
|
-
addLog("info", "Redirecting for authentication. Waiting for callback...");
|
|
334
|
-
return "auth_redirect";
|
|
335
|
-
}
|
|
336
|
-
} catch (sdkAuthError) {
|
|
337
|
-
if (!isMountedRef.current) return "failed";
|
|
338
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
339
|
-
failConnection(
|
|
340
|
-
`Failed to initiate authentication: ${sdkAuthError instanceof Error ? sdkAuthError.message : String(sdkAuthError)}`,
|
|
341
|
-
sdkAuthError instanceof Error ? sdkAuthError : void 0
|
|
342
|
-
);
|
|
343
|
-
return "failed";
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
failConnection(`Failed to connect via ${transportTypeParam.toUpperCase()}: ${errorMessage}`, errorInstance);
|
|
347
|
-
return "failed";
|
|
348
|
-
}
|
|
349
|
-
}, "tryConnectWithTransport");
|
|
350
|
-
let finalStatus = "failed";
|
|
351
|
-
if (transportType === "sse") {
|
|
352
|
-
addLog("debug", "Using SSE-only transport mode");
|
|
353
|
-
finalStatus = await tryConnectWithTransport("sse");
|
|
354
|
-
} else if (transportType === "http") {
|
|
355
|
-
addLog("debug", "Using HTTP-only transport mode");
|
|
356
|
-
finalStatus = await tryConnectWithTransport("http");
|
|
357
|
-
} else {
|
|
358
|
-
addLog("debug", "Using auto transport mode (HTTP with SSE fallback)");
|
|
359
|
-
const httpResult = await tryConnectWithTransport("http");
|
|
360
|
-
if (httpResult === "fallback" && isMountedRef.current && stateRef.current !== "authenticating") {
|
|
361
|
-
addLog("info", "HTTP failed, attempting SSE fallback...");
|
|
362
|
-
const sseResult = await tryConnectWithTransport("sse");
|
|
363
|
-
finalStatus = sseResult;
|
|
364
|
-
} else {
|
|
365
|
-
finalStatus = httpResult;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
if (finalStatus === "success" || finalStatus === "failed" || finalStatus === "auth_redirect") {
|
|
369
|
-
connectingRef.current = false;
|
|
370
|
-
}
|
|
371
|
-
addLog("debug", `Connection sequence finished with status: ${finalStatus}`);
|
|
372
|
-
}, [
|
|
373
|
-
addLog,
|
|
374
|
-
failConnection,
|
|
375
|
-
disconnect,
|
|
376
|
-
url,
|
|
377
|
-
storageKeyPrefix,
|
|
378
|
-
clientName,
|
|
379
|
-
clientUri,
|
|
380
|
-
callbackUrl,
|
|
381
|
-
clientConfig.name,
|
|
382
|
-
clientConfig.version,
|
|
383
|
-
customHeaders,
|
|
384
|
-
transportType,
|
|
385
|
-
preventAutoAuth,
|
|
386
|
-
onPopupWindow,
|
|
387
|
-
enabled,
|
|
388
|
-
timeout,
|
|
389
|
-
sseReadTimeout
|
|
390
|
-
]);
|
|
391
|
-
const callTool = useCallback(
|
|
392
|
-
async (name, args) => {
|
|
393
|
-
if (stateRef.current !== "ready" || !clientRef.current) {
|
|
394
|
-
throw new Error(`MCP client is not ready (current state: ${state}). Cannot call tool "${name}".`);
|
|
395
|
-
}
|
|
396
|
-
addLog("info", `Calling tool: ${name}`, args);
|
|
397
|
-
try {
|
|
398
|
-
const result = await clientRef.current.request({ method: "tools/call", params: { name, arguments: args } }, CallToolResultSchema);
|
|
399
|
-
addLog("info", `Tool "${name}" call successful:`, result);
|
|
400
|
-
return result;
|
|
401
|
-
} catch (err) {
|
|
402
|
-
addLog("error", `Error calling tool "${name}": ${err instanceof Error ? err.message : String(err)}`, err);
|
|
403
|
-
const errorInstance = err instanceof Error ? err : new Error(String(err));
|
|
404
|
-
if (errorInstance instanceof UnauthorizedError || errorInstance.message.includes("Unauthorized") || errorInstance.message.includes("401")) {
|
|
405
|
-
addLog("warn", "Tool call unauthorized, attempting re-authentication...");
|
|
406
|
-
setState("authenticating");
|
|
407
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
408
|
-
authTimeoutRef.current = setTimeout(() => {
|
|
409
|
-
if (isMountedRef.current) {
|
|
410
|
-
const currentState2 = stateRef.current;
|
|
411
|
-
if (currentState2 === "authenticating") {
|
|
412
|
-
failConnection("Authentication timed out. Please try again.");
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}, AUTH_TIMEOUT);
|
|
416
|
-
try {
|
|
417
|
-
assert(authProviderRef.current, "Auth Provider not available for tool re-auth");
|
|
418
|
-
assert(url, "Server URL is required for authentication");
|
|
419
|
-
const baseUrl = new URL(url).origin;
|
|
420
|
-
const authResult = await auth(authProviderRef.current, { serverUrl: baseUrl });
|
|
421
|
-
if (!isMountedRef.current) return;
|
|
422
|
-
if (authResult === "AUTHORIZED") {
|
|
423
|
-
addLog("info", "Re-authentication successful. Retrying tool call is recommended, or reconnecting.");
|
|
424
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
425
|
-
connectingRef.current = false;
|
|
426
|
-
connect();
|
|
427
|
-
} else if (authResult === "REDIRECT") {
|
|
428
|
-
addLog("info", "Redirecting for re-authentication for tool call.");
|
|
429
|
-
}
|
|
430
|
-
} catch (sdkAuthError) {
|
|
431
|
-
if (!isMountedRef.current) return;
|
|
432
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
433
|
-
failConnection(
|
|
434
|
-
`Re-authentication failed: ${sdkAuthError instanceof Error ? sdkAuthError.message : String(sdkAuthError)}`,
|
|
435
|
-
sdkAuthError instanceof Error ? sdkAuthError : void 0
|
|
436
|
-
);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
const currentState = stateRef.current;
|
|
440
|
-
if (currentState !== "authenticating") {
|
|
441
|
-
throw err;
|
|
442
|
-
}
|
|
443
|
-
return void 0;
|
|
444
|
-
}
|
|
445
|
-
},
|
|
446
|
-
[state, url, addLog, failConnection, connect]
|
|
447
|
-
);
|
|
448
|
-
const retry = useCallback(() => {
|
|
449
|
-
if (stateRef.current === "failed") {
|
|
450
|
-
addLog("info", "Retry requested...");
|
|
451
|
-
connect();
|
|
452
|
-
} else {
|
|
453
|
-
addLog("warn", `Retry called but state is not 'failed' (state: ${stateRef.current}). Ignoring.`);
|
|
454
|
-
}
|
|
455
|
-
}, [addLog, connect]);
|
|
456
|
-
const authenticate = useCallback(async () => {
|
|
457
|
-
addLog("info", "Manual authentication requested...");
|
|
458
|
-
const currentState = stateRef.current;
|
|
459
|
-
if (currentState === "failed") {
|
|
460
|
-
addLog("info", "Attempting to reconnect and authenticate via retry...");
|
|
461
|
-
retry();
|
|
462
|
-
} else if (currentState === "pending_auth") {
|
|
463
|
-
addLog("info", "Proceeding with authentication from pending state...");
|
|
464
|
-
setState("authenticating");
|
|
465
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
466
|
-
authTimeoutRef.current = setTimeout(() => {
|
|
467
|
-
if (isMountedRef.current) {
|
|
468
|
-
const currentStateValue = stateRef.current;
|
|
469
|
-
if (currentStateValue === "authenticating") {
|
|
470
|
-
failConnection("Authentication timed out. Please try again.");
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}, AUTH_TIMEOUT);
|
|
474
|
-
try {
|
|
475
|
-
assert(authProviderRef.current, "Auth Provider not available for manual auth");
|
|
476
|
-
assert(url, "Server URL is required for authentication");
|
|
477
|
-
const baseUrl = new URL(url).origin;
|
|
478
|
-
const authResult = await auth(authProviderRef.current, { serverUrl: baseUrl });
|
|
479
|
-
if (!isMountedRef.current) return;
|
|
480
|
-
if (authResult === "AUTHORIZED") {
|
|
481
|
-
addLog("info", "Manual authentication successful. Re-attempting connection...");
|
|
482
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
483
|
-
connectingRef.current = false;
|
|
484
|
-
connect();
|
|
485
|
-
} else if (authResult === "REDIRECT") {
|
|
486
|
-
addLog("info", "Redirecting for manual authentication. Waiting for callback...");
|
|
487
|
-
}
|
|
488
|
-
} catch (authError) {
|
|
489
|
-
if (!isMountedRef.current) return;
|
|
490
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
491
|
-
failConnection(
|
|
492
|
-
`Manual authentication failed: ${authError instanceof Error ? authError.message : String(authError)}`,
|
|
493
|
-
authError instanceof Error ? authError : void 0
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
} else if (currentState === "authenticating") {
|
|
497
|
-
addLog("warn", "Already attempting authentication. Check for blocked popups or wait for timeout.");
|
|
498
|
-
const manualUrl = authProviderRef.current?.getLastAttemptedAuthUrl();
|
|
499
|
-
if (manualUrl && !authUrl) {
|
|
500
|
-
setAuthUrl(manualUrl);
|
|
501
|
-
addLog("info", "Manual authentication URL retrieved:", manualUrl);
|
|
502
|
-
}
|
|
503
|
-
} else {
|
|
504
|
-
addLog(
|
|
505
|
-
"info",
|
|
506
|
-
`Client not in a state requiring manual authentication trigger (state: ${currentState}). If needed, try disconnecting and reconnecting.`
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
}, [addLog, retry, authUrl, url, failConnection, connect]);
|
|
510
|
-
const clearStorage = useCallback(() => {
|
|
511
|
-
if (authProviderRef.current) {
|
|
512
|
-
const count = authProviderRef.current.clearStorage();
|
|
513
|
-
addLog("info", `Cleared ${count} item(s) from localStorage for ${url}.`);
|
|
514
|
-
setAuthUrl(void 0);
|
|
515
|
-
disconnect();
|
|
516
|
-
} else {
|
|
517
|
-
addLog("warn", "Auth provider not initialized, cannot clear storage.");
|
|
518
|
-
}
|
|
519
|
-
}, [url, addLog, disconnect]);
|
|
520
|
-
const listResources = useCallback(async () => {
|
|
521
|
-
if (stateRef.current !== "ready" || !clientRef.current) {
|
|
522
|
-
throw new Error(`MCP client is not ready (current state: ${state}). Cannot list resources.`);
|
|
523
|
-
}
|
|
524
|
-
addLog("info", "Listing resources...");
|
|
525
|
-
try {
|
|
526
|
-
const resourcesResponse = await clientRef.current.request({ method: "resources/list" }, ListResourcesResultSchema);
|
|
527
|
-
if (isMountedRef.current) {
|
|
528
|
-
setResources(resourcesResponse.resources);
|
|
529
|
-
setResourceTemplates(Array.isArray(resourcesResponse.resourceTemplates) ? resourcesResponse.resourceTemplates : []);
|
|
530
|
-
addLog(
|
|
531
|
-
"info",
|
|
532
|
-
`Listed ${resourcesResponse.resources.length} resources, ${Array.isArray(resourcesResponse.resourceTemplates) ? resourcesResponse.resourceTemplates.length : 0} resource templates.`
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
} catch (err) {
|
|
536
|
-
addLog("error", `Error listing resources: ${err instanceof Error ? err.message : String(err)}`, err);
|
|
537
|
-
throw err;
|
|
538
|
-
}
|
|
539
|
-
}, [state, addLog]);
|
|
540
|
-
const readResource = useCallback(
|
|
541
|
-
async (uri) => {
|
|
542
|
-
if (stateRef.current !== "ready" || !clientRef.current) {
|
|
543
|
-
throw new Error(`MCP client is not ready (current state: ${state}). Cannot read resource "${uri}".`);
|
|
544
|
-
}
|
|
545
|
-
addLog("info", `Reading resource: ${uri}`);
|
|
546
|
-
try {
|
|
547
|
-
const result = await clientRef.current.request({ method: "resources/read", params: { uri } }, ReadResourceResultSchema);
|
|
548
|
-
addLog("info", `Resource "${uri}" read successfully`);
|
|
549
|
-
return result;
|
|
550
|
-
} catch (err) {
|
|
551
|
-
addLog("error", `Error reading resource "${uri}": ${err instanceof Error ? err.message : String(err)}`, err);
|
|
552
|
-
throw err;
|
|
553
|
-
}
|
|
554
|
-
},
|
|
555
|
-
[state, addLog]
|
|
556
|
-
);
|
|
557
|
-
const listPrompts = useCallback(async () => {
|
|
558
|
-
if (stateRef.current !== "ready" || !clientRef.current) {
|
|
559
|
-
throw new Error(`MCP client is not ready (current state: ${state}). Cannot list prompts.`);
|
|
560
|
-
}
|
|
561
|
-
addLog("info", "Listing prompts...");
|
|
562
|
-
try {
|
|
563
|
-
const promptsResponse = await clientRef.current.request({ method: "prompts/list" }, ListPromptsResultSchema);
|
|
564
|
-
if (isMountedRef.current) {
|
|
565
|
-
setPrompts(promptsResponse.prompts);
|
|
566
|
-
addLog("info", `Listed ${promptsResponse.prompts.length} prompts.`);
|
|
567
|
-
}
|
|
568
|
-
} catch (err) {
|
|
569
|
-
addLog("error", `Error listing prompts: ${err instanceof Error ? err.message : String(err)}`, err);
|
|
570
|
-
throw err;
|
|
571
|
-
}
|
|
572
|
-
}, [state, addLog]);
|
|
573
|
-
const getPrompt = useCallback(
|
|
574
|
-
async (name, args) => {
|
|
575
|
-
if (stateRef.current !== "ready" || !clientRef.current) {
|
|
576
|
-
throw new Error(`MCP client is not ready (current state: ${state}). Cannot get prompt "${name}".`);
|
|
577
|
-
}
|
|
578
|
-
addLog("info", `Getting prompt: ${name}`, args);
|
|
579
|
-
try {
|
|
580
|
-
const result = await clientRef.current.request({ method: "prompts/get", params: { name, arguments: args } }, GetPromptResultSchema);
|
|
581
|
-
addLog("info", `Prompt "${name}" retrieved successfully`);
|
|
582
|
-
return result;
|
|
583
|
-
} catch (err) {
|
|
584
|
-
addLog("error", `Error getting prompt "${name}": ${err instanceof Error ? err.message : String(err)}`, err);
|
|
585
|
-
throw err;
|
|
586
|
-
}
|
|
587
|
-
},
|
|
588
|
-
[state, addLog]
|
|
589
|
-
);
|
|
590
|
-
const connectRef = useRef(connect);
|
|
591
|
-
const failConnectionRef = useRef(failConnection);
|
|
592
|
-
useEffect(() => {
|
|
593
|
-
connectRef.current = connect;
|
|
594
|
-
failConnectionRef.current = failConnection;
|
|
595
|
-
});
|
|
596
|
-
useEffect(() => {
|
|
597
|
-
const messageHandler = /* @__PURE__ */ __name((event) => {
|
|
598
|
-
if (event.origin !== window.location.origin) return;
|
|
599
|
-
if (event.data?.type === "mcp_auth_callback") {
|
|
600
|
-
addLog("info", "Received auth callback message.", event.data);
|
|
601
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
602
|
-
authTimeoutRef.current = null;
|
|
603
|
-
if (event.data.success) {
|
|
604
|
-
addLog("info", "Authentication successful via popup. Reconnecting client...");
|
|
605
|
-
if (connectingRef.current) {
|
|
606
|
-
addLog("debug", "Connection attempt already in progress, resetting flag to allow reconnection.");
|
|
607
|
-
}
|
|
608
|
-
connectingRef.current = false;
|
|
609
|
-
setTimeout(() => {
|
|
610
|
-
if (isMountedRef.current) {
|
|
611
|
-
addLog("debug", "Initiating reconnection after successful auth callback.");
|
|
612
|
-
connectRef.current();
|
|
613
|
-
}
|
|
614
|
-
}, 100);
|
|
615
|
-
} else {
|
|
616
|
-
failConnectionRef.current(`Authentication failed in callback: ${event.data.error || "Unknown reason."}`);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}, "messageHandler");
|
|
620
|
-
window.addEventListener("message", messageHandler);
|
|
621
|
-
addLog("debug", "Auth callback message listener added.");
|
|
622
|
-
return () => {
|
|
623
|
-
window.removeEventListener("message", messageHandler);
|
|
624
|
-
addLog("debug", "Auth callback message listener removed.");
|
|
625
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
626
|
-
};
|
|
627
|
-
}, [addLog]);
|
|
628
|
-
useEffect(() => {
|
|
629
|
-
isMountedRef.current = true;
|
|
630
|
-
if (!enabled || !url) {
|
|
631
|
-
addLog("debug", enabled ? "No server URL provided, skipping connection." : "Connection disabled via enabled flag.");
|
|
632
|
-
setState("discovering");
|
|
633
|
-
return () => {
|
|
634
|
-
isMountedRef.current = false;
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
addLog("debug", "useMcp mounted, initiating connection.");
|
|
638
|
-
connectAttemptRef.current = 0;
|
|
639
|
-
if (!authProviderRef.current || authProviderRef.current.serverUrl !== url) {
|
|
640
|
-
authProviderRef.current = new BrowserOAuthClientProvider(url, {
|
|
641
|
-
storageKeyPrefix,
|
|
642
|
-
clientName,
|
|
643
|
-
clientUri,
|
|
644
|
-
callbackUrl,
|
|
645
|
-
preventAutoAuth,
|
|
646
|
-
onPopupWindow
|
|
647
|
-
});
|
|
648
|
-
addLog("debug", "BrowserOAuthClientProvider initialized/updated on mount/option change.");
|
|
649
|
-
}
|
|
650
|
-
connect();
|
|
651
|
-
return () => {
|
|
652
|
-
isMountedRef.current = false;
|
|
653
|
-
addLog("debug", "useMcp unmounting, disconnecting.");
|
|
654
|
-
disconnect(true);
|
|
655
|
-
};
|
|
656
|
-
}, [url, enabled, storageKeyPrefix, callbackUrl, clientName, clientUri, clientConfig.name, clientConfig.version]);
|
|
657
|
-
useEffect(() => {
|
|
658
|
-
let retryTimeoutId = null;
|
|
659
|
-
if (state === "failed" && autoRetry && connectAttemptRef.current > 0) {
|
|
660
|
-
const delay = typeof autoRetry === "number" ? autoRetry : DEFAULT_RETRY_DELAY;
|
|
661
|
-
addLog("info", `Connection failed, auto-retrying in ${delay}ms...`);
|
|
662
|
-
retryTimeoutId = setTimeout(() => {
|
|
663
|
-
if (isMountedRef.current && stateRef.current === "failed") {
|
|
664
|
-
retry();
|
|
665
|
-
}
|
|
666
|
-
}, delay);
|
|
667
|
-
}
|
|
668
|
-
return () => {
|
|
669
|
-
if (retryTimeoutId) clearTimeout(retryTimeoutId);
|
|
670
|
-
};
|
|
671
|
-
}, [state, autoRetry, retry, addLog]);
|
|
672
|
-
return {
|
|
673
|
-
state,
|
|
674
|
-
tools,
|
|
675
|
-
resources,
|
|
676
|
-
resourceTemplates,
|
|
677
|
-
prompts,
|
|
678
|
-
error,
|
|
679
|
-
log,
|
|
680
|
-
authUrl,
|
|
681
|
-
callTool,
|
|
682
|
-
listResources,
|
|
683
|
-
readResource,
|
|
684
|
-
listPrompts,
|
|
685
|
-
getPrompt,
|
|
686
|
-
retry,
|
|
687
|
-
disconnect,
|
|
688
|
-
authenticate,
|
|
689
|
-
clearStorage
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
__name(useMcp, "useMcp");
|
|
693
|
-
|
|
694
|
-
// src/react/useWidget.ts
|
|
695
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useMemo, useState as useState2, useSyncExternalStore } from "react";
|
|
696
|
-
|
|
697
|
-
// src/react/widget-types.ts
|
|
698
|
-
var SET_GLOBALS_EVENT_TYPE = "openai:set_globals";
|
|
699
|
-
|
|
700
|
-
// src/react/useWidget.ts
|
|
701
|
-
function useOpenAiGlobal(key) {
|
|
702
|
-
return useSyncExternalStore(
|
|
703
|
-
(onChange) => {
|
|
704
|
-
const handleSetGlobal = /* @__PURE__ */ __name((event) => {
|
|
705
|
-
const customEvent = event;
|
|
706
|
-
const value = customEvent.detail.globals[key];
|
|
707
|
-
if (value === void 0) {
|
|
708
|
-
return;
|
|
709
|
-
}
|
|
710
|
-
onChange();
|
|
711
|
-
}, "handleSetGlobal");
|
|
712
|
-
if (typeof window !== "undefined") {
|
|
713
|
-
window.addEventListener(
|
|
714
|
-
SET_GLOBALS_EVENT_TYPE,
|
|
715
|
-
handleSetGlobal
|
|
716
|
-
);
|
|
717
|
-
}
|
|
718
|
-
return () => {
|
|
719
|
-
if (typeof window !== "undefined") {
|
|
720
|
-
window.removeEventListener(
|
|
721
|
-
SET_GLOBALS_EVENT_TYPE,
|
|
722
|
-
handleSetGlobal
|
|
723
|
-
);
|
|
724
|
-
}
|
|
725
|
-
};
|
|
726
|
-
},
|
|
727
|
-
() => typeof window !== "undefined" && window.openai ? window.openai[key] : void 0
|
|
728
|
-
);
|
|
729
|
-
}
|
|
730
|
-
__name(useOpenAiGlobal, "useOpenAiGlobal");
|
|
731
|
-
function useWidget(defaultProps) {
|
|
732
|
-
console.log(window?.location?.search, window.openai);
|
|
733
|
-
const isOpenAiAvailable = useMemo(() => typeof window !== "undefined" && !!window.openai, []);
|
|
734
|
-
const provider = useMemo(() => {
|
|
735
|
-
return isOpenAiAvailable ? "openai" : "mcp-ui";
|
|
736
|
-
}, [isOpenAiAvailable]);
|
|
737
|
-
const urlParams = useMemo(() => {
|
|
738
|
-
const urlParams2 = new URLSearchParams(window?.location?.search);
|
|
739
|
-
if (urlParams2.has("mcpUseParams")) {
|
|
740
|
-
return JSON.parse(urlParams2.get("mcpUseParams"));
|
|
741
|
-
}
|
|
742
|
-
return {
|
|
743
|
-
toolInput: {},
|
|
744
|
-
toolOutput: {},
|
|
745
|
-
toolId: ""
|
|
746
|
-
};
|
|
747
|
-
}, [window?.location?.search]);
|
|
748
|
-
console.log(urlParams);
|
|
749
|
-
const toolInput = provider === "openai" ? useOpenAiGlobal("toolInput") : urlParams.toolInput;
|
|
750
|
-
const toolOutput = provider === "openai" ? useOpenAiGlobal("toolOutput") : urlParams.toolOutput;
|
|
751
|
-
const toolResponseMetadata = useOpenAiGlobal("toolResponseMetadata");
|
|
752
|
-
const widgetState = useOpenAiGlobal("widgetState");
|
|
753
|
-
const theme = useOpenAiGlobal("theme");
|
|
754
|
-
const displayMode = useOpenAiGlobal("displayMode");
|
|
755
|
-
const safeArea = useOpenAiGlobal("safeArea");
|
|
756
|
-
const maxHeight = useOpenAiGlobal("maxHeight");
|
|
757
|
-
const userAgent = useOpenAiGlobal("userAgent");
|
|
758
|
-
const locale = useOpenAiGlobal("locale");
|
|
759
|
-
const [localWidgetState, setLocalWidgetState] = useState2(null);
|
|
760
|
-
useEffect2(() => {
|
|
761
|
-
if (widgetState !== void 0) {
|
|
762
|
-
setLocalWidgetState(widgetState);
|
|
763
|
-
}
|
|
764
|
-
}, [widgetState]);
|
|
765
|
-
const callTool = useCallback2(
|
|
766
|
-
async (name, args) => {
|
|
767
|
-
if (!window.openai?.callTool) {
|
|
768
|
-
throw new Error("window.openai.callTool is not available");
|
|
769
|
-
}
|
|
770
|
-
return window.openai.callTool(name, args);
|
|
771
|
-
},
|
|
772
|
-
[]
|
|
773
|
-
);
|
|
774
|
-
const sendFollowUpMessage = useCallback2(async (prompt) => {
|
|
775
|
-
if (!window.openai?.sendFollowUpMessage) {
|
|
776
|
-
throw new Error("window.openai.sendFollowUpMessage is not available");
|
|
777
|
-
}
|
|
778
|
-
return window.openai.sendFollowUpMessage({ prompt });
|
|
779
|
-
}, []);
|
|
780
|
-
const openExternal = useCallback2((href) => {
|
|
781
|
-
if (!window.openai?.openExternal) {
|
|
782
|
-
throw new Error("window.openai.openExternal is not available");
|
|
783
|
-
}
|
|
784
|
-
window.openai.openExternal({ href });
|
|
785
|
-
}, []);
|
|
786
|
-
const requestDisplayMode = useCallback2(
|
|
787
|
-
async (mode) => {
|
|
788
|
-
if (!window.openai?.requestDisplayMode) {
|
|
789
|
-
throw new Error("window.openai.requestDisplayMode is not available");
|
|
790
|
-
}
|
|
791
|
-
return window.openai.requestDisplayMode({ mode });
|
|
792
|
-
},
|
|
793
|
-
[]
|
|
794
|
-
);
|
|
795
|
-
const setState = useCallback2(
|
|
796
|
-
async (state) => {
|
|
797
|
-
const newState = typeof state === "function" ? state(localWidgetState) : state;
|
|
798
|
-
if (!window.openai?.setWidgetState) {
|
|
799
|
-
throw new Error("window.openai.setWidgetState is not available");
|
|
800
|
-
}
|
|
801
|
-
setLocalWidgetState(newState);
|
|
802
|
-
return window.openai.setWidgetState(newState);
|
|
803
|
-
},
|
|
804
|
-
[localWidgetState]
|
|
805
|
-
);
|
|
806
|
-
return {
|
|
807
|
-
// Props and state (with defaults)
|
|
808
|
-
props: toolInput || defaultProps || {},
|
|
809
|
-
output: toolOutput ?? null,
|
|
810
|
-
metadata: toolResponseMetadata ?? null,
|
|
811
|
-
state: localWidgetState,
|
|
812
|
-
setState,
|
|
813
|
-
// Layout and theme (with safe defaults)
|
|
814
|
-
theme: theme || "light",
|
|
815
|
-
displayMode: displayMode || "inline",
|
|
816
|
-
safeArea: safeArea || { insets: { top: 0, bottom: 0, left: 0, right: 0 } },
|
|
817
|
-
maxHeight: maxHeight || 600,
|
|
818
|
-
userAgent: userAgent || {
|
|
819
|
-
device: { type: "desktop" },
|
|
820
|
-
capabilities: { hover: true, touch: false }
|
|
821
|
-
},
|
|
822
|
-
locale: locale || "en",
|
|
823
|
-
// Actions
|
|
824
|
-
callTool,
|
|
825
|
-
sendFollowUpMessage,
|
|
826
|
-
openExternal,
|
|
827
|
-
requestDisplayMode,
|
|
828
|
-
// Availability
|
|
829
|
-
isAvailable: isOpenAiAvailable
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
__name(useWidget, "useWidget");
|
|
833
|
-
function useWidgetProps(defaultProps) {
|
|
834
|
-
const { props } = useWidget(defaultProps);
|
|
835
|
-
return props;
|
|
836
|
-
}
|
|
837
|
-
__name(useWidgetProps, "useWidgetProps");
|
|
838
|
-
function useWidgetTheme() {
|
|
839
|
-
const { theme } = useWidget();
|
|
840
|
-
return theme;
|
|
841
|
-
}
|
|
842
|
-
__name(useWidgetTheme, "useWidgetTheme");
|
|
843
|
-
function useWidgetState(defaultState) {
|
|
844
|
-
const { state, setState } = useWidget();
|
|
845
|
-
useEffect2(() => {
|
|
846
|
-
if (state === null && defaultState !== void 0 && window.openai?.setWidgetState) {
|
|
847
|
-
setState(defaultState);
|
|
848
|
-
}
|
|
849
|
-
}, []);
|
|
850
|
-
return [state, setState];
|
|
851
|
-
}
|
|
852
|
-
__name(useWidgetState, "useWidgetState");
|
|
853
|
-
|
|
854
|
-
export {
|
|
855
|
-
useMcp,
|
|
856
|
-
useWidget,
|
|
857
|
-
useWidgetProps,
|
|
858
|
-
useWidgetTheme,
|
|
859
|
-
useWidgetState
|
|
860
|
-
};
|