Skip to content

Commit 3ceaa47

Browse files
Vu-Johnclaude
andcommitted
[logging] inspector: convert second-wave hosted routes to typed events
Adds mcp.tool.execution.failed to the event map and emits it from the /execute callback catch. Converts web/oauth.ts proxy, metadata, and debug/proxy routes to emit mcp.oauth.proxy.failed. resources.ts and prompts.ts have no explicit error paths beyond withEphemeralConnection delegation so are left unchanged. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent d3e3d78 commit 3ceaa47

3 files changed

Lines changed: 65 additions & 12 deletions

File tree

mcpjam-inspector/server/routes/web/oauth.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,20 @@ import {
1010
import { ErrorCode, WebRouteError, mapRuntimeError } from "./errors.js";
1111
import { bearerAuthMiddleware } from "../../middleware/bearer-auth.js";
1212
import { guestRateLimitMiddleware } from "../../middleware/guest-rate-limit.js";
13+
import { getRequestLogger } from "../../utils/request-logger.js";
14+
import { classifyError } from "../../utils/error-classify.js";
1315

1416
const oauthWeb = new Hono();
1517

18+
function safeHostname(url: string | undefined): string {
19+
if (!url) return "unknown";
20+
try {
21+
return new URL(url).hostname || url;
22+
} catch {
23+
return url;
24+
}
25+
}
26+
1627
// Require some form of bearer token (guest or WorkOS) on all OAuth proxy routes
1728
oauthWeb.use("*", bearerAuthMiddleware);
1829

@@ -79,8 +90,10 @@ function getConvexHttpUrl(): string {
7990
* Body: { url: string, method?: string, body?: object, headers?: object }
8091
*/
8192
oauthWeb.post("/proxy", async (c) => {
93+
let proxyUrl: string | undefined;
8294
try {
8395
const { url, method, body, headers } = await c.req.json();
96+
proxyUrl = url;
8497
const result = await executeOAuthProxy({
8598
url,
8699
method,
@@ -90,6 +103,12 @@ oauthWeb.post("/proxy", async (c) => {
90103
});
91104
return c.json(result);
92105
} catch (error) {
106+
getRequestLogger(c, "routes.web.oauth").event("mcp.oauth.proxy.failed", {
107+
targetUrlHost: safeHostname(proxyUrl),
108+
oauthPhase: "proxy",
109+
errorCode: classifyError(error),
110+
...(error instanceof OAuthProxyError ? { statusCode: error.status } : {}),
111+
});
93112
return webErrorCompat(c, toRouteError(error));
94113
}
95114
});
@@ -101,17 +120,17 @@ oauthWeb.post("/proxy", async (c) => {
101120
* Mirrors /api/mcp/oauth/metadata with HTTPS-only + private IP blocking.
102121
*/
103122
oauthWeb.get("/metadata", async (c) => {
123+
const metadataUrl = c.req.query("url");
104124
try {
105-
const url = c.req.query("url");
106-
if (!url) {
125+
if (!metadataUrl) {
107126
throw new WebRouteError(
108127
400,
109128
ErrorCode.VALIDATION_ERROR,
110129
"Missing url parameter",
111130
);
112131
}
113132

114-
const result = await fetchOAuthMetadata(url, true);
133+
const result = await fetchOAuthMetadata(metadataUrl, true);
115134
if ("status" in result && result.status !== undefined) {
116135
throw new WebRouteError(
117136
result.status,
@@ -122,6 +141,12 @@ oauthWeb.get("/metadata", async (c) => {
122141

123142
return c.json(result.metadata);
124143
} catch (error) {
144+
getRequestLogger(c, "routes.web.oauth").event("mcp.oauth.proxy.failed", {
145+
targetUrlHost: safeHostname(metadataUrl),
146+
oauthPhase: "metadata",
147+
errorCode: classifyError(error),
148+
...(error instanceof OAuthProxyError ? { statusCode: error.status } : {}),
149+
});
125150
return webErrorCompat(c, toRouteError(error));
126151
}
127152
});
@@ -161,8 +186,10 @@ oauthWeb.post("/session", async (c) => {
161186
* Body: { url: string, method?: string, body?: object, headers?: object }
162187
*/
163188
oauthWeb.post("/debug/proxy", async (c) => {
189+
let proxyUrl: string | undefined;
164190
try {
165191
const { url, method, body, headers } = await c.req.json();
192+
proxyUrl = url;
166193
const result = await executeDebugOAuthProxy({
167194
url,
168195
method,
@@ -172,6 +199,12 @@ oauthWeb.post("/debug/proxy", async (c) => {
172199
});
173200
return c.json(result);
174201
} catch (error) {
202+
getRequestLogger(c, "routes.web.oauth").event("mcp.oauth.proxy.failed", {
203+
targetUrlHost: safeHostname(proxyUrl),
204+
oauthPhase: "proxy",
205+
errorCode: classifyError(error),
206+
...(error instanceof OAuthProxyError ? { statusCode: error.status } : {}),
207+
});
175208
return webErrorCompat(c, toRouteError(error));
176209
}
177210
});

mcpjam-inspector/server/routes/web/tools.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
WebRouteError,
88
} from "./auth.js";
99
import { listTools } from "../../utils/route-handlers.js";
10+
import { getRequestLogger } from "../../utils/request-logger.js";
11+
import { classifyError } from "../../utils/error-classify.js";
1012

1113
const tools = new Hono();
1214

@@ -26,15 +28,28 @@ tools.post("/execute", async (c) =>
2628
);
2729
}
2830

29-
const result = await manager.executeTool(
30-
body.serverId,
31-
body.toolName,
32-
body.parameters,
33-
);
34-
return {
35-
status: "completed",
36-
result,
37-
};
31+
try {
32+
const result = await manager.executeTool(
33+
body.serverId,
34+
body.toolName,
35+
body.parameters,
36+
);
37+
return {
38+
status: "completed",
39+
result,
40+
};
41+
} catch (error) {
42+
getRequestLogger(c, "routes.web.tools").event(
43+
"mcp.tool.execution.failed",
44+
{
45+
toolName: body.toolName,
46+
serverId: body.serverId,
47+
errorCode: classifyError(error),
48+
},
49+
{ error: error instanceof Error ? error : undefined },
50+
);
51+
throw error;
52+
}
3853
}),
3954
);
4055

mcpjam-inspector/server/utils/log-events.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ export type RequestEventMap = {
108108
cspMode?: "permissive" | "widget-declared";
109109
errorCode: string;
110110
};
111+
"mcp.tool.execution.failed": {
112+
toolName: string;
113+
serverId?: string;
114+
errorCode: string;
115+
};
111116
};
112117

113118
export type SystemEventMap = {

0 commit comments

Comments
 (0)