Skip to content

feat(resources): Add resources/subscribe and resources/unsubscribe support#98

Open
getlarge wants to merge 2 commits intoplatformatic:mainfrom
getlarge:feat/resource-subscriptions
Open

feat(resources): Add resources/subscribe and resources/unsubscribe support#98
getlarge wants to merge 2 commits intoplatformatic:mainfrom
getlarge:feat/resource-subscriptions

Conversation

@getlarge
Copy link

Summary

This PR adds MCP spec-compliant resources/subscribe and resources/unsubscribe methods, plus query parameter URI matching for resources with uriSchema.

Closes #96

Changes

1. Custom Subscription Handlers

Applications can now implement their own subscription storage:

// Application manages its own subscription store
const subscriptions = new Map<string, Set<string>>();

app.mcpSetResourceSubscribeHandler(async (params, context) => {
  const subs = subscriptions.get(params.uri) || new Set();
  subs.add(context.sessionId!);
  subscriptions.set(params.uri, subs);
  return {};
});

app.mcpSetResourceUnsubscribeHandler(async (params, context) => {
  const subs = subscriptions.get(params.uri);
  if (subs) subs.delete(context.sessionId!);
  return {};
});

// Notify on resource change via existing mcpSendToSession
const subs = subscriptions.get(changedUri);
for (const sessionId of subs || []) {
  await app.mcpSendToSession(sessionId, {
    jsonrpc: '2.0',
    method: 'notifications/resources/updated',
    params: { uri: changedUri }
  });
}

2. Query Parameter URI Matching

Resources with uriSchema can now be read with query parameters:

app.mcpAddResource({
  uri: 'aip://findings',
  name: 'Findings',
  uriSchema: Type.Object({ id: Type.String() })
}, async (uri, context) => {
  const url = new URL(uri, 'http://x');
  const id = url.searchParams.get('id');
  // ...
});

// Client can now read: 'aip://findings?id=abc123'
// Falls back to 'aip://findings' base URI because it has uriSchema

Files Changed

  • src/types.ts - Add subscription handler types and Fastify declaration
  • src/decorators/meta.ts - Add setter decorators
  • src/handlers.ts - Add subscription handlers and query param fallback
  • src/index.ts - Create and pass resourceHandlers
  • src/routes/mcp.ts - Include resourceHandlers in dependencies
  • test/integration.test.ts - Fix type guards (cherry-picked)

Design Decisions

  1. Custom handlers, not built-in tracking - Applications manage their own storage
  2. METHOD_NOT_FOUND when not configured - Clear signal that feature isn't enabled
  3. uriSchema as query param indicator - Only falls back if resource expects params

Backwards Compatibility

  • Existing mcpAddResource registrations unchanged
  • Default resources/read behavior unchanged for exact matches
  • New decorators are optional

Test Plan

  • TypeScript compiles (npm run typecheck)
  • Existing tests pass (npm run test)
  • Manual testing with MCP client

🤖 Generated with Claude Code

getlarge and others added 2 commits January 25, 2026 16:33
…on tests

The MCP SDK types use discriminated unions (e.g., text | image content).
TypeScript requires type narrowing before accessing type-specific properties.

This fixes typecheck failures introduced by SDK type changes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…pport

- Add mcpSetResourceSubscribeHandler and mcpSetResourceUnsubscribeHandler
  decorators for custom subscription management
- Implement resources/subscribe and resources/unsubscribe JSON-RPC methods
- Add query parameter URI fallback: when exact match fails and URI has
  query params, try base URI for resources with uriSchema
- Return METHOD_NOT_FOUND when handlers not configured (clear signal)

This enables MCP clients to subscribe/unsubscribe to resource changes
while keeping subscription storage application-managed.

Closes platformatic#96

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(resources): Add resources/subscribe and resources/unsubscribe support

1 participant