* feat(mcp): speak spec revision 2026-07-28 (stateless core) Adopt the 2026-07-28 MCP spec revision on the connector endpoint while keeping every handshake-era client (2025-06-18 and earlier) byte-identical: - Accept per-request _meta protocol negotiation (io.modelcontextprotocol/protocolVersion); unsupported versions return UnsupportedProtocolVersionError (-32022) with the supported list. - Implement server/discover (spec MUST): supported revisions, capabilities including the extensions field, identity, instructions, freshness hints. - Decorate results for stateless clients: required resultType, serverInfo in _meta, and CacheableResult ttlMs/cacheScope on tools/list, prompts/list, resources/list, resources/read. - Validate the standard Mcp-Method/Mcp-Name request headers when present (HeaderMismatchError -32020); absence stays accepted. - Declare the ratified MCP Apps extension (io.modelcontextprotocol/ui) in capabilities; the widgets already use the ratified mime type and _meta.ui.resourceUri shape, so no widget changes are needed. - OAuth: include the RFC 9207 iss parameter on every authorization response (success and error) and advertise authorization_response_iss_parameter_supported in RFC 8414 metadata. Resource-not-found already used -32602 and tools/list ordering was already deterministic; both are covered by the new test file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(mcp): Mcp-Name covers params.uri, base64 sentinel, version-header consistency Review follow-ups against the transport spec text: Mcp-Name mirrors params.name OR params.uri (resources/read), values arrive base64-wrapped in the =?base64?...?= sentinel and must be decoded before comparison, and an MCP-Protocol-Version header that disagrees with the _meta protocol version is a HeaderMismatch. Absence of any header stays accepted since this server supports handshake-era clients (spec-sanctioned leniency). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
37 lines
1.7 KiB
TypeScript
37 lines
1.7 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
import { PUBLIC_OAUTH_METADATA_SCOPES } from '@/lib/auth/api-keys'
|
|
import { resolveDiscoveryBaseUrl } from '@/lib/api/v1/base-url'
|
|
|
|
/**
|
|
* RFC 8414: OAuth 2.0 Authorization Server Metadata.
|
|
* Tells MCP clients where the authorize/token endpoints are.
|
|
*
|
|
* The issuer reflects the (allowlisted) request host: clients that
|
|
* connected via the legacy app.gnubok.se domain must keep seeing a
|
|
* self-consistent issuer there, or their issuer validation breaks on
|
|
* re-auth after the app.accounted.se cutover.
|
|
*/
|
|
export async function GET(request: Request) {
|
|
const appUrl = resolveDiscoveryBaseUrl(request)
|
|
|
|
return NextResponse.json({
|
|
issuer: appUrl,
|
|
authorization_endpoint: `${appUrl}/api/mcp-oauth/authorize`,
|
|
token_endpoint: `${appUrl}/api/mcp-oauth/token`,
|
|
registration_endpoint: `${appUrl}/api/mcp-oauth/register`,
|
|
response_types_supported: ['code'],
|
|
grant_types_supported: ['authorization_code', 'refresh_token'],
|
|
code_challenge_methods_supported: ['S256'],
|
|
token_endpoint_auth_methods_supported: ['none', 'client_secret_post'],
|
|
// RFC 9207: the authorize endpoint includes `iss` in every authorization
|
|
// response (success and error) so clients can detect mix-up attacks.
|
|
authorization_response_iss_parameter_supported: true,
|
|
// Advertise only the safe read-only default scopes plus the coarse
|
|
// `mcp` marker. Destructive scopes (*:write, pending_operations:approve,
|
|
// bookkeeping:write) are still accepted by /authorize when requested
|
|
// explicitly, but enumerating them in public discovery aids
|
|
// scope-escalation reconnaissance (CC6.1, defense-in-depth).
|
|
scopes_supported: ['mcp', ...PUBLIC_OAUTH_METADATA_SCOPES],
|
|
})
|
|
}
|