mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-07-08 07:16:18 +00:00
* 📦 chore: Bump @modelcontextprotocol/sdk to v1.29.0 * ♻️ refactor: Extract WWW-Authenticate Probe Helper for MCP OAuth * 🔐 fix: Prefer WWW-Authenticate resource_metadata Hint for MCP OAuth Per RFC 9728 §5.1, the `resource_metadata=<url>` parameter in a 401 `WWW-Authenticate: Bearer` challenge is the authoritative protected-resource metadata source. Path-aware `.well-known` discovery was winning over the hint, so split deployments that serve valid-but-wrong metadata at the path-aware endpoint stranded OAuth at defunct authorization servers. Threads the hint through `discoverOAuthProtectedResourceMetadata` via `opts.resourceMetadataUrl` in both startup detection and the OAuth handler, matching the behavior of Claude Desktop, the MCP Inspector, OpenAI tooling, and Microsoft Copilot Studio. Fixes #12761. * 🧵 fix: Thread OAuth-Aware fetchFn Through Resource-Metadata Probe Without this, admin-configured `oauthHeaders` (e.g. a gateway API key that fronts the MCP endpoint) were stripped from the probe, causing the gateway to 401 for the wrong reason and masking the real `WWW-Authenticate` hint. The helper now accepts a FetchLike and defaults to global fetch, so the startup detection path is unchanged while the handler passes its OAuth- aware wrapper through. * 🧹 refactor: Address MCP OAuth Probe Review Findings - Thread `fetchFn` through `probeResourceMetadataHint` so admin-configured `oauthHeaders` reach the probe (a gateway API key that fronts the MCP endpoint would otherwise 401 us for the wrong reason and hide the real Bearer challenge). - Skip the redundant HEAD request in `checkAuthErrorFallback` when the probe already observed a 401/403; fall back to a fresh HEAD only when every probe attempt threw (transient network error). - Narrow the oauth barrel: drop `export * from './resourceHint'` so the helper stays an internal module. - Add `scope` extraction coverage (`Bearer scope="read write"`) and a 403-only observation path; isolate `MCP_OAUTH_ON_AUTH_ERROR=true` in a dedicated suite so precise-outcome tests aren't muddied by the safety net. * ✅ fix: Use Zod Schema in MCP Reconnection-Storm Test Tool MCP SDK 1.28 tightened `McpServer.tool()` to require Zod schemas instead of plain JSON-Schema objects. Swap the `{ message: { type: 'string' } }` shape for `z.string()` so the fixture server spins up under SDK 1.29. * 🛡️ fix: Harden MCP OAuth resource_metadata Hint Against SSRF The `resource_metadata` URL is echoed from an untrusted MCP server, so handing it straight to the SDK lets a malicious server redirect discovery at private IPs, the cloud metadata service, or any host the admin did not intend to reach. Caught by the Copilot review on #12763. - `handler.ts`: run the hint through the same `validateOAuthUrl` / `allowedDomains` gate that already guards the authorization-server URL; drop it and fall back to path-aware discovery on rejection. - `detectOAuth.ts`: no admin-scoped allowedDomains here, so apply a strict `isSSRFTarget` + DNS resolution check and silently discard any hint pointing at a private/loopback/metadata address. - Tests cover both the hostname-list and DNS-resolution rejection paths and assert the SDK falls back to path-aware discovery unharmed. * 🧪 test: Mock ~/auth in fallback Suite for Consistency Matches the main `detectOAuth.test.ts` mock so the SSRF guards added in the previous commit don't touch the real `~/auth` module at test time. * 🔍 fix: Scope OAuth Fallback to HEAD + Parse Multi-Scheme WWW-Authenticate Two codex findings on #12763: - **P1**: the merged `authChallenge` flag was letting POST-only 401/403 flip the `MCP_OAUTH_ON_AUTH_ERROR` fallback, misclassifying WAF/CSRF-hardened endpoints (HEAD 200 + POST 403) as OAuth-required. Rename to `headAuthChallenge` and derive it only from the HEAD probe, matching the legacy fallback's HEAD-only semantics. Add a regression test. - **P2**: the SDK's `extractWWWAuthenticateParams` only inspects the first scheme token, so multi-scheme headers like `Basic realm="api", Bearer resource_metadata="..."` silently dropped the authoritative Bearer hint. Fall back to a regex across the full header when the SDK returns nothing but Bearer is present. Add a regression test covering the multi-scheme case. * 🧽 refactor: Tighten MCP OAuth Probe Semantics Addresses the second external review pass plus codex P2: - Merge the two stacked JSDoc blocks on `probeResourceMetadataHint` into one with a proper `@returns` section. - Only short-circuit HEAD when it delivered the `resource_metadata` hint itself — a Bearer-without-params HEAD now lets POST run, since some servers surface their hint only on POST and we were missing it. - Drop the unused `scope` field from `ResourceHintProbeResult`; no caller read it, and YAGNI beats a reserved field. - Remove the redundant `OAUTH_ON_AUTH_ERROR` guard inside `checkAuthErrorFallback` — the only call site already gates on it. - Codex P2: signal "HEAD status unknown" via `null` when the HEAD probe threw and POST returned non-auth. Previously that combination leaked a `{headAuthChallenge: false}` result and silently skipped the fallback's retry HEAD, which could misclassify OAuth-required servers after a transient HEAD failure. Regression tests cover every path: Bearer-no-hint-on-HEAD + hint-on-POST, multi-scheme `Basic + Bearer` headers, HEAD-threw + POST-200 retry, and the WAF/CSRF-only POST 403 case. * 🪥 polish: Tighten Probe Null-Guard Ordering + Add Malformed-Hint Test Two NITs from the follow-up review: - Move `bearerChallenge` computation after the `!wwwAuth` guard so the variable is only derived when it can be meaningfully `true`. The early-return path is now a clean unconditional exit. - Add a regression test that asserts `Bearer resource_metadata="not-a-url"` yields `resourceMetadataUrl: undefined` without throwing, locking in the try/catch safety net in `extractHintFromHeader` and the SDK parser alike.
130 lines
5.4 KiB
JSON
130 lines
5.4 KiB
JSON
{
|
|
"name": "@librechat/api",
|
|
"version": "1.7.28",
|
|
"type": "commonjs",
|
|
"description": "MCP services for LibreChat",
|
|
"main": "dist/index.js",
|
|
"module": "dist/index.es.js",
|
|
"types": "./dist/types/index.d.ts",
|
|
"exports": {
|
|
".": {
|
|
"require": "./dist/index.js",
|
|
"types": "./dist/types/index.d.ts"
|
|
}
|
|
},
|
|
"scripts": {
|
|
"clean": "rimraf dist",
|
|
"build": "npm run clean && rollup -c --bundleConfigAsCjs",
|
|
"build:dev": "npm run clean && NODE_ENV=development rollup -c --bundleConfigAsCjs",
|
|
"build:watch": "NODE_ENV=development rollup -c -w --bundleConfigAsCjs",
|
|
"build:watch:prod": "rollup -c -w --bundleConfigAsCjs",
|
|
"test": "jest --coverage --watch --testPathIgnorePatterns=\"\\.*integration\\.|\\.*helper\\.|__tests__/helpers/|\\.*manual\\.spec\\.\"",
|
|
"test:ci": "jest --coverage --ci --testPathIgnorePatterns=\"\\.*integration\\.|\\.*helper\\.|__tests__/helpers/|\\.*manual\\.spec\\.\"",
|
|
"test:cache-integration:core": "jest --testPathPatterns=\"src/cache/.*\\.cache_integration\\.spec\\.ts$\" --coverage=false",
|
|
"test:cache-integration:cluster": "jest --testPathPatterns=\"src/cluster/.*\\.cache_integration\\.spec\\.ts$\" --coverage=false --runInBand",
|
|
"test:cache-integration:mcp": "jest --testPathPatterns=\"src/mcp/.*\\.cache_integration\\.spec\\.ts$\" --coverage=false",
|
|
"test:cache-integration:stream": "jest --testPathPatterns=\"src/stream/.*\\.stream_integration\\.spec\\.ts$\" --coverage=false --runInBand --forceExit",
|
|
"test:cache-integration": "npm run test:cache-integration:core && npm run test:cache-integration:cluster && npm run test:cache-integration:mcp && npm run test:cache-integration:stream",
|
|
"test:s3-integration": "jest --testPathPatterns=\"src/storage/s3/.*\\.s3_integration\\.spec\\.ts$\" --coverage=false --runInBand",
|
|
"verify": "npm run test:ci",
|
|
"b:clean": "bun run rimraf dist",
|
|
"b:build": "bun run b:clean && bun run rollup -c --silent --bundleConfigAsCjs",
|
|
"b:build:dev": "bun run b:clean && NODE_ENV=development bun run rollup -c --silent --bundleConfigAsCjs",
|
|
"start:everything-sse": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/examples/everything/sse.ts",
|
|
"start:everything": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/demo/everything.ts",
|
|
"start:filesystem": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/demo/filesystem.ts",
|
|
"start:servers": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/demo/servers.ts"
|
|
},
|
|
"repository": {
|
|
"type": "git",
|
|
"url": "git+https://github.com/danny-avila/LibreChat.git"
|
|
},
|
|
"author": "",
|
|
"license": "ISC",
|
|
"bugs": {
|
|
"url": "https://github.com/danny-avila/LibreChat/issues"
|
|
},
|
|
"homepage": "https://librechat.ai",
|
|
"devDependencies": {
|
|
"@babel/preset-env": "^7.21.5",
|
|
"@babel/preset-react": "^7.18.6",
|
|
"@babel/preset-typescript": "^7.21.0",
|
|
"@rollup/plugin-alias": "^5.1.0",
|
|
"@rollup/plugin-commonjs": "^29.0.0",
|
|
"@rollup/plugin-json": "^6.1.0",
|
|
"@rollup/plugin-node-resolve": "^15.1.0",
|
|
"@rollup/plugin-replace": "^5.0.5",
|
|
"@rollup/plugin-typescript": "^12.1.2",
|
|
"@types/bun": "^1.2.15",
|
|
"@types/express": "^5.0.0",
|
|
"@types/express-session": "^1.18.2",
|
|
"@types/jest": "^29.5.2",
|
|
"@types/jsonwebtoken": "^9.0.0",
|
|
"@types/multer": "^1.4.13",
|
|
"@types/node": "^20.3.0",
|
|
"@types/node-fetch": "^2.6.13",
|
|
"@types/react": "^18.2.18",
|
|
"@types/winston": "^2.4.4",
|
|
"@types/yauzl": "^2.10.3",
|
|
"aws-sdk-client-mock": "^4.1.0",
|
|
"jest": "^30.2.0",
|
|
"jest-junit": "^16.0.0",
|
|
"jszip": "^3.10.1",
|
|
"librechat-data-provider": "*",
|
|
"mammoth": "^1.11.0",
|
|
"mongodb": "^6.14.2",
|
|
"pdfjs-dist": "^5.4.624",
|
|
"rimraf": "^6.1.3",
|
|
"rollup": "^4.34.9",
|
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
"ts-node": "^10.9.2",
|
|
"typescript": "^5.0.4",
|
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
|
"yauzl": "^3.2.1"
|
|
},
|
|
"publishConfig": {
|
|
"registry": "https://registry.npmjs.org/"
|
|
},
|
|
"peerDependencies": {
|
|
"@anthropic-ai/vertex-sdk": "^0.14.3",
|
|
"@aws-sdk/client-bedrock-runtime": "^3.1013.0",
|
|
"@aws-sdk/client-s3": "^3.980.0",
|
|
"@azure/identity": "^4.7.0",
|
|
"@azure/search-documents": "^12.0.0",
|
|
"@azure/storage-blob": "^12.30.0",
|
|
"@google/genai": "^1.19.0",
|
|
"@keyv/redis": "^4.3.3",
|
|
"@langchain/core": "^0.3.80",
|
|
"@librechat/agents": "^3.1.68",
|
|
"@librechat/data-schemas": "*",
|
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
"@smithy/node-http-handler": "^4.4.5",
|
|
"ai-tokenizer": "^1.0.6",
|
|
"axios": "^1.15.0",
|
|
"connect-redis": "^8.1.0",
|
|
"eventsource": "^3.0.2",
|
|
"express": "^5.1.0",
|
|
"express-session": "^1.18.2",
|
|
"firebase": "^11.0.2",
|
|
"form-data": "^4.0.4",
|
|
"google-auth-library": "^9.15.1",
|
|
"https-proxy-agent": "^7.0.6",
|
|
"ioredis": "^5.3.2",
|
|
"js-yaml": "^4.1.1",
|
|
"jsonwebtoken": "^9.0.0",
|
|
"keyv": "^5.3.2",
|
|
"keyv-file": "^5.1.2",
|
|
"librechat-data-provider": "*",
|
|
"mammoth": "^1.11.0",
|
|
"mathjs": "^15.2.0",
|
|
"memorystore": "^1.6.7",
|
|
"mongoose": "^8.12.1",
|
|
"node-fetch": "2.7.0",
|
|
"pdfjs-dist": "^5.4.624",
|
|
"rate-limit-redis": "^4.2.0",
|
|
"sharp": "^0.33.5",
|
|
"undici": "^7.24.1",
|
|
"yauzl": "^3.2.1",
|
|
"zod": "^3.22.4"
|
|
}
|
|
}
|