mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 14:21:46 +00:00
fix(mcp): add RFC 9728 PRM, RFC 8707 audience binding, and collab sub-feature gating
Root cause: claude.ai's MCP connector (spec 2025-06-18) requires the resource server to publish Protected Resource Metadata and return WWW-Authenticate on 401s to bind the /mcp endpoint to its AS. Without these, it silently shows no tools after OAuth. - Add /.well-known/oauth-protected-resource (RFC 9728) with addon gating - Emit WWW-Authenticate: Bearer resource_metadata=... on 401/auth-failure 403s - Open CORS (origin: *) on both .well-known/* endpoints per RFC 8414/9728 - Accept resource parameter at authorize + token endpoints (RFC 8707) - Store audience on oauth_tokens; validate on every MCP request - Refresh tokens inherit audience; add resource_parameter_supported to AS metadata - DB migration: ADD COLUMN audience TEXT to oauth_tokens - Gate collab MCP tools/resources by chat/notes/polls sub-features individually - Invalidate MCP sessions when collab sub-features are toggled in admin - Update test mocks and MCP.md
This commit is contained in:
@@ -13,7 +13,7 @@ import { listCategories } from '../services/categoryService';
|
||||
import { listBucketList, listVisitedCountries, getStats as getAtlasStats, listManuallyVisitedRegions } from '../services/atlasService';
|
||||
import { getNotifications } from '../services/inAppNotifications';
|
||||
import { getActivePlanId, getActivePlan, getPlanData, getEntries as getVacayEntries, getHolidays } from '../services/vacayService';
|
||||
import { isAddonEnabled } from '../services/adminService';
|
||||
import { isAddonEnabled, getCollabFeatures } from '../services/adminService';
|
||||
import { ADDON_IDS } from '../addons';
|
||||
import { canAccessJourney, getJourneyFull, listEntries, listJourneys } from '../services/journeyService';
|
||||
import { canRead, canReadTrips } from './scopes';
|
||||
@@ -188,7 +188,8 @@ export function registerResources(server: McpServer, userId: number, scopes: str
|
||||
);
|
||||
|
||||
// Collab notes for a trip
|
||||
if (isAddonEnabled(ADDON_IDS.COLLAB) && canRead(scopes, 'collab')) server.registerResource(
|
||||
const collabFeatures = isAddonEnabled(ADDON_IDS.COLLAB) ? getCollabFeatures() : null;
|
||||
if (collabFeatures?.notes && canRead(scopes, 'collab')) server.registerResource(
|
||||
'trip-collab-notes',
|
||||
new ResourceTemplate('trek://trips/{tripId}/collab-notes', { list: undefined }),
|
||||
{ description: 'Shared collaborative notes for a trip', mimeType: 'application/json' },
|
||||
@@ -319,8 +320,8 @@ export function registerResources(server: McpServer, userId: number, scopes: str
|
||||
);
|
||||
}
|
||||
|
||||
// Collab polls & messages (addon-gated)
|
||||
if (isAddonEnabled(ADDON_IDS.COLLAB) && canRead(scopes, 'collab')) {
|
||||
// Collab polls (addon + sub-feature gated)
|
||||
if (collabFeatures?.polls && canRead(scopes, 'collab')) {
|
||||
server.registerResource(
|
||||
'trip-collab-polls',
|
||||
new ResourceTemplate('trek://trips/{tripId}/collab/polls', { list: undefined }),
|
||||
@@ -332,7 +333,10 @@ export function registerResources(server: McpServer, userId: number, scopes: str
|
||||
return jsonContent(uri.href, polls);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Collab messages (addon + sub-feature gated)
|
||||
if (collabFeatures?.chat && canRead(scopes, 'collab')) {
|
||||
server.registerResource(
|
||||
'trip-collab-messages',
|
||||
new ResourceTemplate('trek://trips/{tripId}/collab/messages', { list: undefined }),
|
||||
|
||||
Reference in New Issue
Block a user