Skip to content

feat(api): NLgov ADR compliance and multi-tenant reporting MCP server#56

Open
rawand-shakir-draad wants to merge 4 commits into
feature/reporting-endpointsfrom
feature/reporting-api-standards
Open

feat(api): NLgov ADR compliance and multi-tenant reporting MCP server#56
rawand-shakir-draad wants to merge 4 commits into
feature/reporting-endpointsfrom
feature/reporting-api-standards

Conversation

@rawand-shakir-draad

Copy link
Copy Markdown

Summary

  • NLgov ADR compliance for the reporting API: OpenAPI 3.x spec, API-Version header on all responses, RFC 9457 (problem+json) error format, wildcard CORS on openapi.json
  • New apps/mcp-server: exposes the reporting API as curated MCP tools so an LLM client (e.g. Claude Desktop) can query reporting data without the model ever seeing a bearer token
  • Redesigned the MCP server as a shared, multi-tenant service: it holds no reporting credentials of its own — each request supplies its own Authorization: Bearer <token> + X-Reporting-Project-Id header, extracted per-request into a request-scoped config, so one deployment can serve every municipality
  • Part of theme Generate bearer token from admin openstad/openstad-headless#1643 "Secure API access for reporting"; closes API standards for common ground openstad/openstad-headless#1668

…oning, RFC 9457 errors)

Brings /api/project/:projectId/reports/* in line with the Dutch government
API Design Rules: version the path (/v1), publish a generated OpenAPI 3.x
spec at /openapi.json, and reshape every error response (400/401/403/500)
into application/problem+json (RFC 9457), collecting all validation errors
found on a request into a single response instead of one at a time.
New apps/mcp-server: a thin, stateless MCP (Model Context Protocol) server
wrapping /api/project/:projectId/reports/v1/* as 12 curated tools (one per
endpoint, no generic passthrough), so LLM clients can query reporting data
without the bearer token ever entering their context. The token and project
id are held server-side and injected per request.

Includes a shared-secret auth gate on /mcp (required whenever the server
binds beyond localhost), safe cleanup of the per-request McpServer/transport
on both success and error paths, and problem+json error detail folded into
the message text so it reaches the model instead of being silently dropped.
…RS on openapi.json

Two NLgov ADR gaps found during a manual audit against developer.overheid.nl:

- API-Version was missing on 403s from api-token-scope-guard.js, which is
  mounted globally before routes/api/reports/index.js and never reached that
  router's own header middleware. Extracted the version string into a shared
  module and set the header directly in the guard too.
- /openapi.json only got a wildcard Access-Control-Allow-Origin in local dev
  (via a dev-only config override), not guaranteed in production. Hardcoded
  '*' on that one route, and cleared Access-Control-Allow-Credentials there
  too (security-headers.js sets it unconditionally elsewhere, and the
  Fetch/CORS spec forbids combining it with a wildcard origin).
…equest

Replace the single-tenant, process-per-project design with a shared server
that holds no reporting credentials of its own. Each /mcp request now
carries its own Authorization: Bearer <token> and X-Reporting-Project-Id
header, extracted per request into a request-scoped config; missing/invalid
credentials surface as an MCP tool error instead of rejecting the
connection. Drops the now-unneeded shared-secret auth gate and its
fail-closed localhost guard, since the server no longer holds a secret
worth gating. Validates X-Reporting-Project-Id as numeric before it reaches
URL construction, since it's interpolated into the outbound reporting API
path.
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.

1 participant