MCP Gateway
Concepts

Session Tool Scoping

Per-session allow and deny lists with wildcards — multi-tenant tool isolation, role-based access, and progressive disclosure at the gateway layer.

Overview

By default, every MCP session can see and execute all tools from all connected servers. Per-session tool scoping adds two optional fields to a session:

  • allowed_tool_names (allowlist) — when set, only matching tools are visible
  • denied_tool_names (denylist) — when set, matching tools are blocked

Both fields support wildcards (PREFIX__*) to target all tools from a server without enumerating each one.

Session scoping filters all tool surfaces: tools/list in LIST mode, SEARCH_TOOLS, EXECUTE_TOOL, and direct tools/call. An agent literally cannot discover or call tools outside its session scope.

Consider a gateway with 500 knowledge base tools from VIVI, 10 tools from HUBSPOT, and 8 tools from GMAIL:

SessionalloweddeniedResult
AVIVI__kb_finance, VIVI__kb_hr, HUBSPOT__*, GMAIL__*HUBSPOT__internal_debug2 VIVI + 9 HUBSPOT + 8 GMAIL = 19 tools
BnullVIVI__secret_toolEverything except VIVI__secret_tool
CnullnullEverything (default, no restrictions)

Session Scope Levels

Every session is scoped to the authenticated user. Beyond that, you can optionally narrow the session to a specific server or bundle:

server_idbundle_idScopeTool universe
nullnullGateway-wideAll tools across all connected servers
uuidnullServer-scopedOnly tools from that one server
nulluuidBundle-scopedOnly tools from that one bundle

Setting both server_id and bundle_id is rejected (400 error). They are mutually exclusive.

The allowed_tool_names and denied_tool_names filters then apply within whichever scope level you chose. For example, a gateway-wide session with an allowlist filters across all servers; a server-scoped session with an allowlist filters within that server's tools only.

Resolution Precedence

For each tool, the gateway evaluates three rules in order:

  1. Deny always wins — if the tool matches any deny pattern, it's blocked
  2. No allow list = allow all — if allowed_tool_names is null, the tool is visible
  3. Must match allow — if allowed_tool_names is set, the tool must match an entry
# Pseudocode
if tool matches any denied pattern → BLOCKED
if allowed is null → VISIBLE
if tool matches any allowed pattern → VISIBLE
elseBLOCKED

How It Works

Create a scoped session

Pass allowed_tool_names and/or denied_tool_names when creating a session. The gateway enforces the scope across all tool operations.

POST /api/v1/sessions
{
  "allowed_tool_names": [
    "MY_KBS__kb_finance",
    "MY_KBS__kb_hr",
    "HUBSPOT__*",
    "GMAIL__*"
  ],
  "denied_tool_names": [
    "HUBSPOT__internal_debug"
  ]
}

No server_id or bundle_id — session spans all servers. Allow/deny filters across the entire gateway.

POST /api/v1/sessions
{
  "server_id": "a1b2c3d4-...",
  "denied_tool_names": [
    "VIVI__secret_tool",
    "VIVI__admin_reset"
  ]
}

Session is tied to one server. Agent sees only that server's tools, minus the two denied.

POST /api/v1/sessions
{
  "bundle_id": "e5f6g7h8-...",
  "allowed_tool_names": [
    "MY_KBS__kb_pricing",
    "MY_KBS__kb_product_docs"
  ]
}

Session is tied to one bundle. Agent sees only the two allowed tools from that bundle.

Omit both fields (or set to null) for unrestricted access — fully backwards compatible.

Agent uses tools normally

Pass the session ID in the Mcp-Session-Id header on every request to /mcp/gateway. The agent has no awareness that scoping is applied — it simply sees fewer tools in tools/list and SEARCH_TOOLS results.

# tools/list — only returns allowed tools, minus denied
curl -X POST http://localhost:8000/mcp/gateway \
  -H "Authorization: Bearer mgw_usr_live_xxx" \
  -H "Mcp-Session-Id: a1b2c3d4-..." \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}'

If the agent attempts to call a denied tool, the gateway rejects it:

{
  "content": [{"type": "text", "text": "Tool 'HUBSPOT__internal_debug' is not in session scope"}],
  "isError": true
}

Update scope at runtime (optional)

Need to grant or revoke tool access mid-conversation? Patch the session. Only provided fields are changed — omit a field to leave it unchanged.

PATCH /api/v1/sessions/{session_id}
{
  "denied_tool_names": ["HUBSPOT__debug", "HUBSPOT__admin"]
}
PATCH /api/v1/sessions/{session_id}
{
  "allowed_tool_names": ["VIVI__kb_finance", "GMAIL__*"]
}
PATCH /api/v1/sessions/{session_id}
{
  "allowed_tool_names": null,
  "denied_tool_names": null
}

Wildcards

Both fields support the whole-server wildcard PREFIX__*:

PatternMeaning
HUBSPOT__searchExact match — only this one tool
HUBSPOT__*All tools from the HUBSPOT server

Partial wildcards (e.g., HUBSPOT__search_*) are not supported and will be rejected.

Tool Name Format

Tool names must use the prefixed format: SERVER_PREFIX__tool_name or SERVER_PREFIX__*.

The prefix is derived from the server name (uppercase, spaces/hyphens replaced with underscores):

Server NameTool NamePrefixed Name
my-knowledge-basessearch_kb_elizabethMY_KNOWLEDGE_BASES__search_kb_elizabeth
github-apicreate_issueGITHUB_API__create_issue
slacksend_messageSLACK__send_message
my-knowledge-bases(all tools)MY_KNOWLEDGE_BASES__*

To find the correct prefixed names, call tools/list without scoping first, or use POST /api/v1/tools/search.

Scoping Behavior Reference

alloweddeniedResult
nullnullAll tools visible (default)
["A__x", "B__y"]nullOnly tools A__x and B__y
null["A__secret"]Everything except A__secret
["A__*"]["A__debug"]All A tools except A__debug
["A__x", "B__*"]["B__admin"]A__x + all B except B__admin
[] (empty)nullBlock all tools

What Gets Filtered

Gateway PathScoped?Details
tools/list (LIST mode)YesOnly allowed tools appear in the tool list
SEARCH_TOOLSYesVector search results filtered after search (over-fetches 3x when scoped)
EXECUTE_TOOLYesCalls to tools outside scope are rejected
tools/call (direct)YesDirect MCP tool calls also respect session scope
SYSTEM__ builtinsAlways visibleGateway meta-tools are never filtered
/mcp/servers/{id} (direct server)Not scopedDirect server connections bypass session scoping by design
POST /api/v1/tools/search (REST API)Not scopedStateless REST endpoint, no session context

Input Validation

Tool names are validated on creation and update:

  • Non-empty — empty strings are rejected
  • Must contain __ — the PREFIX__tool_name separator is required
  • Wildcard must be whole-serverPREFIX__* is allowed, PREFIX__search_* is rejected
  • No wildcards in prefix*__tool is rejected
  • SYSTEM prefix reservedSYSTEM__anything is rejected (builtins can't be scoped)

Invalid names are rejected with a 422 Validation Error.

SDK Integration

Using the MCP Gateway Python SDK:

from mcpgateway_sdk import MCPGateway

async with MCPGateway(base_url="http://localhost:8000", api_key="mgw_usr_live_xxx") as gw:
    # Create a scoped session
    session = await gw.sessions.create(
        allowed_tool_names=[
            "VIVI__kb_finance",
            "VIVI__kb_hr",
            "VIVI__kb_legal",
            "HUBSPOT__*",
            "GMAIL__*",
        ],
        denied_tool_names=[
            "HUBSPOT__internal_debug",
        ],
    )

    # Update only the deny list (allowed list unchanged)
    session = await gw.sessions.update(
        session.id,
        denied_tool_names=["HUBSPOT__internal_debug", "HUBSPOT__admin_reset"],
    )

    # Clear all restrictions
    session = await gw.sessions.update(
        session.id,
        allowed_tool_names=None,
        denied_tool_names=None,
    )

Use Cases

Multi-tenant SaaS — One MCP server exposes 500 knowledge base tools. Each customer's agent gets a session scoped to only their knowledge bases. Customer A sees 3 tools, Customer B sees 12 — from the same server.

Role-based access — A support agent sees customer-facing tools. An admin agent sees internal tools. Same server, different session scopes.

Progressive disclosure — Start with basic tools. As the conversation reveals the user's intent, patch the session to unlock more specific tools. Keeps the initial tool list lean.

Context optimization — Reduce the tool list to only relevant tools, saving tokens and improving model accuracy.

Security isolation — Prevent agents from discovering or calling tools they shouldn't know about.

Industry Context

The MCP specification does not define per-session tool filtering. Every major platform implements this at the gateway or orchestration layer:

PlatformApproachLevel
MCP Gatewayallowed/denied_tool_names per sessionGateway
OpenAIallowed_tools per API requestClient API
AWS BedrockCedar policies per principalGateway
Kong AI GatewayConsumer Group ACLsGateway
LiteLLMAllowlists per API key/teamProxy
CrewAIget_mcp_tools() per agentFramework

MCP Gateway's approach is session-level (not request-level or key-level), which means scope can change during a conversation without creating a new connection. There is an open proposal (SEP-1300) to add tool filtering to the MCP spec, but it is not yet standardized.

On this page