Production-ready Model Context Protocol (MCP) server for CourtListener legal
data. It exposes legal research tools over MCP (stdio and HTTP), with
deployment-ready support for local use, self-hosted remote deployments, hosted
Cloudflare Workers OAuth, structured logging, caching, and CI testing.
- MCP server built on
@modelcontextprotocol/sdk - 49 legal research tools backed by CourtListener API v4
- Three deployment contracts: local
stdio, self-hosted remote HTTP, and hosted remote OAuth - Hosted MCP OAuth transport via Cloudflare Workers OAuth Provider
- Worker-owned browser auth handoff for hosted sign-in and approval
- Optional queue-backed async MCP jobs for durable hosted execution
- Optional Cloudflare Analytics Engine telemetry for route, DO, Turnstile, and async job signals
- Optional Turnstile enforcement on session bootstrap and AI chat routes
- Built-in health checks and metrics endpoints for operations
- Prebuilt MCP client config examples in
configs/
src/core server, tool handlers, API integration, worker runtimeconfigs/ready-to-use MCP client configs for local, self-hosted, and hosted clientsdocs/deployment, testing, and operational documentationtest/unit/integration/e2e test suitesscripts/deployment helpers, diagnostics, inspector tooling, key management
Best for ChatGPT, Claude, Codex, and other remote clients that should not run the server locally:
{
"mcpServers": {
"courtlistener": {
"url": "https://courtlistener-mcp.<subdomain>.workers.dev/mcp",
"transport": "streamable-http"
}
}
}Best for privacy, local development, and bring-your-own-auth setups. The npm
package is not currently published, so the local stdio server should be run
from this repository checkout:
- Download ZIP: https://github.com/blakeox/courtlistener-mcp/archive/refs/heads/main.zip
- Download tar.gz: https://github.com/blakeox/courtlistener-mcp/archive/refs/heads/main.tar.gz
git clone https://github.com/blakeox/courtlistener-mcp.git
cd courtlistener-mcp
pnpm install
pnpm build
node dist/index.js --setupOr run it directly:
node dist/index.jsIf you want a local shell command after building:
npm link
courtlistener-mcp --setupgit clone https://github.com/blakeox/courtlistener-mcp.git
cd courtlistener-mcp
pnpm install
pnpm build
node dist/http.jscp .env.production .env
docker compose -f docker-compose.prod.yml up -dThis repository is configured to publish from .github/workflows/release.yml
when you push a version tag such as v1.0.5.
You have two supported auth paths:
- Repository secret: create an npm publish token and store it as the GitHub
Actions secret
NPM_TOKEN. - Trusted publishing: connect the npm package to this GitHub repository and let npm trust GitHub Actions OIDC, with no token stored in GitHub.
Once one of those is configured, publish with:
git tag v1.0.5
git push origin v1.0.5- Run the MCP server on the same machine as the client
- No hosted auth required
- Best for privacy-sensitive workflows, local development, and bring-your-own-hosting
- Deploy the HTTP/streamable HTTP runtime on your own infrastructure
- Bring your own auth if you need it (
OIDC_*, service token, proxy auth, etc.) - Best for teams that want remote access without using the hosted CourtListener endpoint
- Use the CourtListener Cloudflare Worker as the remote MCP endpoint
- The Worker is the MCP OAuth server and exposes
/mcp,/oauth/authorize,/token,/register, and discovery metadata - The Worker also owns the browser auth handoff routes on the same origin
(
/auth/start,/auth/callback,/oauth/approve,/oauth/logout) - Legacy
/authorize,/auth/approve, and/auth/logoutaliases remain in the runtime for compatibility, but production now publishes the/oauth/*endpoints as canonical - Keep Logto as the current upstream hosted identity provider if you want a managed OIDC tenant; the Worker-facing contract stays generic OIDC
- Best for ChatGPT, Claude, Codex, and browser-native remote clients
Prebuilt configs are provided in configs/:
local-stdio.jsonself-hosted-remote-http.jsonhosted-remote-claude.jsonhosted-oauth-chatgpt.jsonhosted-oauth-codex.jsoncodex.tomlclaude-desktop.jsonclaude-desktop-remote.jsoncursor.jsoncontinue-dev.jsonvscode-copilot.jsonzed.jsonopenai-chatgpt.jsoncodex.json
Explicit contract examples:
configs/local-stdio.jsonfor localstdioconfigs/self-hosted-remote-http.jsonfor self-hosted remote HTTPconfigs/hosted-remote-claude.jsonfor hosted remote use with Claude-style clientsconfigs/hosted-oauth-chatgpt.jsonfor hosted remote OAuth with ChatGPT-style clientsconfigs/codex.tomlfor the current terminal Codex CLI (~/.codex/config.toml, then runcodex mcp login courtlistener)configs/hosted-oauth-codex.jsonfor older JSON-based Codex-style clients
Legacy filenames are still shipped for compatibility:
configs/codex.jsonfor the legacy direct HTTP JSON exampleconfigs/openai-chatgpt.jsonfor hosted OAuthconfigs/claude-desktop-remote.jsonfor hosted remote usemcp-config.jsonfor local/bridge variants used in development
search_opinionssearch_casesadvanced_searchsmart_search
get_case_detailsget_related_casesget_opinion_textlookup_citationanalyze_case_authoritiesanalyze_legal_argumentget_citation_networkget_comprehensive_case_analysis
list_courtsget_judgesget_judgeget_comprehensive_judge_profile
get_docketsget_docketget_docket_entriesget_recap_documentsget_recap_documentget_enhanced_recap_data
get_financial_disclosuresget_financial_disclosureget_financial_disclosure_detailsget_parties_and_attorneys
get_visualization_dataget_bulk_dataget_bankruptcy_datamanage_alertsvalidate_citations
get_oral_argumentsget_oral_argument
For authoritative tool schema/arguments, use MCP tools/list from your client.
- Node.js 18+
pnpm- CourtListener API token (recommended for higher limits)
pnpm install
pnpm buildpnpm run mcppnpm run doctor
pnpm run cloudflare:check
pnpm run ci:auth-release-gatepnpm install
wrangler secret put COURTLISTENER_API_KEY
wrangler secret put MCP_UI_SESSION_SECRET
wrangler secret put OIDC_ISSUER
wrangler secret put OIDC_AUDIENCE
wrangler secret put MCP_OAUTH_REGISTRATION_TOKEN_SECRET
# Optional shared token auth
wrangler secret put MCP_AUTH_TOKEN
# Optional: set MCP_OAUTH_REGISTRATION_TOKEN_TTL_SECONDS in wrangler vars (defaults to 86400)
# Optional: set MCP_TRUST_CLOUDFLARE_ACCESS_ACKNOWLEDGED=true only if intentionally trusting Cloudflare Access assertions/identity headers
pnpm run cloudflare:check
pnpm run cloudflare:deployEndpoints after deploy:
GET /healthPOST /mcp(primary MCP endpoint)GET /sse(legacy SSE compatibility path)GET/POST /authorizePOST /tokenPOST /registerGET /.well-known/oauth-authorization-serverGET /.well-known/oauth-protected-resource
- ✅ UX15–UX19 complete: accessibility AA hardening, design-system consolidation, performance UX optimizations, and dark-mode visual parity are now shipped.
- ✅ Validation safety pass:
pnpm run test:spa,pnpm run build, andpnpm run typecheckare the required UX-wave release gate. - ✅ Focused SPA rule: use
pnpm run test:spa:focus -- <file...>for targeted browser-auth/UI checks so the real SPA Vitest config and setup are always applied. - Control Center (
/app/control-center): live session/auth/key/runtime posture with a guided MCP checklist. - Protocol explorer: initialize/tool/resource/prompt discovery surfaced directly in Control Center metadata panels.
- Async operator workspace (
/app/playground): queue async tool calls (__mcp_async), monitor lifecycle state, deep-link job details, cancel/retry, and fetch results. - Recovery UX: cross-page recovery status banners plus safe fallback routes back to login, keys, and Control Center.
Cloudflare OAuth is the primary hosted auth path for remote MCP routes.
-
The Worker is the OAuth authorization/resource server for MCP clients
-
The Worker serves the minimal auth handoff routes on the same origin
-
Remote MCP clients connect directly to the Worker
-
OAuth endpoints:
GET/POST /authorizePOST /tokenPOST /register
-
Discovery endpoints:
GET /.well-known/oauth-authorization-serverGET /.well-known/oauth-protected-resource
-
/authorizeresolves identity from:- Signed UI session (
clmcp_ui) when present - Cloudflare Access identity headers (
cf-access-authenticated-user-idorcf-access-authenticated-user-email) only whenMCP_TRUST_CLOUDFLARE_ACCESS_IDENTITY_HEADERS=true MCP_OAUTH_DEV_USER_IDonly whenMCP_ALLOW_DEV_FALLBACK=true(development fallback only; unsafe outside controlled dev and blocked by startup invariants whenNODE_ENV=production)
- Signed UI session (
-
If unresolved,
/authorizeredirects to same-origin/auth/start?return_to=<authorize_url>when Worker-native hosted auth config is present -
POST /api/session/bootstrapremains the Worker-native bearer-to-session bootstrap endpoint for trusted same-origin/session handoff cases -
Same-origin Worker auth handoff:
GET /auth/startGET /auth/callbackGET/POST /auth/approveGET/POST /auth/logout(GETrenders a confirmation form;POSTperforms the logout)- Worker-native hosted auth expects
OIDC_ISSUER,MCP_UI_SESSION_SECRET, and a complete upstream OIDC client pair - Required upstream pair:
MCP_AUTH_OIDC_CLIENT_ID+MCP_AUTH_OIDC_CLIENT_SECRET - As an alternative browser-auth boundary, protect
/authorizeand/auth/approvewith Cloudflare Access and enableMCP_TRUST_CLOUDFLARE_ACCESS_IDENTITY_HEADERS=trueplusMCP_TRUST_CLOUDFLARE_ACCESS_ACKNOWLEDGED=true; the Worker will bootstrap its own same-origin UI session from trusted Access identity headers before rendering the approval step - Partial
MCP_AUTH_OIDC_CLIENT_*config fails closed - Dynamic client-registration management tokens should use a dedicated
MCP_OAUTH_REGISTRATION_TOKEN_SECRET; otherwise they fall back toMCP_UI_SESSION_SECRETorCOURTLISTENER_API_KEY MCP_OAUTH_REGISTRATION_TOKEN_TTL_SECONDSdefaults to 86400 and should be set explicitly for hosted deployments- Browser-session authorization now stops at an explicit same-site approval
screen before the Worker completes
/authorize - Hosted-auth probe responses emit
X-Hosted-Auth-*readiness headers with a concrete reason, coarse failure category, stable low-cardinality signal/failure flags, stable flow correlation ID, flow outcome, credential source, and config-error count - Route-level rate limit controls:
MCP_SESSION_BOOTSTRAP_RATE_LIMIT_MAXMCP_SESSION_BOOTSTRAP_RATE_LIMIT_WINDOW_SECONDSMCP_SESSION_BOOTSTRAP_RATE_LIMIT_BLOCK_SECONDS
-
Usage dashboard endpoint:
GET /api/usagereturns per-user counters (totalRequests,dailyRequests,byRoute,lastSeenAt)
- Optional OIDC bearer validation:
OIDC_ISSUEROIDC_AUDIENCEOIDC_JWKS_URLOIDC_REQUIRED_SCOPE
- Optional trusted Cloudflare Access header acceptance:
MCP_TRUST_CLOUDFLARE_ACCESS_JWT_ASSERTION=trueonly when the Worker is deployed behind Cloudflare Access or another edge that strips/spoof-proofsCF-Access-Jwt-AssertionMCP_TRUST_CLOUDFLARE_ACCESS_IDENTITY_HEADERS=trueonly when/authorizeis deployed behind Cloudflare Access or another edge that strips/spoof-proofscf-access-authenticated-user-*MCP_TRUST_CLOUDFLARE_ACCESS_ACKNOWLEDGED=trueis required by deploy checks before either scoped trust flag is allowed- Without those explicit per-surface flags, the Worker ignores
CF-Access-Jwt-Assertionandcf-access-authenticated-user-* MCP_TRUST_CLOUDFLARE_ACCESS_HEADERSis deprecated and ignored
- Optional service-token path:
MCP_AUTH_TOKENMCP_SERVICE_TOKEN_HEADER
Use this mode when you want the CourtListener MCP runtime but need to keep identity, secrets, and deployment policy inside your own infrastructure.
- Local
stdioclients do not need the hosted OAuth surface - Users typically provide their own
COURTLISTENER_API_KEY - This is the simplest path for desktop-local MCP clients and development
The following legacy UI endpoints are disabled in the hard cutover and return
410:
/api/login*/api/logout*/api/signup*/api/password*/api/keys*/oauth/consent
Canonical hosted OAuth contract values (paths, grants, response types, scopes,
PKCE methods, priority clients) live in src/auth/oauth-contract.ts.
Before promoting a hosted Worker deploy, verify:
pnpm run cloudflare:checkis clean except for intentional deprecation warningshttps://<worker>/auth/start?continue=1returns302- a real
/authorizebrowser journey reaches/auth/approvebefore OAuth completion - DCR management token rotation is independent of UI session rotation by setting
MCP_OAUTH_REGISTRATION_TOKEN_SECRET - any Cloudflare Access trust flags are paired with
MCP_TRUST_CLOUDFLARE_ACCESS_ACKNOWLEDGED=true
When metrics are enabled, local server endpoints include:
GET http://localhost:3001/healthGET http://localhost:3001/metricsGET http://localhost:3001/cache
Useful runtime variables:
CACHE_ENABLEDCACHE_TTLLOG_LEVELLOG_FORMATMETRICS_ENABLEDMETRICS_PORTNODE_ENV
pnpm run test:unit
pnpm run test:integration
# install Chromium once before running the default browser-inclusive suite
pnpm exec playwright install chromium
# default repository gate: unit + integration + targeted SPA auth Vitest + auth Playwright
pnpm test
# focused SPA slice under the supported SPA Vitest config
pnpm run test:spa:focus -- src/web-spa/src/__tests__/shell.test.tsx
pnpm run coverage
pnpm run coverage:checkpnpm run test:mcp
pnpm run ci:test-inspector:enhanced
pnpm run ci:test-inspector:enhanced:extended
pnpm run ci:test-inspector:performancepnpm run ci:load-profile-suite -- --light --base-url http://127.0.0.1:3002
pnpm run ci:perf-gate -- baseline.json current.json
pnpm run ci:hardening:soak-leak-checks -- --light --base-url http://127.0.0.1:3002
pnpm run ci:release-readiness-gate -- --light --base-url http://127.0.0.1:3002CI runs these gates in warn mode for pull requests/non-protected branches, and
strict fail mode for main/master/release/* and v* tags.
act -W .github/workflows/ci.yml- Security policy:
SECURITY.md - Contribution guide:
CONTRIBUTING.md - Architecture details:
ARCHITECTURE.md
MIT. See LICENSE.