From 0eb5d72ad6c668aba2275aed60b745316e9fb524 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Wed, 19 Aug 2026 11:06:26 +0200 Subject: [PATCH] fix(mcp): advertise RFC 9728 path-inserted resource metadata and drop redundant lint suppression --- apps/nextjs/next.config.ts | 2 -- apps/nextjs/src/app/api/mcp/[transport]/route.ts | 7 +++++-- .../src/app/well-known/oauth-protected-resource/route.ts | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/nextjs/next.config.ts b/apps/nextjs/next.config.ts index 14b85f561..b8c3c5084 100644 --- a/apps/nextjs/next.config.ts +++ b/apps/nextjs/next.config.ts @@ -64,8 +64,6 @@ const nextConfig: NextConfig = { }, ], }, - // skipcq: JS-0116 - // eslint-disable-next-line @typescript-eslint/require-await,no-restricted-syntax async rewrites() { return [ { source: "/.well-known/:path*", destination: "/well-known/:path*" }, diff --git a/apps/nextjs/src/app/api/mcp/[transport]/route.ts b/apps/nextjs/src/app/api/mcp/[transport]/route.ts index 364fe90c5..505260adc 100644 --- a/apps/nextjs/src/app/api/mcp/[transport]/route.ts +++ b/apps/nextjs/src/app/api/mcp/[transport]/route.ts @@ -7,7 +7,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot import { createTRPCContext, mcpRouter } from "@homarr/api/mcp"; import { API_KEY_HEADER_NAME, getSessionFromApiKeyAsync } from "@homarr/auth/api-key"; -import { extractBaseUrlFromHeaders } from "@homarr/common"; +import { extractBaseUrlFromHeaders, removeTrailingSlash } from "@homarr/common"; import { ipAddressFromHeaders } from "@homarr/common/server"; import { createLogger } from "@homarr/core/infrastructure/logs"; import { db } from "@homarr/db"; @@ -265,13 +265,16 @@ const handler = async (req: NextRequest) => { if (!apiKeyValue) { recordAuthFailure(ipAddress); const baseUrl = extractBaseUrlFromHeaders(req.headers); + const resourcePath = removeTrailingSlash(new URL(req.url).pathname); return jsonErrorResponse( 401, { error: "unauthorized", hint: "Authenticate with an ApiKey header or via OAuth at /.well-known/oauth-authorization-server", }, - { "WWW-Authenticate": `Bearer resource_metadata="${baseUrl}/.well-known/oauth-protected-resource"` }, + { + "WWW-Authenticate": `Bearer resource_metadata="${baseUrl}${resourcePath}/.well-known/oauth-protected-resource"`, + }, ); } diff --git a/apps/nextjs/src/app/well-known/oauth-protected-resource/route.ts b/apps/nextjs/src/app/well-known/oauth-protected-resource/route.ts index ca66ee1a3..eb5f22e5a 100644 --- a/apps/nextjs/src/app/well-known/oauth-protected-resource/route.ts +++ b/apps/nextjs/src/app/well-known/oauth-protected-resource/route.ts @@ -13,7 +13,7 @@ export async function GET() { return Response.json( { - resource: baseUrl, + resource: `${baseUrl}/api/mcp`, authorization_servers: [baseUrl], }, { headers: corsHeaders },