Skip to content

Conversation

@mattpodwysocki
Copy link
Contributor

@mattpodwysocki mattpodwysocki commented Jan 27, 2026

Summary

Implements capability-aware tool registration infrastructure that adapts to client features. Fixes incorrect resource capability detection that caused all clients to receive fallback tools.

Key Changes

1. Fixed Resource Capability Detection (src/index.ts)

Problem: Checked clientCapabilities?.resources which doesn't exist (resources is a SERVER capability, not CLIENT capability)

Solution: Detect clients by name using getClientVersion() - only register fallback tools for clients with 'claude' in their name

2. Tool Categorization (src/tools/toolRegistry.ts)

Split tools into three capability-based categories:

  • CORE_TOOLS (23 tools): Work in all MCP clients, always available
  • ELICITATION_TOOLS (0 tools): Empty, ready for future elicitation support
  • RESOURCE_FALLBACK_TOOLS (2 tools): Only for clients with resource issues (Claude Desktop)
    • get_reference_tool - Access to reference resources
    • get_latest_mapbox_docs_tool - Access to Mapbox documentation resource

3. Dynamic Registration Infrastructure

  1. Register CORE tools before connection
  2. After connection, check client capabilities and version
  3. Conditionally register ELICITATION_TOOLS if client supports elicitation (none yet)
  4. Conditionally register RESOURCE_FALLBACK_TOOLS if client name includes 'claude'
  5. Send notifications/tools/list_changed after dynamic registration
  6. Advertise tools: { listChanged: true } capability

4. Tests (test/tools/toolRegistry.test.ts)

Added 16 comprehensive unit tests covering tool categorization, no overlap between categories, and proper counts.

MCP Inspector Output (After Fix)

Clients with proper resource support (Inspector, VS Code, etc.) now see:

Tools (23):

[
  "bounding_box_tool",
  "check_color_contrast_tool",
  "compare_styles_tool",
  "coordinate_conversion_tool",
  "country_bounding_box_tool",
  "create_style_tool",
  "create_token_tool",
  "delete_style_tool",
  "geojson_preview_tool",
  "get_feedback_tool",
  "list_feedback_tool",
  "list_styles_tool",
  "list_tokens_tool",
  "optimize_style_tool",
  "preview_style_tool",
  "retrieve_style_tool",
  "style_builder_tool",
  "style_comparison_tool",
  "tilequery_tool",
  "update_style_tool",
  "validate_expression_tool",
  "validate_geojson_tool",
  "validate_style_tool"
]

Resources (5):

[
  "resource://mapbox-documentation",
  "resource://mapbox-layer-type-mapping",
  "resource://mapbox-streets-v8-fields",
  "resource://mapbox-style-layers",
  "resource://mapbox-token-scopes"
]

Result: Inspector shows 23 tools (down from 26) - resource fallback tools only appear for Claude clients.

Benefits

Correct behavior: Inspector and proper clients no longer see duplicate resource fallback tools
Better UX: Resource fallback tools only for clients that need them (Claude Desktop)
Infrastructure ready: Framework in place for capability-dependent tools
Zero breaking changes: Claude Desktop still gets fallback tools

Testing

  • ✅ All 536 tests pass
  • ✅ Build successful
  • ✅ Verified in MCP Inspector
  • ✅ Comprehensive logging for debugging

MCP Specification Compliance

Follows MCP 1.0 specification for dynamic tool registration:

  • Uses notifications/tools/list_changed notification
  • Advertises listChanged capability
  • Registers tools after initialization handshake
  • Properly detects client capabilities

mattpodwysocki and others added 2 commits January 27, 2026 12:12
This commit introduces capability-aware tool registration that adapts to
client features, ensuring users only see tools that work with their client.

Key changes:

1. Tool Categorization (src/tools/toolRegistry.ts):
   - Split tools into three categories by capability requirements:
     * CORE_TOOLS (23 tools): Work in all MCP clients
     * ELICITATION_TOOLS (2 tools): Require elicitation capability
       - PreviewStyleTool
       - StyleComparisonTool
     * RESOURCE_FALLBACK_TOOLS (1 tool): Bridge for missing resource support
       - GetReferenceTool

   - Added new getter functions:
     * getCoreTools()
     * getElicitationTools()
     * getResourceFallbackTools()

   - Deprecated getAllTools() in favor of capability-aware functions
   - Maintained ALL_TOOLS for backward compatibility and testing

2. Dynamic Registration (src/index.ts):
   - Register CORE tools before connection (always available)
   - After connection, check client capabilities via getClientCapabilities()
   - Conditionally register ELICITATION_TOOLS if client supports elicitation
   - Conditionally register RESOURCE_FALLBACK_TOOLS if client lacks resource support
   - Send notifications/tools/list_changed notification after dynamic registration
   - Added comprehensive logging for registration decisions

   - Advertise listChanged: true capability to inform clients that tool list
     can change dynamically

Benefits:
- Users only see tools that actually work with their client
- No confusing runtime capability errors
- Cleaner UX for capability-limited clients
- Backward compatible with existing tool configuration system
- Zero breaking changes (all tools still work, just registered conditionally)

All 520 tests pass.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add comprehensive unit tests to verify:
- Tool categorization correctness
- Getter functions return expected tools
- No overlap between categories
- Tool counts are correct

15 tests covering:
- getCoreTools() - 4 tests
- getElicitationTools() - 3 tests
- getResourceFallbackTools() - 2 tests
- getAllTools() - 3 tests
- Tool categorization consistency - 3 tests

All tests pass.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@mattpodwysocki mattpodwysocki requested a review from a team as a code owner January 27, 2026 17:21
mattpodwysocki and others added 3 commits January 27, 2026 12:29
…ged yet)

The elicitation support for preview_style_tool and style_comparison_tool
exists only in the add-preview-token-elicitation branch, which hasn't been
merged to main yet. Moving these tools to ELICITATION_TOOLS made them
unavailable to all clients.

Changes:
- Move preview_style_tool back to CORE_TOOLS
- Move style_comparison_tool back to CORE_TOOLS
- Keep ELICITATION_TOOLS as empty array (ready for future use)
- Update tests to reflect empty ELICITATION_TOOLS
- Add comments explaining this is temporary until elicitation support merges

When elicitation support is merged in the future, these tools can be moved
to ELICITATION_TOOLS in a follow-up PR.

All 535 tests pass.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The get_latest_mapbox_docs_tool duplicates functionality of the
resource://mapbox-documentation resource. It should only be available
to clients that don't properly support resources.

Changes:
- Move GetMapboxDocSourceTool from CORE_TOOLS to RESOURCE_FALLBACK_TOOLS
- CORE_TOOLS now has 24 tools (down from 25)
- RESOURCE_FALLBACK_TOOLS now has 2 tools (up from 1):
  * get_reference_tool (reference resources)
  * get_latest_mapbox_docs_tool (documentation resource)
- Update tests to reflect new categorization
- Add test for get_latest_mapbox_docs_tool in resource fallback tools

Clients that properly support resources will no longer see this tool,
reducing clutter in their tool list.

All 536 tests pass.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The previous logic incorrectly checked clientCapabilities.resources which
doesn't exist (resources is a SERVER capability, not a CLIENT capability).

Now correctly detects clients by name using getClientVersion() and only
registers fallback tools for clients with known resource support issues
(e.g., Claude Desktop).

Result: MCP Inspector and other proper clients now see 23 tools instead
of 26, with resource fallback tools only appearing for Claude clients.

MCP Inspector Output After Fix:

Tools (23):
[
  "bounding_box_tool",
  "check_color_contrast_tool",
  "compare_styles_tool",
  "coordinate_conversion_tool",
  "country_bounding_box_tool",
  "create_style_tool",
  "create_token_tool",
  "delete_style_tool",
  "geojson_preview_tool",
  "get_feedback_tool",
  "list_feedback_tool",
  "list_styles_tool",
  "list_tokens_tool",
  "optimize_style_tool",
  "preview_style_tool",
  "retrieve_style_tool",
  "style_builder_tool",
  "style_comparison_tool",
  "tilequery_tool",
  "update_style_tool",
  "validate_expression_tool",
  "validate_geojson_tool",
  "validate_style_tool"
]

Resources (5):
[
  "resource://mapbox-documentation",
  "resource://mapbox-layer-type-mapping",
  "resource://mapbox-streets-v8-fields",
  "resource://mapbox-style-layers",
  "resource://mapbox-token-scopes"
]

Fixes dynamic tool registration to work as intended.
@mattpodwysocki mattpodwysocki force-pushed the dynamic-tool-registration branch from cefb9e1 to b29c23f Compare January 27, 2026 18:03
src/index.ts Outdated
const clientName = clientVersion?.name?.toLowerCase() || '';

// Known clients with resource support issues
const needsResourceFallback = clientName.includes('claude');

Choose a reason for hiding this comment

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

doubt it's the only client that needs it

Choose a reason for hiding this comment

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

if client needs it and we don't know that - we don't provide fallback tools, this is risky. Should the logic be inverted - we provide fallback tools to all clients unless we know they do not need them?

);
});

it('should have no duplicate tools', () => {

Choose a reason for hiding this comment

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

yup, good edge case 👍

…compatibility

Per @Valiunia's review feedback, invert the resource fallback tool logic to use an allowlist approach instead of blocklist. This is safer for unknown clients.

Changes:
- OLD: Only provide fallback tools if client name includes 'claude' (blocklist)
- NEW: Provide fallback tools to ALL clients UNLESS we know they support resources (allowlist)

Known clients with proper resource support (skip fallback tools):
- inspector
- vscode

Benefits:
- Unknown/new clients get fallback tools by default (safer)
- No risk of missing clients that need resource fallback
- More defensive approach for ecosystem compatibility

Related: PR #61 review comments
All tests passing (536 tests).
@mattpodwysocki
Copy link
Contributor Author

✅ Addressed review feedback - inverted the resource fallback logic:

Old approach (blocklist): Only provide fallback tools if client name includes 'claude'

  • ⚠️ Risky: Unknown clients wouldn't get fallback tools they might need

New approach (allowlist): Provide fallback tools to ALL clients UNLESS we know they support resources

  • ✅ Safer: Unknown/new clients get fallback tools by default
  • ✅ No risk of missing clients that need resource fallback
  • ✅ More defensive for ecosystem compatibility

Known clients with proper resource support (skip fallback tools):

  • inspector
  • vscode

All other clients (including Claude Desktop, Claude Code, and any unknown clients) will receive resource fallback tools to ensure compatibility.

Commit: 5e89269

… (like smolagents)

Update comments and logic to reflect the real use case: resource fallback tools
are for clients that don't support resources at all (like smolagents), not for
buggy resource implementations.

Changes:
- Add 'claude' to allowlist (Claude Desktop now supports resources properly)
- Update comments to clarify this is for clients without resource support
- Change from "may not support properly" to "don't support resources"
- Document smolagents as the primary use case

Known clients WITH resource support (skip fallback tools):
- inspector
- vscode
- claude (Claude Desktop/Code)

Clients WITHOUT resource support (get fallback tools):
- smolagents
- unknown/new clients (safer default)

All tests passing (536 tests).
@mattpodwysocki
Copy link
Contributor Author

📝 Further clarification - the real use case is smolagents:

Claude Desktop now supports resources properly, so the resource fallback tools aren't for "buggy" implementations - they're for clients that don't support resources at all (like smolagents).

Updated approach:

Known clients WITH resource support (skip fallback tools):

  • inspector (MCP Inspector)
  • vscode (VS Code extension)
  • claude (Claude Desktop/Code) ✨ now properly supports resources

Clients WITHOUT resource support (get fallback tools):

  • smolagents - the primary use case
  • Unknown/new clients - safer default

The resource fallback tools (get_reference_tool, get_latest_mapbox_docs_tool) act as bridges, providing the same content as resources but via tool calls for clients that don't implement resource support.

Commits: 5e89269185e067

…(opt-in)

Remove fragile name-based client detection and replace with explicit
environment variable configuration. Use opt-in approach since most MCP
clients support resources.

Changes:
- Remove client name checking logic entirely
- Add CLIENT_NEEDS_RESOURCE_FALLBACK environment variable
- Default (unset): Assume client supports resources, skip fallback tools
- Set to "true": Client needs fallback tools (e.g., smolagents)

Benefits:
- No maintenance burden tracking client names/versions
- Better default (most clients support resources)
- Opt-in only for exceptions (smolagents, etc.)
- Explicit configuration by users who know their client

Configuration:
```bash
# smolagents or clients without resource support
export CLIENT_NEEDS_RESOURCE_FALLBACK=true

# Claude Desktop, VS Code, Inspector, etc. (default)
# Leave unset - assumes resource support
```

Documentation updated in README.md explaining when to set this variable.

All tests passing (536 tests).
@mattpodwysocki
Copy link
Contributor Author

Final solution: Explicit opt-in via environment variable

Removed all client name detection logic and replaced with CLIENT_NEEDS_RESOURCE_FALLBACK environment variable.

Why this approach?

The problem with name-based detection:

  • ❌ Can't keep up with all MCP clients (race condition)
  • ❌ Maintenance burden tracking client names/versions
  • ❌ Brittle (names change, versions evolve)
  • ❌ Defeats the purpose of being adaptive

The solution:
Let users explicitly configure their client's capabilities via environment variable.

Configuration

Default (unset): Skip resource fallback tools

  • Assumes client supports resources (the common case)
  • Works for: Claude Desktop, VS Code, MCP Inspector, and most clients

Opt-in: CLIENT_NEEDS_RESOURCE_FALLBACK=true

  • Includes resource fallback tools for clients without resource support
  • Required for: smolagents and other non-compliant clients

Benefits

✅ No maintenance burden (no client tracking)
✅ Better default (most clients support resources)
✅ Works for all current and future clients
✅ Explicit configuration by users who know their client
✅ Scales indefinitely

Documentation

Added new section in README explaining:

  • What resource fallback tools are
  • When to set the environment variable
  • Which clients need it vs. which don't

Commit: 638569d

@mattpodwysocki mattpodwysocki merged commit be69a25 into main Jan 28, 2026
1 check passed
@mattpodwysocki mattpodwysocki deleted the dynamic-tool-registration branch January 28, 2026 17:57
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.

2 participants