@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.
@@ -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": "application/json", ...CORS });
2944
- return res.end(JSON.stringify({ error: "not_found" }));
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 (2025-03-26 spec) ──
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(204, CORS);
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.writeHead(200, { "Content-Type": "application/json", ...CORS });
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.writeHead(200, { "Content-Type": "application/json", ...CORS });
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.writeHead(200, { "Content-Type": "application/json", ...CORS });
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.writeHead(200, { "Content-Type": "application/json", ...CORS });
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.writeHead(200, { "Content-Type": "application/json", ...CORS });
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", "/register", "/authorize", "/token", "/shutdown", "/restart",
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 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.5.31",
3
+ "version": "1.5.33",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {