Skip to main content

Connect an MCP Server

Tasks

Register an upstream MCP server and choose how AIControls authenticates to it. For how MCP tool-call proxying and authorization work conceptually, see MCP Governance.

Register the server

  1. Go to Settings → MCP Servers → Add server.
  2. Fill in the server fields:

    FieldDescription
    NameUsed as a tool prefix in agent calls, e.g. prometheus → tool prometheus__query
    Typehttp (SSE) or stdio (local command)
    URL / CommandMCP server endpoint or shell command
    AuthenticationSee Choose an authentication method below
note

Developers don't configure MCP servers themselves — they're provisioned centrally and available to all agents automatically based on group membership policies.

Choose an authentication method

Each MCP server can be configured with one of seven authentication methods. The right choice depends on whether the upstream server has shared credentials, per-developer credentials, or an OAuth-capable identity layer.

No credentials are forwarded.

When to use: Internal servers on a private network that trust all proxy traffic implicitly.

Two more fields appear on the form for several of the methods above, regardless of which is selected:

FieldApplies toDescription
Discovery tokenPassthrough, Per-developer (credential store), OAuth 2.0, Cross-App AccessOptional. A one-time bearer token used only to list tools from the upstream during registration — not stored afterward. Leave blank to register with zero tools discovered; the count populates once a developer makes a real call
Connection instructionsAll per-developer methodsOptional guidance shown to developers in Connected Accounts before they connect their own credential or OAuth account. Most useful for Per-developer (credential store), where developers need to know how to generate the credential you expect

Test the connection while adding a server

Before saving, click Test connection in the Add/Edit MCP Server drawer to run the standard MCP discovery sequence (initialize, then tools/list, prompts/list, resources/list) against the form's current values, without registering or persisting anything. The result shows the tool/prompt/resource counts, or an error per category.

For a Cross-App Access server, Test connection uses your own linked enterprise identity (see Link your enterprise identity) to run the real two-leg ID-JAG exchange, if you've linked one — the same exchange a developer's tool call would trigger — rather than substituting a discovery token. The result tells you which happened:

ResultMeaning
✓ Real Cross-App Access exchange validatedYou have a linked identity; the test ran the actual leg-1 + leg-2 exchange against the IdP and Resource Authorization Server and reached the upstream with a real access token
Discovery only — link your identity to validate the real exchangeYou haven't linked an enterprise identity yet, so the test fell back to the Discovery token (or an unauthenticated connection) instead of a real exchange

Test your connection as a developer

For any per-developer authentication method (Passthrough, Per-developer credential store, OAuth 2.0, or Cross-App Access), once you've connected your own credential in Connected Accounts, click Scan next to the server to run a live check with that credential. AIControls calls the standard MCP discovery sequence — initialize, then tools/list, prompts/list, and resources/list — and shows you exactly what your credential can currently reach: the tools, prompts, and resources the upstream server returns, or an error for any category the credential can't access.

This is useful for confirming a new OAuth connection or stored credential actually grants the access you expect, without needing to make a real tool call from an agent first.

note

The Scan button only appears once you've connected a credential for that server — it reflects your access, not the server's discovery-token connection used at registration time.

Resolve a quarantined server

When AIControls sees tool-call traffic to a server that isn't configured as a datasource, it automatically creates a quarantined catalog entry and sends a one-time admin alert — see Shadow MCP traffic & quarantine for why this happens. Resolving it is one of two actions:

  1. Go to Settings → MCP Servers and find the entry marked Quarantined — it shows the endpoint AIControls observed traffic to and when it was first seen.
  2. Choose how to resolve it:

    ActionEffect
    PromoteRuns the same flow as registering a server manually — confirm the endpoint, transport, and authentication. The entry then becomes a normal governed server: ownable, policy-able, and eligible for Access Bindings.
    DenyThe entry is kept for audit history with quarantine_state: denied; further tool calls to that endpoint are blocked by policy.
  3. Save. There's no third option — an unresolved quarantined entry stays audit-only (logged, but ungoverned) until an admin picks one.
note

Quarantine is a one-time alert per newly discovered server, not a repeating notification — if you miss it, find outstanding entries by filtering Settings → MCP Servers for the Quarantined state.

Write a tool-authorization policy

Use object.mcp.tool to match tool calls. Arguments are available as object.mcp.arguments:

Block file writes outside the workspace

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: restrict-write-file
spec:
matchConditions:
- expression: 'object.mcp.tool == "write_file"'
validations:
- expression: |
object.mcp.arguments.path.startsWith("/home/") ||
object.mcp.arguments.path.startsWith("/workspace/")
message: "write_file is only permitted inside /home/ or /workspace/"
validationActions: [Deny]

Audit all bash commands

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: audit-bash
annotations:
proxy.nirmata.io/enforcement-mode: audit
spec:
matchConditions:
- expression: 'object.mcp.tool == "bash"'
validations:
- expression: 'true'
message: "bash call recorded"
validationActions: [Audit]

Block destructive commands

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: block-destructive-bash
spec:
matchConditions:
- expression: 'object.mcp.tool == "bash"'
validations:
- expression: |
!object.mcp.command.matches("(rm -rf|DROP TABLE|truncate)")
message: "Destructive command blocked by policy"
validationActions: [Deny]

See also