Govern MCP Apps and Tasks
MCP 2026-07-28 lets a tool do three new things. It can show the user a page
(MCP Apps). It can run for hours or days (Tasks). It can ask for a model call
while it runs (sampling). Each one is a new way in for hidden instructions, a
new way out for data, or a new way to spend money.
DVARA, the AI governance platform, keeps all three off until you turn them on, per workspace. This guide says what each one risks, what DVARA does about it, and the order to turn them on. Each step links to the page with the details.
Not included in DVARA Open Source.
What is off by default?
If you change nothing, a client that asks for any of these gets core MCP behaviour instead, and the refusal is recorded.
| Feature | Default | Turned on by |
|---|---|---|
| MCP extensions (Tasks, MCP Apps) | None allowed | The workspace's extension allow-list |
| Websites and browser permissions an MCP App page may use | None | The workspace's MCP Apps lists, on Agents → MCP Extensions |
| Sampling (a model call a tool asks for) | Refused with MCP_SAMPLING_REFUSED | The workspace's sampling setting |
| Trimming fields from tool results | Off | The workspace's mcp.response-projection.mode |
Three things are on all the time, whatever you allow:
- Changed tools wait for approval. A tool whose description, schemas, hints or MCP App page change, or a server that reports a new name or version, is quarantined until someone approves it.
- Every step is audited. Each page served, each task status change and each sampling model call writes its own audit event, in the signed, hash-chained audit trail.
- Every call is governed. Polls of a task, and tool calls a page makes, are ordinary governed calls: policy, PII and injection scans, approvals, budgets and rate limits apply.
What does each one risk, and what does DVARA do?
| Risk | MCP Apps | Tasks | Sampling |
|---|---|---|---|
| Hidden instructions | The page is HTML and script the server wrote. DVARA scans it for prompt injection and personal data, as any tool result | A finished task's result goes through the same scans | The prompt and the answer go through the LLM Gateway's PII scan and guardrails |
| Data leaving | A page may name only the websites and browser permissions the workspace allows | A task id works only for the API key and workspace that started it | The model is the one the workspace set. The tool cannot choose it, use tools in the call, or read other servers' context |
| A swap after review | Only the approved page is served. A changed page waits for approval | ||
| Cost and runaway use | Tool calls the page makes count against budgets and rate limits | 1,000 calls per task by default. Task ids expire after 7 days. Cancel from Flightdeck, or kill the agent session. An approval covers one task, for one day at most | 4,096 tokens per model call, 3 round trips and 8 model calls per tool call by default. Each model call has its own cost record under the caller's key |
Before you start
- Set the audit secret on every Gateway replica. Task ids are sealed with a
key made from
DVARA_AUDIT_HMAC_SECRET. Without it, Tasks is not relayed. Every replica needs the same value. Changing it ends every open task id. - Sync each server's tools. A sync records which extensions the server declares. Look for Declared extensions on the server's page in Flightdeck. A server not synced since 1.9.0 shows nothing, which is not the same as "none". The Gateway grants an extension only when at least one of the workspace's servers declares it.
- Review risk hints. On the server's page, Risk hints to review lists
tools whose
readOnlyHint,destructiveHintoropenWorldHintcontradicts what the tool does. Check them before a policy relies on a hint. - Check your clients' revision. Tasks and MCP Apps work only on MCP
2026-07-28. See Move MCP clients and servers to 2026-07-28.
Step 1: allow the extension for a workspace
An extension is in use on a call only when the client declares it on that request, the workspace allows it, and the server declares it.
| Extension | Name |
|---|---|
| Tasks | io.modelcontextprotocol/tasks |
| MCP Apps | io.modelcontextprotocol/ui |
Set the workspace's list in one of these places:
| Where | Who |
|---|---|
| Agents → MCP Extensions in Flightdeck | Workspace admin |
| The workspace form's MCP extensions section in Flightdeck | Platform owner |
mcp.extensions.allowed in the workspace's GitOps metadata | Whoever runs the import |
DVARA_MCP_GATEWAY_EXTENSIONS_ALLOWED, the fleet default for workspaces with no list of their own | Operator |
Allow one workspace first, not the fleet. A workspace's own list replaces the
fleet default, and none allows nothing even when the fleet default allows
some.
A policy can still deny an extension the list allows. For example, to keep Tasks out of one workspace's agents:
version: "1"
rules:
- id: no-tasks
expression: request.metadata["mcp.extension_id"] == "io.modelcontextprotocol/tasks"
action: DENY
Each refusal writes MCP_EXTENSION_DENIED, with a reason such as
not_allowed, policy_denied or not_offered. See
control which MCP extensions a client may use.
Step 2: MCP Apps — approve the pages, then open the lists
-
Sync the server's tools again after you allow MCP Apps. The sync reads each tool's page and pins its fingerprint. A workspace whose tools were already approved is asked to approve once for each tool that has a page.
-
Approve the change. A platform operator uses Release quarantine; a workspace admin uses Approve tool changes.
-
Allow only the websites and permissions the pages need. Both lists are empty by default, so a page that loads nothing from outside and asks for no permission is served, and any other is refused with
MCP_APP_REFUSED(domain_not_allowedorpermission_not_allowed). Set them on the MCP Apps card of Agents → MCP Extensions (workspace admin) or in the workspace form's MCP extensions → MCP Apps section (platform owner). Flightdeck refuses a website that is not an origin, a wildcard over a whole top-level domain such ashttps://*.com, and a permission other thancamera,microphone,geolocationandclipboardWrite. In GitOps:workspaces:- name: analyticsmetadata:mcp.extensions.allowed: io.modelcontextprotocol/uimcp.apps.allowed-domains: https://api.weather.examplemcp.apps.allowed-permissions: clipboardWriteThe fleet defaults are
DVARA_MCP_GATEWAY_APPS_ALLOWED_DOMAINSandDVARA_MCP_GATEWAY_APPS_ALLOWED_PERMISSIONS. The largest page is 1 MiB (DVARA_MCP_GATEWAY_APPS_MAX_RESOURCE_BYTES). -
Check each page on the server's page. From 1.9.0 the App pages card on the MCP server's page shows each page's approved version, its websites and permissions against your lists, the rule that decides, and whether it is served.
MCP_APP_TEMPLATES_SYNCEDin the audit trail keeps the same facts for every sync. -
Turn MCP Apps off where you do not want it. Rules on the same card narrow the workspace's decision for one server or one tool:
deny github,deny github/delete_repo, ordeny *withallow github/create_chartto keep a single App. The most specific rule decides, and a rule never turns MCP Apps on. A tool whose App is off still works as a plain tool. In GitOps the key ismcp.apps.rules. See Turn MCP Apps off for one server or one tool.
A tool meant only for the page is never listed to a client without MCP Apps, so it never reaches a model. Details: Govern MCP Apps.
Step 3: Tasks — set the limits, then watch the list
A task can run for days, so DVARA governs the whole task, not only each call.
| Limit | Default | Change it with |
|---|---|---|
| Calls one task may make (polls, answers, cancels) | 1,000 | DVARA_MCP_GATEWAY_TASKS_MAX_CALLS |
| How long a task id stays valid | 7 days | DVARA_MCP_GATEWAY_TASKS_MAX_AGE (for example P30D) |
| How long an approval covers its task | 1 day, or less if the task ends first | DVARA_MCPGATEWAY_AGENTIC_APPROVAL_TASKMAXAGE (for example PT12H) |
A task over its call limit is cancelled by the Gateway, and the server is asked to stop. Every call also counts against the usual rate limits and budgets.
To see and stop tasks:
| Page in Flightdeck | Who sees | Who cancels |
|---|---|---|
| MCP → Tasks | Platform owners and policy admins, every workspace | The same |
| Agents → MCP Tasks | Every workspace role, its own workspace | Workspace admin |
Killing an agent session in Flightdeck also cancels the tasks that session started. A cancel reaches every Gateway replica within about 5 seconds, and survives restarts. Stopping the work is up to the server: a task whose client never calls again may run on at the server until its own lifetime ends.
Each status change writes MCP_TASK_STATUS_CHANGED, once, however many replicas
see it.
If a tool needs approval, the approval a person gives covers the one task that call starts, and nothing else. Polls and answers on the task are not sent for approval again. The approval ends with the task, and cancelling the task withdraws it. To withdraw an approval while its task runs, cancel the task on the MCP Tasks page. Details: Run long tool calls as MCP Tasks.
Step 4: sampling — let a tool ask for a model call, under your rules
Sampling is not an extension, so the allow-list does not turn it on. It is refused until a workspace chooses otherwise.
- Open Agents → MCP Sampling in Flightdeck (workspace admin), or the workspace's settings, section MCP sampling (platform roles).
- Choose Make the call through the LLM Gateway.
- Enter the Model. It must be a model the workspace may call.
In GitOps the same setting is two keys:
mcp.input_requests.sampling: gateway
mcp.input_requests.sampling.model: gpt-4o-mini
The model call is made with the API key of the application that called the
tool, so it gets that key's policies, PII scan, budget and cost record. Each one
writes MCP_SAMPLING_CALL; prompt and answer text is never written to the
audit trail. A caller signed in with OAuth has no key to make it with, so its
sampling is refused.
A task that asks for a model call while it runs gets it the same way, when the application that created it polls it. The model call's own audit record names the server and the task. Details: Govern model calls that MCP tools ask for.
Step 5 (optional): keep only the fields a result needs
A long task or a rich App can return far more than an agent needs. Response
trimming is off by default. Set mcp.response-projection.mode to discover
first, read what each tool returns in the audit trail, then list the fields and
switch to enforce. It applies to tool call results only, not to results read
later through a task. See Keep only the fields an MCP tool result needs.
What to watch in the audit trail
| Event | Means |
|---|---|
MCP_EXTENSION_DENIED | A client asked for an extension the workspace does not allow, a policy denied it, or no server offers it |
MCP_TOOL_DEFINITION_CHANGED, MCP_SERVER_IDENTITY_CHANGED, MCP_SERVER_PIN_MISMATCH | A server changed, and waits for approval |
MCP_TOOL_ANNOTATION_CONTRADICTION | A tool's risk hint contradicts what it does |
MCP_APP_RESOURCE_SERVED, MCP_APP_REFUSED | A page was served, or refused and why |
MCP_TASK_CREATED, MCP_TASK_STATUS_CHANGED, MCP_TASK_CANCELLED, MCP_TASK_ADMIN_CANCELLED | A task started, changed status, or was stopped by the Gateway or by someone in Flightdeck |
MCP_APPROVAL_TASK_BOUND, MCP_APPROVAL_TASK_USED, MCP_APPROVAL_TASK_EXPIRED, MCP_APPROVAL_TASK_REVOKED | An approval was tied to a task, used by a call on it, ended, or withdrawn when the task was cancelled |
MCP_SAMPLING_CALL | The Gateway made a model call a tool asked for |
What is not covered yet
- A server that builds a new page for each request cannot be served: its fingerprint never matches.
- Sampling from servers on older MCP revisions, and tool use in sampling, are refused.
- Results read through a task are not trimmed by response projection.