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.

Built-in Admin MCP Data tools​

Data tools appear under Inventory → MCP Servers → Registration → Built-in Admin MCP endpoint, separately from Registered upstream servers and their counts. Connect administrators to https://YOUR_ORG.aicontrols.dev/mcp/admin to use its four read-only tools alongside the management tools. Applications and service accounts continue to use /mcp with their existing reporting grants. Data has no upstream URL, command, upstream credentials, connection scan, or editable transport. See the Admin MCP reference for deployment availability and authentication requirements.

The Data tool group and its Details drawer show an Administrator connection guide with the endpoint, credential requirements, and Data grant distinction. Use the administrator SQL steps below to discover the business schema and run a query.

The Details drawer covers half of the window. Use the arrow buttons in its header, or the ← and → keys, to move to the previous or next server on the Registration tab without closing the drawer. The page URL contains the server while the drawer is open, for example ?server=<name>, so you can copy it to share the server. Back closes the drawer.

Its four Data tools provide authorized reporting and coaching:

ToolPurpose
data__catalogDiscover authorized logical reporting metadata, or the paged business SQL schema for an administrator with SQL authority.
data__queryRun a prepared report, semantic query, or administrator read-only SQL query.
data__explain_queryValidate a semantic query and inspect its plan without reading source rows.
data__coaching_contextReturn prepared session, usage, and delivery evidence; accepts only an optional limit.

Prepared reports include recent_sessions, recent_pull_requests, coaching_context, and team_directory. They default to 50 rows and accept a limit from 1 through 100. Prepared results are bounded samples, not complete exports. Administrators with an explicit SQL grant can also submit read-only SQLite queries as described below. The provider allows one report execution at a time; additional concurrent calls fail until capacity is available. Reports also fail when a source exceeds its fixed read bound, instead of returning a silently incomplete aggregate.

Source cells are limited to 64 KiB, and encoded result rows to 64 KiB. A prepared-report response cannot exceed 8 MiB. Oversized values or results fail the request; fields are never silently truncated to fit.

Semantic metric aliases must be single identifiers of at most 512 bytes, using ASCII letters, digits, and underscores, with a letter or underscore first. Output names must be unique, and order_by must name a field in the query output, such as a selected field or metric alias. SQL expressions are not accepted in either position.

For fields cataloged as int64, filter values must be decimal JSON strings, including each value in an in filter. For example, use {"field":"input_tokens","operator":"gt","value":"100"}. Bare numbers such as "value":100 are rejected; strings preserve exact values above JavaScript's safe integer range. Timestamp filters use UTC RFC3339 strings, such as "2026-10-01T00:00:00Z". Floating-point fields accept JSON numbers.

Catalog discovery with no arguments includes only authorized logical metadata. Administrator SQL discovery separately exposes reviewed physical business table names and explains excluded sources and columns. Credential values are never queryable. A revoked grant remains revoked when the demo is activated again.

Calls use the same AIControls gateway policies, access bindings, approvals, rate limits, response controls, and audit path as other MCP tools. They also require a current Data Access grant for the verified caller. Seeing the tool group does not grant access to reporting data. Its built-in provider cannot be renamed, replaced, refreshed, scanned, or deleted.

Policies, access bindings, schema pinning, and audit history retain the AIControls Data identity and reserved data tool prefix. The group is part of Admin MCP; it does not require a separate administrator connection. Before upgrading a deployment that already has an MCP server named data, an AIControls Data server, or a custom data tool prefix, rename that server or prefix. AIControls refuses to start with an ambiguous reserved identity rather than route calls to the wrong provider.

Query business data with administrator SQL​

For field definitions, typed values, limits, and grant responses, see the AIControls Data reference.

  1. Sign in as an active administrator and connect your MCP client to https://YOUR_ORG.aicontrols.dev/mcp/admin. Use your current AIControls-issued local session JWT or a personal PAT bound to your active administrator account's immutable ID. Send the credential in the Authorization: Bearer YOUR_ADMIN_TOKEN header on every request; earlier MCP session credentials and URL query tokens do not establish Data authority. For example:

    {
    "mcpServers": {
    "aicontrols": {
    "url": "https://YOUR_ORG.aicontrols.dev/mcp/admin",
    "headers": {"Authorization": "Bearer YOUR_ADMIN_TOKEN"}
    }
    }
    }

    For a PAT, allow the exact qualified tools you need, such as data__catalog and data__query. A valid empty tool list ([]) permits all Data tools. Malformed or null lists, wildcard names, bare names, and legacy email-only PATs do not authorize Data access. Raw external OIDC tokens do not qualify for administrator SQL.

    Data access also requires an explicit Data Access grant for the caller. Normal startup does not create grants. A fresh demo grants its seeded administrator the admin_sql capability. Existing grants remain unchanged after an upgrade or restart.

    Use your current local session JWT for both grant-management requests below, even when your MCP client uses a PAT. PATs cannot read or change these grants. Read GET /api/v1/data-access/admin-sql/grant on the admin endpoint. If it returns 404 grant_not_found, explicitly create your grant with:

    {"expected_revision":"0","enabled":true}

    Send this body to PUT /api/v1/data-access/admin-sql/grant. The new grant lasts 365 days and includes discover and admin_sql only; it does not grant prepared or semantic dataset access.

    For an existing active grant, explicitly enable SQL with PUT /api/v1/data-access/admin-sql/grant and the returned revision:

    {"expected_revision":"1","enabled":true}

    Use enabled:false to remove SQL authority. A stale revision returns 409. This operation preserves dataset permissions, expiry, and revocation; it does not renew an expired grant or restore a revoked one. SQL authority covers the entire reviewed business schema and all of its rows across the bound deployment, independently of your logical dataset field and row grants. This preview supports deployment administrators; it does not isolate customers sharing a source database.

  2. Call data__catalog with these arguments:

    {"schema":"admin_sql","limit":25,"offset":0}

    Follow next_offset until it is absent. Use tables to select exact table names, include_columns:false to list table summaries, or section:"relationships" to inspect documented joins. Use section:"exclusions" without a table filter to page through credential, secret, and internal-state exclusions. The maximum page size is 50. Unknown columns are not automatically published.

  3. Pass a read-only SQLite statement to data__query. For example:

    {
    "sql":"WITH counts AS (SELECT developer_id, count(*) AS sessions FROM developer_sessions GROUP BY developer_id) SELECT developer_id, sessions, dense_rank() OVER (ORDER BY sessions DESC) AS rank FROM counts WHERE sessions >= :minimum ORDER BY sessions DESC, developer_id",
    "parameters":{"minimum":{"type":"integer","value":"1"}},
    "limit":10
    }

    SQL supports joins, CTEs, recursive queries, subqueries, grouping, windows, unions, and reviewed built-in scalar functions. Named parameters use :name in SQL. Values use type and value: integer uses a decimal string, real a JSON number, text a string, blob a base64 string, and null JSON null. Result cells use the same lossless representation; an expression's type can differ by row.

    Only reviewed business tables are available. Writes, schema changes, multiple statements, system catalogs, PRAGMA, ATTACH, extension loading, virtual tables, implicit rowid aliases, and filesystem or network functions are rejected. Use the primary-key columns returned by schema discovery. SQL runs against an isolated snapshot of the referenced business tables.

  4. Inspect complete, truncated, source_tables, source_rows, and source_bytes alongside the result. A result beyond your row limit returns an explicit truncated page; source overflow fails the entire query, including aggregates. A complete SQL result describes the retained source snapshot, not all historical activity.

    SQL queries use the interactive profile by default: a five-second total deadline and at most 100,000 cumulative source rows. For larger reports, verify that catalog discovery advertises report in execution_profiles, then add "profile":"report" to the data__query arguments. This profile allows up to 1,000,000 cumulative source rows and 60 seconds; a shorter caller deadline still applies. Upgrade the server if it does not advertise the report profile. Prepared and semantic queries do not accept SQL execution profiles.

    Both SQL profiles allow at most 100 result rows, an 8 MiB response, 64 KiB cells and encoded rows, and 256 MiB of accounted source data. SQL text is limited to 64 KiB, parameters to 64, result columns to 256, expression depth to 100, and compound SELECT terms to 64. These are demo safety limits, not production capacity or memory guarantees. Errors distinguish invalid_request, access_denied, limit_exceeded, deadline_exceeded, and unavailable without exposing backend details.

The gateway rechecks the administrator's current role and SQL grant before execution and delivery. Normal policy, enabled-state, and response controls apply. SQL audit records retain request-size/count metadata and event correlation without SQL literals, parameter values, or a content-derived request fingerprint. Stored SQL approval justifications omit reflected request values. Prepared and semantic query forms remain available; data__explain_query continues to accept only semantic requests.

Register the server​

Trying it out?

A public server that needs no sign-in, such as DeepWiki at https://mcp.deepwiki.com/mcp, is enough to see tool governance work. See Try AIControls with free models and a public MCP server.

  1. Go to Inventory → MCP Servers → Registration tab → Add MCP Server.
  2. Fill in the server fields:

    FieldDescription
    NameUsed as a tool prefix in agent calls, e.g. prometheus → tool prometheus__query
    Typehttp (remote server) or stdio (local command)
    TransportFor http servers: SSE (legacy, and what most MCP servers speak) or Streamable HTTP (FastMCP 3.x and any server built against the 2025-03-26 MCP spec). Use Detect settings from URL if you aren't sure.
    URL / CommandMCP server endpoint or executable command
    AuthenticationSee Choose an authentication method below
tip

Pick Streamable HTTP for servers with slow tools, and for any tool that isn't safe to run twice.

SSE holds one long-lived connection, so AIControls probes it with a short timeout to tell a stalled connection apart from a working one. A tool that takes longer than 15 seconds to reply is treated as a possibly-dead connection: the call is abandoned and retried on a fresh connection. If the server had in fact received the first call, the tool runs twice — which matters for anything that isn't idempotent, such as creating a ticket, sending a message, or committing a change.

Streamable HTTP sends each tool call as an independent request. AIControls checks an idle MCP session with a lightweight ping before sending the next tool call. If the session has expired, AIControls creates a new session first. The tool call itself is still sent exactly once and may run until the calling client's request deadline.

note

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

note

Server names must be unique. Adding a server with a name that's already registered fails with an error instead of replacing the existing one — edit the existing server instead.

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.

Discovery — whether run from Test connection, on save, or by a later Refresh — is bounded by a 30-second deadline. An upstream that accepts the connection but never responds fails with a clear scan timed out error instead of leaving the request hanging.

The same 30-second bound applies to the proxy's own connection handshake when a developer's first tool call opens a session to the upstream — the call fails with a clear connection-timeout error rather than hanging. Only the handshake is bounded: a healthy server's tool call may still run as long as it needs (streaming responses are never capped).

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 Inventory → MCP Servers → Registration tab 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 Inventory → MCP Servers → Registration tab 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​

A stdio connection test starts a local process on the proxy. It requires an administrator session; builder credentials and developer PATs cannot run it. Only configure commands you trust with the proxy's operating-system privileges.