@lifeaitools/clauth 1.5.31 → 1.5.33
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/cli/commands/serve.js +35 -18
- package/package.json +1 -1
package/cli/commands/serve.js
CHANGED
|
@@ -2938,10 +2938,11 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
2938
2938
|
// claude.ai ignores metadata endpoints and constructs /register, /authorize,
|
|
2939
2939
|
// /token from the domain root (issue #82). Keep well-known as 404 so claude.ai
|
|
2940
2940
|
// uses the fallback paths. OAuth endpoints below are live.
|
|
2941
|
+
// well-known OAuth discovery — HTML 404, no CORS, no JSON (match Express/regen-media)
|
|
2941
2942
|
if (reqPath.startsWith("/.well-known/oauth-protected-resource") ||
|
|
2942
2943
|
reqPath === "/.well-known/oauth-authorization-server") {
|
|
2943
|
-
res.writeHead(404, { "Content-Type": "
|
|
2944
|
-
return res.end(
|
|
2944
|
+
res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
|
|
2945
|
+
return res.end("<!DOCTYPE html><html><head><title>404</title></head><body><h1>Not Found</h1></body></html>");
|
|
2945
2946
|
}
|
|
2946
2947
|
|
|
2947
2948
|
// ── OAuth endpoints REMOVED ──────────────
|
|
@@ -2977,8 +2978,9 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
2977
2978
|
// fall through to MCP handling
|
|
2978
2979
|
}
|
|
2979
2980
|
|
|
2980
|
-
// ── MCP Streamable HTTP transport
|
|
2981
|
+
// ── MCP Streamable HTTP transport ──
|
|
2981
2982
|
// POST /sse, /mcp, /gws, /clauth — JSON-RPC over HTTP
|
|
2983
|
+
// claude.ai requires text/event-stream SSE format (like regen-media/Express).
|
|
2982
2984
|
if (method === "POST" && (reqPath === "/sse" || isMcpPath)) {
|
|
2983
2985
|
let body;
|
|
2984
2986
|
try { body = await readBody(req); } catch {
|
|
@@ -2991,26 +2993,37 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
2991
2993
|
const logMsg = `[${new Date().toISOString()}] Streamable HTTP [${reqPath}]: ${rpcMethod} id=${id}\n`;
|
|
2992
2994
|
try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
|
|
2993
2995
|
|
|
2996
|
+
// Check if client accepts SSE — claude.ai sends Accept: application/json, text/event-stream
|
|
2997
|
+
const acceptsSSE = (req.headers.accept || "").includes("text/event-stream");
|
|
2998
|
+
|
|
2999
|
+
// Helper: respond in SSE or JSON format based on client preference
|
|
3000
|
+
function mcpRespond(res, jsonRpcResponse) {
|
|
3001
|
+
if (acceptsSSE) {
|
|
3002
|
+
const ssePayload = `event: message\ndata: ${JSON.stringify(jsonRpcResponse)}\n\n`;
|
|
3003
|
+
res.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", ...CORS });
|
|
3004
|
+
return res.end(ssePayload);
|
|
3005
|
+
}
|
|
3006
|
+
res.writeHead(200, { "Content-Type": "application/json", ...CORS });
|
|
3007
|
+
return res.end(JSON.stringify(jsonRpcResponse));
|
|
3008
|
+
}
|
|
3009
|
+
|
|
2994
3010
|
// Notifications — no response needed
|
|
2995
3011
|
if (rpcMethod === "notifications/initialized" || rpcMethod === "initialized") {
|
|
2996
|
-
res.writeHead(
|
|
3012
|
+
res.writeHead(202, CORS);
|
|
2997
3013
|
return res.end();
|
|
2998
3014
|
}
|
|
2999
3015
|
|
|
3000
3016
|
if (rpcMethod === "initialize") {
|
|
3001
|
-
// Always return 2025-03-26 — returning 2025-11-25 causes claude.ai to require OAuth
|
|
3002
3017
|
const result = {
|
|
3003
3018
|
protocolVersion: "2025-03-26",
|
|
3004
3019
|
serverInfo: { name: serverNameForPath(reqPath), version: VERSION },
|
|
3005
|
-
capabilities: { tools: {} }
|
|
3020
|
+
capabilities: { tools: { listChanged: true } }
|
|
3006
3021
|
};
|
|
3007
|
-
res
|
|
3008
|
-
return res.end(JSON.stringify({ jsonrpc: "2.0", id, result }));
|
|
3022
|
+
return mcpRespond(res, { jsonrpc: "2.0", id, result });
|
|
3009
3023
|
}
|
|
3010
3024
|
|
|
3011
3025
|
if (rpcMethod === "tools/list") {
|
|
3012
|
-
res
|
|
3013
|
-
return res.end(JSON.stringify({ jsonrpc: "2.0", id, result: { tools: toolsForPath(reqPath) } }));
|
|
3026
|
+
return mcpRespond(res, { jsonrpc: "2.0", id, result: { tools: toolsForPath(reqPath) } });
|
|
3014
3027
|
}
|
|
3015
3028
|
|
|
3016
3029
|
if (rpcMethod === "tools/call") {
|
|
@@ -3020,17 +3033,14 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3020
3033
|
try {
|
|
3021
3034
|
const result = await handleMcpTool(vault, name, args || {});
|
|
3022
3035
|
password = vault.password;
|
|
3023
|
-
res
|
|
3024
|
-
return res.end(JSON.stringify({ jsonrpc: "2.0", id, result }));
|
|
3036
|
+
return mcpRespond(res, { jsonrpc: "2.0", id, result });
|
|
3025
3037
|
} catch (err) {
|
|
3026
|
-
res
|
|
3027
|
-
return res.end(JSON.stringify({ jsonrpc: "2.0", id, result: mcpError(`Internal error: ${err.message}`) }));
|
|
3038
|
+
return mcpRespond(res, { jsonrpc: "2.0", id, result: mcpError(`Internal error: ${err.message}`) });
|
|
3028
3039
|
}
|
|
3029
3040
|
}
|
|
3030
3041
|
|
|
3031
3042
|
// Unknown method
|
|
3032
|
-
res
|
|
3033
|
-
return res.end(JSON.stringify({ jsonrpc: "2.0", id, error: { code: -32601, message: `Unknown method: ${rpcMethod}` } }));
|
|
3043
|
+
return mcpRespond(res, { jsonrpc: "2.0", id, error: { code: -32601, message: `Unknown method: ${rpcMethod}` } });
|
|
3034
3044
|
}
|
|
3035
3045
|
|
|
3036
3046
|
// ── MCP SSE transport — /sse and namespaced paths ────
|
|
@@ -4301,11 +4311,18 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
4301
4311
|
}
|
|
4302
4312
|
}
|
|
4303
4313
|
|
|
4314
|
+
// OAuth paths — return plain HTML 404 (not JSON) to match Express/regen-media pattern.
|
|
4315
|
+
// claude.ai treats JSON 404 as "OAuth endpoint exists but errored" vs HTML 404 = "path doesn't exist"
|
|
4316
|
+
if (["/register", "/authorize", "/token"].includes(reqPath) ||
|
|
4317
|
+
reqPath.startsWith("/.well-known/oauth")) {
|
|
4318
|
+
res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
|
|
4319
|
+
return res.end("<!DOCTYPE html><html><head><title>404</title></head><body><h1>Not Found</h1></body></html>");
|
|
4320
|
+
}
|
|
4321
|
+
|
|
4304
4322
|
// Unknown route — don't count browser/MCP noise as auth failures
|
|
4305
|
-
// Don't count browser noise, MCP discovery probes, or OAuth probes as auth failures
|
|
4306
4323
|
const isBenign = reqPath.startsWith("/.well-known/") || [
|
|
4307
4324
|
"/favicon.ico", "/robots.txt", "/apple-touch-icon.png", "/apple-touch-icon-precomposed.png",
|
|
4308
|
-
"/sse", "/mcp", "/gws", "/clauth", "/message", "/
|
|
4325
|
+
"/sse", "/mcp", "/gws", "/clauth", "/message", "/shutdown", "/restart",
|
|
4309
4326
|
].includes(reqPath);
|
|
4310
4327
|
if (isBenign) {
|
|
4311
4328
|
res.writeHead(404, { "Content-Type": "application/json", ...CORS });
|