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/src/react/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
9
|
var __export = (target, all) => {
|
|
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
18
|
}
|
|
17
19
|
return to;
|
|
18
20
|
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
19
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
30
|
|
|
21
31
|
// src/react/index.ts
|
|
@@ -31,13 +41,1297 @@ __export(react_exports, {
|
|
|
31
41
|
module.exports = __toCommonJS(react_exports);
|
|
32
42
|
|
|
33
43
|
// src/react/useMcp.ts
|
|
34
|
-
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
35
44
|
var import_react = require("react");
|
|
36
|
-
var
|
|
45
|
+
var import_strict_url_sanitise2 = require("strict-url-sanitise");
|
|
46
|
+
|
|
47
|
+
// src/connectors/http.ts
|
|
37
48
|
var import_client = require("@modelcontextprotocol/sdk/client/index.js");
|
|
49
|
+
var import_streamableHttp2 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
50
|
+
|
|
51
|
+
// src/logging.ts
|
|
52
|
+
var import_winston = require("winston");
|
|
53
|
+
async function getNodeModules() {
|
|
54
|
+
if (typeof process !== "undefined" && process.platform) {
|
|
55
|
+
try {
|
|
56
|
+
const fs = await import("fs");
|
|
57
|
+
const path = await import("path");
|
|
58
|
+
return { fs: fs.default, path: path.default };
|
|
59
|
+
} catch {
|
|
60
|
+
return { fs: null, path: null };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { fs: null, path: null };
|
|
64
|
+
}
|
|
65
|
+
__name(getNodeModules, "getNodeModules");
|
|
66
|
+
var { combine, timestamp, label, printf, colorize, splat } = import_winston.format;
|
|
67
|
+
var DEFAULT_LOGGER_NAME = "mcp-use";
|
|
68
|
+
function isNodeJSEnvironment() {
|
|
69
|
+
try {
|
|
70
|
+
if (typeof navigator !== "undefined" && navigator.userAgent?.includes("Cloudflare-Workers")) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
if (typeof globalThis.EdgeRuntime !== "undefined" || typeof globalThis.Deno !== "undefined") {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const hasNodeGlobals = typeof process !== "undefined" && typeof process.platform !== "undefined" && typeof __dirname !== "undefined";
|
|
77
|
+
const hasNodeModules = typeof import_winston.createLogger === "function";
|
|
78
|
+
return hasNodeGlobals && hasNodeModules;
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
__name(isNodeJSEnvironment, "isNodeJSEnvironment");
|
|
84
|
+
var SimpleConsoleLogger = class {
|
|
85
|
+
static {
|
|
86
|
+
__name(this, "SimpleConsoleLogger");
|
|
87
|
+
}
|
|
88
|
+
_level;
|
|
89
|
+
name;
|
|
90
|
+
constructor(name = DEFAULT_LOGGER_NAME, level = "info") {
|
|
91
|
+
this.name = name;
|
|
92
|
+
this._level = level;
|
|
93
|
+
}
|
|
94
|
+
shouldLog(level) {
|
|
95
|
+
const levels = [
|
|
96
|
+
"error",
|
|
97
|
+
"warn",
|
|
98
|
+
"info",
|
|
99
|
+
"http",
|
|
100
|
+
"verbose",
|
|
101
|
+
"debug",
|
|
102
|
+
"silly"
|
|
103
|
+
];
|
|
104
|
+
const currentIndex = levels.indexOf(this._level);
|
|
105
|
+
const messageIndex = levels.indexOf(level);
|
|
106
|
+
return messageIndex <= currentIndex;
|
|
107
|
+
}
|
|
108
|
+
formatMessage(level, message) {
|
|
109
|
+
const timestamp2 = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
110
|
+
return `${timestamp2} [${this.name}] ${level}: ${message}`;
|
|
111
|
+
}
|
|
112
|
+
error(message) {
|
|
113
|
+
if (this.shouldLog("error")) {
|
|
114
|
+
console.error(this.formatMessage("error", message));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
warn(message) {
|
|
118
|
+
if (this.shouldLog("warn")) {
|
|
119
|
+
console.warn(this.formatMessage("warn", message));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
info(message) {
|
|
123
|
+
if (this.shouldLog("info")) {
|
|
124
|
+
console.info(this.formatMessage("info", message));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
debug(message) {
|
|
128
|
+
if (this.shouldLog("debug")) {
|
|
129
|
+
console.debug(this.formatMessage("debug", message));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
http(message) {
|
|
133
|
+
if (this.shouldLog("http")) {
|
|
134
|
+
console.log(this.formatMessage("http", message));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
verbose(message) {
|
|
138
|
+
if (this.shouldLog("verbose")) {
|
|
139
|
+
console.log(this.formatMessage("verbose", message));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
silly(message) {
|
|
143
|
+
if (this.shouldLog("silly")) {
|
|
144
|
+
console.log(this.formatMessage("silly", message));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Make it compatible with Winston interface
|
|
148
|
+
get level() {
|
|
149
|
+
return this._level;
|
|
150
|
+
}
|
|
151
|
+
set level(newLevel) {
|
|
152
|
+
this._level = newLevel;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
function resolveLevel(env) {
|
|
156
|
+
const envValue = typeof process !== "undefined" && process.env ? env : void 0;
|
|
157
|
+
switch (envValue?.trim()) {
|
|
158
|
+
case "2":
|
|
159
|
+
return "debug";
|
|
160
|
+
case "1":
|
|
161
|
+
return "info";
|
|
162
|
+
default:
|
|
163
|
+
return "info";
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
__name(resolveLevel, "resolveLevel");
|
|
167
|
+
var minimalFormatter = printf(({ level, message, label: label2, timestamp: timestamp2 }) => {
|
|
168
|
+
return `${timestamp2} [${label2}] ${level}: ${message}`;
|
|
169
|
+
});
|
|
170
|
+
var detailedFormatter = printf(({ level, message, label: label2, timestamp: timestamp2 }) => {
|
|
171
|
+
return `${timestamp2} [${label2}] ${level.toUpperCase()}: ${message}`;
|
|
172
|
+
});
|
|
173
|
+
var emojiFormatter = printf(({ level, message, label: label2, timestamp: timestamp2 }) => {
|
|
174
|
+
return `${timestamp2} [${label2}] ${level.toUpperCase()}: ${message}`;
|
|
175
|
+
});
|
|
176
|
+
var Logger = class {
|
|
177
|
+
static {
|
|
178
|
+
__name(this, "Logger");
|
|
179
|
+
}
|
|
180
|
+
static instances = {};
|
|
181
|
+
static simpleInstances = {};
|
|
182
|
+
static currentFormat = "minimal";
|
|
183
|
+
static get(name = DEFAULT_LOGGER_NAME) {
|
|
184
|
+
if (!isNodeJSEnvironment()) {
|
|
185
|
+
if (!this.simpleInstances[name]) {
|
|
186
|
+
const debugEnv = typeof process !== "undefined" && process.env?.DEBUG || void 0;
|
|
187
|
+
this.simpleInstances[name] = new SimpleConsoleLogger(
|
|
188
|
+
name,
|
|
189
|
+
resolveLevel(debugEnv)
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
return this.simpleInstances[name];
|
|
193
|
+
}
|
|
194
|
+
if (!this.instances[name]) {
|
|
195
|
+
this.instances[name] = (0, import_winston.createLogger)({
|
|
196
|
+
level: resolveLevel(process.env.DEBUG),
|
|
197
|
+
format: combine(
|
|
198
|
+
colorize(),
|
|
199
|
+
splat(),
|
|
200
|
+
label({ label: name }),
|
|
201
|
+
timestamp({ format: "HH:mm:ss" }),
|
|
202
|
+
this.getFormatter()
|
|
203
|
+
),
|
|
204
|
+
transports: []
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return this.instances[name];
|
|
208
|
+
}
|
|
209
|
+
static getFormatter() {
|
|
210
|
+
switch (this.currentFormat) {
|
|
211
|
+
case "minimal":
|
|
212
|
+
return minimalFormatter;
|
|
213
|
+
case "detailed":
|
|
214
|
+
return detailedFormatter;
|
|
215
|
+
case "emoji":
|
|
216
|
+
return emojiFormatter;
|
|
217
|
+
default:
|
|
218
|
+
return minimalFormatter;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
static async configure(options = {}) {
|
|
222
|
+
const { level, console: console2 = true, file, format: format2 = "minimal" } = options;
|
|
223
|
+
const debugEnv = typeof process !== "undefined" && process.env?.DEBUG || void 0;
|
|
224
|
+
const resolvedLevel = level ?? resolveLevel(debugEnv);
|
|
225
|
+
this.currentFormat = format2;
|
|
226
|
+
const root = this.get();
|
|
227
|
+
root.level = resolvedLevel;
|
|
228
|
+
const winstonRoot = root;
|
|
229
|
+
if (!isNodeJSEnvironment()) {
|
|
230
|
+
Object.values(this.simpleInstances).forEach((logger2) => {
|
|
231
|
+
logger2.level = resolvedLevel;
|
|
232
|
+
});
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
winstonRoot.clear();
|
|
236
|
+
if (console2) {
|
|
237
|
+
winstonRoot.add(new import_winston.transports.Console());
|
|
238
|
+
}
|
|
239
|
+
if (file) {
|
|
240
|
+
const { fs: nodeFs, path: nodePath } = await getNodeModules();
|
|
241
|
+
if (nodeFs && nodePath) {
|
|
242
|
+
const dir = nodePath.dirname(nodePath.resolve(file));
|
|
243
|
+
if (!nodeFs.existsSync(dir)) {
|
|
244
|
+
nodeFs.mkdirSync(dir, { recursive: true });
|
|
245
|
+
}
|
|
246
|
+
winstonRoot.add(new import_winston.transports.File({ filename: file }));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
Object.values(this.instances).forEach((logger2) => {
|
|
250
|
+
if (logger2 && "format" in logger2) {
|
|
251
|
+
logger2.level = resolvedLevel;
|
|
252
|
+
logger2.format = combine(
|
|
253
|
+
colorize(),
|
|
254
|
+
splat(),
|
|
255
|
+
label({ label: DEFAULT_LOGGER_NAME }),
|
|
256
|
+
timestamp({ format: "HH:mm:ss" }),
|
|
257
|
+
this.getFormatter()
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
static setDebug(enabled) {
|
|
263
|
+
let level;
|
|
264
|
+
if (enabled === 2 || enabled === true) level = "debug";
|
|
265
|
+
else if (enabled === 1) level = "info";
|
|
266
|
+
else level = "info";
|
|
267
|
+
Object.values(this.simpleInstances).forEach((logger2) => {
|
|
268
|
+
logger2.level = level;
|
|
269
|
+
});
|
|
270
|
+
Object.values(this.instances).forEach((logger2) => {
|
|
271
|
+
if (logger2) {
|
|
272
|
+
logger2.level = level;
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
if (typeof process !== "undefined" && process.env) {
|
|
276
|
+
process.env.DEBUG = enabled ? enabled === true ? "2" : String(enabled) : "0";
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
static setFormat(format2) {
|
|
280
|
+
this.currentFormat = format2;
|
|
281
|
+
this.configure({ format: format2 });
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
if (isNodeJSEnvironment()) {
|
|
285
|
+
Logger.configure();
|
|
286
|
+
} else {
|
|
287
|
+
Logger.configure({ console: true });
|
|
288
|
+
}
|
|
289
|
+
var logger = Logger.get();
|
|
290
|
+
|
|
291
|
+
// src/task_managers/sse.ts
|
|
38
292
|
var import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
293
|
+
|
|
294
|
+
// src/task_managers/base.ts
|
|
295
|
+
var ConnectionManager = class {
|
|
296
|
+
static {
|
|
297
|
+
__name(this, "ConnectionManager");
|
|
298
|
+
}
|
|
299
|
+
_readyPromise;
|
|
300
|
+
_readyResolver;
|
|
301
|
+
_donePromise;
|
|
302
|
+
_doneResolver;
|
|
303
|
+
_exception = null;
|
|
304
|
+
_connection = null;
|
|
305
|
+
_task = null;
|
|
306
|
+
_abortController = null;
|
|
307
|
+
constructor() {
|
|
308
|
+
this.reset();
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Start the connection manager and establish a connection.
|
|
312
|
+
*
|
|
313
|
+
* @returns The established connection.
|
|
314
|
+
* @throws If the connection cannot be established.
|
|
315
|
+
*/
|
|
316
|
+
async start() {
|
|
317
|
+
this.reset();
|
|
318
|
+
logger.debug(`Starting ${this.constructor.name}`);
|
|
319
|
+
this._task = this.connectionTask();
|
|
320
|
+
await this._readyPromise;
|
|
321
|
+
if (this._exception) {
|
|
322
|
+
throw this._exception;
|
|
323
|
+
}
|
|
324
|
+
if (this._connection === null) {
|
|
325
|
+
throw new Error("Connection was not established");
|
|
326
|
+
}
|
|
327
|
+
return this._connection;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Stop the connection manager and close the connection.
|
|
331
|
+
*/
|
|
332
|
+
async stop() {
|
|
333
|
+
if (this._task && this._abortController) {
|
|
334
|
+
logger.debug(`Cancelling ${this.constructor.name} task`);
|
|
335
|
+
this._abortController.abort();
|
|
336
|
+
try {
|
|
337
|
+
await this._task;
|
|
338
|
+
} catch (e) {
|
|
339
|
+
if (e instanceof Error && e.name === "AbortError") {
|
|
340
|
+
logger.debug(`${this.constructor.name} task aborted successfully`);
|
|
341
|
+
} else {
|
|
342
|
+
logger.warn(`Error stopping ${this.constructor.name} task: ${e}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
await this._donePromise;
|
|
347
|
+
logger.debug(`${this.constructor.name} task completed`);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Reset all internal state.
|
|
351
|
+
*/
|
|
352
|
+
reset() {
|
|
353
|
+
this._readyPromise = new Promise((res) => this._readyResolver = res);
|
|
354
|
+
this._donePromise = new Promise((res) => this._doneResolver = res);
|
|
355
|
+
this._exception = null;
|
|
356
|
+
this._connection = null;
|
|
357
|
+
this._task = null;
|
|
358
|
+
this._abortController = new AbortController();
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* The background task responsible for establishing and maintaining the
|
|
362
|
+
* connection until it is cancelled.
|
|
363
|
+
*/
|
|
364
|
+
async connectionTask() {
|
|
365
|
+
logger.debug(`Running ${this.constructor.name} task`);
|
|
366
|
+
try {
|
|
367
|
+
this._connection = await this.establishConnection();
|
|
368
|
+
logger.debug(`${this.constructor.name} connected successfully`);
|
|
369
|
+
this._readyResolver();
|
|
370
|
+
await this.waitForAbort();
|
|
371
|
+
} catch (err) {
|
|
372
|
+
this._exception = err;
|
|
373
|
+
logger.error(`Error in ${this.constructor.name} task: ${err}`);
|
|
374
|
+
this._readyResolver();
|
|
375
|
+
} finally {
|
|
376
|
+
if (this._connection !== null) {
|
|
377
|
+
try {
|
|
378
|
+
await this.closeConnection(this._connection);
|
|
379
|
+
} catch (closeErr) {
|
|
380
|
+
logger.warn(
|
|
381
|
+
`Error closing connection in ${this.constructor.name}: ${closeErr}`
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
this._connection = null;
|
|
385
|
+
}
|
|
386
|
+
this._doneResolver();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Helper that returns a promise which resolves when the abort signal fires.
|
|
391
|
+
*/
|
|
392
|
+
async waitForAbort() {
|
|
393
|
+
return new Promise((_resolve, _reject) => {
|
|
394
|
+
if (!this._abortController) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const signal = this._abortController.signal;
|
|
398
|
+
if (signal.aborted) {
|
|
399
|
+
_resolve();
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const onAbort = /* @__PURE__ */ __name(() => {
|
|
403
|
+
signal.removeEventListener("abort", onAbort);
|
|
404
|
+
_resolve();
|
|
405
|
+
}, "onAbort");
|
|
406
|
+
signal.addEventListener("abort", onAbort);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
// src/task_managers/sse.ts
|
|
412
|
+
var SseConnectionManager = class extends ConnectionManager {
|
|
413
|
+
static {
|
|
414
|
+
__name(this, "SseConnectionManager");
|
|
415
|
+
}
|
|
416
|
+
url;
|
|
417
|
+
opts;
|
|
418
|
+
_transport = null;
|
|
419
|
+
/**
|
|
420
|
+
* Create an SSE connection manager.
|
|
421
|
+
*
|
|
422
|
+
* @param url The SSE endpoint URL.
|
|
423
|
+
* @param opts Optional transport options (auth, headers, etc.).
|
|
424
|
+
*/
|
|
425
|
+
constructor(url, opts) {
|
|
426
|
+
super();
|
|
427
|
+
this.url = typeof url === "string" ? new URL(url) : url;
|
|
428
|
+
this.opts = opts;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Spawn a new `SSEClientTransport` and start the connection.
|
|
432
|
+
*/
|
|
433
|
+
async establishConnection() {
|
|
434
|
+
this._transport = new import_sse.SSEClientTransport(this.url, this.opts);
|
|
435
|
+
logger.debug(`${this.constructor.name} connected successfully`);
|
|
436
|
+
return this._transport;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Close the underlying transport and clean up resources.
|
|
440
|
+
*/
|
|
441
|
+
async closeConnection(_connection) {
|
|
442
|
+
if (this._transport) {
|
|
443
|
+
try {
|
|
444
|
+
await this._transport.close();
|
|
445
|
+
} catch (e) {
|
|
446
|
+
logger.warn(`Error closing SSE transport: ${e}`);
|
|
447
|
+
} finally {
|
|
448
|
+
this._transport = null;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
// src/task_managers/streamable_http.ts
|
|
39
455
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
40
|
-
var
|
|
456
|
+
var StreamableHttpConnectionManager = class extends ConnectionManager {
|
|
457
|
+
static {
|
|
458
|
+
__name(this, "StreamableHttpConnectionManager");
|
|
459
|
+
}
|
|
460
|
+
url;
|
|
461
|
+
opts;
|
|
462
|
+
_transport = null;
|
|
463
|
+
/**
|
|
464
|
+
* Create a Streamable HTTP connection manager.
|
|
465
|
+
*
|
|
466
|
+
* @param url The HTTP endpoint URL.
|
|
467
|
+
* @param opts Optional transport options (auth, headers, etc.).
|
|
468
|
+
*/
|
|
469
|
+
constructor(url, opts) {
|
|
470
|
+
super();
|
|
471
|
+
this.url = typeof url === "string" ? new URL(url) : url;
|
|
472
|
+
this.opts = opts;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Spawn a new `StreamableHTTPClientTransport` and return it.
|
|
476
|
+
* The Client.connect() method will handle starting the transport.
|
|
477
|
+
*/
|
|
478
|
+
async establishConnection() {
|
|
479
|
+
this._transport = new import_streamableHttp.StreamableHTTPClientTransport(this.url, this.opts);
|
|
480
|
+
logger.debug(`${this.constructor.name} created successfully`);
|
|
481
|
+
return this._transport;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Close the underlying transport and clean up resources.
|
|
485
|
+
*/
|
|
486
|
+
async closeConnection(_connection) {
|
|
487
|
+
if (this._transport) {
|
|
488
|
+
try {
|
|
489
|
+
await this._transport.close();
|
|
490
|
+
} catch (e) {
|
|
491
|
+
logger.warn(`Error closing Streamable HTTP transport: ${e}`);
|
|
492
|
+
} finally {
|
|
493
|
+
this._transport = null;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Get the session ID from the transport if available.
|
|
499
|
+
*/
|
|
500
|
+
get sessionId() {
|
|
501
|
+
return this._transport?.sessionId;
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// src/connectors/base.ts
|
|
506
|
+
var BaseConnector = class {
|
|
507
|
+
static {
|
|
508
|
+
__name(this, "BaseConnector");
|
|
509
|
+
}
|
|
510
|
+
client = null;
|
|
511
|
+
connectionManager = null;
|
|
512
|
+
toolsCache = null;
|
|
513
|
+
capabilitiesCache = null;
|
|
514
|
+
connected = false;
|
|
515
|
+
opts;
|
|
516
|
+
constructor(opts = {}) {
|
|
517
|
+
this.opts = opts;
|
|
518
|
+
}
|
|
519
|
+
/** Disconnect and release resources. */
|
|
520
|
+
async disconnect() {
|
|
521
|
+
if (!this.connected) {
|
|
522
|
+
logger.debug("Not connected to MCP implementation");
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
logger.debug("Disconnecting from MCP implementation");
|
|
526
|
+
await this.cleanupResources();
|
|
527
|
+
this.connected = false;
|
|
528
|
+
logger.debug("Disconnected from MCP implementation");
|
|
529
|
+
}
|
|
530
|
+
/** Check if the client is connected */
|
|
531
|
+
get isClientConnected() {
|
|
532
|
+
return this.client != null;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Initialise the MCP session **after** `connect()` has succeeded.
|
|
536
|
+
*
|
|
537
|
+
* In the SDK, `Client.connect(transport)` automatically performs the
|
|
538
|
+
* protocol‑level `initialize` handshake, so we only need to cache the list of
|
|
539
|
+
* tools and expose some server info.
|
|
540
|
+
*/
|
|
541
|
+
async initialize(defaultRequestOptions = this.opts.defaultRequestOptions ?? {}) {
|
|
542
|
+
if (!this.client) {
|
|
543
|
+
throw new Error("MCP client is not connected");
|
|
544
|
+
}
|
|
545
|
+
logger.debug("Caching server capabilities & tools");
|
|
546
|
+
const capabilities = this.client.getServerCapabilities();
|
|
547
|
+
this.capabilitiesCache = capabilities;
|
|
548
|
+
const listToolsRes = await this.client.listTools(
|
|
549
|
+
void 0,
|
|
550
|
+
defaultRequestOptions
|
|
551
|
+
);
|
|
552
|
+
this.toolsCache = listToolsRes.tools ?? [];
|
|
553
|
+
logger.debug(`Fetched ${this.toolsCache.length} tools from server`);
|
|
554
|
+
logger.debug("Server capabilities:", capabilities);
|
|
555
|
+
return capabilities;
|
|
556
|
+
}
|
|
557
|
+
/** Lazily expose the cached tools list. */
|
|
558
|
+
get tools() {
|
|
559
|
+
if (!this.toolsCache) {
|
|
560
|
+
throw new Error("MCP client is not initialized; call initialize() first");
|
|
561
|
+
}
|
|
562
|
+
return this.toolsCache;
|
|
563
|
+
}
|
|
564
|
+
/** Call a tool on the server. */
|
|
565
|
+
async callTool(name, args, options) {
|
|
566
|
+
if (!this.client) {
|
|
567
|
+
throw new Error("MCP client is not connected");
|
|
568
|
+
}
|
|
569
|
+
logger.debug(`Calling tool '${name}' with args`, args);
|
|
570
|
+
const res = await this.client.callTool(
|
|
571
|
+
{ name, arguments: args },
|
|
572
|
+
void 0,
|
|
573
|
+
options
|
|
574
|
+
);
|
|
575
|
+
logger.debug(`Tool '${name}' returned`, res);
|
|
576
|
+
return res;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* List resources from the server with optional pagination
|
|
580
|
+
*
|
|
581
|
+
* @param cursor - Optional cursor for pagination
|
|
582
|
+
* @param options - Request options
|
|
583
|
+
* @returns Resource list with optional nextCursor for pagination
|
|
584
|
+
*/
|
|
585
|
+
async listResources(cursor, options) {
|
|
586
|
+
if (!this.client) {
|
|
587
|
+
throw new Error("MCP client is not connected");
|
|
588
|
+
}
|
|
589
|
+
logger.debug("Listing resources", cursor ? `with cursor: ${cursor}` : "");
|
|
590
|
+
return await this.client.listResources({ cursor }, options);
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* List all resources from the server, automatically handling pagination
|
|
594
|
+
*
|
|
595
|
+
* @param options - Request options
|
|
596
|
+
* @returns Complete list of all resources
|
|
597
|
+
*/
|
|
598
|
+
async listAllResources(options) {
|
|
599
|
+
if (!this.client) {
|
|
600
|
+
throw new Error("MCP client is not connected");
|
|
601
|
+
}
|
|
602
|
+
if (!this.capabilitiesCache?.resources) {
|
|
603
|
+
logger.debug("Server does not advertise resources capability, skipping");
|
|
604
|
+
return { resources: [] };
|
|
605
|
+
}
|
|
606
|
+
try {
|
|
607
|
+
logger.debug("Listing all resources (with auto-pagination)");
|
|
608
|
+
const allResources = [];
|
|
609
|
+
let cursor = void 0;
|
|
610
|
+
do {
|
|
611
|
+
const result = await this.client.listResources({ cursor }, options);
|
|
612
|
+
allResources.push(...result.resources || []);
|
|
613
|
+
cursor = result.nextCursor;
|
|
614
|
+
} while (cursor);
|
|
615
|
+
return { resources: allResources };
|
|
616
|
+
} catch (err) {
|
|
617
|
+
if (err.code === -32601) {
|
|
618
|
+
logger.debug("Server advertised resources but method not found");
|
|
619
|
+
return { resources: [] };
|
|
620
|
+
}
|
|
621
|
+
throw err;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* List resource templates from the server
|
|
626
|
+
*
|
|
627
|
+
* @param options - Request options
|
|
628
|
+
* @returns List of available resource templates
|
|
629
|
+
*/
|
|
630
|
+
async listResourceTemplates(options) {
|
|
631
|
+
if (!this.client) {
|
|
632
|
+
throw new Error("MCP client is not connected");
|
|
633
|
+
}
|
|
634
|
+
logger.debug("Listing resource templates");
|
|
635
|
+
return await this.client.listResourceTemplates(void 0, options);
|
|
636
|
+
}
|
|
637
|
+
/** Read a resource by URI. */
|
|
638
|
+
async readResource(uri, options) {
|
|
639
|
+
if (!this.client) {
|
|
640
|
+
throw new Error("MCP client is not connected");
|
|
641
|
+
}
|
|
642
|
+
logger.debug(`Reading resource ${uri}`);
|
|
643
|
+
const res = await this.client.readResource({ uri }, options);
|
|
644
|
+
return res;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Subscribe to resource updates
|
|
648
|
+
*
|
|
649
|
+
* @param uri - URI of the resource to subscribe to
|
|
650
|
+
* @param options - Request options
|
|
651
|
+
*/
|
|
652
|
+
async subscribeToResource(uri, options) {
|
|
653
|
+
if (!this.client) {
|
|
654
|
+
throw new Error("MCP client is not connected");
|
|
655
|
+
}
|
|
656
|
+
logger.debug(`Subscribing to resource: ${uri}`);
|
|
657
|
+
return await this.client.subscribeResource({ uri }, options);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Unsubscribe from resource updates
|
|
661
|
+
*
|
|
662
|
+
* @param uri - URI of the resource to unsubscribe from
|
|
663
|
+
* @param options - Request options
|
|
664
|
+
*/
|
|
665
|
+
async unsubscribeFromResource(uri, options) {
|
|
666
|
+
if (!this.client) {
|
|
667
|
+
throw new Error("MCP client is not connected");
|
|
668
|
+
}
|
|
669
|
+
logger.debug(`Unsubscribing from resource: ${uri}`);
|
|
670
|
+
return await this.client.unsubscribeResource({ uri }, options);
|
|
671
|
+
}
|
|
672
|
+
async listPrompts() {
|
|
673
|
+
if (!this.client) {
|
|
674
|
+
throw new Error("MCP client is not connected");
|
|
675
|
+
}
|
|
676
|
+
if (!this.capabilitiesCache?.prompts) {
|
|
677
|
+
logger.debug("Server does not advertise prompts capability, skipping");
|
|
678
|
+
return { prompts: [] };
|
|
679
|
+
}
|
|
680
|
+
try {
|
|
681
|
+
logger.debug("Listing prompts");
|
|
682
|
+
return await this.client.listPrompts();
|
|
683
|
+
} catch (err) {
|
|
684
|
+
if (err.code === -32601) {
|
|
685
|
+
logger.debug("Server advertised prompts but method not found");
|
|
686
|
+
return { prompts: [] };
|
|
687
|
+
}
|
|
688
|
+
throw err;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
async getPrompt(name, args) {
|
|
692
|
+
if (!this.client) {
|
|
693
|
+
throw new Error("MCP client is not connected");
|
|
694
|
+
}
|
|
695
|
+
logger.debug(`Getting prompt ${name}`);
|
|
696
|
+
return await this.client.getPrompt({ name, arguments: args });
|
|
697
|
+
}
|
|
698
|
+
/** Send a raw request through the client. */
|
|
699
|
+
async request(method, params = null, options) {
|
|
700
|
+
if (!this.client) {
|
|
701
|
+
throw new Error("MCP client is not connected");
|
|
702
|
+
}
|
|
703
|
+
logger.debug(`Sending raw request '${method}' with params`, params);
|
|
704
|
+
return await this.client.request(
|
|
705
|
+
{ method, params: params ?? {} },
|
|
706
|
+
void 0,
|
|
707
|
+
options
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Helper to tear down the client & connection manager safely.
|
|
712
|
+
*/
|
|
713
|
+
async cleanupResources() {
|
|
714
|
+
const issues = [];
|
|
715
|
+
if (this.client) {
|
|
716
|
+
try {
|
|
717
|
+
if (typeof this.client.close === "function") {
|
|
718
|
+
await this.client.close();
|
|
719
|
+
}
|
|
720
|
+
} catch (e) {
|
|
721
|
+
const msg = `Error closing client: ${e}`;
|
|
722
|
+
logger.warn(msg);
|
|
723
|
+
issues.push(msg);
|
|
724
|
+
} finally {
|
|
725
|
+
this.client = null;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
if (this.connectionManager) {
|
|
729
|
+
try {
|
|
730
|
+
await this.connectionManager.stop();
|
|
731
|
+
} catch (e) {
|
|
732
|
+
const msg = `Error stopping connection manager: ${e}`;
|
|
733
|
+
logger.warn(msg);
|
|
734
|
+
issues.push(msg);
|
|
735
|
+
} finally {
|
|
736
|
+
this.connectionManager = null;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
this.toolsCache = null;
|
|
740
|
+
if (issues.length) {
|
|
741
|
+
logger.warn(`Resource cleanup finished with ${issues.length} issue(s)`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
// src/connectors/http.ts
|
|
747
|
+
var HttpConnector = class extends BaseConnector {
|
|
748
|
+
static {
|
|
749
|
+
__name(this, "HttpConnector");
|
|
750
|
+
}
|
|
751
|
+
baseUrl;
|
|
752
|
+
headers;
|
|
753
|
+
timeout;
|
|
754
|
+
sseReadTimeout;
|
|
755
|
+
clientInfo;
|
|
756
|
+
preferSse;
|
|
757
|
+
transportType = null;
|
|
758
|
+
constructor(baseUrl, opts = {}) {
|
|
759
|
+
super(opts);
|
|
760
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
761
|
+
this.headers = { ...opts.headers ?? {} };
|
|
762
|
+
if (opts.authToken) {
|
|
763
|
+
this.headers.Authorization = `Bearer ${opts.authToken}`;
|
|
764
|
+
}
|
|
765
|
+
this.timeout = opts.timeout ?? 3e4;
|
|
766
|
+
this.sseReadTimeout = opts.sseReadTimeout ?? 3e5;
|
|
767
|
+
this.clientInfo = opts.clientInfo ?? {
|
|
768
|
+
name: "http-connector",
|
|
769
|
+
version: "1.0.0"
|
|
770
|
+
};
|
|
771
|
+
this.preferSse = opts.preferSse ?? false;
|
|
772
|
+
}
|
|
773
|
+
/** Establish connection to the MCP implementation via HTTP (streamable or SSE). */
|
|
774
|
+
async connect() {
|
|
775
|
+
if (this.connected) {
|
|
776
|
+
logger.debug("Already connected to MCP implementation");
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
const baseUrl = this.baseUrl;
|
|
780
|
+
if (this.preferSse) {
|
|
781
|
+
logger.debug(`Connecting to MCP implementation via HTTP/SSE: ${baseUrl}`);
|
|
782
|
+
await this.connectWithSse(baseUrl);
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
logger.debug(`Connecting to MCP implementation via HTTP: ${baseUrl}`);
|
|
786
|
+
try {
|
|
787
|
+
logger.info("\u{1F504} Attempting streamable HTTP transport...");
|
|
788
|
+
await this.connectWithStreamableHttp(baseUrl);
|
|
789
|
+
logger.info("\u2705 Successfully connected via streamable HTTP");
|
|
790
|
+
} catch (err) {
|
|
791
|
+
let fallbackReason = "Unknown error";
|
|
792
|
+
let is401Error = false;
|
|
793
|
+
if (err instanceof import_streamableHttp2.StreamableHTTPError) {
|
|
794
|
+
is401Error = err.code === 401;
|
|
795
|
+
if (err.code === 400 && err.message.includes("Missing session ID")) {
|
|
796
|
+
fallbackReason = "Server requires session ID (FastMCP compatibility) - using SSE transport";
|
|
797
|
+
logger.warn(`\u26A0\uFE0F ${fallbackReason}`);
|
|
798
|
+
} else if (err.code === 404 || err.code === 405) {
|
|
799
|
+
fallbackReason = `Server returned ${err.code} - server likely doesn't support streamable HTTP`;
|
|
800
|
+
logger.debug(fallbackReason);
|
|
801
|
+
} else {
|
|
802
|
+
fallbackReason = `Server returned ${err.code}: ${err.message}`;
|
|
803
|
+
logger.debug(fallbackReason);
|
|
804
|
+
}
|
|
805
|
+
} else if (err instanceof Error) {
|
|
806
|
+
const errorStr = err.toString();
|
|
807
|
+
const errorMsg = err.message || "";
|
|
808
|
+
is401Error = errorStr.includes("401") || errorMsg.includes("Unauthorized");
|
|
809
|
+
if (errorStr.includes("Missing session ID") || errorStr.includes("Bad Request: Missing session ID") || errorMsg.includes("FastMCP session ID error")) {
|
|
810
|
+
fallbackReason = "Server requires session ID (FastMCP compatibility) - using SSE transport";
|
|
811
|
+
logger.warn(`\u26A0\uFE0F ${fallbackReason}`);
|
|
812
|
+
} else if (errorStr.includes("405 Method Not Allowed") || errorStr.includes("404 Not Found")) {
|
|
813
|
+
fallbackReason = "Server doesn't support streamable HTTP (405/404)";
|
|
814
|
+
logger.debug(fallbackReason);
|
|
815
|
+
} else {
|
|
816
|
+
fallbackReason = `Streamable HTTP failed: ${err.message}`;
|
|
817
|
+
logger.debug(fallbackReason);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
if (is401Error) {
|
|
821
|
+
logger.info("Authentication required - skipping SSE fallback");
|
|
822
|
+
await this.cleanupResources();
|
|
823
|
+
const authError = new Error("Authentication required");
|
|
824
|
+
authError.code = 401;
|
|
825
|
+
throw authError;
|
|
826
|
+
}
|
|
827
|
+
logger.info("\u{1F504} Falling back to SSE transport...");
|
|
828
|
+
try {
|
|
829
|
+
await this.connectWithSse(baseUrl);
|
|
830
|
+
} catch (sseErr) {
|
|
831
|
+
logger.error(`Failed to connect with both transports:`);
|
|
832
|
+
logger.error(` Streamable HTTP: ${fallbackReason}`);
|
|
833
|
+
logger.error(` SSE: ${sseErr}`);
|
|
834
|
+
await this.cleanupResources();
|
|
835
|
+
const sseIs401 = sseErr?.message?.includes("401") || sseErr?.message?.includes("Unauthorized");
|
|
836
|
+
if (sseIs401) {
|
|
837
|
+
const authError = new Error("Authentication required");
|
|
838
|
+
authError.code = 401;
|
|
839
|
+
throw authError;
|
|
840
|
+
}
|
|
841
|
+
throw new Error(
|
|
842
|
+
"Could not connect to server with any available transport"
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
async connectWithStreamableHttp(baseUrl) {
|
|
848
|
+
try {
|
|
849
|
+
this.connectionManager = new StreamableHttpConnectionManager(baseUrl, {
|
|
850
|
+
authProvider: this.opts.authProvider,
|
|
851
|
+
// ← Pass OAuth provider to SDK
|
|
852
|
+
requestInit: {
|
|
853
|
+
headers: this.headers
|
|
854
|
+
},
|
|
855
|
+
// Pass through timeout and other options
|
|
856
|
+
reconnectionOptions: {
|
|
857
|
+
maxReconnectionDelay: 3e4,
|
|
858
|
+
initialReconnectionDelay: 1e3,
|
|
859
|
+
reconnectionDelayGrowFactor: 1.5,
|
|
860
|
+
maxRetries: 2
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
const transport = await this.connectionManager.start();
|
|
864
|
+
this.client = new import_client.Client(this.clientInfo, this.opts.clientOptions);
|
|
865
|
+
try {
|
|
866
|
+
await this.client.connect(transport);
|
|
867
|
+
} catch (connectErr) {
|
|
868
|
+
if (connectErr instanceof Error) {
|
|
869
|
+
const errMsg = connectErr.message || connectErr.toString();
|
|
870
|
+
if (errMsg.includes("Missing session ID") || errMsg.includes("Bad Request: Missing session ID")) {
|
|
871
|
+
const wrappedError = new Error(
|
|
872
|
+
`FastMCP session ID error: ${errMsg}`
|
|
873
|
+
);
|
|
874
|
+
wrappedError.cause = connectErr;
|
|
875
|
+
throw wrappedError;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
throw connectErr;
|
|
879
|
+
}
|
|
880
|
+
this.connected = true;
|
|
881
|
+
this.transportType = "streamable-http";
|
|
882
|
+
logger.debug(
|
|
883
|
+
`Successfully connected to MCP implementation via streamable HTTP: ${baseUrl}`
|
|
884
|
+
);
|
|
885
|
+
} catch (err) {
|
|
886
|
+
await this.cleanupResources();
|
|
887
|
+
throw err;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
async connectWithSse(baseUrl) {
|
|
891
|
+
try {
|
|
892
|
+
this.connectionManager = new SseConnectionManager(baseUrl, {
|
|
893
|
+
requestInit: {
|
|
894
|
+
headers: this.headers
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
const transport = await this.connectionManager.start();
|
|
898
|
+
this.client = new import_client.Client(this.clientInfo, this.opts.clientOptions);
|
|
899
|
+
await this.client.connect(transport);
|
|
900
|
+
this.connected = true;
|
|
901
|
+
this.transportType = "sse";
|
|
902
|
+
logger.debug(
|
|
903
|
+
`Successfully connected to MCP implementation via HTTP/SSE: ${baseUrl}`
|
|
904
|
+
);
|
|
905
|
+
} catch (err) {
|
|
906
|
+
await this.cleanupResources();
|
|
907
|
+
throw err;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
get publicIdentifier() {
|
|
911
|
+
return {
|
|
912
|
+
type: "http",
|
|
913
|
+
url: this.baseUrl,
|
|
914
|
+
transport: this.transportType || "unknown"
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Get the transport type being used (streamable-http or sse)
|
|
919
|
+
*/
|
|
920
|
+
getTransportType() {
|
|
921
|
+
return this.transportType;
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
// src/connectors/websocket.ts
|
|
926
|
+
var import_uuid = require("uuid");
|
|
927
|
+
|
|
928
|
+
// src/task_managers/websocket.ts
|
|
929
|
+
var import_ws = __toESM(require("ws"), 1);
|
|
930
|
+
var WebSocketConnectionManager = class extends ConnectionManager {
|
|
931
|
+
static {
|
|
932
|
+
__name(this, "WebSocketConnectionManager");
|
|
933
|
+
}
|
|
934
|
+
url;
|
|
935
|
+
headers;
|
|
936
|
+
_ws = null;
|
|
937
|
+
/**
|
|
938
|
+
* @param url The WebSocket URL to connect to.
|
|
939
|
+
* @param headers Optional headers to include in the connection handshake.
|
|
940
|
+
*/
|
|
941
|
+
constructor(url, headers = {}) {
|
|
942
|
+
super();
|
|
943
|
+
this.url = url;
|
|
944
|
+
this.headers = headers;
|
|
945
|
+
}
|
|
946
|
+
/** Establish a WebSocket connection and wait until it is open. */
|
|
947
|
+
async establishConnection() {
|
|
948
|
+
logger.debug(`Connecting to WebSocket: ${this.url}`);
|
|
949
|
+
return new Promise((resolve, reject) => {
|
|
950
|
+
const ws = new import_ws.default(this.url, {
|
|
951
|
+
headers: this.headers
|
|
952
|
+
});
|
|
953
|
+
this._ws = ws;
|
|
954
|
+
const onOpen = /* @__PURE__ */ __name(() => {
|
|
955
|
+
cleanup();
|
|
956
|
+
logger.debug("WebSocket connected successfully");
|
|
957
|
+
resolve(ws);
|
|
958
|
+
}, "onOpen");
|
|
959
|
+
const onError = /* @__PURE__ */ __name((err) => {
|
|
960
|
+
cleanup();
|
|
961
|
+
logger.error(`Failed to connect to WebSocket: ${err}`);
|
|
962
|
+
reject(err);
|
|
963
|
+
}, "onError");
|
|
964
|
+
const cleanup = /* @__PURE__ */ __name(() => {
|
|
965
|
+
ws.off("open", onOpen);
|
|
966
|
+
ws.off("error", onError);
|
|
967
|
+
}, "cleanup");
|
|
968
|
+
ws.on("open", onOpen);
|
|
969
|
+
ws.on("error", onError);
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
/** Cleanly close the WebSocket connection. */
|
|
973
|
+
async closeConnection(connection) {
|
|
974
|
+
logger.debug("Closing WebSocket connection");
|
|
975
|
+
return new Promise((resolve) => {
|
|
976
|
+
const onClose = /* @__PURE__ */ __name(() => {
|
|
977
|
+
connection.off("close", onClose);
|
|
978
|
+
this._ws = null;
|
|
979
|
+
resolve();
|
|
980
|
+
}, "onClose");
|
|
981
|
+
if (connection.readyState === import_ws.default.CLOSED) {
|
|
982
|
+
onClose();
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
connection.on("close", onClose);
|
|
986
|
+
try {
|
|
987
|
+
connection.close();
|
|
988
|
+
} catch (e) {
|
|
989
|
+
logger.warn(`Error closing WebSocket connection: ${e}`);
|
|
990
|
+
onClose();
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
// src/connectors/websocket.ts
|
|
997
|
+
var WebSocketConnector = class extends BaseConnector {
|
|
998
|
+
static {
|
|
999
|
+
__name(this, "WebSocketConnector");
|
|
1000
|
+
}
|
|
1001
|
+
url;
|
|
1002
|
+
headers;
|
|
1003
|
+
connectionManager = null;
|
|
1004
|
+
ws = null;
|
|
1005
|
+
receiverTask = null;
|
|
1006
|
+
pending = /* @__PURE__ */ new Map();
|
|
1007
|
+
toolsCache = null;
|
|
1008
|
+
constructor(url, opts = {}) {
|
|
1009
|
+
super();
|
|
1010
|
+
this.url = url;
|
|
1011
|
+
this.headers = { ...opts.headers ?? {} };
|
|
1012
|
+
if (opts.authToken) this.headers.Authorization = `Bearer ${opts.authToken}`;
|
|
1013
|
+
}
|
|
1014
|
+
async connect() {
|
|
1015
|
+
if (this.connected) {
|
|
1016
|
+
logger.debug("Already connected to MCP implementation");
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
logger.debug(`Connecting via WebSocket: ${this.url}`);
|
|
1020
|
+
try {
|
|
1021
|
+
this.connectionManager = new WebSocketConnectionManager(
|
|
1022
|
+
this.url,
|
|
1023
|
+
this.headers
|
|
1024
|
+
);
|
|
1025
|
+
this.ws = await this.connectionManager.start();
|
|
1026
|
+
this.receiverTask = this.receiveLoop();
|
|
1027
|
+
this.connected = true;
|
|
1028
|
+
logger.debug("WebSocket connected successfully");
|
|
1029
|
+
} catch (e) {
|
|
1030
|
+
logger.error(`Failed to connect: ${e}`);
|
|
1031
|
+
await this.cleanupResources();
|
|
1032
|
+
throw e;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
async disconnect() {
|
|
1036
|
+
if (!this.connected) {
|
|
1037
|
+
logger.debug("Not connected to MCP implementation");
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
logger.debug("Disconnecting \u2026");
|
|
1041
|
+
await this.cleanupResources();
|
|
1042
|
+
this.connected = false;
|
|
1043
|
+
}
|
|
1044
|
+
sendRequest(method, params = null) {
|
|
1045
|
+
if (!this.ws) throw new Error("WebSocket is not connected");
|
|
1046
|
+
const id = (0, import_uuid.v4)();
|
|
1047
|
+
const payload = JSON.stringify({ id, method, params: params ?? {} });
|
|
1048
|
+
return new Promise((resolve, reject) => {
|
|
1049
|
+
this.pending.set(id, { resolve, reject });
|
|
1050
|
+
this.ws.send(payload, (err) => {
|
|
1051
|
+
if (err) {
|
|
1052
|
+
this.pending.delete(id);
|
|
1053
|
+
reject(err);
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
async receiveLoop() {
|
|
1059
|
+
if (!this.ws) return;
|
|
1060
|
+
const socket = this.ws;
|
|
1061
|
+
const onMessage = /* @__PURE__ */ __name((msg) => {
|
|
1062
|
+
let data;
|
|
1063
|
+
try {
|
|
1064
|
+
data = JSON.parse(msg.data ?? msg);
|
|
1065
|
+
} catch (e) {
|
|
1066
|
+
logger.warn("Received non\u2011JSON frame", e);
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
const id = data.id;
|
|
1070
|
+
if (id && this.pending.has(id)) {
|
|
1071
|
+
const { resolve, reject } = this.pending.get(id);
|
|
1072
|
+
this.pending.delete(id);
|
|
1073
|
+
if ("result" in data) resolve(data.result);
|
|
1074
|
+
else if ("error" in data) reject(data.error);
|
|
1075
|
+
} else {
|
|
1076
|
+
logger.debug("Received unsolicited message", data);
|
|
1077
|
+
}
|
|
1078
|
+
}, "onMessage");
|
|
1079
|
+
if (socket.addEventListener) {
|
|
1080
|
+
socket.addEventListener("message", onMessage);
|
|
1081
|
+
} else {
|
|
1082
|
+
socket.on("message", onMessage);
|
|
1083
|
+
}
|
|
1084
|
+
return new Promise((resolve) => {
|
|
1085
|
+
const onClose = /* @__PURE__ */ __name(() => {
|
|
1086
|
+
if (socket.removeEventListener) {
|
|
1087
|
+
socket.removeEventListener("message", onMessage);
|
|
1088
|
+
} else {
|
|
1089
|
+
socket.off("message", onMessage);
|
|
1090
|
+
}
|
|
1091
|
+
this.rejectAll(new Error("WebSocket closed"));
|
|
1092
|
+
resolve();
|
|
1093
|
+
}, "onClose");
|
|
1094
|
+
if (socket.addEventListener) {
|
|
1095
|
+
socket.addEventListener("close", onClose);
|
|
1096
|
+
} else {
|
|
1097
|
+
socket.on("close", onClose);
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
rejectAll(err) {
|
|
1102
|
+
for (const { reject } of this.pending.values()) reject(err);
|
|
1103
|
+
this.pending.clear();
|
|
1104
|
+
}
|
|
1105
|
+
async initialize() {
|
|
1106
|
+
logger.debug("Initializing MCP session over WebSocket");
|
|
1107
|
+
const result = await this.sendRequest("initialize");
|
|
1108
|
+
const toolsList = await this.listTools();
|
|
1109
|
+
this.toolsCache = toolsList.map((t) => t);
|
|
1110
|
+
logger.debug(`Initialized with ${this.toolsCache.length} tools`);
|
|
1111
|
+
return result;
|
|
1112
|
+
}
|
|
1113
|
+
async listTools() {
|
|
1114
|
+
const res = await this.sendRequest("tools/list");
|
|
1115
|
+
return res.tools ?? [];
|
|
1116
|
+
}
|
|
1117
|
+
async callTool(name, args) {
|
|
1118
|
+
return await this.sendRequest("tools/call", { name, arguments: args });
|
|
1119
|
+
}
|
|
1120
|
+
async listResources() {
|
|
1121
|
+
const resources = await this.sendRequest("resources/list");
|
|
1122
|
+
return { resources: Array.isArray(resources) ? resources : [] };
|
|
1123
|
+
}
|
|
1124
|
+
async readResource(uri) {
|
|
1125
|
+
const res = await this.sendRequest("resources/read", { uri });
|
|
1126
|
+
return res;
|
|
1127
|
+
}
|
|
1128
|
+
async request(method, params = null) {
|
|
1129
|
+
return await this.sendRequest(method, params);
|
|
1130
|
+
}
|
|
1131
|
+
get tools() {
|
|
1132
|
+
if (!this.toolsCache) throw new Error("MCP client is not initialized");
|
|
1133
|
+
return this.toolsCache;
|
|
1134
|
+
}
|
|
1135
|
+
async cleanupResources() {
|
|
1136
|
+
if (this.receiverTask) await this.receiverTask.catch(() => {
|
|
1137
|
+
});
|
|
1138
|
+
this.receiverTask = null;
|
|
1139
|
+
this.rejectAll(new Error("WebSocket disconnected"));
|
|
1140
|
+
if (this.connectionManager) {
|
|
1141
|
+
await this.connectionManager.stop();
|
|
1142
|
+
this.connectionManager = null;
|
|
1143
|
+
this.ws = null;
|
|
1144
|
+
}
|
|
1145
|
+
this.toolsCache = null;
|
|
1146
|
+
}
|
|
1147
|
+
get publicIdentifier() {
|
|
1148
|
+
return {
|
|
1149
|
+
type: "websocket",
|
|
1150
|
+
url: this.url
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
// src/session.ts
|
|
1156
|
+
var MCPSession = class {
|
|
1157
|
+
static {
|
|
1158
|
+
__name(this, "MCPSession");
|
|
1159
|
+
}
|
|
1160
|
+
connector;
|
|
1161
|
+
autoConnect;
|
|
1162
|
+
constructor(connector, autoConnect = true) {
|
|
1163
|
+
this.connector = connector;
|
|
1164
|
+
this.autoConnect = autoConnect;
|
|
1165
|
+
}
|
|
1166
|
+
async connect() {
|
|
1167
|
+
await this.connector.connect();
|
|
1168
|
+
}
|
|
1169
|
+
async disconnect() {
|
|
1170
|
+
await this.connector.disconnect();
|
|
1171
|
+
}
|
|
1172
|
+
async initialize() {
|
|
1173
|
+
if (!this.isConnected && this.autoConnect) {
|
|
1174
|
+
await this.connect();
|
|
1175
|
+
}
|
|
1176
|
+
await this.connector.initialize();
|
|
1177
|
+
}
|
|
1178
|
+
get isConnected() {
|
|
1179
|
+
return this.connector && this.connector.isClientConnected;
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
// src/client/base.ts
|
|
1184
|
+
var BaseMCPClient = class {
|
|
1185
|
+
static {
|
|
1186
|
+
__name(this, "BaseMCPClient");
|
|
1187
|
+
}
|
|
1188
|
+
config = {};
|
|
1189
|
+
sessions = {};
|
|
1190
|
+
activeSessions = [];
|
|
1191
|
+
constructor(config) {
|
|
1192
|
+
if (config) {
|
|
1193
|
+
this.config = config;
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
static fromDict(_cfg) {
|
|
1197
|
+
throw new Error("fromDict must be implemented by concrete class");
|
|
1198
|
+
}
|
|
1199
|
+
addServer(name, serverConfig) {
|
|
1200
|
+
this.config.mcpServers = this.config.mcpServers || {};
|
|
1201
|
+
this.config.mcpServers[name] = serverConfig;
|
|
1202
|
+
}
|
|
1203
|
+
removeServer(name) {
|
|
1204
|
+
if (this.config.mcpServers?.[name]) {
|
|
1205
|
+
delete this.config.mcpServers[name];
|
|
1206
|
+
this.activeSessions = this.activeSessions.filter((n) => n !== name);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
getServerNames() {
|
|
1210
|
+
return Object.keys(this.config.mcpServers ?? {});
|
|
1211
|
+
}
|
|
1212
|
+
getServerConfig(name) {
|
|
1213
|
+
return this.config.mcpServers?.[name];
|
|
1214
|
+
}
|
|
1215
|
+
getConfig() {
|
|
1216
|
+
return this.config ?? {};
|
|
1217
|
+
}
|
|
1218
|
+
async createSession(serverName, autoInitialize = true) {
|
|
1219
|
+
const servers = this.config.mcpServers ?? {};
|
|
1220
|
+
if (Object.keys(servers).length === 0) {
|
|
1221
|
+
logger.warn("No MCP servers defined in config");
|
|
1222
|
+
}
|
|
1223
|
+
if (!servers[serverName]) {
|
|
1224
|
+
throw new Error(`Server '${serverName}' not found in config`);
|
|
1225
|
+
}
|
|
1226
|
+
const connector = this.createConnectorFromConfig(servers[serverName]);
|
|
1227
|
+
const session = new MCPSession(connector);
|
|
1228
|
+
if (autoInitialize) {
|
|
1229
|
+
await session.initialize();
|
|
1230
|
+
}
|
|
1231
|
+
this.sessions[serverName] = session;
|
|
1232
|
+
if (!this.activeSessions.includes(serverName)) {
|
|
1233
|
+
this.activeSessions.push(serverName);
|
|
1234
|
+
}
|
|
1235
|
+
return session;
|
|
1236
|
+
}
|
|
1237
|
+
async createAllSessions(autoInitialize = true) {
|
|
1238
|
+
const servers = this.config.mcpServers ?? {};
|
|
1239
|
+
if (Object.keys(servers).length === 0) {
|
|
1240
|
+
logger.warn("No MCP servers defined in config");
|
|
1241
|
+
}
|
|
1242
|
+
for (const name of Object.keys(servers)) {
|
|
1243
|
+
await this.createSession(name, autoInitialize);
|
|
1244
|
+
}
|
|
1245
|
+
return this.sessions;
|
|
1246
|
+
}
|
|
1247
|
+
getSession(serverName) {
|
|
1248
|
+
const session = this.sessions[serverName];
|
|
1249
|
+
if (!session) {
|
|
1250
|
+
return null;
|
|
1251
|
+
}
|
|
1252
|
+
return session;
|
|
1253
|
+
}
|
|
1254
|
+
getAllActiveSessions() {
|
|
1255
|
+
return Object.fromEntries(
|
|
1256
|
+
this.activeSessions.map((n) => [n, this.sessions[n]])
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
async closeSession(serverName) {
|
|
1260
|
+
const session = this.sessions[serverName];
|
|
1261
|
+
if (!session) {
|
|
1262
|
+
logger.warn(
|
|
1263
|
+
`No session exists for server ${serverName}, nothing to close`
|
|
1264
|
+
);
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
try {
|
|
1268
|
+
logger.debug(`Closing session for server ${serverName}`);
|
|
1269
|
+
await session.disconnect();
|
|
1270
|
+
} catch (e) {
|
|
1271
|
+
logger.error(`Error closing session for server '${serverName}': ${e}`);
|
|
1272
|
+
} finally {
|
|
1273
|
+
delete this.sessions[serverName];
|
|
1274
|
+
this.activeSessions = this.activeSessions.filter((n) => n !== serverName);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
async closeAllSessions() {
|
|
1278
|
+
const serverNames = Object.keys(this.sessions);
|
|
1279
|
+
const errors = [];
|
|
1280
|
+
for (const serverName of serverNames) {
|
|
1281
|
+
try {
|
|
1282
|
+
logger.debug(`Closing session for server ${serverName}`);
|
|
1283
|
+
await this.closeSession(serverName);
|
|
1284
|
+
} catch (e) {
|
|
1285
|
+
const errorMsg = `Failed to close session for server '${serverName}': ${e}`;
|
|
1286
|
+
logger.error(errorMsg);
|
|
1287
|
+
errors.push(errorMsg);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
if (errors.length) {
|
|
1291
|
+
logger.error(
|
|
1292
|
+
`Encountered ${errors.length} errors while closing sessions`
|
|
1293
|
+
);
|
|
1294
|
+
} else {
|
|
1295
|
+
logger.debug("All sessions closed successfully");
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
};
|
|
1299
|
+
|
|
1300
|
+
// src/client/browser.ts
|
|
1301
|
+
var BrowserMCPClient = class _BrowserMCPClient extends BaseMCPClient {
|
|
1302
|
+
static {
|
|
1303
|
+
__name(this, "BrowserMCPClient");
|
|
1304
|
+
}
|
|
1305
|
+
constructor(config) {
|
|
1306
|
+
super(config);
|
|
1307
|
+
}
|
|
1308
|
+
static fromDict(cfg) {
|
|
1309
|
+
return new _BrowserMCPClient(cfg);
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* Create a connector from server configuration (Browser version)
|
|
1313
|
+
* Supports HTTP and WebSocket connectors only
|
|
1314
|
+
*/
|
|
1315
|
+
createConnectorFromConfig(serverConfig) {
|
|
1316
|
+
const { url, transport, headers, authToken, authProvider } = serverConfig;
|
|
1317
|
+
if (!url) {
|
|
1318
|
+
throw new Error("Server URL is required");
|
|
1319
|
+
}
|
|
1320
|
+
const connectorOptions = {
|
|
1321
|
+
headers,
|
|
1322
|
+
authToken,
|
|
1323
|
+
authProvider
|
|
1324
|
+
// ← Pass OAuth provider to connector
|
|
1325
|
+
};
|
|
1326
|
+
if (transport === "websocket" || url.startsWith("ws://") || url.startsWith("wss://")) {
|
|
1327
|
+
return new WebSocketConnector(url, connectorOptions);
|
|
1328
|
+
} else if (transport === "http" || url.startsWith("http://") || url.startsWith("https://")) {
|
|
1329
|
+
return new HttpConnector(url, connectorOptions);
|
|
1330
|
+
} else {
|
|
1331
|
+
return new HttpConnector(url, connectorOptions);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
};
|
|
41
1335
|
|
|
42
1336
|
// src/auth/browser-provider.ts
|
|
43
1337
|
var import_strict_url_sanitise = require("strict-url-sanitise");
|
|
@@ -88,7 +1382,10 @@ var BrowserOAuthClientProvider = class {
|
|
|
88
1382
|
try {
|
|
89
1383
|
return JSON.parse(data);
|
|
90
1384
|
} catch (e) {
|
|
91
|
-
console.warn(
|
|
1385
|
+
console.warn(
|
|
1386
|
+
`[${this.storageKeyPrefix}] Failed to parse client information:`,
|
|
1387
|
+
e
|
|
1388
|
+
);
|
|
92
1389
|
localStorage.removeItem(key);
|
|
93
1390
|
return void 0;
|
|
94
1391
|
}
|
|
@@ -170,7 +1467,11 @@ var BrowserOAuthClientProvider = class {
|
|
|
170
1467
|
const sanitizedAuthUrl = await this.prepareAuthorizationUrl(authorizationUrl);
|
|
171
1468
|
const popupFeatures = "width=600,height=700,resizable=yes,scrollbars=yes,status=yes";
|
|
172
1469
|
try {
|
|
173
|
-
const popup = window.open(
|
|
1470
|
+
const popup = window.open(
|
|
1471
|
+
sanitizedAuthUrl,
|
|
1472
|
+
`mcp_auth_${this.serverUrlHash}`,
|
|
1473
|
+
popupFeatures
|
|
1474
|
+
);
|
|
174
1475
|
if (this.onPopupWindow) {
|
|
175
1476
|
this.onPopupWindow(sanitizedAuthUrl, popupFeatures, popup);
|
|
176
1477
|
}
|
|
@@ -180,10 +1481,15 @@ var BrowserOAuthClientProvider = class {
|
|
|
180
1481
|
);
|
|
181
1482
|
} else {
|
|
182
1483
|
popup.focus();
|
|
183
|
-
console.info(
|
|
1484
|
+
console.info(
|
|
1485
|
+
`[${this.storageKeyPrefix}] Redirecting to authorization URL in popup.`
|
|
1486
|
+
);
|
|
184
1487
|
}
|
|
185
1488
|
} catch (e) {
|
|
186
|
-
console.error(
|
|
1489
|
+
console.error(
|
|
1490
|
+
`[${this.storageKeyPrefix}] Error opening popup window:`,
|
|
1491
|
+
e
|
|
1492
|
+
);
|
|
187
1493
|
}
|
|
188
1494
|
}
|
|
189
1495
|
// --- Helper Methods ---
|
|
@@ -214,7 +1520,10 @@ var BrowserOAuthClientProvider = class {
|
|
|
214
1520
|
}
|
|
215
1521
|
}
|
|
216
1522
|
} catch (e) {
|
|
217
|
-
console.warn(
|
|
1523
|
+
console.warn(
|
|
1524
|
+
`[${this.storageKeyPrefix}] Error parsing state key ${key} during clearStorage:`,
|
|
1525
|
+
e
|
|
1526
|
+
);
|
|
218
1527
|
}
|
|
219
1528
|
}
|
|
220
1529
|
}
|
|
@@ -257,7 +1566,9 @@ function useMcp(options) {
|
|
|
257
1566
|
enabled = true,
|
|
258
1567
|
clientName,
|
|
259
1568
|
clientUri,
|
|
260
|
-
callbackUrl = typeof window !== "undefined" ? (0, import_strict_url_sanitise2.sanitizeUrl)(
|
|
1569
|
+
callbackUrl = typeof window !== "undefined" ? (0, import_strict_url_sanitise2.sanitizeUrl)(
|
|
1570
|
+
new URL("/oauth/callback", window.location.origin).toString()
|
|
1571
|
+
) : "/oauth/callback",
|
|
261
1572
|
storageKeyPrefix = "mcp:auth",
|
|
262
1573
|
clientConfig = {},
|
|
263
1574
|
customHeaders = {},
|
|
@@ -281,7 +1592,6 @@ function useMcp(options) {
|
|
|
281
1592
|
const [log, setLog] = (0, import_react.useState)([]);
|
|
282
1593
|
const [authUrl, setAuthUrl] = (0, import_react.useState)(void 0);
|
|
283
1594
|
const clientRef = (0, import_react.useRef)(null);
|
|
284
|
-
const transportRef = (0, import_react.useRef)(null);
|
|
285
1595
|
const authProviderRef = (0, import_react.useRef)(null);
|
|
286
1596
|
const connectingRef = (0, import_react.useRef)(false);
|
|
287
1597
|
const isMountedRef = (0, import_react.useRef)(true);
|
|
@@ -299,7 +1609,10 @@ function useMcp(options) {
|
|
|
299
1609
|
const fullMessage = args.length > 0 ? `${message} ${args.map((arg) => JSON.stringify(arg)).join(" ")}` : message;
|
|
300
1610
|
console[level](`[useMcp] ${fullMessage}`);
|
|
301
1611
|
if (isMountedRef.current) {
|
|
302
|
-
setLog((prevLog) => [
|
|
1612
|
+
setLog((prevLog) => [
|
|
1613
|
+
...prevLog.slice(-100),
|
|
1614
|
+
{ level, message: fullMessage, timestamp: Date.now() }
|
|
1615
|
+
]);
|
|
303
1616
|
}
|
|
304
1617
|
},
|
|
305
1618
|
[]
|
|
@@ -310,9 +1623,15 @@ function useMcp(options) {
|
|
|
310
1623
|
connectingRef.current = false;
|
|
311
1624
|
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
312
1625
|
authTimeoutRef.current = null;
|
|
313
|
-
|
|
1626
|
+
if (clientRef.current) {
|
|
1627
|
+
try {
|
|
1628
|
+
const serverName = "inspector-server";
|
|
1629
|
+
await clientRef.current.closeSession(serverName);
|
|
1630
|
+
} catch (err) {
|
|
1631
|
+
if (!quiet) addLog("warn", "Error closing session:", err);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
314
1634
|
clientRef.current = null;
|
|
315
|
-
transportRef.current = null;
|
|
316
1635
|
if (isMountedRef.current && !quiet) {
|
|
317
1636
|
setState("discovering");
|
|
318
1637
|
setTools([]);
|
|
@@ -322,14 +1641,6 @@ function useMcp(options) {
|
|
|
322
1641
|
setError(void 0);
|
|
323
1642
|
setAuthUrl(void 0);
|
|
324
1643
|
}
|
|
325
|
-
if (transport) {
|
|
326
|
-
try {
|
|
327
|
-
await transport.close();
|
|
328
|
-
if (!quiet) addLog("debug", "Transport closed");
|
|
329
|
-
} catch (err) {
|
|
330
|
-
if (!quiet) addLog("warn", "Error closing transport:", err);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
1644
|
},
|
|
334
1645
|
[addLog]
|
|
335
1646
|
);
|
|
@@ -342,7 +1653,11 @@ function useMcp(options) {
|
|
|
342
1653
|
const manualUrl = authProviderRef.current?.getLastAttemptedAuthUrl();
|
|
343
1654
|
if (manualUrl) {
|
|
344
1655
|
setAuthUrl(manualUrl);
|
|
345
|
-
addLog(
|
|
1656
|
+
addLog(
|
|
1657
|
+
"info",
|
|
1658
|
+
"Manual authentication URL may be available.",
|
|
1659
|
+
manualUrl
|
|
1660
|
+
);
|
|
346
1661
|
}
|
|
347
1662
|
}
|
|
348
1663
|
connectingRef.current = false;
|
|
@@ -351,7 +1666,10 @@ function useMcp(options) {
|
|
|
351
1666
|
);
|
|
352
1667
|
const connect = (0, import_react.useCallback)(async () => {
|
|
353
1668
|
if (!enabled || !url) {
|
|
354
|
-
addLog(
|
|
1669
|
+
addLog(
|
|
1670
|
+
"debug",
|
|
1671
|
+
enabled ? "No server URL provided, skipping connection." : "Connection disabled via enabled flag."
|
|
1672
|
+
);
|
|
355
1673
|
return;
|
|
356
1674
|
}
|
|
357
1675
|
if (connectingRef.current) {
|
|
@@ -368,7 +1686,10 @@ function useMcp(options) {
|
|
|
368
1686
|
setAuthUrl(void 0);
|
|
369
1687
|
successfulTransportRef.current = null;
|
|
370
1688
|
setState("discovering");
|
|
371
|
-
addLog(
|
|
1689
|
+
addLog(
|
|
1690
|
+
"info",
|
|
1691
|
+
`Connecting attempt #${connectAttemptRef.current} to ${url}...`
|
|
1692
|
+
);
|
|
372
1693
|
if (!authProviderRef.current) {
|
|
373
1694
|
authProviderRef.current = new BrowserOAuthClientProvider(url, {
|
|
374
1695
|
storageKeyPrefix,
|
|
@@ -381,187 +1702,63 @@ function useMcp(options) {
|
|
|
381
1702
|
addLog("debug", "BrowserOAuthClientProvider initialized in connect.");
|
|
382
1703
|
}
|
|
383
1704
|
if (!clientRef.current) {
|
|
384
|
-
clientRef.current = new
|
|
385
|
-
|
|
386
|
-
{ capabilities: {} }
|
|
387
|
-
);
|
|
388
|
-
addLog("debug", "MCP Client initialized in connect.");
|
|
1705
|
+
clientRef.current = new BrowserMCPClient();
|
|
1706
|
+
addLog("debug", "BrowserMCPClient initialized in connect.");
|
|
389
1707
|
}
|
|
390
1708
|
const tryConnectWithTransport = /* @__PURE__ */ __name(async (transportTypeParam, isAuthRetry = false) => {
|
|
391
|
-
addLog(
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
let transportInstance;
|
|
1709
|
+
addLog(
|
|
1710
|
+
"info",
|
|
1711
|
+
`Attempting connection with transport: ${transportTypeParam}`
|
|
1712
|
+
);
|
|
396
1713
|
try {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
transportRef.current = null;
|
|
402
|
-
}
|
|
403
|
-
const commonOptions = {
|
|
404
|
-
authProvider: authProviderRef.current,
|
|
405
|
-
requestInit: {
|
|
406
|
-
headers: {
|
|
407
|
-
Accept: "application/json, text/event-stream",
|
|
408
|
-
...customHeaders
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
// Note: The MCP SDK's SSEClientTransport doesn't expose timeout configuration directly
|
|
412
|
-
// Timeout handling is managed by the underlying EventSource and browser/Node.js fetch implementations
|
|
413
|
-
// The timeout and sseReadTimeout options are preserved for future use or custom implementations
|
|
1714
|
+
const serverName = "inspector-server";
|
|
1715
|
+
const serverConfig = {
|
|
1716
|
+
url,
|
|
1717
|
+
transport: transportTypeParam === "sse" ? "http" : transportTypeParam
|
|
414
1718
|
};
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
addLog("debug", `Creating ${transportTypeParam.toUpperCase()} transport for URL: ${targetUrl.toString()}`);
|
|
418
|
-
if (transportTypeParam === "http") {
|
|
419
|
-
addLog("debug", "Creating StreamableHTTPClientTransport...");
|
|
420
|
-
transportInstance = new import_streamableHttp.StreamableHTTPClientTransport(targetUrl, commonOptions);
|
|
421
|
-
addLog("debug", "StreamableHTTPClientTransport created successfully");
|
|
422
|
-
} else {
|
|
423
|
-
addLog("debug", "Creating SSEClientTransport...");
|
|
424
|
-
transportInstance = new import_sse.SSEClientTransport(targetUrl, commonOptions);
|
|
425
|
-
addLog("debug", "SSEClientTransport created successfully");
|
|
426
|
-
}
|
|
427
|
-
transportRef.current = transportInstance;
|
|
428
|
-
addLog("debug", `${transportTypeParam.toUpperCase()} transport created and assigned to ref.`);
|
|
429
|
-
} catch (err) {
|
|
430
|
-
failConnection(
|
|
431
|
-
`Failed to create ${transportTypeParam.toUpperCase()} transport: ${err instanceof Error ? err.message : String(err)}`,
|
|
432
|
-
err instanceof Error ? err : void 0
|
|
433
|
-
);
|
|
434
|
-
return "failed";
|
|
435
|
-
}
|
|
436
|
-
transportInstance.onmessage = (message) => {
|
|
437
|
-
addLog("debug", `[Transport] Received: ${JSON.stringify(message)}`);
|
|
438
|
-
clientRef.current?.handleMessage?.(message);
|
|
439
|
-
};
|
|
440
|
-
transportInstance.onerror = (err) => {
|
|
441
|
-
addLog("warn", `Transport error event (${transportTypeParam.toUpperCase()}):`, err);
|
|
442
|
-
failConnection(`Transport error (${transportTypeParam.toUpperCase()}): ${err.message}`, err);
|
|
443
|
-
};
|
|
444
|
-
transportInstance.onclose = () => {
|
|
445
|
-
if (!isMountedRef.current || connectingRef.current) return;
|
|
446
|
-
addLog("info", `Transport connection closed (${successfulTransportRef.current || "unknown"} type).`);
|
|
447
|
-
const currentState = stateRef.current;
|
|
448
|
-
const currentAutoReconnect = autoReconnectRef.current;
|
|
449
|
-
if (currentState === "ready" && currentAutoReconnect) {
|
|
450
|
-
const delay = typeof currentAutoReconnect === "number" ? currentAutoReconnect : DEFAULT_RECONNECT_DELAY;
|
|
451
|
-
addLog("info", `Attempting to reconnect in ${delay}ms...`);
|
|
452
|
-
setState("connecting");
|
|
453
|
-
setTimeout(() => {
|
|
454
|
-
if (isMountedRef.current) {
|
|
455
|
-
connect();
|
|
456
|
-
}
|
|
457
|
-
}, delay);
|
|
458
|
-
} else if (currentState !== "failed" && currentState !== "authenticating") {
|
|
459
|
-
failConnection("Cannot connect to server");
|
|
460
|
-
}
|
|
461
|
-
};
|
|
462
|
-
try {
|
|
463
|
-
addLog("info", `Connecting client via ${transportTypeParam.toUpperCase()}...`);
|
|
464
|
-
await clientRef.current.connect(transportInstance);
|
|
465
|
-
addLog("info", `Client connected via ${transportTypeParam.toUpperCase()}. Loading tools, resources, and prompts...`);
|
|
466
|
-
successfulTransportRef.current = transportTypeParam;
|
|
467
|
-
setState("loading");
|
|
468
|
-
const toolsResponse = await clientRef.current.request({ method: "tools/list" }, import_types.ListToolsResultSchema);
|
|
469
|
-
let resourcesResponse = { resources: [], resourceTemplates: [] };
|
|
470
|
-
try {
|
|
471
|
-
resourcesResponse = await clientRef.current.request({ method: "resources/list" }, import_types.ListResourcesResultSchema);
|
|
472
|
-
} catch (err) {
|
|
473
|
-
addLog("debug", "Server does not support resources/list method", err);
|
|
474
|
-
}
|
|
475
|
-
let promptsResponse = { prompts: [] };
|
|
476
|
-
try {
|
|
477
|
-
promptsResponse = await clientRef.current.request({ method: "prompts/list" }, import_types.ListPromptsResultSchema);
|
|
478
|
-
} catch (err) {
|
|
479
|
-
addLog("debug", "Server does not support prompts/list method", err);
|
|
1719
|
+
if (customHeaders && Object.keys(customHeaders).length > 0) {
|
|
1720
|
+
serverConfig.headers = customHeaders;
|
|
480
1721
|
}
|
|
481
|
-
if (
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
summary.push(`${resourcesResponse.resources.length} resources`);
|
|
489
|
-
if (Array.isArray(resourcesResponse.resourceTemplates) && resourcesResponse.resourceTemplates.length > 0) {
|
|
490
|
-
summary.push(`${resourcesResponse.resourceTemplates.length} resource templates`);
|
|
491
|
-
}
|
|
1722
|
+
if (authProviderRef.current) {
|
|
1723
|
+
const tokens = await authProviderRef.current.tokens();
|
|
1724
|
+
if (tokens?.access_token) {
|
|
1725
|
+
serverConfig.headers = {
|
|
1726
|
+
...serverConfig.headers,
|
|
1727
|
+
Authorization: `Bearer ${tokens.access_token}`
|
|
1728
|
+
};
|
|
492
1729
|
}
|
|
493
|
-
if (promptsResponse.prompts.length > 0) {
|
|
494
|
-
summary.push(`${promptsResponse.prompts.length} prompts`);
|
|
495
|
-
}
|
|
496
|
-
addLog("info", summary.join(", ") + ".");
|
|
497
|
-
setState("ready");
|
|
498
|
-
connectAttemptRef.current = 0;
|
|
499
|
-
return "success";
|
|
500
|
-
} else {
|
|
501
|
-
return "failed";
|
|
502
|
-
}
|
|
503
|
-
} catch (connectErr) {
|
|
504
|
-
addLog("debug", `Client connect error via ${transportTypeParam.toUpperCase()}:`, connectErr);
|
|
505
|
-
const errorInstance = connectErr instanceof Error ? connectErr : new Error(String(connectErr));
|
|
506
|
-
const errorMessage = errorInstance.message;
|
|
507
|
-
const is404 = errorMessage.includes("404") || errorMessage.includes("Not Found");
|
|
508
|
-
const is405 = errorMessage.includes("405") || errorMessage.includes("Method Not Allowed");
|
|
509
|
-
const isLikelyCors = errorMessage === "Failed to fetch" || errorMessage === "NetworkError when attempting to fetch resource." || errorMessage === "Load failed";
|
|
510
|
-
if (transportTypeParam === "http" && (is404 || is405 || isLikelyCors)) {
|
|
511
|
-
addLog("warn", `HTTP transport failed (${isLikelyCors ? "CORS" : is404 ? "404" : "405"}), will try fallback.`);
|
|
512
|
-
return "fallback";
|
|
513
1730
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
if (currentState === "authenticating") {
|
|
535
|
-
failConnection("Authentication timed out. Please try again.");
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}, AUTH_TIMEOUT);
|
|
539
|
-
}
|
|
540
|
-
try {
|
|
541
|
-
assert(url, "Server URL is required for authentication");
|
|
542
|
-
const baseUrl = new URL(url).origin;
|
|
543
|
-
const authResult = await (0, import_auth.auth)(authProviderRef.current, { serverUrl: baseUrl });
|
|
544
|
-
if (!isMountedRef.current) return "failed";
|
|
545
|
-
if (authResult === "AUTHORIZED") {
|
|
546
|
-
addLog("info", "Authentication successful via existing token or refresh. Retrying transport connection...");
|
|
547
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
548
|
-
authTimeoutRef.current = null;
|
|
549
|
-
return await tryConnectWithTransport(transportTypeParam, true);
|
|
550
|
-
} else if (authResult === "REDIRECT") {
|
|
551
|
-
addLog("info", "Redirecting for authentication. Waiting for callback...");
|
|
552
|
-
return "auth_redirect";
|
|
553
|
-
}
|
|
554
|
-
} catch (sdkAuthError) {
|
|
555
|
-
if (!isMountedRef.current) return "failed";
|
|
556
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
1731
|
+
clientRef.current.addServer(serverName, {
|
|
1732
|
+
...serverConfig,
|
|
1733
|
+
authProvider: authProviderRef.current
|
|
1734
|
+
// ← SDK handles OAuth automatically!
|
|
1735
|
+
});
|
|
1736
|
+
const session = await clientRef.current.createSession(serverName);
|
|
1737
|
+
await session.initialize();
|
|
1738
|
+
addLog("info", "\u2705 Successfully connected to MCP server");
|
|
1739
|
+
setState("ready");
|
|
1740
|
+
successfulTransportRef.current = transportTypeParam;
|
|
1741
|
+
setTools(session.connector.tools || []);
|
|
1742
|
+
const resourcesResult = await session.connector.listAllResources();
|
|
1743
|
+
setResources(resourcesResult.resources || []);
|
|
1744
|
+
const promptsResult = await session.connector.listPrompts();
|
|
1745
|
+
setPrompts(promptsResult.prompts || []);
|
|
1746
|
+
return "success";
|
|
1747
|
+
} catch (err) {
|
|
1748
|
+
const errorMessage = err?.message || String(err);
|
|
1749
|
+
if (err.code === 401 || errorMessage.includes("401") || errorMessage.includes("Unauthorized")) {
|
|
1750
|
+
if (customHeaders && Object.keys(customHeaders).length > 0) {
|
|
557
1751
|
failConnection(
|
|
558
|
-
|
|
559
|
-
sdkAuthError instanceof Error ? sdkAuthError : void 0
|
|
1752
|
+
"Authentication failed: Server returned 401 Unauthorized. Check your Authorization header value is correct."
|
|
560
1753
|
);
|
|
561
1754
|
return "failed";
|
|
562
1755
|
}
|
|
1756
|
+
failConnection(
|
|
1757
|
+
"Authentication required: Server returned 401 Unauthorized. Add an Authorization header in the Custom Headers section (e.g., Authorization: Bearer YOUR_API_KEY)."
|
|
1758
|
+
);
|
|
1759
|
+
return "failed";
|
|
563
1760
|
}
|
|
564
|
-
failConnection(
|
|
1761
|
+
failConnection(errorMessage, err);
|
|
565
1762
|
return "failed";
|
|
566
1763
|
}
|
|
567
1764
|
}, "tryConnectWithTransport");
|
|
@@ -609,66 +1806,36 @@ function useMcp(options) {
|
|
|
609
1806
|
const callTool = (0, import_react.useCallback)(
|
|
610
1807
|
async (name, args) => {
|
|
611
1808
|
if (stateRef.current !== "ready" || !clientRef.current) {
|
|
612
|
-
throw new Error(
|
|
1809
|
+
throw new Error(
|
|
1810
|
+
`MCP client is not ready (current state: ${state}). Cannot call tool "${name}".`
|
|
1811
|
+
);
|
|
613
1812
|
}
|
|
614
1813
|
addLog("info", `Calling tool: ${name}`, args);
|
|
615
1814
|
try {
|
|
616
|
-
const
|
|
1815
|
+
const serverName = "inspector-server";
|
|
1816
|
+
const session = clientRef.current.getSession(serverName);
|
|
1817
|
+
if (!session) {
|
|
1818
|
+
throw new Error("No active session found");
|
|
1819
|
+
}
|
|
1820
|
+
const result = await session.connector.callTool(name, args || {});
|
|
617
1821
|
addLog("info", `Tool "${name}" call successful:`, result);
|
|
618
1822
|
return result;
|
|
619
1823
|
} catch (err) {
|
|
620
|
-
addLog("error", `
|
|
621
|
-
|
|
622
|
-
if (errorInstance instanceof import_auth.UnauthorizedError || errorInstance.message.includes("Unauthorized") || errorInstance.message.includes("401")) {
|
|
623
|
-
addLog("warn", "Tool call unauthorized, attempting re-authentication...");
|
|
624
|
-
setState("authenticating");
|
|
625
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
626
|
-
authTimeoutRef.current = setTimeout(() => {
|
|
627
|
-
if (isMountedRef.current) {
|
|
628
|
-
const currentState2 = stateRef.current;
|
|
629
|
-
if (currentState2 === "authenticating") {
|
|
630
|
-
failConnection("Authentication timed out. Please try again.");
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}, AUTH_TIMEOUT);
|
|
634
|
-
try {
|
|
635
|
-
assert(authProviderRef.current, "Auth Provider not available for tool re-auth");
|
|
636
|
-
assert(url, "Server URL is required for authentication");
|
|
637
|
-
const baseUrl = new URL(url).origin;
|
|
638
|
-
const authResult = await (0, import_auth.auth)(authProviderRef.current, { serverUrl: baseUrl });
|
|
639
|
-
if (!isMountedRef.current) return;
|
|
640
|
-
if (authResult === "AUTHORIZED") {
|
|
641
|
-
addLog("info", "Re-authentication successful. Retrying tool call is recommended, or reconnecting.");
|
|
642
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
643
|
-
connectingRef.current = false;
|
|
644
|
-
connect();
|
|
645
|
-
} else if (authResult === "REDIRECT") {
|
|
646
|
-
addLog("info", "Redirecting for re-authentication for tool call.");
|
|
647
|
-
}
|
|
648
|
-
} catch (sdkAuthError) {
|
|
649
|
-
if (!isMountedRef.current) return;
|
|
650
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
651
|
-
failConnection(
|
|
652
|
-
`Re-authentication failed: ${sdkAuthError instanceof Error ? sdkAuthError.message : String(sdkAuthError)}`,
|
|
653
|
-
sdkAuthError instanceof Error ? sdkAuthError : void 0
|
|
654
|
-
);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
const currentState = stateRef.current;
|
|
658
|
-
if (currentState !== "authenticating") {
|
|
659
|
-
throw err;
|
|
660
|
-
}
|
|
661
|
-
return void 0;
|
|
1824
|
+
addLog("error", `Tool "${name}" call failed:`, err);
|
|
1825
|
+
throw err;
|
|
662
1826
|
}
|
|
663
1827
|
},
|
|
664
|
-
[state
|
|
1828
|
+
[state]
|
|
665
1829
|
);
|
|
666
1830
|
const retry = (0, import_react.useCallback)(() => {
|
|
667
1831
|
if (stateRef.current === "failed") {
|
|
668
1832
|
addLog("info", "Retry requested...");
|
|
669
1833
|
connect();
|
|
670
1834
|
} else {
|
|
671
|
-
addLog(
|
|
1835
|
+
addLog(
|
|
1836
|
+
"warn",
|
|
1837
|
+
`Retry called but state is not 'failed' (state: ${stateRef.current}). Ignoring.`
|
|
1838
|
+
);
|
|
672
1839
|
}
|
|
673
1840
|
}, [addLog, connect]);
|
|
674
1841
|
const authenticate = (0, import_react.useCallback)(async () => {
|
|
@@ -690,19 +1857,15 @@ function useMcp(options) {
|
|
|
690
1857
|
}
|
|
691
1858
|
}, AUTH_TIMEOUT);
|
|
692
1859
|
try {
|
|
693
|
-
assert(
|
|
1860
|
+
assert(
|
|
1861
|
+
authProviderRef.current,
|
|
1862
|
+
"Auth Provider not available for manual auth"
|
|
1863
|
+
);
|
|
694
1864
|
assert(url, "Server URL is required for authentication");
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
addLog("info", "Manual authentication successful. Re-attempting connection...");
|
|
700
|
-
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
701
|
-
connectingRef.current = false;
|
|
702
|
-
connect();
|
|
703
|
-
} else if (authResult === "REDIRECT") {
|
|
704
|
-
addLog("info", "Redirecting for manual authentication. Waiting for callback...");
|
|
705
|
-
}
|
|
1865
|
+
addLog(
|
|
1866
|
+
"info",
|
|
1867
|
+
"Redirecting for manual authentication. Waiting for callback..."
|
|
1868
|
+
);
|
|
706
1869
|
} catch (authError) {
|
|
707
1870
|
if (!isMountedRef.current) return;
|
|
708
1871
|
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
@@ -712,7 +1875,10 @@ function useMcp(options) {
|
|
|
712
1875
|
);
|
|
713
1876
|
}
|
|
714
1877
|
} else if (currentState === "authenticating") {
|
|
715
|
-
addLog(
|
|
1878
|
+
addLog(
|
|
1879
|
+
"warn",
|
|
1880
|
+
"Already attempting authentication. Check for blocked popups or wait for timeout."
|
|
1881
|
+
);
|
|
716
1882
|
const manualUrl = authProviderRef.current?.getLastAttemptedAuthUrl();
|
|
717
1883
|
if (manualUrl && !authUrl) {
|
|
718
1884
|
setAuthUrl(manualUrl);
|
|
@@ -737,73 +1903,93 @@ function useMcp(options) {
|
|
|
737
1903
|
}, [url, addLog, disconnect]);
|
|
738
1904
|
const listResources = (0, import_react.useCallback)(async () => {
|
|
739
1905
|
if (stateRef.current !== "ready" || !clientRef.current) {
|
|
740
|
-
throw new Error(
|
|
1906
|
+
throw new Error(
|
|
1907
|
+
`MCP client is not ready (current state: ${state}). Cannot list resources.`
|
|
1908
|
+
);
|
|
741
1909
|
}
|
|
742
|
-
addLog("info", "Listing resources
|
|
1910
|
+
addLog("info", "Listing resources");
|
|
743
1911
|
try {
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
addLog(
|
|
749
|
-
"info",
|
|
750
|
-
`Listed ${resourcesResponse.resources.length} resources, ${Array.isArray(resourcesResponse.resourceTemplates) ? resourcesResponse.resourceTemplates.length : 0} resource templates.`
|
|
751
|
-
);
|
|
1912
|
+
const serverName = "inspector-server";
|
|
1913
|
+
const session = clientRef.current.getSession(serverName);
|
|
1914
|
+
if (!session) {
|
|
1915
|
+
throw new Error("No active session found");
|
|
752
1916
|
}
|
|
1917
|
+
const resourcesResult = await session.connector.listAllResources();
|
|
1918
|
+
setResources(resourcesResult.resources || []);
|
|
1919
|
+
addLog("info", "Resources listed successfully");
|
|
753
1920
|
} catch (err) {
|
|
754
|
-
addLog("error",
|
|
1921
|
+
addLog("error", "List resources failed:", err);
|
|
755
1922
|
throw err;
|
|
756
1923
|
}
|
|
757
|
-
}, [state
|
|
1924
|
+
}, [state]);
|
|
758
1925
|
const readResource = (0, import_react.useCallback)(
|
|
759
1926
|
async (uri) => {
|
|
760
1927
|
if (stateRef.current !== "ready" || !clientRef.current) {
|
|
761
|
-
throw new Error(
|
|
1928
|
+
throw new Error(
|
|
1929
|
+
`MCP client is not ready (current state: ${state}). Cannot read resource.`
|
|
1930
|
+
);
|
|
762
1931
|
}
|
|
763
1932
|
addLog("info", `Reading resource: ${uri}`);
|
|
764
1933
|
try {
|
|
765
|
-
const
|
|
766
|
-
|
|
1934
|
+
const serverName = "inspector-server";
|
|
1935
|
+
const session = clientRef.current.getSession(serverName);
|
|
1936
|
+
if (!session) {
|
|
1937
|
+
throw new Error("No active session found");
|
|
1938
|
+
}
|
|
1939
|
+
const result = await session.connector.readResource(uri);
|
|
1940
|
+
addLog("info", "Resource read successful:", result);
|
|
767
1941
|
return result;
|
|
768
1942
|
} catch (err) {
|
|
769
|
-
addLog("error",
|
|
1943
|
+
addLog("error", "Resource read failed:", err);
|
|
770
1944
|
throw err;
|
|
771
1945
|
}
|
|
772
1946
|
},
|
|
773
|
-
[state
|
|
1947
|
+
[state]
|
|
774
1948
|
);
|
|
775
1949
|
const listPrompts = (0, import_react.useCallback)(async () => {
|
|
776
1950
|
if (stateRef.current !== "ready" || !clientRef.current) {
|
|
777
|
-
throw new Error(
|
|
1951
|
+
throw new Error(
|
|
1952
|
+
`MCP client is not ready (current state: ${state}). Cannot list prompts.`
|
|
1953
|
+
);
|
|
778
1954
|
}
|
|
779
|
-
addLog("info", "Listing prompts
|
|
1955
|
+
addLog("info", "Listing prompts");
|
|
780
1956
|
try {
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
1957
|
+
const serverName = "inspector-server";
|
|
1958
|
+
const session = clientRef.current.getSession(serverName);
|
|
1959
|
+
if (!session) {
|
|
1960
|
+
throw new Error("No active session found");
|
|
785
1961
|
}
|
|
1962
|
+
const promptsResult = await session.connector.listPrompts();
|
|
1963
|
+
setPrompts(promptsResult.prompts || []);
|
|
1964
|
+
addLog("info", "Prompts listed successfully");
|
|
786
1965
|
} catch (err) {
|
|
787
|
-
addLog("error",
|
|
1966
|
+
addLog("error", "List prompts failed:", err);
|
|
788
1967
|
throw err;
|
|
789
1968
|
}
|
|
790
|
-
}, [state
|
|
1969
|
+
}, [state]);
|
|
791
1970
|
const getPrompt = (0, import_react.useCallback)(
|
|
792
1971
|
async (name, args) => {
|
|
793
1972
|
if (stateRef.current !== "ready" || !clientRef.current) {
|
|
794
|
-
throw new Error(
|
|
1973
|
+
throw new Error(
|
|
1974
|
+
`MCP client is not ready (current state: ${state}). Cannot get prompt.`
|
|
1975
|
+
);
|
|
795
1976
|
}
|
|
796
1977
|
addLog("info", `Getting prompt: ${name}`, args);
|
|
797
1978
|
try {
|
|
798
|
-
const
|
|
799
|
-
|
|
1979
|
+
const serverName = "inspector-server";
|
|
1980
|
+
const session = clientRef.current.getSession(serverName);
|
|
1981
|
+
if (!session) {
|
|
1982
|
+
throw new Error("No active session found");
|
|
1983
|
+
}
|
|
1984
|
+
const result = await session.connector.getPrompt(name, args || {});
|
|
1985
|
+
addLog("info", `Prompt "${name}" retrieved successfully:`, result);
|
|
800
1986
|
return result;
|
|
801
1987
|
} catch (err) {
|
|
802
|
-
addLog("error", `
|
|
1988
|
+
addLog("error", `Prompt "${name}" retrieval failed:`, err);
|
|
803
1989
|
throw err;
|
|
804
1990
|
}
|
|
805
1991
|
},
|
|
806
|
-
[state
|
|
1992
|
+
[state]
|
|
807
1993
|
);
|
|
808
1994
|
const connectRef = (0, import_react.useRef)(connect);
|
|
809
1995
|
const failConnectionRef = (0, import_react.useRef)(failConnection);
|
|
@@ -819,19 +2005,30 @@ function useMcp(options) {
|
|
|
819
2005
|
if (authTimeoutRef.current) clearTimeout(authTimeoutRef.current);
|
|
820
2006
|
authTimeoutRef.current = null;
|
|
821
2007
|
if (event.data.success) {
|
|
822
|
-
addLog(
|
|
2008
|
+
addLog(
|
|
2009
|
+
"info",
|
|
2010
|
+
"Authentication successful via popup. Reconnecting client..."
|
|
2011
|
+
);
|
|
823
2012
|
if (connectingRef.current) {
|
|
824
|
-
addLog(
|
|
2013
|
+
addLog(
|
|
2014
|
+
"debug",
|
|
2015
|
+
"Connection attempt already in progress, resetting flag to allow reconnection."
|
|
2016
|
+
);
|
|
825
2017
|
}
|
|
826
2018
|
connectingRef.current = false;
|
|
827
2019
|
setTimeout(() => {
|
|
828
2020
|
if (isMountedRef.current) {
|
|
829
|
-
addLog(
|
|
2021
|
+
addLog(
|
|
2022
|
+
"debug",
|
|
2023
|
+
"Initiating reconnection after successful auth callback."
|
|
2024
|
+
);
|
|
830
2025
|
connectRef.current();
|
|
831
2026
|
}
|
|
832
2027
|
}, 100);
|
|
833
2028
|
} else {
|
|
834
|
-
failConnectionRef.current(
|
|
2029
|
+
failConnectionRef.current(
|
|
2030
|
+
`Authentication failed in callback: ${event.data.error || "Unknown reason."}`
|
|
2031
|
+
);
|
|
835
2032
|
}
|
|
836
2033
|
}
|
|
837
2034
|
}, "messageHandler");
|
|
@@ -846,7 +2043,10 @@ function useMcp(options) {
|
|
|
846
2043
|
(0, import_react.useEffect)(() => {
|
|
847
2044
|
isMountedRef.current = true;
|
|
848
2045
|
if (!enabled || !url) {
|
|
849
|
-
addLog(
|
|
2046
|
+
addLog(
|
|
2047
|
+
"debug",
|
|
2048
|
+
enabled ? "No server URL provided, skipping connection." : "Connection disabled via enabled flag."
|
|
2049
|
+
);
|
|
850
2050
|
setState("discovering");
|
|
851
2051
|
return () => {
|
|
852
2052
|
isMountedRef.current = false;
|
|
@@ -863,7 +2063,10 @@ function useMcp(options) {
|
|
|
863
2063
|
preventAutoAuth,
|
|
864
2064
|
onPopupWindow
|
|
865
2065
|
});
|
|
866
|
-
addLog(
|
|
2066
|
+
addLog(
|
|
2067
|
+
"debug",
|
|
2068
|
+
"BrowserOAuthClientProvider initialized/updated on mount/option change."
|
|
2069
|
+
);
|
|
867
2070
|
}
|
|
868
2071
|
connect();
|
|
869
2072
|
return () => {
|
|
@@ -871,7 +2074,16 @@ function useMcp(options) {
|
|
|
871
2074
|
addLog("debug", "useMcp unmounting, disconnecting.");
|
|
872
2075
|
disconnect(true);
|
|
873
2076
|
};
|
|
874
|
-
}, [
|
|
2077
|
+
}, [
|
|
2078
|
+
url,
|
|
2079
|
+
enabled,
|
|
2080
|
+
storageKeyPrefix,
|
|
2081
|
+
callbackUrl,
|
|
2082
|
+
clientName,
|
|
2083
|
+
clientUri,
|
|
2084
|
+
clientConfig.name,
|
|
2085
|
+
clientConfig.version
|
|
2086
|
+
]);
|
|
875
2087
|
(0, import_react.useEffect)(() => {
|
|
876
2088
|
let retryTimeoutId = null;
|
|
877
2089
|
if (state === "failed" && autoRetry && connectAttemptRef.current > 0) {
|
|
@@ -896,9 +2108,10 @@ function useMcp(options) {
|
|
|
896
2108
|
error,
|
|
897
2109
|
log,
|
|
898
2110
|
authUrl,
|
|
2111
|
+
client: clientRef.current,
|
|
899
2112
|
callTool,
|
|
900
|
-
listResources,
|
|
901
2113
|
readResource,
|
|
2114
|
+
listResources,
|
|
902
2115
|
listPrompts,
|
|
903
2116
|
getPrompt,
|
|
904
2117
|
retry,
|
|
@@ -910,7 +2123,7 @@ function useMcp(options) {
|
|
|
910
2123
|
__name(useMcp, "useMcp");
|
|
911
2124
|
|
|
912
2125
|
// src/auth/callback.ts
|
|
913
|
-
var
|
|
2126
|
+
var import_auth = require("@modelcontextprotocol/sdk/client/auth.js");
|
|
914
2127
|
async function onMcpAuthorization() {
|
|
915
2128
|
const queryParams = new URLSearchParams(window.location.search);
|
|
916
2129
|
const code = queryParams.get("code");
|
|
@@ -918,23 +2131,36 @@ async function onMcpAuthorization() {
|
|
|
918
2131
|
const error = queryParams.get("error");
|
|
919
2132
|
const errorDescription = queryParams.get("error_description");
|
|
920
2133
|
const logPrefix = "[mcp-callback]";
|
|
921
|
-
console.log(`${logPrefix} Handling callback...`, {
|
|
2134
|
+
console.log(`${logPrefix} Handling callback...`, {
|
|
2135
|
+
code,
|
|
2136
|
+
state,
|
|
2137
|
+
error,
|
|
2138
|
+
errorDescription
|
|
2139
|
+
});
|
|
922
2140
|
let provider = null;
|
|
923
2141
|
let storedStateData = null;
|
|
924
2142
|
const stateKey = state ? `mcp:auth:state_${state}` : null;
|
|
925
2143
|
try {
|
|
926
2144
|
if (error) {
|
|
927
|
-
throw new Error(
|
|
2145
|
+
throw new Error(
|
|
2146
|
+
`OAuth error: ${error} - ${errorDescription || "No description provided."}`
|
|
2147
|
+
);
|
|
928
2148
|
}
|
|
929
2149
|
if (!code) {
|
|
930
|
-
throw new Error(
|
|
2150
|
+
throw new Error(
|
|
2151
|
+
"Authorization code not found in callback query parameters."
|
|
2152
|
+
);
|
|
931
2153
|
}
|
|
932
2154
|
if (!state || !stateKey) {
|
|
933
|
-
throw new Error(
|
|
2155
|
+
throw new Error(
|
|
2156
|
+
"State parameter not found or invalid in callback query parameters."
|
|
2157
|
+
);
|
|
934
2158
|
}
|
|
935
2159
|
const storedStateJSON = localStorage.getItem(stateKey);
|
|
936
2160
|
if (!storedStateJSON) {
|
|
937
|
-
throw new Error(
|
|
2161
|
+
throw new Error(
|
|
2162
|
+
`Invalid or expired state parameter "${state}". No matching state found in storage.`
|
|
2163
|
+
);
|
|
938
2164
|
}
|
|
939
2165
|
try {
|
|
940
2166
|
storedStateData = JSON.parse(storedStateJSON);
|
|
@@ -943,38 +2169,59 @@ async function onMcpAuthorization() {
|
|
|
943
2169
|
}
|
|
944
2170
|
if (!storedStateData.expiry || storedStateData.expiry < Date.now()) {
|
|
945
2171
|
localStorage.removeItem(stateKey);
|
|
946
|
-
throw new Error(
|
|
2172
|
+
throw new Error(
|
|
2173
|
+
"OAuth state has expired. Please try initiating authentication again."
|
|
2174
|
+
);
|
|
947
2175
|
}
|
|
948
2176
|
if (!storedStateData.providerOptions) {
|
|
949
2177
|
throw new Error("Stored state is missing required provider options.");
|
|
950
2178
|
}
|
|
951
2179
|
const { serverUrl, ...providerOptions } = storedStateData.providerOptions;
|
|
952
|
-
console.log(
|
|
2180
|
+
console.log(
|
|
2181
|
+
`${logPrefix} Re-instantiating provider for server: ${serverUrl}`
|
|
2182
|
+
);
|
|
953
2183
|
provider = new BrowserOAuthClientProvider(serverUrl, providerOptions);
|
|
954
2184
|
console.log(`${logPrefix} Calling SDK auth() to exchange code...`);
|
|
955
2185
|
const baseUrl = new URL(serverUrl).origin;
|
|
956
|
-
const authResult = await (0,
|
|
2186
|
+
const authResult = await (0, import_auth.auth)(provider, {
|
|
2187
|
+
serverUrl: baseUrl,
|
|
2188
|
+
authorizationCode: code
|
|
2189
|
+
});
|
|
957
2190
|
if (authResult === "AUTHORIZED") {
|
|
958
|
-
console.log(
|
|
2191
|
+
console.log(
|
|
2192
|
+
`${logPrefix} Authorization successful via SDK auth(). Notifying opener...`
|
|
2193
|
+
);
|
|
959
2194
|
if (window.opener && !window.opener.closed) {
|
|
960
|
-
window.opener.postMessage(
|
|
2195
|
+
window.opener.postMessage(
|
|
2196
|
+
{ type: "mcp_auth_callback", success: true },
|
|
2197
|
+
window.location.origin
|
|
2198
|
+
);
|
|
961
2199
|
window.close();
|
|
962
2200
|
} else {
|
|
963
|
-
console.warn(
|
|
2201
|
+
console.warn(
|
|
2202
|
+
`${logPrefix} No opener window detected. Redirecting to root.`
|
|
2203
|
+
);
|
|
964
2204
|
const pathParts = window.location.pathname.split("/").filter(Boolean);
|
|
965
2205
|
const basePath = pathParts.length > 0 && pathParts[pathParts.length - 1] === "callback" ? "/" + pathParts.slice(0, -2).join("/") : "/";
|
|
966
2206
|
window.location.href = basePath || "/";
|
|
967
2207
|
}
|
|
968
2208
|
localStorage.removeItem(stateKey);
|
|
969
2209
|
} else {
|
|
970
|
-
console.warn(
|
|
971
|
-
|
|
2210
|
+
console.warn(
|
|
2211
|
+
`${logPrefix} SDK auth() returned unexpected status: ${authResult}`
|
|
2212
|
+
);
|
|
2213
|
+
throw new Error(
|
|
2214
|
+
`Unexpected result from authentication library: ${authResult}`
|
|
2215
|
+
);
|
|
972
2216
|
}
|
|
973
2217
|
} catch (err) {
|
|
974
2218
|
console.error(`${logPrefix} Error during OAuth callback handling:`, err);
|
|
975
2219
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
976
2220
|
if (window.opener && !window.opener.closed) {
|
|
977
|
-
window.opener.postMessage(
|
|
2221
|
+
window.opener.postMessage(
|
|
2222
|
+
{ type: "mcp_auth_callback", success: false, error: errorMessage },
|
|
2223
|
+
window.location.origin
|
|
2224
|
+
);
|
|
978
2225
|
}
|
|
979
2226
|
try {
|
|
980
2227
|
document.body.innerHTML = `
|
|
@@ -988,7 +2235,10 @@ async function onMcpAuthorization() {
|
|
|
988
2235
|
</div>
|
|
989
2236
|
`;
|
|
990
2237
|
} catch (displayError) {
|
|
991
|
-
console.error(
|
|
2238
|
+
console.error(
|
|
2239
|
+
`${logPrefix} Could not display error in callback window:`,
|
|
2240
|
+
displayError
|
|
2241
|
+
);
|
|
992
2242
|
}
|
|
993
2243
|
if (stateKey) {
|
|
994
2244
|
localStorage.removeItem(stateKey);
|
|
@@ -1020,17 +2270,11 @@ function useOpenAiGlobal(key) {
|
|
|
1020
2270
|
onChange();
|
|
1021
2271
|
}, "handleSetGlobal");
|
|
1022
2272
|
if (typeof window !== "undefined") {
|
|
1023
|
-
window.addEventListener(
|
|
1024
|
-
SET_GLOBALS_EVENT_TYPE,
|
|
1025
|
-
handleSetGlobal
|
|
1026
|
-
);
|
|
2273
|
+
window.addEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal);
|
|
1027
2274
|
}
|
|
1028
2275
|
return () => {
|
|
1029
2276
|
if (typeof window !== "undefined") {
|
|
1030
|
-
window.removeEventListener(
|
|
1031
|
-
SET_GLOBALS_EVENT_TYPE,
|
|
1032
|
-
handleSetGlobal
|
|
1033
|
-
);
|
|
2277
|
+
window.removeEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal);
|
|
1034
2278
|
}
|
|
1035
2279
|
};
|
|
1036
2280
|
},
|
|
@@ -1040,7 +2284,10 @@ function useOpenAiGlobal(key) {
|
|
|
1040
2284
|
__name(useOpenAiGlobal, "useOpenAiGlobal");
|
|
1041
2285
|
function useWidget(defaultProps) {
|
|
1042
2286
|
console.log(window?.location?.search, window.openai);
|
|
1043
|
-
const isOpenAiAvailable = (0, import_react2.useMemo)(
|
|
2287
|
+
const isOpenAiAvailable = (0, import_react2.useMemo)(
|
|
2288
|
+
() => typeof window !== "undefined" && !!window.openai,
|
|
2289
|
+
[]
|
|
2290
|
+
);
|
|
1044
2291
|
const provider = (0, import_react2.useMemo)(() => {
|
|
1045
2292
|
return isOpenAiAvailable ? "openai" : "mcp-ui";
|
|
1046
2293
|
}, [isOpenAiAvailable]);
|
|
@@ -1081,12 +2328,15 @@ function useWidget(defaultProps) {
|
|
|
1081
2328
|
},
|
|
1082
2329
|
[]
|
|
1083
2330
|
);
|
|
1084
|
-
const sendFollowUpMessage = (0, import_react2.useCallback)(
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
2331
|
+
const sendFollowUpMessage = (0, import_react2.useCallback)(
|
|
2332
|
+
async (prompt) => {
|
|
2333
|
+
if (!window.openai?.sendFollowUpMessage) {
|
|
2334
|
+
throw new Error("window.openai.sendFollowUpMessage is not available");
|
|
2335
|
+
}
|
|
2336
|
+
return window.openai.sendFollowUpMessage({ prompt });
|
|
2337
|
+
},
|
|
2338
|
+
[]
|
|
2339
|
+
);
|
|
1090
2340
|
const openExternal = (0, import_react2.useCallback)((href) => {
|
|
1091
2341
|
if (!window.openai?.openExternal) {
|
|
1092
2342
|
throw new Error("window.openai.openExternal is not available");
|