@mcp-use/inspector 20.0.0-beta.16 → 20.0.0-beta.17

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.
Files changed (73) hide show
  1. package/README.md +25 -58
  2. package/dist/app/inspector.css +2 -0
  3. package/dist/{cdn → app}/inspector.js +7855 -7629
  4. package/dist/cli.js +292 -286
  5. package/dist/client/auth/manufact-auth.d.ts +1 -1
  6. package/dist/client/auth/manufact-auth.d.ts.map +1 -1
  7. package/dist/client/index.js +1 -1
  8. package/dist/server/{chunk-XCNS5CIW.js → chunk-2K7OKCVV.js} +67 -67
  9. package/dist/server/{chunk-UJLXHNQP.js → chunk-EKD2XITW.js} +3 -2
  10. package/dist/server/{chunk-FHC5ZGMM.js → chunk-EPPZCSJQ.js} +23 -77
  11. package/dist/server/chunk-JWDVSQED.js +156 -0
  12. package/dist/server/{chunk-M5TZA42I.js → chunk-PZF2PUDE.js} +15 -11
  13. package/dist/server/{chunk-R5ZEVRE5.js → chunk-RFJAZOK7.js} +27 -8
  14. package/dist/server/{chunk-OWR2BIBW.js → chunk-RKFQG67Z.js} +1 -3
  15. package/dist/server/{chunk-K2F2AJOI.js → chunk-V5A5XLFJ.js} +148 -5
  16. package/dist/server/cli.js +11 -16
  17. package/dist/server/create-dev-api-app.d.ts.map +1 -1
  18. package/dist/server/create-dev-api-app.js +4 -4
  19. package/dist/server/favicon-links.d.ts +2 -2
  20. package/dist/server/favicon-links.d.ts.map +1 -1
  21. package/dist/server/index.d.ts +2 -3
  22. package/dist/server/index.d.ts.map +1 -1
  23. package/dist/server/index.js +8 -13
  24. package/dist/server/{cdn-shell.d.ts → inspector-shell.d.ts} +7 -5
  25. package/dist/server/inspector-shell.d.ts.map +1 -0
  26. package/dist/server/inspector-shell.js +10 -0
  27. package/dist/server/middleware.d.ts +52 -27
  28. package/dist/server/middleware.d.ts.map +1 -1
  29. package/dist/server/middleware.js +8 -10
  30. package/dist/server/proxy/index.js +2 -2
  31. package/dist/server/proxy/mcp-proxy.d.ts.map +1 -1
  32. package/dist/server/proxy/mcp-proxy.js +2 -2
  33. package/dist/server/proxy/oauth-proxy.d.ts +2 -0
  34. package/dist/server/proxy/oauth-proxy.d.ts.map +1 -1
  35. package/dist/server/proxy/oauth-proxy.js +1 -1
  36. package/dist/server/proxy-routes.d.ts.map +1 -1
  37. package/dist/server/proxy-routes.js +3 -3
  38. package/dist/server/server.js +4 -4
  39. package/dist/server/static-assets.d.ts +3 -3
  40. package/dist/server/static-assets.d.ts.map +1 -1
  41. package/dist/server/static-assets.js +3 -3
  42. package/dist/server/version.js +1 -1
  43. package/dist/server/vite-dev-api-plugin.js +4 -4
  44. package/dist/web/assets/index-Dj_E94k4.css +1 -0
  45. package/dist/web/assets/index-O3gsi3le.js +643 -0
  46. package/dist/web/index.html +3 -3
  47. package/package.json +30 -11
  48. package/dist/cdn/inspector.css +0 -2
  49. package/dist/server/asset-urls.d.ts +0 -15
  50. package/dist/server/asset-urls.d.ts.map +0 -1
  51. package/dist/server/asset-urls.js +0 -13
  52. package/dist/server/cdn-shell.d.ts.map +0 -1
  53. package/dist/server/cdn-shell.js +0 -12
  54. package/dist/server/chunk-J4FNYLBF.js +0 -54
  55. package/dist/server/chunk-JZT7N2BJ.js +0 -58
  56. package/dist/server/chunk-QXOCY5ZE.js +0 -72
  57. package/dist/server/favicon-static.d.ts +0 -4
  58. package/dist/server/favicon-static.d.ts.map +0 -1
  59. package/dist/server/favicon-static.js +0 -8
  60. package/dist/web/assets/index-BgHcVJO3.js +0 -643
  61. package/dist/web/assets/index-bCIH1Sdx.css +0 -1
  62. /package/dist/{cdn → app}/apple-touch-icon.png +0 -0
  63. /package/dist/{cdn → app}/favicon-96x96.png +0 -0
  64. /package/dist/{cdn → app}/favicon-black.svg +0 -0
  65. /package/dist/{cdn → app}/favicon-white.svg +0 -0
  66. /package/dist/{cdn → app}/favicon.ico +0 -0
  67. /package/dist/{cdn → app}/favicon.svg +0 -0
  68. /package/dist/{cdn → app}/providers/anthropic.png +0 -0
  69. /package/dist/{cdn → app}/providers/google.png +0 -0
  70. /package/dist/{cdn → app}/providers/openai.png +0 -0
  71. /package/dist/{cdn → app}/site.webmanifest +0 -0
  72. /package/dist/{cdn → app}/web-app-manifest-192x192.png +0 -0
  73. /package/dist/{cdn → app}/web-app-manifest-512x512.png +0 -0
@@ -13,7 +13,7 @@ interface ManufactAuthSnapshot {
13
13
  }
14
14
  export declare function canShareManufactSession(inspectorUrl: string, cloudOrigin: string): boolean;
15
15
  export declare function getSharedManufactSession(chatApiUrl: string): Promise<ManufactUser | null>;
16
- export declare function authorizeManufact(chatApiUrl: string, options?: {
16
+ export declare function authorizeManufact(chatApiUrl: string, _options?: {
17
17
  isRetry?: boolean;
18
18
  }): Promise<void>;
19
19
  export declare function completeManufactAuthorization(url?: URL): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"manufact-auth.d.ts","sourceRoot":"","sources":["../../../src/client/auth/manufact-auth.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AA6BD,UAAU,oBAAoB;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC;CAClC;AAgDD,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAClB,OAAO,CAeT;AAiUD,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAE9B;AAgED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9B,OAAO,CAAC,IAAI,CAAC,CAsEf;AAED,wBAAsB,6BAA6B,CACjD,GAAG,MAAgC,GAClC,OAAO,CAAC,IAAI,CAAC,CAmCf;AA0FD,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,oBAAoB,CAAC,MAAM,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxB;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG;IACtE,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B,CAiDA"}
1
+ {"version":3,"file":"manufact-auth.d.ts","sourceRoot":"","sources":["../../../src/client/auth/manufact-auth.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AA6BD,UAAU,oBAAoB;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC;CAClC;AAgDD,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAClB,OAAO,CAeT;AAiUD,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAE9B;AAgED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/B,OAAO,CAAC,IAAI,CAAC,CAsEf;AAED,wBAAsB,6BAA6B,CACjD,GAAG,MAAgC,GAClC,OAAO,CAAC,IAAI,CAAC,CAmCf;AA0FD,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,oBAAoB,CAAC,MAAM,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxB;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG;IACtE,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B,CAiDA"}
@@ -15774,7 +15774,7 @@ async function load(origin) {
15774
15774
  loading.set(origin, promise);
15775
15775
  return promise;
15776
15776
  }
15777
- async function authorizeManufact(chatApiUrl, options) {
15777
+ async function authorizeManufact(chatApiUrl, _options) {
15778
15778
  const origin = authOrigin(chatApiUrl);
15779
15779
  pendingAuthorizeChatApiUrl = chatApiUrl;
15780
15780
  setSkipSharedSession(origin, false);
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  isSafeProxyTarget
3
- } from "./chunk-K2F2AJOI.js";
3
+ } from "./chunk-V5A5XLFJ.js";
4
4
 
5
5
  // src/server/proxy/mcp-proxy.ts
6
6
  import { cors } from "hono/cors";
7
7
  import { logger } from "hono/logger";
8
+ var MAX_REDIRECTS = 3;
8
9
  function isOpenEndedSseResponse(contentType, contentLength) {
9
10
  return contentType.includes("text/event-stream") && !contentLength;
10
11
  }
@@ -88,81 +89,48 @@ function mountMcpProxy(app, options = {}) {
88
89
  const requestHeaders = c.req.header();
89
90
  for (const [key, value] of Object.entries(requestHeaders)) {
90
91
  const lowerKey = key.toLowerCase();
91
- if (!lowerKey.startsWith("x-proxy-") && !lowerKey.startsWith("x-target-") && !lowerKey.startsWith("x-mcp-") && !lowerKey.startsWith("x-forwarded-") && !lowerKey.startsWith("cf-") && lowerKey !== "x-original-host" && lowerKey !== "host" && lowerKey !== "accept-encoding" && lowerKey !== "cdn-loop") {
92
+ if (!lowerKey.startsWith("x-proxy-") && !lowerKey.startsWith("x-target-") && !lowerKey.startsWith("x-mcp-") && !lowerKey.startsWith("x-forwarded-") && !lowerKey.startsWith("cf-") && lowerKey !== "x-original-host" && lowerKey !== "host" && lowerKey !== "origin" && lowerKey !== "referer" && lowerKey !== "cookie" && lowerKey !== "proxy-authorization" && lowerKey !== "accept-encoding" && lowerKey !== "cdn-loop" && !lowerKey.startsWith("sec-fetch-")) {
92
93
  headers[key] = value;
93
94
  }
94
95
  }
95
96
  headers["Accept-Encoding"] = "identity";
96
- try {
97
- const targetUrlObj = new URL(targetUrl);
98
- headers.Host = targetUrlObj.host;
99
- } catch {
100
- return c.json({ error: "Invalid target URL" }, 400);
101
- }
102
97
  const body = method !== "GET" && method !== "HEAD" ? new Uint8Array(await c.req.arrayBuffer()).slice() : void 0;
103
- const response = await fetch(targetUrl, {
104
- method,
105
- headers,
106
- body,
107
- redirect: "manual"
108
- });
109
- if (response.status >= 300 && response.status < 400) {
98
+ let currentUrl = targetUrl;
99
+ let currentMethod = method;
100
+ let currentBody = body;
101
+ for (let redirectCount = 0; ; redirectCount += 1) {
102
+ const response = await fetch(currentUrl, {
103
+ method: currentMethod,
104
+ headers,
105
+ body: currentBody,
106
+ redirect: "manual"
107
+ });
110
108
  const location = response.headers.get("location");
111
- if (location) {
112
- const redirectUrl = new URL(location, targetUrl).toString();
113
- if (!await isSafeProxyTarget(
114
- redirectUrl,
115
- options.allowLoopback ?? false
116
- ) || options.validateRequest && !await options.validateRequest(redirectUrl, c)) {
117
- return c.json({ error: "Redirect target is not allowed" }, 403);
118
- }
119
- const redirectResponse = await fetch(redirectUrl, {
120
- method,
121
- headers,
122
- body,
123
- redirect: "manual"
124
- });
125
- const redirectHeaders = {};
126
- redirectResponse.headers.forEach((value, key) => {
127
- const lowerKey = key.toLowerCase();
128
- if (lowerKey !== "content-encoding" && lowerKey !== "transfer-encoding" && lowerKey !== "content-length") {
129
- redirectHeaders[key] = value;
130
- }
131
- });
132
- return new Response(redirectResponse.body, {
133
- status: redirectResponse.status,
134
- statusText: redirectResponse.statusText,
135
- headers: redirectHeaders
136
- });
109
+ if (!(response.status >= 300 && response.status < 400 && location)) {
110
+ return proxyResponse(response);
137
111
  }
138
- }
139
- const responseHeaders = {};
140
- response.headers.forEach((value, key) => {
141
- const lowerKey = key.toLowerCase();
142
- if (lowerKey !== "content-encoding" && lowerKey !== "transfer-encoding" && lowerKey !== "content-length") {
143
- responseHeaders[key] = value;
112
+ if (redirectCount >= MAX_REDIRECTS) {
113
+ return c.json({ error: "Too many upstream redirects" }, 502);
144
114
  }
145
- });
146
- const contentType = response.headers.get("content-type") || "";
147
- const upstreamContentLength = response.headers.get("content-length");
148
- const isTrueStream = isOpenEndedSseResponse(
149
- contentType,
150
- upstreamContentLength
151
- );
152
- if (isTrueStream) {
153
- return new Response(response.body, {
154
- status: response.status,
155
- statusText: response.statusText,
156
- headers: responseHeaders
157
- });
115
+ const redirectUrl = new URL(location, currentUrl);
116
+ if (!await isSafeProxyTarget(
117
+ redirectUrl.toString(),
118
+ options.allowLoopback ?? false
119
+ ) || options.validateRequest && !await options.validateRequest(redirectUrl.toString(), c)) {
120
+ return c.json({ error: "Redirect target is not allowed" }, 403);
121
+ }
122
+ if (redirectUrl.origin !== new URL(currentUrl).origin) {
123
+ deleteHeader(headers, "authorization");
124
+ deleteHeader(headers, "proxy-authorization");
125
+ }
126
+ if (response.status === 303 || (response.status === 301 || response.status === 302) && currentMethod === "POST") {
127
+ currentMethod = "GET";
128
+ currentBody = void 0;
129
+ deleteHeader(headers, "content-type");
130
+ deleteHeader(headers, "content-length");
131
+ }
132
+ currentUrl = redirectUrl.toString();
158
133
  }
159
- const bodyBuffer = await response.arrayBuffer();
160
- responseHeaders["Content-Length"] = String(bodyBuffer.byteLength);
161
- return new Response(bodyBuffer, {
162
- status: response.status,
163
- statusText: response.statusText,
164
- headers: responseHeaders
165
- });
166
134
  } catch (error) {
167
135
  const message = error instanceof Error ? error.message : "Unknown error";
168
136
  const targetUrl = c.req.header("X-Target-URL");
@@ -192,6 +160,38 @@ function mountMcpProxy(app, options = {}) {
192
160
  }
193
161
  });
194
162
  }
163
+ function deleteHeader(headers, name) {
164
+ for (const key of Object.keys(headers)) {
165
+ if (key.toLowerCase() === name) delete headers[key];
166
+ }
167
+ }
168
+ async function proxyResponse(response) {
169
+ const responseHeaders = {};
170
+ response.headers.forEach((value, key) => {
171
+ const lowerKey = key.toLowerCase();
172
+ if (lowerKey !== "content-encoding" && lowerKey !== "transfer-encoding" && lowerKey !== "content-length" && lowerKey !== "set-cookie") {
173
+ responseHeaders[key] = value;
174
+ }
175
+ });
176
+ const isTrueStream = isOpenEndedSseResponse(
177
+ response.headers.get("content-type") || "",
178
+ response.headers.get("content-length")
179
+ );
180
+ if (isTrueStream) {
181
+ return new Response(response.body, {
182
+ status: response.status,
183
+ statusText: response.statusText,
184
+ headers: responseHeaders
185
+ });
186
+ }
187
+ const bodyBuffer = await response.arrayBuffer();
188
+ responseHeaders["Content-Length"] = String(bodyBuffer.byteLength);
189
+ return new Response(bodyBuffer, {
190
+ status: response.status,
191
+ statusText: response.statusText,
192
+ headers: responseHeaders
193
+ });
194
+ }
195
195
 
196
196
  export {
197
197
  isOpenEndedSseResponse,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  mountMcpProxy
3
- } from "./chunk-XCNS5CIW.js";
3
+ } from "./chunk-2K7OKCVV.js";
4
4
  import {
5
5
  mountOAuthProxy
6
- } from "./chunk-K2F2AJOI.js";
6
+ } from "./chunk-V5A5XLFJ.js";
7
7
 
8
8
  // src/server/proxy-routes.ts
9
9
  function registerInspectorProxyRoutes(app, config, basePath = "") {
@@ -25,6 +25,7 @@ function registerInspectorProxyRoutes(app, config, basePath = "") {
25
25
  if (mountOAuth) {
26
26
  mountOAuthProxy(app, {
27
27
  basePath: p("/inspector/api/oauth"),
28
+ callbackPath: p("/inspector/oauth/callback"),
28
29
  enableLogging: true,
29
30
  allowedOrigins: config?.oauthProxyAllowedOrigins ?? [],
30
31
  allowLoopback
@@ -1,76 +1,21 @@
1
1
  import {
2
2
  registerInspectorStaticAssets
3
- } from "./chunk-M5TZA42I.js";
3
+ } from "./chunk-PZF2PUDE.js";
4
4
  import {
5
5
  getInspectorVersion
6
- } from "./chunk-R5ZEVRE5.js";
7
- import {
8
- resolveInspectorAssetUrls
9
- } from "./chunk-JZT7N2BJ.js";
10
- import {
11
- registerInspectorFaviconStatic
12
- } from "./chunk-J4FNYLBF.js";
6
+ } from "./chunk-RFJAZOK7.js";
13
7
  import {
14
8
  renderInspectorFaviconLinks
15
9
  } from "./chunk-WFT4JHYI.js";
16
10
 
17
- // src/server/cdn-shell.ts
11
+ // src/server/inspector-shell.ts
18
12
  var INSPECTOR_VERSION = getInspectorVersion();
19
- var INSPECTOR_VERSION_RESOLVER_URL = "https://data.jsdelivr.com/v1/packages/npm/@mcp-use/inspector/resolved?specifier=beta";
20
- var INSPECTOR_CDN_PACKAGE_URL = "https://cdn.jsdelivr.net/npm/@mcp-use/inspector";
21
13
  var OAUTH_POPUP_CLOSED_HTML = `<!doctype html>
22
14
  <html lang="en"><head><meta charset="utf-8"><title>Signed in</title><meta name="robots" content="noindex"><style>html,body{margin:0;height:100%;display:flex;align-items:center;justify-content:center;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:#4b5563;background:#fff}</style></head>
23
15
  <body><div>Signed in. You can close this window.</div>
24
16
  <script>try{if(window.opener&&!window.opener.closed)window.opener.postMessage({type:"manufact:oauth-complete"},"*")}catch(e){}try{window.close()}catch(e){}</script>
25
17
  </body></html>`;
26
- function renderDefaultInspectorAssetLoader() {
27
- return `<script type="module">
28
- const resolverUrl = ${JSON.stringify(INSPECTOR_VERSION_RESOLVER_URL)};
29
- const packageUrl = ${JSON.stringify(INSPECTOR_CDN_PACKAGE_URL)};
30
- const versionPattern = /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/;
31
-
32
- try {
33
- const controller = new AbortController();
34
- const timeout = setTimeout(() => controller.abort(), 10_000);
35
- let response;
36
- try {
37
- response = await fetch(resolverUrl, { signal: controller.signal });
38
- } finally {
39
- clearTimeout(timeout);
40
- }
41
- if (!response.ok) {
42
- throw new Error(\`Inspector version resolver returned HTTP \${response.status}\`);
43
- }
44
-
45
- const resolved = await response.json();
46
- if (
47
- resolved?.name !== "@mcp-use/inspector" ||
48
- typeof resolved.version !== "string" ||
49
- !versionPattern.test(resolved.version)
50
- ) {
51
- throw new Error("Inspector version resolver returned an invalid release");
52
- }
53
-
54
- const version = resolved.version;
55
- const assetBase = \`\${packageUrl}@\${version}/dist/cdn\`;
56
- const stylesheet = document.createElement("link");
57
- stylesheet.rel = "stylesheet";
58
- stylesheet.href = \`\${assetBase}/inspector.css\`;
59
- document.head.append(stylesheet);
60
-
61
- window.__INSPECTOR_VERSION__ = version;
62
- await import(\`\${assetBase}/inspector.js\`);
63
- } catch (error) {
64
- console.error("[Inspector] Failed to load the latest Inspector release:", error);
65
- document.querySelector(".mcp-boot-spinner")?.remove();
66
- const label = document.querySelector(".mcp-boot-label");
67
- if (label) {
68
- label.textContent = "Unable to load Inspector. Reload to try again.";
69
- }
70
- }
71
- </script>`;
72
- }
73
- function generateCdnShellHtml(config, basePath, assets) {
18
+ function generateInspectorShellHtml(config, basePath, assets) {
74
19
  const scripts = [];
75
20
  if (config?.basePath !== void 0) {
76
21
  scripts.push(
@@ -105,22 +50,20 @@ function generateCdnShellHtml(config, basePath, assets) {
105
50
  `<script>window.__MCP_USE_ANONYMIZED_TELEMETRY__ = false;try{localStorage.setItem("MCP_USE_ANONYMIZED_TELEMETRY","false");}catch(e){}</script>`
106
51
  );
107
52
  }
53
+ if (process.env.MCP_USE_DEV_CLI === "1") {
54
+ scripts.push(`<script>window.__MCP_DEV_CLI__ = true;</script>`);
55
+ }
108
56
  const runtimeScripts = scripts.join("\n ");
109
- const assetPreconnects = assets.resolveLatest ? ` <link rel="preconnect" href="https://data.jsdelivr.com" crossorigin />
110
- <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />` : "";
111
- const stylesheetTag = assets.resolveLatest ? "" : ` <link rel="stylesheet" href="${assets.cssUrl}" />`;
112
- const assetLoader = assets.resolveLatest ? renderDefaultInspectorAssetLoader() : `<script type="module" src="${assets.jsUrl}"></script>`;
113
57
  return `<!doctype html>
114
58
  <html lang="en">
115
59
  <head>
116
60
  <meta charset="UTF-8" />
117
- ${renderInspectorFaviconLinks(basePath)}
61
+ ${renderInspectorFaviconLinks(`${basePath}/inspector/assets`)}
118
62
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
119
63
  <link rel="preconnect" href="https://fonts.googleapis.com" />
120
64
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
121
- ${assetPreconnects}
122
65
  <link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;500;700&display=swap" rel="stylesheet" />
123
- ${stylesheetTag}
66
+ <link rel="stylesheet" href="${assets.cssUrl}" />
124
67
  <title>Inspector | mcp-use</title>
125
68
  <meta name="description" content="Free, open-source MCP Inspector by mcp-use. Connect to any MCP server, test tools, prompts, and resources, inspect RPC logs, and debug MCP apps \u2014 all in your browser." />
126
69
  <style>
@@ -200,12 +143,17 @@ ${stylesheetTag}
200
143
  </div>
201
144
  </div>
202
145
  </div>
203
- ${assetLoader}
146
+ <script type="module" src="${assets.jsUrl}"></script>
204
147
  </body>
205
148
  </html>`;
206
149
  }
207
- function registerInspectorCdnShell(app, config, basePath = "") {
208
- const assets = resolveInspectorAssetUrls(config?.inspectorMode, basePath);
150
+ function registerInspectorShell(app, config, basePath = "") {
151
+ const assetsPath = `${basePath}/inspector/assets`;
152
+ const version = encodeURIComponent(INSPECTOR_VERSION);
153
+ const assets = {
154
+ jsUrl: `${assetsPath}/inspector.js?v=${version}`,
155
+ cssUrl: `${assetsPath}/inspector.css?v=${version}`
156
+ };
209
157
  const p = (suffix) => `${basePath}${suffix}`;
210
158
  const effectiveConfig = {
211
159
  ...config,
@@ -214,10 +162,11 @@ function registerInspectorCdnShell(app, config, basePath = "") {
214
162
  disableTelemetry: config?.disableTelemetry ?? process.env.MCP_USE_ANONYMIZED_TELEMETRY === "false"
215
163
  };
216
164
  const serveShell = (c) => {
217
- const assets2 = resolveInspectorAssetUrls(config?.inspectorMode, basePath);
218
- return c.html(generateCdnShellHtml(effectiveConfig, basePath, assets2));
165
+ return c.html(
166
+ generateInspectorShellHtml(effectiveConfig, basePath, assets)
167
+ );
219
168
  };
220
- registerInspectorFaviconStatic(app, basePath);
169
+ registerInspectorStaticAssets(app, assetsPath);
221
170
  app.get(
222
171
  p("/inspector/oauth-popup-closed.html"),
223
172
  (c) => c.html(OAUTH_POPUP_CLOSED_HTML)
@@ -237,17 +186,14 @@ function registerInspectorCdnShell(app, config, basePath = "") {
237
186
  }
238
187
  return serveShell(c);
239
188
  });
240
- if (basePath === "") {
189
+ if (basePath === "" && config?.rootRedirect !== false) {
241
190
  app.get("/", (c) => {
242
191
  const url = new URL(c.req.url);
243
192
  return c.redirect(`${p("/inspector")}${url.search}`);
244
193
  });
245
194
  }
246
- if (assets.useLocal) {
247
- registerInspectorStaticAssets(app, basePath);
248
- }
249
195
  }
250
196
 
251
197
  export {
252
- registerInspectorCdnShell
198
+ registerInspectorShell
253
199
  };
@@ -0,0 +1,156 @@
1
+ import {
2
+ registerInspectorShell
3
+ } from "./chunk-EPPZCSJQ.js";
4
+ import {
5
+ registerInspectorProxyRoutes
6
+ } from "./chunk-EKD2XITW.js";
7
+
8
+ // src/server/middleware.ts
9
+ import { Hono } from "hono";
10
+ import { Readable } from "stream";
11
+ function mountInspector(appOrOptions, maybeOptions) {
12
+ if (isMountableApp(appOrOptions)) {
13
+ const app = appOrOptions;
14
+ const options2 = maybeOptions;
15
+ const basePath2 = normalizeInspectorBasePath(options2?.basePath);
16
+ if (isHonoApp(app)) {
17
+ registerInspectorRoutes(app, options2, basePath2);
18
+ return;
19
+ }
20
+ const inspectorApp = createInspectorApp(options2, basePath2);
21
+ const inspectorFetch = toInspectorFetchHandler(inspectorApp);
22
+ app.use(
23
+ createExpressInspectorMiddleware(
24
+ inspectorFetch,
25
+ inspectorApp.routes.map((route) => route.path)
26
+ )
27
+ );
28
+ return;
29
+ }
30
+ const options = appOrOptions;
31
+ const basePath = normalizeInspectorBasePath(options?.basePath);
32
+ return toInspectorFetchHandler(createInspectorApp(options, basePath));
33
+ }
34
+ function createInspectorApp(options, basePath) {
35
+ const app = new Hono();
36
+ registerInspectorRoutes(app, options, basePath);
37
+ return app;
38
+ }
39
+ function registerInspectorRoutes(app, options, basePath) {
40
+ const routesConfig = {
41
+ oauthProxyAllowedOrigins: options?.oauthProxyAllowedOrigins ?? [],
42
+ // mountInspector is development tooling. Loopback is therefore useful and
43
+ // safe by default, while callers embedding it elsewhere can opt out.
44
+ oauthProxyAllowLoopback: options?.oauthProxyAllowLoopback ?? true
45
+ };
46
+ if (options?.autoConnectUrl !== void 0) {
47
+ routesConfig.autoConnectUrl = options.autoConnectUrl;
48
+ } else {
49
+ app.get(`${basePath}/inspector/config.json`, (c) => {
50
+ const requestUrl = new URL(c.req.url);
51
+ return c.json({ autoConnectUrl: `${requestUrl.origin}${basePath}` });
52
+ });
53
+ }
54
+ registerInspectorProxyRoutes(app, routesConfig, basePath);
55
+ registerInspectorShell(
56
+ app,
57
+ {
58
+ devMode: options?.devMode ?? true,
59
+ sandboxOrigin: options?.sandboxOrigin,
60
+ inspectorMode: "embedded",
61
+ rootRedirect: false,
62
+ basePath,
63
+ proxyUrl: `${basePath}/inspector/api/proxy`,
64
+ manufactChatUrl: options?.manufactChatUrl
65
+ },
66
+ basePath
67
+ );
68
+ }
69
+ function toInspectorFetchHandler(app) {
70
+ return (request) => Promise.resolve(app.fetch(request));
71
+ }
72
+ function createExpressInspectorMiddleware(fetch, registeredPaths) {
73
+ const exactPaths = new Set(
74
+ registeredPaths.filter((path) => !path.includes("*"))
75
+ );
76
+ const prefixPaths = registeredPaths.filter((path) => path.endsWith("/*")).map((path) => path.slice(0, -1));
77
+ return (req, res, next) => {
78
+ const url = new URL(req.originalUrl || req.url || "", requestOrigin(req));
79
+ if (!exactPaths.has(url.pathname) && !prefixPaths.some((prefix) => url.pathname.startsWith(prefix))) {
80
+ next();
81
+ return;
82
+ }
83
+ const request = expressRequestToFetchRequest(req, url);
84
+ void fetch(request).then((fetchResponse) => writeFetchResponse(res, fetchResponse)).catch(next);
85
+ };
86
+ }
87
+ function requestOrigin(req) {
88
+ const forwardedProtocol = firstHeaderValue(req.headers["x-forwarded-proto"]);
89
+ const protocol = forwardedProtocol || req.protocol || "http";
90
+ const forwardedHost = firstHeaderValue(req.headers["x-forwarded-host"]);
91
+ const host = forwardedHost || firstHeaderValue(req.headers.host) || "localhost";
92
+ return `${protocol}://${host}`;
93
+ }
94
+ function firstHeaderValue(value) {
95
+ return Array.isArray(value) ? value[0] ?? "" : (value ?? "").split(",")[0].trim();
96
+ }
97
+ function expressRequestToFetchRequest(req, url) {
98
+ const init = {
99
+ method: req.method,
100
+ headers: req.headers
101
+ };
102
+ if (req.method !== "GET" && req.method !== "HEAD") {
103
+ if (req.body !== void 0) {
104
+ init.body = bodyToRequestBody(req.body);
105
+ } else {
106
+ init.body = Readable.toWeb(req);
107
+ init.duplex = "half";
108
+ }
109
+ }
110
+ return new globalThis.Request(url, init);
111
+ }
112
+ function bodyToRequestBody(body) {
113
+ if (typeof body === "string" || body instanceof ArrayBuffer || ArrayBuffer.isView(body) || body instanceof Blob || body instanceof FormData || body instanceof URLSearchParams || body instanceof ReadableStream) {
114
+ return body;
115
+ }
116
+ return JSON.stringify(body);
117
+ }
118
+ async function writeFetchResponse(res, fetchResponse) {
119
+ res.status(fetchResponse.status);
120
+ fetchResponse.headers.forEach((value, key) => {
121
+ if (key !== "set-cookie") res.setHeader(key, value);
122
+ });
123
+ const setCookies = fetchResponse.headers.getSetCookie();
124
+ if (setCookies.length > 0) res.setHeader("set-cookie", setCookies);
125
+ if (!fetchResponse.body) {
126
+ res.end();
127
+ return;
128
+ }
129
+ const reader = fetchResponse.body.getReader();
130
+ while (true) {
131
+ const { done, value } = await reader.read();
132
+ if (done) break;
133
+ res.write(value);
134
+ }
135
+ res.end();
136
+ }
137
+ function isMountableApp(value) {
138
+ if (!value || typeof value !== "object" && typeof value !== "function") {
139
+ return false;
140
+ }
141
+ return typeof value.use === "function" || isHonoApp(value);
142
+ }
143
+ function isHonoApp(value) {
144
+ return !!value && typeof value === "object" && typeof value.fetch === "function" && typeof value.route === "function";
145
+ }
146
+ function normalizeInspectorBasePath(raw) {
147
+ if (raw === void 0) return "/mcp";
148
+ let value = raw.trim().replace(/\/{2,}/g, "/").replace(/\/+$/, "");
149
+ if (value === "" || value === "/") return "";
150
+ if (!value.startsWith("/")) value = `/${value}`;
151
+ return value;
152
+ }
153
+
154
+ export {
155
+ mountInspector
156
+ };
@@ -10,45 +10,49 @@ var CONTENT_TYPES = {
10
10
  ".ico": "image/x-icon",
11
11
  ".webmanifest": "application/manifest+json"
12
12
  };
13
- function resolveDistCdnDir() {
13
+ function resolveInspectorAppDir() {
14
14
  const here = path.dirname(fileURLToPath(import.meta.url));
15
15
  for (const dir of [
16
- path.resolve(here, "cdn"),
16
+ path.resolve(here, "app"),
17
17
  // dist/cli.js (bundled)
18
- path.resolve(here, "../cdn")
18
+ path.resolve(here, "../app"),
19
19
  // dist/server/*.js
20
+ path.resolve(here, "../../dist/app")
21
+ // src/server/*.ts (workspace dev)
20
22
  ]) {
21
23
  if (existsSync(path.join(dir, "inspector.js"))) {
22
24
  return dir;
23
25
  }
24
26
  }
25
27
  throw new Error(
26
- "Inspector bundle not found (expected dist/cdn/inspector.js)"
28
+ "Inspector bundle not found (expected dist/app/inspector.js)"
27
29
  );
28
30
  }
29
- function registerInspectorStaticAssets(app, basePath = "") {
30
- const cdnDir = resolveDistCdnDir();
31
- const mountPath = `${basePath}/dist/cdn`;
31
+ function registerInspectorStaticAssets(app, mountPath = "/inspector/assets") {
32
+ const appDir = resolveInspectorAppDir();
32
33
  app.get(`${mountPath}/*`, (c) => {
33
34
  const subPath = c.req.path.slice(mountPath.length);
34
35
  const relative = subPath.startsWith("/") ? subPath.slice(1) : subPath;
35
36
  if (!relative || relative.includes("..")) {
36
37
  return c.notFound();
37
38
  }
38
- const file = path.resolve(cdnDir, relative);
39
- const root = cdnDir.endsWith(path.sep) ? cdnDir : `${cdnDir}${path.sep}`;
39
+ const file = path.resolve(appDir, relative);
40
+ const root = appDir.endsWith(path.sep) ? appDir : `${appDir}${path.sep}`;
40
41
  if (!file.startsWith(root) || !existsSync(file)) {
41
42
  return c.notFound();
42
43
  }
43
44
  const ext = path.extname(file);
44
45
  return c.body(readFileSync(file), 200, {
45
46
  "Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
46
- "Cache-Control": "public, max-age=3600"
47
+ // Standalone assets use stable URLs across CLI restarts. Revalidate them
48
+ // so a rebuilt or upgraded Inspector cannot keep running an hour-old UI
49
+ // bundle that predates its storage migrations or proxy contract.
50
+ "Cache-Control": "no-cache"
47
51
  });
48
52
  });
49
53
  }
50
54
 
51
55
  export {
52
- resolveDistCdnDir,
56
+ resolveInspectorAppDir,
53
57
  registerInspectorStaticAssets
54
58
  };
@@ -2,7 +2,7 @@
2
2
  var package_default = {
3
3
  name: "@mcp-use/inspector",
4
4
  type: "module",
5
- version: "20.0.0-beta.15",
5
+ version: "20.0.0-beta.16",
6
6
  description: "MCP Inspector - A tool for inspecting and debugging MCP servers",
7
7
  author: "",
8
8
  license: "MIT",
@@ -24,7 +24,13 @@ var package_default = {
24
24
  exports: {
25
25
  ".": {
26
26
  types: "./dist/server/index.d.ts",
27
- import: "./dist/server/index.js"
27
+ import: "./dist/server/index.js",
28
+ default: "./dist/server/index.js"
29
+ },
30
+ "./dev": {
31
+ types: "./dist/server/index.d.ts",
32
+ import: "./dist/server/index.js",
33
+ default: "./dist/server/index.js"
28
34
  },
29
35
  "./proxy": {
30
36
  types: "./dist/server/proxy-routes.d.ts",
@@ -48,12 +54,13 @@ var package_default = {
48
54
  "dev:client": "pnpm run dev",
49
55
  "dev:server": "VITE_DEV=true tsx watch src/server/server.ts",
50
56
  "dev:standalone": "tsx watch src/server/server.ts",
51
- build: "npm run build:client && npm run build:client-exports && npm run build:server && npm run build:cli && npm run build:cdn",
52
- "build:cdn": "vite build --config vite.cdn.config.ts",
53
- "watch:cdn": "vite build --watch --config vite.cdn.config.ts",
54
- "preview:cdn": "vite preview --config vite.cdn.config.ts --host 127.0.0.1",
55
- "serve:cdn": "pnpm build:cdn && pnpm preview:cdn",
56
- "analyze:cdn": "ANALYZE=true vite build --config vite.cdn.config.ts",
57
+ clean: "rimraf dist",
58
+ build: "npm run clean && npm run build:client && npm run build:client-exports && npm run build:server && npm run build:cli && npm run build:app",
59
+ "build:app": "vite build --config vite.app.config.ts",
60
+ "watch:app": "vite build --watch --config vite.app.config.ts",
61
+ "preview:app": "vite preview --config vite.app.config.ts --host 127.0.0.1",
62
+ "serve:app": "pnpm build:app && pnpm preview:app",
63
+ "analyze:app": "ANALYZE=true vite build --config vite.app.config.ts",
57
64
  "build:client": "vite build",
58
65
  "build:client-exports": "tsup --config tsup.client.ts && tsc -p tsconfig.client.json --emitDeclarationOnly --declaration",
59
66
  "build:server": "tsup 'src/server/**/*.ts' --format esm --out-dir dist/server && tsc -p tsconfig.server.json --emitDeclarationOnly --declaration",
@@ -93,8 +100,20 @@ var package_default = {
93
100
  "react-router": "^7.12.0"
94
101
  },
95
102
  peerDependenciesMeta: {
103
+ "@mcp-use/client": {
104
+ optional: true
105
+ },
96
106
  express: {
97
107
  optional: true
108
+ },
109
+ react: {
110
+ optional: true
111
+ },
112
+ "react-dom": {
113
+ optional: true
114
+ },
115
+ "react-router": {
116
+ optional: true
98
117
  }
99
118
  },
100
119
  dependencies: {