@modelcontextprotocol/inspector 0.16.6 → 0.16.7
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/README.md +25 -1
- package/client/dist/assets/{OAuthCallback-Cmffn8om.js → OAuthCallback-aXDAN43J.js} +1 -1
- package/client/dist/assets/{OAuthDebugCallback-BhzcPtuF.js → OAuthDebugCallback-BanjyQ_V.js} +1 -1
- package/client/dist/assets/{index-C217Ot8c.css → index-DZ1A0VH0.css} +33 -0
- package/client/dist/assets/{index-B_qh-Btq.js → index-Dg7mXHRE.js} +88 -13
- package/client/dist/index.html +2 -2
- package/package.json +5 -5
- package/server/build/index.js +176 -119
package/README.md
CHANGED
|
@@ -98,6 +98,16 @@ The MCP Inspector provides convenient buttons to export server launch configurat
|
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
+
**Streamable HTTP transport example:**
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"type": "streamable-http",
|
|
106
|
+
"url": "http://localhost:3000/mcp",
|
|
107
|
+
"note": "For Streamable HTTP connections, add this URL directly in your MCP Client"
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
101
111
|
- **Servers File** - Copies a complete MCP configuration file structure to your clipboard, with your current server configuration added as `default-server`. This can be saved directly as `mcp.json`.
|
|
102
112
|
|
|
103
113
|
**STDIO transport example:**
|
|
@@ -131,9 +141,23 @@ The MCP Inspector provides convenient buttons to export server launch configurat
|
|
|
131
141
|
}
|
|
132
142
|
```
|
|
133
143
|
|
|
144
|
+
**Streamable HTTP transport example:**
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"mcpServers": {
|
|
149
|
+
"default-server": {
|
|
150
|
+
"type": "streamable-http",
|
|
151
|
+
"url": "http://localhost:3000/mcp",
|
|
152
|
+
"note": "For Streamable HTTP connections, add this URL directly in your MCP Client"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
134
158
|
These buttons appear in the Inspector UI after you've configured your server settings, making it easy to save and reuse your configurations.
|
|
135
159
|
|
|
136
|
-
For SSE transport connections, the Inspector provides similar functionality for both buttons. The "Server Entry" button copies the
|
|
160
|
+
For SSE and Streamable HTTP transport connections, the Inspector provides similar functionality for both buttons. The "Server Entry" button copies the configuration that can be added to your existing configuration file, while the "Servers File" button creates a complete configuration file containing the URL for direct use in clients.
|
|
137
161
|
|
|
138
162
|
You can paste the Server Entry into your existing `mcp.json` file under your chosen server name, or use the complete Servers File payload to create a new configuration file.
|
|
139
163
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as useToast, r as reactExports, j as jsxRuntimeExports, p as parseOAuthCallbackParams, g as generateOAuthErrorDescription, S as SESSION_KEYS, I as InspectorOAuthClientProvider, a as auth } from "./index-
|
|
1
|
+
import { u as useToast, r as reactExports, j as jsxRuntimeExports, p as parseOAuthCallbackParams, g as generateOAuthErrorDescription, S as SESSION_KEYS, I as InspectorOAuthClientProvider, a as auth } from "./index-Dg7mXHRE.js";
|
|
2
2
|
const OAuthCallback = ({ onConnect }) => {
|
|
3
3
|
const { toast } = useToast();
|
|
4
4
|
const hasProcessedRef = reactExports.useRef(false);
|
package/client/dist/assets/{OAuthDebugCallback-BhzcPtuF.js → OAuthDebugCallback-BanjyQ_V.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-
|
|
1
|
+
import { r as reactExports, S as SESSION_KEYS, p as parseOAuthCallbackParams, j as jsxRuntimeExports, g as generateOAuthErrorDescription } from "./index-Dg7mXHRE.js";
|
|
2
2
|
const OAuthDebugCallback = ({ onConnect }) => {
|
|
3
3
|
reactExports.useEffect(() => {
|
|
4
4
|
let isProcessed = false;
|
|
@@ -735,6 +735,39 @@ video {
|
|
|
735
735
|
background-color: hsl(var(--background));
|
|
736
736
|
color: hsl(var(--foreground));
|
|
737
737
|
}
|
|
738
|
+
.container {
|
|
739
|
+
width: 100%;
|
|
740
|
+
}
|
|
741
|
+
@media (min-width: 640px) {
|
|
742
|
+
|
|
743
|
+
.container {
|
|
744
|
+
max-width: 640px;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
@media (min-width: 768px) {
|
|
748
|
+
|
|
749
|
+
.container {
|
|
750
|
+
max-width: 768px;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
@media (min-width: 1024px) {
|
|
754
|
+
|
|
755
|
+
.container {
|
|
756
|
+
max-width: 1024px;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
@media (min-width: 1280px) {
|
|
760
|
+
|
|
761
|
+
.container {
|
|
762
|
+
max-width: 1280px;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
@media (min-width: 1536px) {
|
|
766
|
+
|
|
767
|
+
.container {
|
|
768
|
+
max-width: 1536px;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
738
771
|
.sr-only {
|
|
739
772
|
position: absolute;
|
|
740
773
|
width: 1px;
|
|
@@ -15230,6 +15230,20 @@ const CancelledNotificationSchema = NotificationSchema.extend({
|
|
|
15230
15230
|
reason: stringType().optional()
|
|
15231
15231
|
})
|
|
15232
15232
|
});
|
|
15233
|
+
const IconSchema = objectType({
|
|
15234
|
+
/**
|
|
15235
|
+
* URL or data URI for the icon.
|
|
15236
|
+
*/
|
|
15237
|
+
src: stringType(),
|
|
15238
|
+
/**
|
|
15239
|
+
* Optional MIME type for the icon.
|
|
15240
|
+
*/
|
|
15241
|
+
mimeType: optionalType(stringType()),
|
|
15242
|
+
/**
|
|
15243
|
+
* Optional string specifying icon dimensions (e.g., "48x48 96x96").
|
|
15244
|
+
*/
|
|
15245
|
+
sizes: optionalType(stringType())
|
|
15246
|
+
}).passthrough();
|
|
15233
15247
|
const BaseMetadataSchema = objectType({
|
|
15234
15248
|
/** Intended for programmatic or logical use, but used as a display name in past specs or fallback */
|
|
15235
15249
|
name: stringType(),
|
|
@@ -15244,7 +15258,20 @@ const BaseMetadataSchema = objectType({
|
|
|
15244
15258
|
title: optionalType(stringType())
|
|
15245
15259
|
}).passthrough();
|
|
15246
15260
|
const ImplementationSchema = BaseMetadataSchema.extend({
|
|
15247
|
-
version: stringType()
|
|
15261
|
+
version: stringType(),
|
|
15262
|
+
/**
|
|
15263
|
+
* An optional URL of the website for this implementation.
|
|
15264
|
+
*/
|
|
15265
|
+
websiteUrl: optionalType(stringType()),
|
|
15266
|
+
/**
|
|
15267
|
+
* An optional list of icons for this implementation.
|
|
15268
|
+
* This can be used by clients to display the implementation in a user interface.
|
|
15269
|
+
* Each icon should have a `kind` property that specifies whether it is a data representation or a URL source, a `src` property that points to the icon file or data representation, and may also include a `mimeType` and `sizes` property.
|
|
15270
|
+
* The `mimeType` property should be a valid MIME type for the icon file, such as "image/png" or "image/svg+xml".
|
|
15271
|
+
* The `sizes` property should be a string that specifies one or more sizes at which the icon file can be used, such as "48x48" or "any" for scalable formats like SVG.
|
|
15272
|
+
* The `sizes` property is optional, and if not provided, the client should assume that the icon can be used at any size.
|
|
15273
|
+
*/
|
|
15274
|
+
icons: optionalType(arrayType(IconSchema))
|
|
15248
15275
|
});
|
|
15249
15276
|
const ClientCapabilitiesSchema = objectType({
|
|
15250
15277
|
/**
|
|
@@ -15435,6 +15462,10 @@ const ResourceSchema = BaseMetadataSchema.extend({
|
|
|
15435
15462
|
* The MIME type of this resource, if known.
|
|
15436
15463
|
*/
|
|
15437
15464
|
mimeType: optionalType(stringType()),
|
|
15465
|
+
/**
|
|
15466
|
+
* An optional list of icons for this resource.
|
|
15467
|
+
*/
|
|
15468
|
+
icons: optionalType(arrayType(IconSchema)),
|
|
15438
15469
|
/**
|
|
15439
15470
|
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
15440
15471
|
* for notes on _meta usage.
|
|
@@ -15539,6 +15570,10 @@ const PromptSchema = BaseMetadataSchema.extend({
|
|
|
15539
15570
|
* A list of arguments to use for templating the prompt.
|
|
15540
15571
|
*/
|
|
15541
15572
|
arguments: optionalType(arrayType(PromptArgumentSchema)),
|
|
15573
|
+
/**
|
|
15574
|
+
* An optional list of icons for this prompt.
|
|
15575
|
+
*/
|
|
15576
|
+
icons: optionalType(arrayType(IconSchema)),
|
|
15542
15577
|
/**
|
|
15543
15578
|
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
15544
15579
|
* for notes on _meta usage.
|
|
@@ -15706,6 +15741,10 @@ const ToolSchema = BaseMetadataSchema.extend({
|
|
|
15706
15741
|
* Optional additional tool information.
|
|
15707
15742
|
*/
|
|
15708
15743
|
annotations: optionalType(ToolAnnotationsSchema),
|
|
15744
|
+
/**
|
|
15745
|
+
* An optional list of icons for this tool.
|
|
15746
|
+
*/
|
|
15747
|
+
icons: optionalType(arrayType(IconSchema)),
|
|
15709
15748
|
/**
|
|
15710
15749
|
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
15711
15750
|
* for notes on _meta usage.
|
|
@@ -17176,7 +17215,7 @@ const oauthTransitions = {
|
|
|
17176
17215
|
);
|
|
17177
17216
|
context.provider.saveCodeVerifier(codeVerifier);
|
|
17178
17217
|
context.updateState({
|
|
17179
|
-
authorizationUrl
|
|
17218
|
+
authorizationUrl,
|
|
17180
17219
|
oauthStep: "authorization_code"
|
|
17181
17220
|
});
|
|
17182
17221
|
}
|
|
@@ -24866,7 +24905,7 @@ class StreamableHTTPClientTransport {
|
|
|
24866
24905
|
return this._protocolVersion;
|
|
24867
24906
|
}
|
|
24868
24907
|
}
|
|
24869
|
-
const version$1 = "0.16.
|
|
24908
|
+
const version$1 = "0.16.7";
|
|
24870
24909
|
const packageJson = {
|
|
24871
24910
|
version: version$1
|
|
24872
24911
|
};
|
|
@@ -25432,11 +25471,15 @@ function useConnection({
|
|
|
25432
25471
|
setCompletionsSupported(false);
|
|
25433
25472
|
setServerCapabilities(null);
|
|
25434
25473
|
};
|
|
25474
|
+
const clearRequestHistory = () => {
|
|
25475
|
+
setRequestHistory([]);
|
|
25476
|
+
};
|
|
25435
25477
|
return {
|
|
25436
25478
|
connectionStatus,
|
|
25437
25479
|
serverCapabilities,
|
|
25438
25480
|
mcpClient,
|
|
25439
25481
|
requestHistory,
|
|
25482
|
+
clearRequestHistory,
|
|
25440
25483
|
makeRequest,
|
|
25441
25484
|
sendNotification,
|
|
25442
25485
|
handleCompletion,
|
|
@@ -26730,7 +26773,7 @@ const OAuthFlowProgress = ({
|
|
|
26730
26773
|
children: authState.authorizationUrl && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 p-3 border rounded-md bg-muted", children: [
|
|
26731
26774
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "font-medium mb-2 text-sm", children: "Authorization URL:" }),
|
|
26732
26775
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
26733
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs break-all", children: authState.authorizationUrl }),
|
|
26776
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs break-all", children: String(authState.authorizationUrl) }),
|
|
26734
26777
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
26735
26778
|
"button",
|
|
26736
26779
|
{
|
|
@@ -26992,7 +27035,7 @@ const AuthDebugger = ({
|
|
|
26992
27035
|
SESSION_KEYS.AUTH_DEBUGGER_STATE,
|
|
26993
27036
|
JSON.stringify(currentState)
|
|
26994
27037
|
);
|
|
26995
|
-
window.location.href = currentState.authorizationUrl;
|
|
27038
|
+
window.location.href = currentState.authorizationUrl.toString();
|
|
26996
27039
|
break;
|
|
26997
27040
|
}
|
|
26998
27041
|
}
|
|
@@ -27383,7 +27426,9 @@ const JsonNode = reactExports.memo(
|
|
|
27383
27426
|
JsonNode.displayName = "JsonNode";
|
|
27384
27427
|
const HistoryAndNotifications = ({
|
|
27385
27428
|
requestHistory,
|
|
27386
|
-
serverNotifications
|
|
27429
|
+
serverNotifications,
|
|
27430
|
+
onClearHistory,
|
|
27431
|
+
onClearNotifications
|
|
27387
27432
|
}) => {
|
|
27388
27433
|
const [expandedRequests, setExpandedRequests] = reactExports.useState({});
|
|
27389
27434
|
const [expandedNotifications, setExpandedNotifications] = reactExports.useState({});
|
|
@@ -27395,7 +27440,19 @@ const HistoryAndNotifications = ({
|
|
|
27395
27440
|
};
|
|
27396
27441
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-card overflow-hidden flex h-full", children: [
|
|
27397
27442
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto p-4 border-r", children: [
|
|
27398
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
27443
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
27444
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-lg font-semibold", children: "History" }),
|
|
27445
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
27446
|
+
Button,
|
|
27447
|
+
{
|
|
27448
|
+
variant: "outline",
|
|
27449
|
+
size: "sm",
|
|
27450
|
+
onClick: onClearHistory,
|
|
27451
|
+
disabled: requestHistory.length === 0,
|
|
27452
|
+
children: "Clear"
|
|
27453
|
+
}
|
|
27454
|
+
)
|
|
27455
|
+
] }),
|
|
27399
27456
|
requestHistory.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400 italic", children: "No history yet" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "space-y-3", children: requestHistory.slice().reverse().map((request, index2) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
27400
27457
|
"li",
|
|
27401
27458
|
{
|
|
@@ -27445,7 +27502,19 @@ const HistoryAndNotifications = ({
|
|
|
27445
27502
|
)) })
|
|
27446
27503
|
] }),
|
|
27447
27504
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto p-4", children: [
|
|
27448
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
27505
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
27506
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-lg font-semibold", children: "Server Notifications" }),
|
|
27507
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
27508
|
+
Button,
|
|
27509
|
+
{
|
|
27510
|
+
variant: "outline",
|
|
27511
|
+
size: "sm",
|
|
27512
|
+
onClick: onClearNotifications,
|
|
27513
|
+
disabled: serverNotifications.length === 0,
|
|
27514
|
+
children: "Clear"
|
|
27515
|
+
}
|
|
27516
|
+
)
|
|
27517
|
+
] }),
|
|
27449
27518
|
serverNotifications.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400 italic", children: "No notifications yet" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "space-y-3", children: serverNotifications.slice().reverse().map((notification, index2) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
27450
27519
|
"li",
|
|
27451
27520
|
{
|
|
@@ -36472,7 +36541,7 @@ const useTheme = () => {
|
|
|
36472
36541
|
[theme, setThemeWithSideEffect]
|
|
36473
36542
|
);
|
|
36474
36543
|
};
|
|
36475
|
-
const version = "0.16.
|
|
36544
|
+
const version = "0.16.7";
|
|
36476
36545
|
var [createTooltipContext, createTooltipScope] = createContextScope("Tooltip", [
|
|
36477
36546
|
createPopperScope
|
|
36478
36547
|
]);
|
|
@@ -37046,7 +37115,7 @@ const Sidebar = ({
|
|
|
37046
37115
|
setCopiedServerEntry(true);
|
|
37047
37116
|
toast2({
|
|
37048
37117
|
title: "Config entry copied",
|
|
37049
|
-
description: transportType === "stdio" ? "Server configuration has been copied to clipboard. Add this to your mcp.json inside the 'mcpServers' object with your preferred server name." : "SSE URL has been copied. Use this URL directly in your MCP Client."
|
|
37118
|
+
description: transportType === "stdio" ? "Server configuration has been copied to clipboard. Add this to your mcp.json inside the 'mcpServers' object with your preferred server name." : transportType === "streamable-http" ? "Streamable HTTP URL has been copied. Use this URL directly in your MCP Client." : "SSE URL has been copied. Use this URL directly in your MCP Client."
|
|
37050
37119
|
});
|
|
37051
37120
|
setTimeout(() => {
|
|
37052
37121
|
setCopiedServerEntry(false);
|
|
@@ -38636,6 +38705,7 @@ const App = () => {
|
|
|
38636
38705
|
serverCapabilities,
|
|
38637
38706
|
mcpClient,
|
|
38638
38707
|
requestHistory,
|
|
38708
|
+
clearRequestHistory,
|
|
38639
38709
|
makeRequest,
|
|
38640
38710
|
sendNotification,
|
|
38641
38711
|
handleCompletion,
|
|
@@ -39083,6 +39153,9 @@ const App = () => {
|
|
|
39083
39153
|
const handleRootsChange = async () => {
|
|
39084
39154
|
await sendNotification({ method: "notifications/roots/list_changed" });
|
|
39085
39155
|
};
|
|
39156
|
+
const handleClearNotifications = () => {
|
|
39157
|
+
setNotifications([]);
|
|
39158
|
+
};
|
|
39086
39159
|
const sendLogLevelRequest = async (level) => {
|
|
39087
39160
|
await sendMCPRequest(
|
|
39088
39161
|
{
|
|
@@ -39104,13 +39177,13 @@ const App = () => {
|
|
|
39104
39177
|
) });
|
|
39105
39178
|
if (window.location.pathname === "/oauth/callback") {
|
|
39106
39179
|
const OAuthCallback = React.lazy(
|
|
39107
|
-
() => __vitePreload(() => import("./OAuthCallback-
|
|
39180
|
+
() => __vitePreload(() => import("./OAuthCallback-aXDAN43J.js"), true ? [] : void 0)
|
|
39108
39181
|
);
|
|
39109
39182
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthCallback, { onConnect: onOAuthConnect }) });
|
|
39110
39183
|
}
|
|
39111
39184
|
if (window.location.pathname === "/oauth/callback/debug") {
|
|
39112
39185
|
const OAuthDebugCallback = React.lazy(
|
|
39113
|
-
() => __vitePreload(() => import("./OAuthDebugCallback-
|
|
39186
|
+
() => __vitePreload(() => import("./OAuthDebugCallback-BanjyQ_V.js"), true ? [] : void 0)
|
|
39114
39187
|
);
|
|
39115
39188
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsxRuntimeExports.jsx(OAuthDebugCallback, { onConnect: onOAuthDebugConnect }) });
|
|
39116
39189
|
}
|
|
@@ -39457,7 +39530,9 @@ const App = () => {
|
|
|
39457
39530
|
HistoryAndNotifications,
|
|
39458
39531
|
{
|
|
39459
39532
|
requestHistory,
|
|
39460
|
-
serverNotifications: notifications
|
|
39533
|
+
serverNotifications: notifications,
|
|
39534
|
+
onClearHistory: clearRequestHistory,
|
|
39535
|
+
onClearNotifications: handleClearNotifications
|
|
39461
39536
|
}
|
|
39462
39537
|
) })
|
|
39463
39538
|
]
|
package/client/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/mcp.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>MCP Inspector</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-Dg7mXHRE.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DZ1A0VH0.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root" class="w-full"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelcontextprotocol/inspector",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.7",
|
|
4
4
|
"description": "Model Context Protocol inspector",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Anthropic, PBC (https://anthropic.com)",
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"check-version": "node scripts/check-version-consistency.js"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@modelcontextprotocol/inspector-cli": "^0.16.
|
|
51
|
-
"@modelcontextprotocol/inspector-client": "^0.16.
|
|
52
|
-
"@modelcontextprotocol/inspector-server": "^0.16.
|
|
53
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
50
|
+
"@modelcontextprotocol/inspector-cli": "^0.16.7",
|
|
51
|
+
"@modelcontextprotocol/inspector-client": "^0.16.7",
|
|
52
|
+
"@modelcontextprotocol/inspector-server": "^0.16.7",
|
|
53
|
+
"@modelcontextprotocol/sdk": "^1.18.0",
|
|
54
54
|
"concurrently": "^9.2.0",
|
|
55
55
|
"open": "^10.2.0",
|
|
56
56
|
"shell-quote": "^1.8.3",
|
package/server/build/index.js
CHANGED
|
@@ -12,12 +12,6 @@ import { findActualExecutable } from "spawn-rx";
|
|
|
12
12
|
import mcpProxy from "./mcpProxy.js";
|
|
13
13
|
import { randomUUID, randomBytes, timingSafeEqual } from "node:crypto";
|
|
14
14
|
const DEFAULT_MCP_PROXY_LISTEN_PORT = "6277";
|
|
15
|
-
const SSE_HEADERS_PASSTHROUGH = ["authorization"];
|
|
16
|
-
const STREAMABLE_HTTP_HEADERS_PASSTHROUGH = [
|
|
17
|
-
"authorization",
|
|
18
|
-
"mcp-session-id",
|
|
19
|
-
"last-event-id",
|
|
20
|
-
];
|
|
21
15
|
const defaultEnvironment = {
|
|
22
16
|
...getDefaultEnvironment(),
|
|
23
17
|
...(process.env.MCP_ENV_VARS ? JSON.parse(process.env.MCP_ENV_VARS) : {}),
|
|
@@ -33,34 +27,71 @@ const { values } = parseArgs({
|
|
|
33
27
|
},
|
|
34
28
|
});
|
|
35
29
|
// Function to get HTTP headers.
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
30
|
+
const getHttpHeaders = (req) => {
|
|
31
|
+
const headers = {};
|
|
32
|
+
// Iterate over all headers in the request
|
|
33
|
+
for (const key in req.headers) {
|
|
34
|
+
const lowerKey = key.toLowerCase();
|
|
35
|
+
// Check if the header is one we want to forward
|
|
36
|
+
if (lowerKey.startsWith("mcp-") ||
|
|
37
|
+
lowerKey === "authorization" ||
|
|
38
|
+
lowerKey === "last-event-id") {
|
|
39
|
+
// Exclude the proxy's own authentication header
|
|
40
|
+
if (lowerKey !== "x-mcp-proxy-auth") {
|
|
41
|
+
const value = req.headers[key];
|
|
42
|
+
if (typeof value === "string") {
|
|
43
|
+
// If the value is a string, use it directly
|
|
44
|
+
headers[key] = value;
|
|
45
|
+
}
|
|
46
|
+
else if (Array.isArray(value)) {
|
|
47
|
+
// If the value is an array, use the last element
|
|
48
|
+
const lastValue = value.at(-1);
|
|
49
|
+
if (lastValue !== undefined) {
|
|
50
|
+
headers[key] = lastValue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// If value is undefined, it's skipped, which is correct.
|
|
54
|
+
}
|
|
49
55
|
}
|
|
50
|
-
const value = req.headers[key];
|
|
51
|
-
headers[key] = Array.isArray(value) ? value[value.length - 1] : value;
|
|
52
56
|
}
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
// Handle the custom auth header separately. We expect `x-custom-auth-header`
|
|
58
|
+
// to be a string containing the name of the actual authentication header.
|
|
59
|
+
const customAuthHeaderName = req.headers["x-custom-auth-header"];
|
|
60
|
+
if (typeof customAuthHeaderName === "string") {
|
|
61
|
+
const lowerCaseHeaderName = customAuthHeaderName.toLowerCase();
|
|
62
|
+
const value = req.headers[lowerCaseHeaderName];
|
|
63
|
+
if (typeof value === "string") {
|
|
64
|
+
headers[customAuthHeaderName] = value;
|
|
65
|
+
}
|
|
66
|
+
else if (Array.isArray(value)) {
|
|
67
|
+
// If the actual auth header was sent multiple times, use the last value.
|
|
68
|
+
const lastValue = value.at(-1);
|
|
69
|
+
if (lastValue !== undefined) {
|
|
70
|
+
headers[customAuthHeaderName] = lastValue;
|
|
71
|
+
}
|
|
60
72
|
}
|
|
61
73
|
}
|
|
62
74
|
return headers;
|
|
63
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* Updates a headers object in-place, preserving the original Accept header.
|
|
78
|
+
* This is necessary to ensure that transports holding a reference to the headers
|
|
79
|
+
* object see the updates.
|
|
80
|
+
* @param currentHeaders The headers object to update.
|
|
81
|
+
* @param newHeaders The new headers to apply.
|
|
82
|
+
*/
|
|
83
|
+
const updateHeadersInPlace = (currentHeaders, newHeaders) => {
|
|
84
|
+
// Preserve the Accept header, which is set at transport creation and
|
|
85
|
+
// is not present in subsequent client requests.
|
|
86
|
+
const accept = currentHeaders["Accept"];
|
|
87
|
+
// Clear the old headers and apply the new ones.
|
|
88
|
+
Object.keys(currentHeaders).forEach((key) => delete currentHeaders[key]);
|
|
89
|
+
Object.assign(currentHeaders, newHeaders);
|
|
90
|
+
// Restore the Accept header.
|
|
91
|
+
if (accept) {
|
|
92
|
+
currentHeaders["Accept"] = accept;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
64
95
|
const app = express();
|
|
65
96
|
app.use(cors());
|
|
66
97
|
app.use((req, res, next) => {
|
|
@@ -69,6 +100,7 @@ app.use((req, res, next) => {
|
|
|
69
100
|
});
|
|
70
101
|
const webAppTransports = new Map(); // Web app transports by web app sessionId
|
|
71
102
|
const serverTransports = new Map(); // Server Transports by web app sessionId
|
|
103
|
+
const sessionHeaderHolders = new Map(); // For dynamic header updates
|
|
72
104
|
// Use provided token from environment or generate a new one
|
|
73
105
|
const sessionToken = process.env.MCP_PROXY_AUTH_TOKEN || randomBytes(32).toString("hex");
|
|
74
106
|
const authDisabled = !!process.env.DANGEROUSLY_OMIT_AUTH;
|
|
@@ -126,6 +158,27 @@ const authMiddleware = (req, res, next) => {
|
|
|
126
158
|
}
|
|
127
159
|
next();
|
|
128
160
|
};
|
|
161
|
+
/**
|
|
162
|
+
* Creates a `fetch` function that merges dynamic session headers with the
|
|
163
|
+
* headers from the actual request, ensuring that request-specific headers like
|
|
164
|
+
* `Content-Type` are preserved.
|
|
165
|
+
*/
|
|
166
|
+
const createCustomFetch = (headerHolder) => {
|
|
167
|
+
return (input, init) => {
|
|
168
|
+
// Determine the headers from the original request/init.
|
|
169
|
+
// The SDK may pass a Request object or a URL and an init object.
|
|
170
|
+
const originalHeaders = input instanceof Request ? input.headers : init?.headers;
|
|
171
|
+
// Start with our dynamic session headers.
|
|
172
|
+
const finalHeaders = new Headers(headerHolder.headers);
|
|
173
|
+
// Merge the SDK's request-specific headers, letting them overwrite.
|
|
174
|
+
// This is crucial for preserving Content-Type on POST requests.
|
|
175
|
+
new Headers(originalHeaders).forEach((value, key) => {
|
|
176
|
+
finalHeaders.set(key, value);
|
|
177
|
+
});
|
|
178
|
+
// This works for both `fetch(url, init)` and `fetch(request)` style calls.
|
|
179
|
+
return fetch(input, { ...init, headers: finalHeaders });
|
|
180
|
+
};
|
|
181
|
+
};
|
|
129
182
|
const createTransport = async (req) => {
|
|
130
183
|
const query = req.query;
|
|
131
184
|
console.log("Query parameters:", JSON.stringify(query));
|
|
@@ -144,32 +197,35 @@ const createTransport = async (req) => {
|
|
|
144
197
|
stderr: "pipe",
|
|
145
198
|
});
|
|
146
199
|
await transport.start();
|
|
147
|
-
return transport;
|
|
200
|
+
return { transport };
|
|
148
201
|
}
|
|
149
202
|
else if (transportType === "sse") {
|
|
150
203
|
const url = query.url;
|
|
151
|
-
const headers = getHttpHeaders(req
|
|
204
|
+
const headers = getHttpHeaders(req);
|
|
205
|
+
headers["Accept"] = "text/event-stream";
|
|
206
|
+
const headerHolder = { headers };
|
|
152
207
|
console.log(`SSE transport: url=${url}, headers=${JSON.stringify(headers)}`);
|
|
153
208
|
const transport = new SSEClientTransport(new URL(url), {
|
|
154
209
|
eventSourceInit: {
|
|
155
|
-
fetch: (
|
|
210
|
+
fetch: createCustomFetch(headerHolder),
|
|
156
211
|
},
|
|
157
212
|
requestInit: {
|
|
158
|
-
headers,
|
|
213
|
+
headers: headerHolder.headers,
|
|
159
214
|
},
|
|
160
215
|
});
|
|
161
216
|
await transport.start();
|
|
162
|
-
return transport;
|
|
217
|
+
return { transport, headerHolder };
|
|
163
218
|
}
|
|
164
219
|
else if (transportType === "streamable-http") {
|
|
165
|
-
const headers = getHttpHeaders(req
|
|
220
|
+
const headers = getHttpHeaders(req);
|
|
221
|
+
headers["Accept"] = "text/event-stream, application/json";
|
|
222
|
+
const headerHolder = { headers };
|
|
166
223
|
const transport = new StreamableHTTPClientTransport(new URL(query.url), {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
},
|
|
224
|
+
// Pass a custom fetch to inject the latest headers on each request
|
|
225
|
+
fetch: createCustomFetch(headerHolder),
|
|
170
226
|
});
|
|
171
227
|
await transport.start();
|
|
172
|
-
return transport;
|
|
228
|
+
return { transport, headerHolder };
|
|
173
229
|
}
|
|
174
230
|
else {
|
|
175
231
|
console.error(`Invalid transport type: ${transportType}`);
|
|
@@ -179,6 +235,10 @@ const createTransport = async (req) => {
|
|
|
179
235
|
app.get("/mcp", originValidationMiddleware, authMiddleware, async (req, res) => {
|
|
180
236
|
const sessionId = req.headers["mcp-session-id"];
|
|
181
237
|
console.log(`Received GET message for sessionId ${sessionId}`);
|
|
238
|
+
const headerHolder = sessionHeaderHolders.get(sessionId);
|
|
239
|
+
if (headerHolder) {
|
|
240
|
+
updateHeadersInPlace(headerHolder.headers, getHttpHeaders(req));
|
|
241
|
+
}
|
|
182
242
|
try {
|
|
183
243
|
const transport = webAppTransports.get(sessionId);
|
|
184
244
|
if (!transport) {
|
|
@@ -196,29 +256,45 @@ app.get("/mcp", originValidationMiddleware, authMiddleware, async (req, res) =>
|
|
|
196
256
|
});
|
|
197
257
|
app.post("/mcp", originValidationMiddleware, authMiddleware, async (req, res) => {
|
|
198
258
|
const sessionId = req.headers["mcp-session-id"];
|
|
199
|
-
|
|
200
|
-
|
|
259
|
+
if (sessionId) {
|
|
260
|
+
console.log(`Received POST message for sessionId ${sessionId}`);
|
|
261
|
+
const headerHolder = sessionHeaderHolders.get(sessionId);
|
|
262
|
+
if (headerHolder) {
|
|
263
|
+
updateHeadersInPlace(headerHolder.headers, getHttpHeaders(req));
|
|
264
|
+
}
|
|
201
265
|
try {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
266
|
+
const transport = webAppTransports.get(sessionId);
|
|
267
|
+
if (!transport) {
|
|
268
|
+
res.status(404).end("Transport not found for sessionId " + sessionId);
|
|
205
269
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
console.error("Received 401 Unauthorized from MCP server:", error.message);
|
|
209
|
-
res.status(401).json(error);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
throw error;
|
|
270
|
+
else {
|
|
271
|
+
await transport.handleRequest(req, res);
|
|
213
272
|
}
|
|
214
|
-
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
console.error("Error in /mcp route:", error);
|
|
276
|
+
res.status(500).json(error);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
console.log("New StreamableHttp connection request");
|
|
281
|
+
try {
|
|
282
|
+
const { transport: serverTransport, headerHolder } = await createTransport(req);
|
|
215
283
|
const webAppTransport = new StreamableHTTPServerTransport({
|
|
216
284
|
sessionIdGenerator: randomUUID,
|
|
217
285
|
onsessioninitialized: (sessionId) => {
|
|
218
286
|
webAppTransports.set(sessionId, webAppTransport);
|
|
219
|
-
serverTransports.set(sessionId, serverTransport);
|
|
287
|
+
serverTransports.set(sessionId, serverTransport); // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
288
|
+
if (headerHolder) {
|
|
289
|
+
sessionHeaderHolders.set(sessionId, headerHolder);
|
|
290
|
+
}
|
|
220
291
|
console.log("Client <-> Proxy sessionId: " + sessionId);
|
|
221
292
|
},
|
|
293
|
+
onsessionclosed: (sessionId) => {
|
|
294
|
+
webAppTransports.delete(sessionId);
|
|
295
|
+
serverTransports.delete(sessionId);
|
|
296
|
+
sessionHeaderHolders.delete(sessionId);
|
|
297
|
+
},
|
|
222
298
|
});
|
|
223
299
|
console.log("Created StreamableHttp client transport");
|
|
224
300
|
await webAppTransport.start();
|
|
@@ -229,23 +305,12 @@ app.post("/mcp", originValidationMiddleware, authMiddleware, async (req, res) =>
|
|
|
229
305
|
await webAppTransport.handleRequest(req, res, req.body);
|
|
230
306
|
}
|
|
231
307
|
catch (error) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
else {
|
|
237
|
-
console.log(`Received POST message for sessionId ${sessionId}`);
|
|
238
|
-
try {
|
|
239
|
-
const transport = webAppTransports.get(sessionId);
|
|
240
|
-
if (!transport) {
|
|
241
|
-
res.status(404).end("Transport not found for sessionId " + sessionId);
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
await transport.handleRequest(req, res);
|
|
308
|
+
if (error instanceof SseError && error.code === 401) {
|
|
309
|
+
console.error("Received 401 Unauthorized from MCP server:", error.message);
|
|
310
|
+
res.status(401).json(error);
|
|
311
|
+
return;
|
|
245
312
|
}
|
|
246
|
-
|
|
247
|
-
catch (error) {
|
|
248
|
-
console.error("Error in /mcp route:", error);
|
|
313
|
+
console.error("Error in /mcp POST route:", error);
|
|
249
314
|
res.status(500).json(error);
|
|
250
315
|
}
|
|
251
316
|
}
|
|
@@ -253,10 +318,9 @@ app.post("/mcp", originValidationMiddleware, authMiddleware, async (req, res) =>
|
|
|
253
318
|
app.delete("/mcp", originValidationMiddleware, authMiddleware, async (req, res) => {
|
|
254
319
|
const sessionId = req.headers["mcp-session-id"];
|
|
255
320
|
console.log(`Received DELETE message for sessionId ${sessionId}`);
|
|
256
|
-
let serverTransport;
|
|
257
321
|
if (sessionId) {
|
|
258
322
|
try {
|
|
259
|
-
serverTransport = serverTransports.get(sessionId);
|
|
323
|
+
const serverTransport = serverTransports.get(sessionId);
|
|
260
324
|
if (!serverTransport) {
|
|
261
325
|
res.status(404).end("Transport not found for sessionId " + sessionId);
|
|
262
326
|
}
|
|
@@ -264,6 +328,7 @@ app.delete("/mcp", originValidationMiddleware, authMiddleware, async (req, res)
|
|
|
264
328
|
await serverTransport.terminateSession();
|
|
265
329
|
webAppTransports.delete(sessionId);
|
|
266
330
|
serverTransports.delete(sessionId);
|
|
331
|
+
sessionHeaderHolders.delete(sessionId);
|
|
267
332
|
console.log(`Transports removed for sessionId ${sessionId}`);
|
|
268
333
|
}
|
|
269
334
|
res.status(200).end();
|
|
@@ -277,18 +342,7 @@ app.delete("/mcp", originValidationMiddleware, authMiddleware, async (req, res)
|
|
|
277
342
|
app.get("/stdio", originValidationMiddleware, authMiddleware, async (req, res) => {
|
|
278
343
|
try {
|
|
279
344
|
console.log("New STDIO connection request");
|
|
280
|
-
|
|
281
|
-
try {
|
|
282
|
-
serverTransport = await createTransport(req);
|
|
283
|
-
}
|
|
284
|
-
catch (error) {
|
|
285
|
-
if (error instanceof SseError && error.code === 401) {
|
|
286
|
-
console.error("Received 401 Unauthorized from MCP server. Authentication failure.");
|
|
287
|
-
res.status(401).json(error);
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
throw error;
|
|
291
|
-
}
|
|
345
|
+
const { transport: serverTransport } = await createTransport(req);
|
|
292
346
|
const proxyFullAddress = req.query.proxyFullAddress || "";
|
|
293
347
|
const prefix = proxyFullAddress || "";
|
|
294
348
|
const endpoint = `${prefix}/message`;
|
|
@@ -317,6 +371,7 @@ app.get("/stdio", originValidationMiddleware, authMiddleware, async (req, res) =
|
|
|
317
371
|
serverTransport.close();
|
|
318
372
|
webAppTransports.delete(webAppTransport.sessionId);
|
|
319
373
|
serverTransports.delete(webAppTransport.sessionId);
|
|
374
|
+
sessionHeaderHolders.delete(webAppTransport.sessionId);
|
|
320
375
|
console.error(message);
|
|
321
376
|
}
|
|
322
377
|
else {
|
|
@@ -382,6 +437,11 @@ app.get("/stdio", originValidationMiddleware, authMiddleware, async (req, res) =
|
|
|
382
437
|
});
|
|
383
438
|
}
|
|
384
439
|
catch (error) {
|
|
440
|
+
if (error instanceof SseError && error.code === 401) {
|
|
441
|
+
console.error("Received 401 Unauthorized from MCP server. Authentication failure.");
|
|
442
|
+
res.status(401).json(error);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
385
445
|
console.error("Error in /stdio route:", error);
|
|
386
446
|
res.status(500).json(error);
|
|
387
447
|
}
|
|
@@ -389,46 +449,39 @@ app.get("/stdio", originValidationMiddleware, authMiddleware, async (req, res) =
|
|
|
389
449
|
app.get("/sse", originValidationMiddleware, authMiddleware, async (req, res) => {
|
|
390
450
|
try {
|
|
391
451
|
console.log("New SSE connection request. NOTE: The SSE transport is deprecated and has been replaced by StreamableHttp");
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
else if (error instanceof SseError && error.code === 404) {
|
|
403
|
-
console.error("Received 404 not found from MCP server. Does the MCP server support SSE?");
|
|
404
|
-
res.status(404).json(error);
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
else if (JSON.stringify(error).includes("ECONNREFUSED")) {
|
|
408
|
-
console.error("Connection refused. Is the MCP server running?");
|
|
409
|
-
res.status(500).json(error);
|
|
410
|
-
}
|
|
411
|
-
else {
|
|
412
|
-
throw error;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
if (serverTransport) {
|
|
416
|
-
const proxyFullAddress = req.query.proxyFullAddress || "";
|
|
417
|
-
const prefix = proxyFullAddress || "";
|
|
418
|
-
const endpoint = `${prefix}/message`;
|
|
419
|
-
const webAppTransport = new SSEServerTransport(endpoint, res);
|
|
420
|
-
webAppTransports.set(webAppTransport.sessionId, webAppTransport);
|
|
421
|
-
console.log("Created client transport");
|
|
422
|
-
serverTransports.set(webAppTransport.sessionId, serverTransport);
|
|
423
|
-
console.log("Created server transport");
|
|
424
|
-
await webAppTransport.start();
|
|
425
|
-
mcpProxy({
|
|
426
|
-
transportToClient: webAppTransport,
|
|
427
|
-
transportToServer: serverTransport,
|
|
428
|
-
});
|
|
452
|
+
const { transport: serverTransport, headerHolder } = await createTransport(req);
|
|
453
|
+
const proxyFullAddress = req.query.proxyFullAddress || "";
|
|
454
|
+
const prefix = proxyFullAddress || "";
|
|
455
|
+
const endpoint = `${prefix}/message`;
|
|
456
|
+
const webAppTransport = new SSEServerTransport(endpoint, res);
|
|
457
|
+
webAppTransports.set(webAppTransport.sessionId, webAppTransport);
|
|
458
|
+
console.log("Created client transport");
|
|
459
|
+
serverTransports.set(webAppTransport.sessionId, serverTransport); // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
460
|
+
if (headerHolder) {
|
|
461
|
+
sessionHeaderHolders.set(webAppTransport.sessionId, headerHolder);
|
|
429
462
|
}
|
|
463
|
+
console.log("Created server transport");
|
|
464
|
+
await webAppTransport.start();
|
|
465
|
+
mcpProxy({
|
|
466
|
+
transportToClient: webAppTransport,
|
|
467
|
+
transportToServer: serverTransport,
|
|
468
|
+
});
|
|
430
469
|
}
|
|
431
470
|
catch (error) {
|
|
471
|
+
if (error instanceof SseError && error.code === 401) {
|
|
472
|
+
console.error("Received 401 Unauthorized from MCP server. Authentication failure.");
|
|
473
|
+
res.status(401).json(error);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
else if (error instanceof SseError && error.code === 404) {
|
|
477
|
+
console.error("Received 404 not found from MCP server. Does the MCP server support SSE?");
|
|
478
|
+
res.status(404).json(error);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
else if (JSON.stringify(error).includes("ECONNREFUSED")) {
|
|
482
|
+
console.error("Connection refused. Is the MCP server running?");
|
|
483
|
+
res.status(500).json(error);
|
|
484
|
+
}
|
|
432
485
|
console.error("Error in /sse route:", error);
|
|
433
486
|
res.status(500).json(error);
|
|
434
487
|
}
|
|
@@ -437,6 +490,10 @@ app.post("/message", originValidationMiddleware, authMiddleware, async (req, res
|
|
|
437
490
|
try {
|
|
438
491
|
const sessionId = req.query.sessionId;
|
|
439
492
|
console.log(`Received POST message for sessionId ${sessionId}`);
|
|
493
|
+
const headerHolder = sessionHeaderHolders.get(sessionId);
|
|
494
|
+
if (headerHolder) {
|
|
495
|
+
updateHeadersInPlace(headerHolder.headers, getHttpHeaders(req));
|
|
496
|
+
}
|
|
440
497
|
const transport = webAppTransports.get(sessionId);
|
|
441
498
|
if (!transport) {
|
|
442
499
|
res.status(404).end("Session not found");
|