feat(mcp): Origin-header validation + serverInfo title + connect-claude docs export (P0-4 follow-up) (#684)
Closes the two code-side gaps found while auditing the Claude Connectors Directory submission checklist after #682/#683: 1. Origin-header validation on the /mcp endpoint (POST/GET/DELETE) — an explicit directory submission requirement and an MCP spec MUST for the Streamable HTTP transport (DNS-rebinding defense). Requests without an Origin header (claude.ai backend, Claude Desktop, npx gnubok-mcp, Claude Code, MCP Inspector's proxy — every known client) pass through unchanged. A present Origin is allowed only when its host matches the request Host (covers Vercel previews + self-hosted without hardcoding) or NEXT_PUBLIC_APP_URL (proxy-rewritten Host); anything else is 403 with a JSON-RPC error envelope. The endpoint sets no CORS headers, so no currently-working browser flow is affected. 2. serverInfo.title: 'Accounted' (MCP 2025-06-18 display name). name stays 'gnubok' — stable identifier clients may key state on. 3. export-docs-to-website.mts now also exports CONNECT_CLAUDE_MD to the gnubok-website repo, so docs.gnubok.se/connect-claude (the target of the canonical /docs/api redirect) stays in sync. Companion website PR: jakobwennberg/gnubok-website#1. Tests: new origin-guard.test.ts (10 tests — no-Origin pass-through, same-origin, preview host, proxy host via env, foreign/port-mismatch/ null/malformed rejection, 403 envelope, and per-method enforcement on the registered apiRoutes). Full MCP suite 295/295 green. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
809120c4b8
commit
03a2130919
@@ -1,15 +1,18 @@
|
||||
/**
|
||||
* One-shot script that exports the registry-derived docs content (errors +
|
||||
* reference) as static TypeScript modules into the gnubok-website repo.
|
||||
* reference) plus the static Connect-with-Claude page as TypeScript modules
|
||||
* into the gnubok-website repo.
|
||||
*
|
||||
* Run with `npx tsx scripts/export-docs-to-website.mts`. Re-run whenever
|
||||
* structured-errors or the v1 endpoint registry materially changes.
|
||||
* structured-errors, the v1 endpoint registry, or connect-claude materially
|
||||
* changes.
|
||||
*/
|
||||
import { writeFileSync, mkdirSync } from 'node:fs'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
|
||||
const errors = await import('@/lib/docs/content/errors')
|
||||
const reference = await import('@/lib/docs/content/reference')
|
||||
const connectClaude = await import('@/lib/docs/content/connect-claude')
|
||||
|
||||
const buildErrorReferenceMd = errors.buildErrorReferenceMd ?? (errors as any).default?.buildErrorReferenceMd
|
||||
const buildResourcePages = reference.buildResourcePages ?? (reference as any).default?.buildResourcePages
|
||||
@@ -56,4 +59,14 @@ write(
|
||||
)} as const\n\nexport const RESOURCE_PAGES: ResourcePage[] = ${JSON.stringify(pagesPayload, null, 2)}\n\nexport function findResourcePage(slug: string): ResourcePage | undefined {\n return RESOURCE_PAGES.find((p) => p.slug === slug)\n}\n`,
|
||||
)
|
||||
|
||||
const connectClaudeMd = connectClaude.CONNECT_CLAUDE_MD
|
||||
if (!connectClaudeMd) {
|
||||
console.error('Missing CONNECT_CLAUDE_MD export. Inspect:', { connectClaudeKeys: Object.keys(connectClaude) })
|
||||
process.exit(1)
|
||||
}
|
||||
write(
|
||||
'lib/docs/content/connect-claude.generated.ts',
|
||||
`// AUTO-GENERATED from erp-base — do not hand-edit.\n// Regenerate via \`npx tsx scripts/export-docs-to-website.mts\` in erp-base.\nexport const CONNECT_CLAUDE_MD = ${JSON.stringify(connectClaudeMd)}\n`,
|
||||
)
|
||||
|
||||
console.log('done.')
|
||||
|
||||
Reference in New Issue
Block a user