Skip to content

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

Merged
mcollina merged 2 commits intoplatformatic:mainfrom
getlarge:feat/resource-subscriptions
Apr 8, 2026
Merged

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

Conversation

@getlarge
Copy link
Copy Markdown
Contributor

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>
Copy link
Copy Markdown
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

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

2 participants