-
Notifications
You must be signed in to change notification settings - Fork 614
[FEATURE]: Dynamic tools/resources based on user context and server-side signals #2171
Description
🧭 Type of Feature
Please select the most appropriate category:
- Enhancement to existing functionality
- New feature or capability
- New MCP-compliant server
- New component or integration
- Developer tooling or test improvement
- Packaging, automation and deployment (ex: pypi, docker, quay.io, kubernetes, terraform)
- Other (please describe below)
🧭 Epic
Title: Dynamic tools/resources based on user context and server-side signals
Goal: Gateway/Virtual Server to support an MCP server architecture that will control the access/visibility to tools/resources based on user context/flag passed via _meta.
Why now:
In one of the MCP server architectures we're building (that's partly based off of apps extension), there's a need for the gateway/virtual server to be able to dynamically refresh the tools/resources that are available to a specific user accessing the client. The information of a user is bundled inside _meta field in all supported RPC methods as per MCP specification. This is because the tools/resources are not static and can change based on the user's context at any point within a single chat session (note: not MCP session). For example, if the user is in a different workspace, the tools available to them should change accordingly.
The current architecture of context forge seems to have built around a static tool registry model where tools are cached in the database upon gateway registration. The auto refresh implementation done here for #172 , seems to be a periodic health check based approach controlled by ENV variable which won't be sufficient.
There's a recent related issue #2094 focusses on adding _meta to tools/call but this feature request may need support for other RPC methods as well making the gateway fully compliant with the latest MCP spec 2025-11-25.
📖 User Stories
US-1: MCP Server Developer - Dynamic Tool Visibility
As an MCP server developer building permission-based tools based on customer
I want Any MCP client to be able to see only a specific set of tools/resources based on the user context passed via _meta during tools/list or resources/list calls
So that LLM Agents and UIs have restricted set of tools/resources for that user
Acceptance Criteria:
Scenario: Propagation of _meta to Server for tool visibility
Given a banking user entering a chat without any permissions with default chat context (e.g., phone number/name)
And an MCP server that has implemented a mechanism to expose tools based on the user info
When the user requests for list of services that can be performed
And the MCP client invokes (via virtual server/gateway) tools/list with _meta containing the chat context
Then the MCP client should get the list of tools that is specific to the user as implemented by the MCP server
Scenario: Server-side signal triggers tool refresh via _meta
Given a user has "basic" permissions with tools [agent_handoff, pin_authentication]
When the user enters a valid pin via pin_authentication tool
Then the MCP server can include a custom flag in _meta (e.g., "refresh_capabilities": true) in the tool response
And the client can make a call to virtual server for tools/list (retaining the _meta context)
And the client can see the list of new tools available to the current user [view_balance, view_transactions, transfer_money, invest]
And the refresh happens within the same request cycle with no delayUS-2: Platform Operator - Configure Gateway Refresh Strategy
As a Platform Operator managing MCP gateways
I want to configure per-gateway refresh strategies (cached vs direct proxy)
So that I can choose the appropriate mode based on whether my MCP servers need dynamic tool visibility
Acceptance Criteria:
Scenario: Configure gateway for direct proxy mode
Given I am registering a new MCP gateway
When I set refresh_strategy to "direct_proxy"
Then all RPC calls should bypass the database cache
And tools/list should return directly from the upstream MCP server
And _meta should be fully propagated to the upstream server
Scenario: Configure gateway for cached mode (default)
Given I am registering a new MCP gateway
When I set refresh_strategy to "cached" (or leave default)
Then tools should be cached in the database upon registration
And tools/list should return from the database cache
And periodic health checks can refresh the cacheUS-3: AI Agent - Receive Updated Tools After Permission Change
As an AI Agent executing a multi-step authentication workflow
I want to automatically receive updated tool lists after user permissions change
So that I can offer newly available capabilities to the user
Acceptance Criteria:
Scenario: Tool list updates after successful authentication
Given I am an AI agent helping a user with banking tasks
And the user has only basic tools available [agent_handoff, pin_authentication]
When the user successfully authenticates via pin_authentication
And the upstream MCP server returns refresh_capabilities: true in _meta
Then I should call tools/list to get the updated tool list
And I should receive new tools [view_balance, view_transactions, transfer_money]
And I can now offer these new capabilities to the user
Scenario: Tool list remains stable without refresh signal
Given I have received a tool list [view_balance, view_transactions]
When I call tools/call on view_balance
And the response does NOT include refresh_capabilities in _meta
Then I should NOT need to refresh the tool list
And my existing tool list remains validUS-4: Gateway Admin - Monitor Direct Proxy Performance
As a Gateway Administrator
I want metrics for direct proxy mode operations
So that I can monitor latency and troubleshoot issues
Acceptance Criteria:
Scenario: Direct proxy metrics are exposed
Given a gateway is configured with refresh_strategy="direct_proxy"
When RPC calls are made through the gateway
Then the following metrics should be recorded:
| Metric | Description |
| direct_proxy_requests_total | Total direct proxy requests |
| direct_proxy_latency_seconds | Latency histogram |
| direct_proxy_errors_total | Errors by type |
And these metrics should be available at /metrics endpoint🏗 Architecture
Direct Proxy Mode Flow
sequenceDiagram
participant Client as MCP Client
participant Gateway as MCP Gateway
participant DB as Database
participant Upstream as Upstream MCP Server
Note over Gateway: refresh_strategy = direct_proxy
Client->>Gateway: tools/list (_meta: {user: "alice"})
Gateway->>Gateway: Auth check
Gateway->>Upstream: tools/list (_meta: {user: "alice"})
Note over Upstream: Filter tools based on _meta
Upstream-->>Gateway: [pin_auth, agent_handoff]
Gateway-->>Client: [pin_auth, agent_handoff]
Client->>Gateway: tools/call (pin_auth, _meta: {user: "alice", pin: "1234"})
Gateway->>Upstream: tools/call (pin_auth, _meta: {...})
Upstream-->>Gateway: Result + _meta: {refresh_capabilities: true}
Gateway-->>Client: Result + _meta: {refresh_capabilities: true}
Client->>Gateway: tools/list (_meta: {user: "alice", authenticated: true})
Gateway->>Upstream: tools/list (_meta: {...})
Note over Upstream: User now authenticated
Upstream-->>Gateway: [view_balance, transfer, invest]
Gateway-->>Client: [view_balance, transfer, invest]
Cached vs Direct Proxy Comparison
flowchart LR
subgraph "Cached Mode (Default)"
A1[Client] --> B1[Gateway]
B1 --> C1[Database Cache]
C1 --> B1
B1 --> A1
D1[Health Check] -.->|Periodic Refresh| E1[Upstream]
E1 -.->|Update Cache| C1
end
subgraph "Direct Proxy Mode"
A2[Client] --> B2[Gateway]
B2 --> E2[Upstream]
E2 --> B2
B2 --> A2
Note2[No DB Cache for tools/list]
end
Gateway Configuration Schema
classDiagram
class Gateway {
+id: str
+name: str
+url: str
+refresh_strategy: RefreshStrategy
+meta_propagation_enabled: bool
+created_at: datetime
+updated_at: datetime
}
class RefreshStrategy {
<<enumeration>>
CACHED
DIRECT_PROXY
}
Gateway --> RefreshStrategy
📋 Implementation Tasks
Phase 1: _meta Propagation Foundation
- Add
_metaparameter support totools/listRPC method - Add
_metaparameter support toresources/listRPC method - Add
_metaparameter support toprompts/listRPC method - Propagate
_metathrough tool invocation path intool_service.py - Propagate
_metathrough streamable HTTP transport - Add tests for _meta propagation
Phase 2: Gateway Configuration
- Add
refresh_strategyfield to Gateway model (enum: cached, direct_proxy) - Add
meta_propagation_enabledfield to Gateway model (boolean) - Create Alembic migration for new Gateway fields
- Update Gateway create/update schemas
- Update Admin UI to show refresh strategy selector
- Document refresh strategy options
Phase 3: Direct Proxy Implementation
- Implement direct proxy mode in
streamablehttp_transport.py - Skip database caching for tools when direct_proxy enabled
- Skip database caching for resources when direct_proxy enabled
- Skip database caching for prompts when direct_proxy enabled
- Pass through all RPC payloads directly to upstream after auth check
- Handle errors gracefully in direct proxy mode
Phase 4: Metrics and Observability
- Add
direct_proxy_requests_totalcounter metric - Add
direct_proxy_latency_secondshistogram metric - Add
direct_proxy_errors_totalcounter metric - Add tracing spans for direct proxy requests
- Update observability documentation
Phase 5: Testing
- Unit tests for _meta propagation in tool service
- Unit tests for direct proxy mode in transport
- Integration tests with mock MCP server returning dynamic tools
- Integration tests for refresh_capabilities signal handling
- Performance tests comparing cached vs direct proxy modes
Phase 6: Documentation
- Document direct proxy mode in operator guide
- Document _meta propagation for MCP server developers
- Add architecture decision record for refresh strategies
- Update API documentation with new Gateway fields
⚙️ Configuration Example
Gateway Registration (Direct Proxy Mode)
{
"name": "Dynamic Banking MCP",
"url": "http://banking-mcp:8080/sse",
"transport": "streamable-http",
"refresh_strategy": "direct_proxy",
"meta_propagation_enabled": true,
"authentication": {
"type": "bearer",
"token": "..."
}
}Gateway Registration (Cached Mode - Default)
{
"name": "Static Tools MCP",
"url": "http://tools-mcp:8080/sse",
"transport": "streamable-http",
"refresh_strategy": "cached",
"auto_refresh_interval": 300
}Environment Variables
# Global default for new gateways (optional)
GATEWAY_DEFAULT_REFRESH_STRATEGY=cached
# Enable _meta propagation globally (optional)
MCPGATEWAY_META_PROPAGATION_ENABLED=true✅ Success Criteria
-
_metapropagates throughtools/list,resources/list,prompts/listcalls -
_metapropagates throughtools/callcalls (builds on [FEATURE]: Support _meta field propagation in MCP tool calls #2094) - Direct proxy mode skips database caching for tools/resources/prompts
- Upstream MCP servers receive user context via
_meta -
refresh_capabilitiessignal in response _meta triggers client refresh - Gateway model supports
refresh_strategyconfiguration - Admin UI allows selecting refresh strategy per gateway
- Metrics exposed for direct proxy operations
- No performance regression for cached mode (default)
- Integration tests pass with dynamic tool visibility scenarios
🏁 Definition of Done
-
_metaparameter propagated to all list RPC methods - Direct proxy mode implemented in streamable HTTP transport
- Gateway model extended with refresh_strategy field
- Alembic migration created and tested
- Admin UI updated with refresh strategy selector
- Direct proxy metrics exposed
- Unit tests written and passing
- Integration tests written and passing
- Performance comparison documented
- Operator documentation updated
- MCP server developer guide updated
- Code passes
make verify - PR reviewed and approved
🔗 MCP Standards Check
- Change adheres to current MCP specifications
- No breaking changes to existing MCP-compliant integrations
- If deviations exist, please describe them below:
MCP Specification Compliance:
From the MCP Tools specification (2025-11-25):
"When the list of available tools changes, servers that declared the
listChangedcapability SHOULD send a notification"
{
"jsonrpc": "2.0",
"method": "notifications/tools/list_changed"
}However, in the scenario above, the server is stateless and the clients doesn't need to persist the connection either. For notification to work, we need an open transport between the MCP client and Server which is not scalable/feasible in a cluster setup and for production use.
Extension for Stateless Servers:
The _meta-based refresh signal is a valid extension using the MCP-reserved _meta field for protocol-level control signals. From MCP Base Protocol:
"The
_metaproperty/parameter is reserved by MCP to allow clients and servers to attach additional metadata to their interactions."
This approach is necessary because, (like mentioned above) notifications/tools/list_changed requires persistent connections, which many production MCP servers cannot maintain (serverless, HTTP-only, load-balanced architectures).
📝 Design Decisions
| Decision | Resolution | Rationale |
|---|---|---|
| Refresh strategy scope | Per-gateway | Different MCP servers have different needs |
| Default mode | Cached | Preserves existing behavior, better performance |
| _meta signal name | refresh_capabilities |
Descriptive, follows MCP conventions |
| DB bypass approach | Full bypass for list methods | Simplest implementation, clear behavior |
🔗 Related Issues
- Issue #1910 - Automatic tools/prompts/resources refresh
- Issue #2094 - Add
_metato tool/call - Issue #2332 - Support
_metafor all RPC methods - Issue #2344 - Bypass DB/cache lookup option for gateways
Related PRs:
- PR #1950 - AUTO_REFRESH_SERVERS implementation (ENV-based)