Skip to content

[FEATURE]: Dynamic tools/resources based on user context and server-side signals #2171

@010gvr

Description

@010gvr

🧭 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 delay
US-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 cache
US-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 valid
US-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]
Loading

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
Loading

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
Loading

📋 Implementation Tasks

Phase 1: _meta Propagation Foundation

  • Add _meta parameter support to tools/list RPC method
  • Add _meta parameter support to resources/list RPC method
  • Add _meta parameter support to prompts/list RPC method
  • Propagate _meta through tool invocation path in tool_service.py
  • Propagate _meta through streamable HTTP transport
  • Add tests for _meta propagation

Phase 2: Gateway Configuration

  • Add refresh_strategy field to Gateway model (enum: cached, direct_proxy)
  • Add meta_propagation_enabled field 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_total counter metric
  • Add direct_proxy_latency_seconds histogram metric
  • Add direct_proxy_errors_total counter 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

  • _meta propagates through tools/list, resources/list, prompts/list calls
  • _meta propagates through tools/call calls (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_capabilities signal in response _meta triggers client refresh
  • Gateway model supports refresh_strategy configuration
  • 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

  • _meta parameter 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 listChanged capability 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 _meta property/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

Related PRs:

  • PR #1950 - AUTO_REFRESH_SERVERS implementation (ENV-based)

Metadata

Metadata

Labels

MUSTP1: Non-negotiable, critical requirements without which the product is non-functional or unsafeenhancementNew feature or requestpythonPython / backend development (FastAPI)sweng-group-19Group 19 - AI-Powered Conversational Gateway & Semantic DiscoverytcdSwEng Projectswxowxo integration

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions