Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions mcpjam-inspector/server/routes/web/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,20 @@ import {
import { ErrorCode, WebRouteError, mapRuntimeError } from "./errors.js";
import { bearerAuthMiddleware } from "../../middleware/bearer-auth.js";
import { guestRateLimitMiddleware } from "../../middleware/guest-rate-limit.js";
import { getRequestLogger } from "../../utils/request-logger.js";
import { classifyError } from "../../utils/error-classify.js";

const oauthWeb = new Hono();

function safeHostname(url: string | undefined): string {
if (!url) return "unknown";
try {
return new URL(url).hostname || url;
} catch {
return url;
}
}

// Require some form of bearer token (guest or WorkOS) on all OAuth proxy routes
oauthWeb.use("*", bearerAuthMiddleware);

Expand Down Expand Up @@ -79,8 +90,10 @@ function getConvexHttpUrl(): string {
* Body: { url: string, method?: string, body?: object, headers?: object }
*/
oauthWeb.post("/proxy", async (c) => {
let proxyUrl: string | undefined;
try {
const { url, method, body, headers } = await c.req.json();
proxyUrl = url;
const result = await executeOAuthProxy({
url,
method,
Expand All @@ -90,6 +103,12 @@ oauthWeb.post("/proxy", async (c) => {
});
return c.json(result);
} catch (error) {
getRequestLogger(c, "routes.web.oauth").event("mcp.oauth.proxy.failed", {
targetUrlHost: safeHostname(proxyUrl),
oauthPhase: "proxy",
errorCode: classifyError(error),
...(error instanceof OAuthProxyError ? { statusCode: error.status } : {}),
});
return webErrorCompat(c, toRouteError(error));
}
});
Expand All @@ -101,17 +120,17 @@ oauthWeb.post("/proxy", async (c) => {
* Mirrors /api/mcp/oauth/metadata with HTTPS-only + private IP blocking.
*/
oauthWeb.get("/metadata", async (c) => {
const metadataUrl = c.req.query("url");
try {
const url = c.req.query("url");
if (!url) {
if (!metadataUrl) {
throw new WebRouteError(
400,
ErrorCode.VALIDATION_ERROR,
"Missing url parameter",
);
}

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

return c.json(result.metadata);
} catch (error) {
getRequestLogger(c, "routes.web.oauth").event("mcp.oauth.proxy.failed", {
targetUrlHost: safeHostname(metadataUrl),
oauthPhase: "metadata",
errorCode: classifyError(error),
...(error instanceof OAuthProxyError ? { statusCode: error.status } : {}),
});
return webErrorCompat(c, toRouteError(error));
}
});
Expand Down Expand Up @@ -161,8 +186,10 @@ oauthWeb.post("/session", async (c) => {
* Body: { url: string, method?: string, body?: object, headers?: object }
*/
oauthWeb.post("/debug/proxy", async (c) => {
let proxyUrl: string | undefined;
try {
const { url, method, body, headers } = await c.req.json();
proxyUrl = url;
const result = await executeDebugOAuthProxy({
url,
method,
Expand All @@ -172,6 +199,12 @@ oauthWeb.post("/debug/proxy", async (c) => {
});
return c.json(result);
} catch (error) {
getRequestLogger(c, "routes.web.oauth").event("mcp.oauth.proxy.failed", {
targetUrlHost: safeHostname(proxyUrl),
oauthPhase: "proxy",
errorCode: classifyError(error),
...(error instanceof OAuthProxyError ? { statusCode: error.status } : {}),
});
return webErrorCompat(c, toRouteError(error));
}
});
Expand Down
33 changes: 24 additions & 9 deletions mcpjam-inspector/server/routes/web/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
WebRouteError,
} from "./auth.js";
import { listTools } from "../../utils/route-handlers.js";
import { getRequestLogger } from "../../utils/request-logger.js";
import { classifyError } from "../../utils/error-classify.js";

const tools = new Hono();

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

const result = await manager.executeTool(
body.serverId,
body.toolName,
body.parameters,
);
return {
status: "completed",
result,
};
try {
const result = await manager.executeTool(
body.serverId,
body.toolName,
body.parameters,
);
return {
status: "completed",
result,
};
} catch (error) {
getRequestLogger(c, "routes.web.tools").event(
"mcp.tool.execution.failed",
{
toolName: body.toolName,
serverId: body.serverId,
errorCode: classifyError(error),
},
{ error: error instanceof Error ? error : undefined },
);
throw error;
}
}),
);

Expand Down
5 changes: 5 additions & 0 deletions mcpjam-inspector/server/utils/log-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ export type RequestEventMap = {
cspMode?: "permissive" | "widget-declared";
errorCode: string;
};
"mcp.tool.execution.failed": {
toolName: string;
serverId?: string;
errorCode: string;
};
};

export type SystemEventMap = {
Expand Down
Loading