MCP gateway
Agents never call Polarion, GitLab, GitHub or any other system directly. Every tool call goes through the MCP gateway in the Agent Runner. The gateway decides what an agent may see and do, holds what needs a person, and records everything on the run.
Run tokens
Section titled “Run tokens”Each run gets a run token when it starts. The token stands for one agent on one task with one set of granted tools. It is closed when the run ends.
- Calls without a live run token get HTTP 401.
- The agent loop sends the token as a bearer header. In a sandbox, Claude Code and Codex get it for the run’s duration; the files that hold it are removed after the run.
- Run tokens live in the runner’s memory only.
What an agent sees
Section titled “What an agent sees”tools/list returns only what this run may use:
- Polarion tools the agent’s approved skills allow, plus the tools every agent has (see Tools).
- Tools of connected MCP servers, named
<connection>__<tool>, for every connection that passes three checks: the pool agentai_usesthe connection, the connection isactive, and the engagement grants it in this project. A connection that needs a credential is left out if the secret is missing.
Each external tool’s description starts with the connection’s name and says whether it is limited to a scope,
needs human approval or is blocked. Tools that need approval get a required why argument: the agent’s
reason for the approver. The gateway removes it before the call.
Tools are always offered with the reviewed definition from the connection’s tool policy, never with what the server currently advertises. See Tool drift.
Policy per tool
Section titled “Policy per tool”flowchart TB
C["<b>Tool call</b><br/><small>agent, with its run token</small>"]:::run --> K["<b>Gateway checks</b><br/><small>token valid · tool granted · inside project scope</small>"]:::mute
K -- fails --> X["<b>Refused</b><br/><small>recorded on the run</small>"]:::bad
K -- passes --> P{"<b>Policy of the tool</b>"}:::mute
P -- read --> R["<b>Runs</b><br/><small>recorded</small>"]:::ok
P -- approve --> SR{"<b>Standing rule?</b>"}:::mute
P -- block --> B["<b>Blocked</b><br/><small>recorded</small>"]:::bad
SR -- yes --> RA["<b>Runs</b><br/><small>auto-approved · recorded</small>"]:::ok
SR -- no --> H["<b>Held</b><br/><small>approval card for a person</small>"]:::you
H -- approve --> RH["<b>Runs exactly as held</b>"]:::ok
H -- deny --> N["<b>Not run</b><br/><small>note goes to the agent</small>"]:::bad
classDef run fill:#ece5fb,stroke:#7c3aed,stroke-width:1.2px,color:#2b1d4f
classDef you fill:#fdf0d8,stroke:#b7791f,stroke-width:1.2px,color:#4a3210
classDef ok fill:#e3f2e7,stroke:#1f7a3d,stroke-width:1.2px,color:#143d22
classDef bad fill:#fbe4e2,stroke:#b3261e,stroke-width:1.2px,color:#4d1612
classDef mute fill:#f1f4f6,stroke:#8a96a3,stroke-width:1.2px,color:#2a3440
Every tool of a connection has one policy, set when the connection is reviewed:
| Policy | What happens |
|---|---|
read |
The call runs and is recorded. |
approve |
The call is held: stored with its exact arguments and the agent’s reason, and the task goes to the supervisor. |
block |
The call is refused and recorded as blocked. |
When a server is registered, policies are suggested from the server’s own hints: read-only tools get
read, destructive tools get block, everything else (including tools without hints) gets approve. A
person reviews and can change every policy before the connection is activated.
Before any call, the gateway also checks the scope (below). Write calls within one run are serialized.
Approvals
Section titled “Approvals”When a call is held:
- The gateway stores it in PocketBase (
approvals): project, task, agent, connection, tool, arguments, reason, statuspending. The same call already pending for the task is not stored twice. - The task goes to
waiting_input, assigned to the agent and the supervisor. The agent is told to stop. - The supervisor sees the held calls in the task panel and approves or denies them (a reason is required to deny). The decision is posted as a Polarion comment written with the decider’s own session.
- On approval, the runner checks that the connection is still granted and the tool still has policy
approve, then runs exactly the stored call with the connection’s credential. The agent posts the result on the task as itself. - When nothing is pending for the task any more, the agent is woken with the trigger
approval.
Changes agents want to make to Polarion items written by people (update_field, update_fields,
add_image, set_images) are held the same way and applied as the agent’s bot user when approved. See
Decisions.
Standing approvals
Section titled “Standing approvals”When approving, the supervisor can choose Always allow. The rule is stored on the engagement’s grants
(tool, who, when), written with the supervisor’s own session. Later calls of that tool by that agent in that
project run without asking. They are still recorded, as approvals with status approved and reason
“standing approval”.
- Standing approvals are per project, agent, connection and tool.
- They are never offered for changes to human-written Polarion items.
- You remove one on the Team page. A copy in PocketBase (
standing_rules) restores the rule when the agent is hired into the project again.
Connections and their lifecycle
Section titled “Connections and their lifecycle”An MCP connection is a work item of type Agent Connection (aiConnection) in the Agent Pool. Register one with the wizard in
Agency → MCP servers: the runner reads the server’s tool list, you review the suggested policies, and a
draft connection is created.
| Status | Meaning |
|---|---|
draft |
Being prepared; not usable |
in_review |
Submitted for security review |
active |
Approved; can be granted and is served by the gateway |
disabled |
Switched off; agents lose its tools |
Credentials are stored in the runner’s secret store by name; the connection only holds the name. See Connections.
Scopes
Section titled “Scopes”Some systems hold many separate things (repositories, projects). A connection marked scoped must be
limited per work project.
scopeArgumentsnames the tool arguments that carry the scope, e.g.project_id.owner/repojoins two arguments into one value.- For each call, every scope argument the tool has must be given and must contain one of the engagement’s scope values. Otherwise the call is refused as out of scope.
- A scoped connection without a scope in the engagement is locked: every call is refused, and the agent is told to ask for one.
Scope questions. The agent asks with ask_human and marks the question with scope_for = the
connection.
The answer options come only from the connection’s scope discovery (scopeOptions: a read tool, its
arguments and the result field), run with the connection’s credential. The agent’s own suggestions are
dropped. Without discovery, the question is free text. The answer is written into the engagement’s grants by
the task panel, as the person who answered, and unlocks the connection.
Tool drift
Section titled “Tool drift”MCP servers change their tools over time. The gateway compares each active server with its reviewed policy (results cached for 15 minutes; Check server now in the MCP server panel forces a fresh check).
| Change on the server | Effect on agents |
|---|---|
| Tool added | Listed for review. Never offered before a person adds it to the policy. |
| Tool changed (description, inputs, read/destructive hints) | Held: not offered until the policy is reviewed again. |
| Tool removed | Held: not offered until the policy is reviewed again. |
A person reviews the changes in the MCP server panel and saves the policy; it is written to Polarion under their name with the comment “Tool policy re-reviewed”. The Agency shows how many tools are held. If a server cannot be reached, nothing is held (calls fail anyway).
Git over the gateway
Section titled “Git over the gateway”Sandbox agents use plain git. For every granted connection with a gitRemote, the run configures git to
send that host’s URLs to the gateway’s git proxy (on the sandbox entrance) and sets the run token as git’s
auth header. The header is removed after the run. Commits are authored as “<Agent> (AI agent)”.
The git proxy:
- refuses runs without a live run token and connections that are not granted or are locked;
- allows only repositories in the engagement’s scope for that connection (exact match);
- allows clone and fetch;
- allows push only to branches matching
gitPushBranches(defaultagent/*, feature/*), never to other refs, never deletes a branch, and refuses pushes over 50 MB; - adds the connection’s credential on the way out (
gitAuth: basic with a user such asx-access-tokenoroauth2, or bearer). The credential never enters the sandbox.
Every fetch, push and refusal goes into the run ledger. Merging stays a decision for a person: a merge is an
MCP tool call under its policy, typically approve.
The audit trail
Section titled “The audit trail”| Where | What |
|---|---|
| agentRun description | Table of gateway calls: tool, decision, result, time, argument summary |
agentRun ledger |
Every artifact read, created, changed, requested or blocked, in Polarion and connected systems |
| Task comments | Approval decisions (as the person) and results of approved calls (as the agent) |
PocketBase approvals |
Held and auto-approved calls with arguments, reason, decider and result |
| Administration → Gateway | Actions per day and server, blocked attempts, standing-rule audit |