@lifeaitools/clauth 1.5.7 → 1.5.9
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 +46 -12
- package/package.json +1 -1
package/cli/commands/serve.js
CHANGED
|
@@ -2870,13 +2870,18 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
2870
2870
|
}
|
|
2871
2871
|
|
|
2872
2872
|
// ── OAuth Discovery (RFC 9728 + RFC 8414) ──────────────
|
|
2873
|
-
if (reqPath
|
|
2874
|
-
reqPath === "/.well-known/oauth-protected-resource/mcp" ||
|
|
2875
|
-
reqPath === "/.well-known/oauth-protected-resource/sse") {
|
|
2873
|
+
if (reqPath.startsWith("/.well-known/oauth-protected-resource")) {
|
|
2876
2874
|
const base = oauthBase();
|
|
2875
|
+
// Derive resource URL from the well-known path suffix
|
|
2876
|
+
// /.well-known/oauth-protected-resource → /mcp
|
|
2877
|
+
// /.well-known/oauth-protected-resource/mcp → /mcp
|
|
2878
|
+
// /.well-known/oauth-protected-resource/gws → /gws
|
|
2879
|
+
// /.well-known/oauth-protected-resource/clauth → /clauth
|
|
2880
|
+
const suffix = reqPath.replace("/.well-known/oauth-protected-resource", "").replace(/^\//, "") || "mcp";
|
|
2881
|
+
const resourcePath = suffix === "sse" ? "mcp" : suffix;
|
|
2877
2882
|
res.writeHead(200, { "Content-Type": "application/json", ...CORS });
|
|
2878
2883
|
return res.end(JSON.stringify({
|
|
2879
|
-
resource: `${base}
|
|
2884
|
+
resource: `${base}/${resourcePath}`,
|
|
2880
2885
|
authorization_servers: [base],
|
|
2881
2886
|
scopes_supported: ["mcp:tools"],
|
|
2882
2887
|
bearer_methods_supported: ["header"],
|
|
@@ -3000,9 +3005,23 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3000
3005
|
return res.end(JSON.stringify({ access_token: accessToken, token_type: "Bearer", expires_in: 86400 }));
|
|
3001
3006
|
}
|
|
3002
3007
|
|
|
3008
|
+
// ── MCP path helpers ──
|
|
3009
|
+
const MCP_PATHS = ["/mcp", "/gws", "/clauth"];
|
|
3010
|
+
const isMcpPath = MCP_PATHS.includes(reqPath);
|
|
3011
|
+
function toolsForPath(p) {
|
|
3012
|
+
if (p === "/gws") return MCP_TOOLS.filter(t => t.name.startsWith("gws_"));
|
|
3013
|
+
if (p === "/clauth") return MCP_TOOLS.filter(t => t.name.startsWith("clauth_"));
|
|
3014
|
+
return MCP_TOOLS; // /mcp — all tools
|
|
3015
|
+
}
|
|
3016
|
+
function serverNameForPath(p) {
|
|
3017
|
+
if (p === "/gws") return "gws";
|
|
3018
|
+
if (p === "/clauth") return "clauth";
|
|
3019
|
+
return "clauth";
|
|
3020
|
+
}
|
|
3021
|
+
|
|
3003
3022
|
// ── MCP OAuth-protected endpoint (for claude.ai web) ──
|
|
3004
|
-
// POST /mcp — requires Bearer token; returns 401 to trigger OAuth flow
|
|
3005
|
-
if (method === "POST" &&
|
|
3023
|
+
// POST /mcp|/gws|/clauth — requires Bearer token; returns 401 to trigger OAuth flow
|
|
3024
|
+
if (method === "POST" && isMcpPath) {
|
|
3006
3025
|
const authHeader = req.headers.authorization;
|
|
3007
3026
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
3008
3027
|
const base = oauthBase();
|
|
@@ -3023,9 +3042,24 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3023
3042
|
// fall through to MCP handling below
|
|
3024
3043
|
}
|
|
3025
3044
|
|
|
3045
|
+
// For namespaced paths, send path-specific 401 so claude.ai fetches the right resource metadata
|
|
3046
|
+
if (method === "POST" && (reqPath === "/gws" || reqPath === "/clauth")) {
|
|
3047
|
+
const authHeader = req.headers.authorization;
|
|
3048
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
3049
|
+
const base = oauthBase();
|
|
3050
|
+
const pathName = reqPath.slice(1); // "gws" or "clauth"
|
|
3051
|
+
res.writeHead(401, {
|
|
3052
|
+
"Content-Type": "application/json",
|
|
3053
|
+
"WWW-Authenticate": `Bearer resource_metadata="${base}/.well-known/oauth-protected-resource/${pathName}"`,
|
|
3054
|
+
...CORS,
|
|
3055
|
+
});
|
|
3056
|
+
return res.end(JSON.stringify({ error: "unauthorized" }));
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
|
|
3026
3060
|
// ── MCP Streamable HTTP transport (2025-03-26 spec) ──
|
|
3027
|
-
// POST /sse
|
|
3028
|
-
if (method === "POST" && (reqPath === "/sse" ||
|
|
3061
|
+
// POST /sse, /mcp, /gws, /clauth — JSON-RPC over HTTP
|
|
3062
|
+
if (method === "POST" && (reqPath === "/sse" || isMcpPath)) {
|
|
3029
3063
|
let body;
|
|
3030
3064
|
try { body = await readBody(req); } catch {
|
|
3031
3065
|
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
@@ -3034,7 +3068,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3034
3068
|
|
|
3035
3069
|
const id = body.id;
|
|
3036
3070
|
const rpcMethod = body.method;
|
|
3037
|
-
const logMsg = `[${new Date().toISOString()}] Streamable HTTP: ${rpcMethod} id=${id}\n`;
|
|
3071
|
+
const logMsg = `[${new Date().toISOString()}] Streamable HTTP [${reqPath}]: ${rpcMethod} id=${id}\n`;
|
|
3038
3072
|
try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
|
|
3039
3073
|
|
|
3040
3074
|
// Notifications — no response needed
|
|
@@ -3046,7 +3080,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3046
3080
|
if (rpcMethod === "initialize") {
|
|
3047
3081
|
const result = {
|
|
3048
3082
|
protocolVersion: "2025-03-26",
|
|
3049
|
-
serverInfo: { name:
|
|
3083
|
+
serverInfo: { name: serverNameForPath(reqPath), version: VERSION },
|
|
3050
3084
|
capabilities: { tools: {} }
|
|
3051
3085
|
};
|
|
3052
3086
|
res.writeHead(200, { "Content-Type": "application/json", ...CORS });
|
|
@@ -3055,7 +3089,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3055
3089
|
|
|
3056
3090
|
if (rpcMethod === "tools/list") {
|
|
3057
3091
|
res.writeHead(200, { "Content-Type": "application/json", ...CORS });
|
|
3058
|
-
return res.end(JSON.stringify({ jsonrpc: "2.0", id, result: { tools:
|
|
3092
|
+
return res.end(JSON.stringify({ jsonrpc: "2.0", id, result: { tools: toolsForPath(reqPath) } }));
|
|
3059
3093
|
}
|
|
3060
3094
|
|
|
3061
3095
|
if (rpcMethod === "tools/call") {
|
|
@@ -4321,7 +4355,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
4321
4355
|
// Don't count browser noise, MCP discovery probes, or OAuth probes as auth failures
|
|
4322
4356
|
const isBenign = reqPath.startsWith("/.well-known/") || [
|
|
4323
4357
|
"/favicon.ico", "/robots.txt", "/apple-touch-icon.png", "/apple-touch-icon-precomposed.png",
|
|
4324
|
-
"/sse", "/mcp", "/message", "/register", "/authorize", "/token", "/shutdown", "/restart",
|
|
4358
|
+
"/sse", "/mcp", "/gws", "/clauth", "/message", "/register", "/authorize", "/token", "/shutdown", "/restart",
|
|
4325
4359
|
].includes(reqPath);
|
|
4326
4360
|
if (isBenign) {
|
|
4327
4361
|
res.writeHead(404, { "Content-Type": "application/json", ...CORS });
|