• Zero Trust & SASE

Securing an MCP server in production: which tools it exposes, to which agent, under which authentication

Franck-Emanuel Goguer

10 min read

Securing an MCP server in production: which tools it exposes, to which agent, under which authentication

The server that ran on a workstation becomes a shared service: the question is no longer whether it works, but what it lets agents invoke. Once several agents or several teams connect to the same endpoint, the control surface changes in kind. What follows describes the mechanisms available today to secure an MCP server in production, the traces they produce, and the limits that need naming before any commitment.

What does an MCP server actually expose?

Three types of capability: tools, prompts and resources. Tools are the ones that execute.

An MCP server, in the sense of the Model Context Protocol (MCP), is a process that responds to structured requests from a client: it announces its capabilities, then executes them on demand. The list of three comes from the specification, revision 2026-07-28.

The specification is direct about tools:

Tools represent arbitrary code execution and must be treated with appropriate caution.

MCP specification, revision 2026-07-28, Tool Safety

And on trusting their descriptions:

descriptions of tool behavior such as annotations should be considered untrusted, unless obtained from a trusted server.

MCP specification, revision 2026-07-28, Tool Safety

What is exposed is not data at rest. It is execution on demand, triggered by an agent on the basis of a description the protocol itself declares untrusted by default.

┌─────────────┐     ┌──────────────────┐     ┌──────────────────────┐     ┌──────────────────────┐
│  MCP Client │────▶│  MCP Server      │────▶│  Tools / Prompts /   │────▶│  Downstream systems  │
│  (agent)    │     │  (HTTP endpoint) │     │  Resources exposed   │     │  (APIs, DBs, SaaS)   │
└─────────────┘     └──────────────────┘     └──────────────────────┘     └──────────────────────┘
                                                        ↑
                                        What is exposed is not data.
                                                It is execution.

How do you know what is already running?

Before any control, a read-only inventory: without one, every policy applies to a surface you have not seen.

Four questions structure that inventory:

  1. Which MCP servers are reachable within the organisation, and on which endpoints?
  2. Which tools does each server expose, and with what description?
  3. Which clients connect: automated agents, developer workstations, CI pipelines?
  4. Where do the admin credentials used to synchronise capabilities actually live?

Applying an access policy to a server whose tool list is unknown is closing a door without knowing what it protects. A Cloudflare configuration assessment establishes that baseline before any change is made.

How do you put an MCP server behind authentication?

Two setups are available, and the criterion that separates them is the documentation's own: who manages the server code, and who manages its hostname.

Table 1 · Two authentication setups (source: Cloudflare, page dated 9 July 2026)

SetupWhen to use itWho manages authenticationPrerequisitesSilent failure
Customer-managed third-party serverThird-party code, hostname is yours in CloudflareThird-party MCP server, with its own OAuth flowProxy status enabled in Cloudflare DNSDo not enable Managed OAuth on this setup
SaaS-managed third-party serverThe provider hosts the server and accepts an OAuth or OIDC identity provider you supplyThird-party MCP server, with Access as the OIDC providerNo DNS configuration on your side, the provider holds the hostnameThe server receives an access_token it uses downstream: what it does with it sits outside your control

That catch is worth spelling out, because it does not surface as an error. Managed OAuth should only be enabled for an MCP server that actually validates the Access JWT Cloudflare sends it. On third-party code you cannot modify, keeping the server's native OAuth flow is the only tenable option.

Warning

Managed OAuth on a server that does not validate the Access JWT is an access control that authenticates without verifying anything.

Service token double authorisation

For automated access, the MCP server portal accepts Access service tokens. Two controls are required simultaneously:

  1. A Service Auth policy on the portal's own Access application.
  2. A Service Auth policy on each upstream MCP server reached through that portal.

Two direct consequences:

  • A server without a matching Service Auth policy is hidden from the tool list presented to the agent: it does not appear, it cannot be called.
  • A server configured with on_behalf: true is excluded from service token sessions, because that mode requires a per-user OAuth grant.

The service token also only counts for a server placed behind Access. Where the server carries its own OAuth, that flow holds the authority and the token plays no part. Beyond the wiring, the agent's identity and what it is allowed to reach belong to non-human identity governance.

Wiring is one thing. Governance is another.

Agent identity, blast radius, scope review: the full playbook sits on the agentic AI security use case.

How do you reduce what an agent can invoke?

With an MCP server portal: the administrator chooses, tool by tool, what stays callable.

The portal consolidates several servers behind a single HTTP endpoint (https://<subdomain>.<domain>/mcp), and policy attaches to the portal rather than to each server.

Tool curation

By default, all tools from a server added to the portal are available. An administrator can disable them individually: a disabled tool does not appear in the list presented to the agent and cannot be invoked.

To invert the logic and move to an explicit allowlist, the API accepts default_disabled: true on the server-to-portal mapping, then an enumeration of the only permitted tools in updated_tools:

Portal update · tool allowlist
{
  "servers": [
    {
      "id": "example-server",
      "default_disabled": true,
      "updated_tools": [
        { "name": "search_documents", "enabled": true },
        { "name": "list_projects", "enabled": true }
      ]
    }
  ]
}

This excerpt is illustrative. Adapt the identifiers to your configuration and test in a pre-production environment before any deployment.

This is least privilege applied at the level where the protocol operates: the agent sees only what the administrator has explicitly permitted.

Aliases

Tools and prompts can be renamed at portal level without touching the upstream server. An alias runs from 1 to 40 characters and follows the pattern ^[a-zA-Z0-9]+([_-][a-zA-Z0-9]+)*$. Precedence is portal_alias > server_alias > alias > original name. It is also the way to rewrite a description supplied by a third party, which is precisely what the specification tells you not to take at face value.

Code Mode

Code Mode replaces upstream tool definitions with two tools: a search tool and a code execution tool, running in an isolated Dynamic Worker environment. Credentials stay outside the model context. Four policies are available: off, opt_in (default), default_on, enforced.

A portal accepts up to 40 MCP servers, with a warning displayed as you approach the limit.

Before and after the portal

BEFORE (without a portal)
─────────────────────────────────────────────────────────
Client A ──▶ Server 1   Client B ──▶ Server 2   Client C ──▶ Server 3
Client A ──▶ Server 2   Client B ──▶ Server 3
Client A ──▶ Server 3
→ direct paths, no common policy

AFTER (with a portal)
─────────────────────────────────────────────────────────
Client A ─┐
Client B ─┼──▶ Portal endpoint ──▶ Per-portal policy ──▶ Curated tools ──▶ Server 1
Client C ─┘        /mcp                                                 ──▶ Server 2
                                                                        ──▶ Server 3
→ 1 endpoint, centralised policy, reduced surface
— Proof of concept

A demonstration on your real environment is worth a thousand words.

On a narrow perimeter (one portal and a handful of tools), you see exactly what your agents can invoke and what gets traced. We scope the environment together before we start.

A work email. We get back to you to scope the perimeter.

What logs remain after a tool call?

The portal logs every call. The level of detail depends on how you read the logs.

Table 2 · What MCP portal logs contain

What you needDashboard viewLogpush export
WhenTime ✓Datetime ✓
Which serverServer ✓ServerURL + ServerID ✓
Which toolCapability ✓ToolCallName ✓
Which methodMethod ✓ (tools/call, prompts/get, resources/read)
Which sessionSessionID ✓
Which identityUserEmail + UserID ✓
How longDuration ✓ServerResponseDurationMs ✓

The Logpush export of the mcp_portal_logs dataset is available on Enterprise plans only. It is the one place that answers "who called which tool, and when". The dashboard view is enough to diagnose a failure, not to run an access review. These records hold personal data, so their retention period is a question to settle before the export is switched on.

AI Gateway and identity in logs

AI Gateway adds native user identity in one case only: when the request reaches it through a custom domain protected by Cloudflare Access. The cf.user_id field then holds the value of the sub claim from the verified Access JWT, not the email address.

Service token requests, and those without a user subject, do not receive cf.user_id. For an agent authenticating with a service token, attribution therefore relies on custom metadata, capped at five entries per request, with anything beyond the fifth discarded. Keys starting with cf. are reserved and stripped if the client sends them. Visibility over every model call is a design-time decision, not something inherited afterwards.

Where does traffic go, and where does it not?

Routing portal traffic through Cloudflare Gateway is optional, and it is what decides how much you will see.

When Gateway routing is enabled:

  • Calls to upstream servers pass through Gateway, appear in the HTTP logs and are subject to data loss prevention policies, which is how you detect sensitive data before it leaves.
  • Outbound requests can use dedicated egress IPs or Gateway ranges, which makes the portal acceptable to an upstream server that filters by source IP.
  • The portal terminates the MCP client connection and re-issues the request: Gateway decrypts and inspects that traffic automatically, without the account-level TLS decryption setting being turned on.

Explicit restriction: routing applies only to real-time tool calls made by users through the portal. Background operations, including admin credential synchronisation, do not pass through Gateway and do not use the egress IPs.

Outside the portal, for instance an agent on a workstation connecting straight to an upstream server, Gateway sees nothing by default. TLS decryption has to be enabled as it would be for any other HTTP traffic.

API Shield governs the APIs you expose at the inbound perimeter: endpoint discovery, schema validation, mTLS, JWT validation, volumetric abuse. It does not govern an agent's calls to third parties.

Agent in Workers: a Worker does not egress through Gateway by default. The on-ramp is configured explicitly, otherwise the traffic is simply not seen.

WHAT GATEWAY ACTUALLY INSPECTS
─────────────────────────────────────────────────────────────────────────

✓ Portal tool call, Gateway routing enabled
  MCP client ──▶ Portal ──▶ [Gateway: DLP, HTTP logs] ──▶ Upstream server
  → inspected

✗ Portal background operation (admin credential sync)
  Portal ──▶ Upstream server (direct, bypasses Gateway)
  → not inspected

✗ Agent in Workers without a Gateway on-ramp
  Worker ──▶ Upstream server (direct)
  → not inspected

✗ Agent on a workstation, client connecting directly
  Local client ──▶ Upstream server (direct)
  → not inspected without TLS decryption enabled

Gateway only filters what goes through it.

What about MCP clients installed on developer workstations?

Three questions, and three answers of a different nature.

What the server exposes to the workstation. A server running stdio transport alone does not expose a remote HTTP endpoint: it cannot join a portal. To bring it under centralised control, it has to be hosted behind an HTTP endpoint. That is an architectural prerequisite, not a configuration option.

Which credentials live in the local configuration. MCP client configuration files frequently hold tokens, API keys or internal server URLs. Those credentials pass through no centralised vault: they live in text files, often in plaintext, on machines that travel.

Who revokes, and through which path. Revocation runs through the Access policy where the server sits behind a protected HTTP endpoint. For a local stdio server there is no centralised revocation path: removing the token from the configuration file stays a manual operation, workstation by workstation.

The nuance that changes the diagnosis: a stdio server readily calls remote services, APIs and databases, from the local workstation, without exposing any inbound endpoint at all. The risk is not the absence of an inbound endpoint, it is the absence of visibility over outbound calls. The only inspection path is the client on the workstation, with TLS decryption enabled.

What this setup does not cover

Silent admin token expiry. The token used to synchronise a server's capabilities expires according to the provider's policy. When it expires, the server moves to Error or Sync Required status and disappears from the portal for end users. No notification is sent to administrators, so checking server status belongs in the review cadence rather than in alerting.

Note

An admin token that expires raises no alert: the server simply vanishes from the portal.

Servers that reject a proxy-based client. Some MCP servers refuse requests coming from a portal-type client and return a 403 on the registration endpoint. The portal cannot override that server-side behaviour: those servers stay incompatible until their provider supports Cloudflare as an MCP client.

Capabilities frozen at first authorisation under manual OAuth. Where OAuth credentials are configured manually, Cloudflare stores the tools and prompts returned during the first user authorisation. Background and manual capability synchronisation do not refresh them: a server that adds tools after that point does not expose them.

Independent MFA and purpose justification: unsupported, not unconfigured. MCP servers use a dedicated Access application type that does not support either control when the server is authorised through a portal. The user is not prompted for MFA, regardless of whether global MFA enforcement is on and regardless of whether an MFA policy is assigned to the server. This is a limit of the application type, and there is nothing to configure that lifts it.

A portal exposed without an Access policy. A portal created without a policy stays reachable. It is the easiest mistake to make in a fast rollout, and the fastest to check.

Workers isolates and Spectre. V8 isolates prevent code from reaching memory outside the isolate, and the runtime removes the timers and concurrency that would let an attacker rebuild a time measurement. Cloudflare's security model documentation, page dated 11 August 2026, still devotes a full section to the absence of a fix:

Spectre does not have an official solution. Not even when using heavyweight virtual machines. Everyone is still vulnerable.

Cloudflare, Workers security model, 11 August 2026

Lateral movement between isolates therefore stays within the risk perimeter to consider. The Workers architecture makes it difficult, it does not make it impossible, and a serious setup relies on surface reduction rather than on a sealed boundary.

Prompt injection. This vector sits outside MCP server access control: it bears on the content exchanged, not on the scope opened. It is addressed under AI security with Cloudflare.

How Brixio runs this in production

Brixio works on Cloudflare only: assessment, deployment, day-to-day operations, incident response, with no other line of business. Brixio is an Authorized Cloudflare Service Delivery Partner (ASDP), certified ISO 27001:2022, with follow-the-sun coverage across four hubs: Luxembourg, Paris, Dubai and Singapore.

On an MCP deployment, that means an inventory of the servers and the tools they genuinely declare, the choice of authentication setup server by server, the configuration of Access policies and portals, the Gateway routing settings, the Logpush exports, and periodic review of synchronisation status.

You do not secure an MCP server by hardening it. You secure it by reducing what it agrees to execute.

Not sure what your Cloudflare setup actually covers?

Metryx grades the application security of your Cloudflare zones: DNS, SSL, WAF, bot protection and performance. It does not cover the Cloudflare One perimeter, so neither SASE nor Zero Trust. Free, with unlimited scans.

Frequently asked questions

It covers three dimensions: controlling who can connect, limiting what an agent can invoke, and keeping a record of every call. The MCP specification of 28 July 2026 states that tools represent arbitrary code execution and must be treated with appropriate caution, which puts the exposed scope ahead of hardening the server.

An agent authenticating with an Access service token is checked twice: once at the portal's Access application, once at every upstream server it reaches through that portal. A server without a matching Service Auth policy is hidden from the bot's tool list. Revocation runs through deleting or renewing the token in Access, token by token, with no change on the server.

It depends on the setup. An Access service token counts only for a server placed behind Access. Where the server carries its own OAuth, that flow holds the authority and the service token plays no part. Behind a portal, a service token is checked twice: at the portal, then at each upstream server. A server configured with on_behalf: true cannot be reached with a service token at all.

Yes. An MCP server portal lets you disable tools one by one, or invert the logic: with default_disabled set to true on the server-to-portal mapping, every tool is hidden and only those listed in updated_tools remain callable. A disabled tool does not appear in the portal's tool list and cannot be invoked.

The portal dashboard exposes timestamp, status, server, tool called and duration. The Logpush export of the mcp_portal_logs dataset, on Enterprise plans, adds the authenticated user's identity (UserEmail, UserID), the exact tool name (ToolCallName), the method, the session identifier and the upstream server URL. For service token sessions, attribution relies on custom metadata.

No. A server running stdio transport alone does not expose a remote HTTP endpoint and cannot join a portal, which only protects servers reachable over HTTP. For a local server, the only path for inspecting outbound calls is TLS decryption enabled on the workstation.

YOUR CLOUDFLARE ENVIRONMENT, AUDITED

Find out where your posture stands today.

Get a free audit, automated, read-only, delivered as a PDF report in five minutes. No credit card required.

Get a free audit Talk to an expert

Keep reading

All articles