/** * MCP Well-Known Metadata Endpoints * * Mounts three discovery documents under `/.well-known/*` for MCP clients: * * - `/.well-known/oauth-protected-resource` — RFC 9728 Protected Resource * Metadata. The entry point: an MCP client that hits the protected resource * without credentials receives a `401 + WWW-Authenticate: Bearer * resource_metadata="..."` (issued by withMCPAuth, Stage 5) pointing here, * then fetches this document to discover the authorization server. * - `/.well-known/oauth-authorization-server` — RFC 8414 Authorization Server * Metadata. Advertises authorize / token / register / JWKS endpoints and * the supported algorithms / methods / response types. * - `/.well-known/jwks.json` — Public key set used to verify issued JWTs. * Returns an empty key set until Stage 4 of issue #86 lands key material in * the `harper_oauth_mcp_keys` table. * * Endpoints are registered through `server.http(handler, { urlPath })` so * routing works for `.well-known` paths that don't fit Harper's Resource API. * Harper's urlPath matching is prefix-based (segment-boundary aware), so each * handler also checks the exact path before responding — sub-paths like * `/.well-known/oauth-authorization-server/foo` fall through to 404. */ import type { Logger, MCPConfig } from '../../types.ts'; interface HarperRequest { pathname?: string; url?: string; protocol?: string; host?: string; headers?: Record & { host?: string; }; } export declare const PRM_PATH = "/.well-known/oauth-protected-resource"; /** * Resolve the issuer (authorization-server origin). Configured value wins; * otherwise derive from the request — scheme + host. * * Security note: the request-derived path trusts the Host header, which a * client controls. For Stage 2 (metadata-only) this is self-defeating — an * attacker who spoofs Host gets back metadata describing their own origin. * Stage 4 will sign JWTs with `iss`; at that point `mcp.issuer` MUST be * pinned at startup (or derived from a fixed config) to prevent attacker- * controlled `iss` claims. Production deployments should set `mcp.issuer` * explicitly regardless. Documented in docs/configuration.md. */ export declare function resolveIssuer(request: HarperRequest, mcpConfig: MCPConfig): string; /** * Resolve the canonical resource URI (the MCP endpoint clients talk to). * Configured value wins; otherwise derive as `/mcp`. */ export declare function resolveResource(request: HarperRequest, mcpConfig: MCPConfig): string; /** * Canonical RFC 9728 §3.1 Protected Resource Metadata URL for the configured * resource: `/.well-known/oauth-protected-resource[/]`. * * This is exactly the URL the PRM handler serves — bare for a resource at the * origin root, path-appended when the resource carries a path (e.g. `.../mcp`); * see {@link wellKnownPathMatches}. withMCPAuth's `WWW-Authenticate: Bearer * resource_metadata="..."` challenge points here, so a client that uses the * challenge value verbatim fetches a document this server actually answers. * Falls back to the request-derived issuer origin (bare path) if the resource * URI can't be parsed, so the deny path never throws. * * The result is interpolated into a quoted `resource_metadata="..."` header * param, so it MUST be header-safe: a client-controlled Host (when the issuer * is unpinned) must never inject a `"` or control char that breaks the quoting. * Every branch normalizes through `new URL().origin`, which rejects/encodes * such input; if even the issuer can't be parsed, fall back to the host-less * relative path, which is always safe. */ export declare function protectedResourceMetadataUrl(request: HarperRequest, mcpConfig: MCPConfig): string; /** * RFC 9728 Protected Resource Metadata document. */ export declare function buildProtectedResourceMetadata(request: HarperRequest, mcpConfig: MCPConfig): Record; /** * RFC 8414 Authorization Server Metadata document. * Advertises the spec-required fields for the MCP authorization spec 2025-06-18. */ export declare function buildAuthorizationServerMetadata(request: HarperRequest, mcpConfig: MCPConfig): Record; /** * JWKS document — the public half of the signing key(s) in * `harper_oauth_mcp_keys`, serialized to JWK. Read-only: it never triggers key * generation (an unauthenticated fetch must not mint key material), so the set * is empty until the first access token is issued. */ export declare function buildJWKS(mcpConfig: MCPConfig): Promise>; /** * Register all MCP well-known handlers against the Harper server. * * `getConfig` is a getter so middleware sees the current config on each * request — the OAuth plugin re-initializes its config block on options * change, and we want those changes to apply without re-registering routes * (Harper's server.http() does not support deregistration). */ export declare function registerWellKnownHandlers(server: any, getConfig: () => MCPConfig | undefined, logger?: Logger): void; export {};