Skip to content

feat(mcp): DingDuff connector support — OAuth redirect discovery and chat case retrieval - #331

Open
duncanmcqueen wants to merge 2 commits into
Open-Legal-Products:mainfrom
duncanmcqueen:dingduff-mcp-support
Open

feat(mcp): DingDuff connector support — OAuth redirect discovery and chat case retrieval#331
duncanmcqueen wants to merge 2 commits into
Open-Legal-Products:mainfrom
duncanmcqueen:dingduff-mcp-support

Conversation

@duncanmcqueen

@duncanmcqueen duncanmcqueen commented Aug 13, 2026

Copy link
Copy Markdown

Summary

Adds support for DingDuff as a user MCP connector, so the assistant can use DingDuff tools for case retrieval, case reading, and case-specific document lookup directly from chat.

Two changes make this work:

1. OAuth discovery behind redirects (backend/src/lib/mcp/client.ts, oauth.ts)

DingDuff's server 302s its well-known OAuth metadata to a path-scoped issuer, and the MCP SDK's own discovery does not follow redirects, so the OAuth flow could not complete.

  • guardedFetch now follows redirects hop by hop instead of refusing them outright: every 3xx target is re-validated against the existing SSRF guard (HTTPS-only, private-IP/metadata-host blocks, DNS-pinned dispatcher), credentials are stripped on cross-origin hops (per the fetch spec), and redirected POSTs are converted to GET with the body dropped. Undici still never auto-follows (redirect: "manual" on every hop), so no redirect can smuggle egress past the guard.
  • New discoverOAuthServerState plus the SDK's OAuthClientProvider.discoveryState hook hand the SDK pre-discovered metadata, so connectors whose well-known documents live behind a redirect complete the flow.

2. Chat integration (backend/src/lib/chat/streaming.ts)

When DingDuff MCP tools are present on a turn, the built-in CourtListener tools are withheld and the system prompt directs case retrieval at the DingDuff tools, so the two case-law sources are never mixed within a single turn.

Tests

  • New cases in client.ssrf.test.ts: redirect following with per-hop re-validation, refusal to follow a redirect to a blocked address, credential stripping on cross-origin redirects, and POST→GET conversion.
  • Full backend suite passes (554 tests); tsc --noEmit clean.

Notes

  • No schema changes; no new dependencies.
  • The legal-monitors DingDuff connector source is intentionally not part of this PR (that feature set isn't upstream).

… chat case retrieval

DingDuff's MCP server 302s its well-known OAuth metadata to a
path-scoped issuer, which the MCP SDK's own discovery cannot follow.

- guardedFetch now follows redirects hop by hop: each 3xx target is
  re-validated against the SSRF guard (HTTPS-only, private-IP and
  metadata-host blocks, DNS-pinned dispatcher), credentials are stripped
  on cross-origin hops, and redirected POSTs become GETs per the fetch
  spec. Redirects are never auto-followed by undici (redirect: manual
  on every hop).
- discoverOAuthServerState + the OAuthClientProvider.discoveryState hook
  hand the SDK pre-discovered metadata, so OAuth connectors whose
  well-known documents live behind a redirect can complete the flow.
- Chat: when DingDuff MCP tools are present on a turn, the built-in
  CourtListener tools are withheld and the system prompt directs case
  retrieval, case reading, and document lookup at the DingDuff tools,
  so the two case-law sources are never mixed in one turn.
@CLAassistant

CLAassistant commented Aug 13, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ duncanmcqueen
❌ dwmcqueen
You have signed the CLA already but the status is still pending? Let us recheck it.

@duncanmcqueen

Copy link
Copy Markdown
Author

Being a lawyer a bit here (and did accept it), but the CLA says this:

  1. Governing Law
    This Agreement shall be governed by and construed in accordance with the laws of [YOUR JURISDICTION, e.g., "the State of California, United States"], without regard to its conflict-of-law provisions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants