Write approval and configuration governance
Explicitly read-only, published tools execute after permission checks. Writes and unclassified tools need approval per operation. Unauthenticated local administration and YAML-only deployments can execute only explicitly read-only published tools; write approval requires authenticated administration, including a signed-in built-in local console.
| Role | Responsibility |
|---|---|
| Configuration administrator | Connect services, publish tools, assign policies and inspect diagnostics |
| Write reviewer | Review the operation, target, arguments and preview; perform independent review and step-up authentication when required |
| Security reviewer | With configuration approval enabled, review and apply proposals; cannot edit configuration directly |
Grant combined roles explicitly when needed; administrator access does not itself grant write-review permission. Production writes also need atomic version checks and business idempotency at the backend. Notification links do not replace browser approval, and approval does not bypass execution-time checks.
See one-time write approval, governance and quorum, and notifications and independent archives. User and reviewer actions are described under write approvals.
One-time write approval
Both MCP backends and HTTP groups support tool_rules[].effect: read / write. Explicit reads execute after publication, scope and resource checks. Writes and unclassified tools require per-operation approval. A matching approval policy also makes a tool a write; a broader read rule cannot override it. HTTP methods and upstream readOnlyHint never grant permission.
Enable authenticated administration with built-in accounts, or remote administration for team use. Grant separate scopes to configuration administrators and reviewers. MCP caller tokens retain the MCP audience; reviewer browser sessions use the management audience without needing configuration access:
admin:
# Retain the selected template's management mode, public URL and database settings.
required_scopes: [mcphub:admin]
approvals:
required_scopes: [mcphub:approve]
pending_ttl: 30m
execution_ttl: 5m
retention: 720h
# Built-in default; add enterprise ACRs enforced by your MFA/Passkey provider.
step_up_acr_values: ["urn:mcphub:auth:password-totp"]
The review deadline starts at creation (default 30 minutes, range 1 minute–24 hours); the execution deadline starts at approval (default 5 minutes, range 1–30 minutes). Retention defaults to 30 days and permits 1–365 days. Static administration settings require restart. Configuration administrators cannot approve by default; reviewers cannot read or change backend configuration. Explicitly grant both scope sets to accounts needing both roles. The sign-in page offers a separate reviewer login.
Configure policies in YAML or the existing Tool Rules (JSON) editor:
published_tools: [get_project, preview_update, update_project]
tool_rules:
- match: get_project
effect: read
- match: preview_update
effect: read
- match: update_project
effect: write
required_scopes: [projects:write]
resource_rules:
- argument: /project
allowed_values: [work]
approval:
action: Change project quota
environment: production
resource_arguments: [/project]
require_different_reviewer: true
require_step_up: true
approvers:
- subjects: [reviewer-oidc-sub]
resources:
- argument: /project
allowed_values: [work]
preview_tool: preview_update
version_argument: /expected_version
Reviewer subjects are exact, verified subjects from the same issuer, never caller-supplied usernames. All resource constraints within a grant must pass; grants are alternatives. Every matching tool policy must allow the reviewer. Selectors can restrict projects, databases, directories or environments. An omitted approvers list permits any account with the reviewer scope. require_different_reviewer prohibits self-approval. Lists and details enforce the same resource boundaries; requesters can view their own requests without acquiring approval authority.
- A write returns
structuredContent.code: approval_pending,approval_idandapproval_url. No write has run; a configured read-only preview runs first. - Open the link and sign in as a reviewer. Inspect the action, environment, resource values, complete request and available before/after preview. Enter a reason and approve once or reject.
- For
require_step_up: true, select Verify identity first. Local accounts re-verify password and TOTP; ordinary password sign-in is insufficient. For enterprise identities, MCPHub requests OIDCmax_age=0,prompt=login, a nonce and configured ACR values. It verifies signature, issuer, client audience, the same subject, nonce,auth_time, returned ACR, andat_hashwhen present, allowing at most 30 seconds of clock skew. Verification is tied to this request and browser session, lasts two minutes, and is single-use. The user must still click Approve. Missing OIDC or insufficient authentication strength fails closed. The provider defines and enforces the MFA/Passkey meaning of an ACR; there is no universal MFA string. - The original caller invokes
mcphub_resume_approvalwith only{"approval_id":"..."}. Current scopes, resources, publication and rate limits are checked before forwarding the immutable saved request. Identical active requests are deduplicated; use the dedicated status tool to poll. Existingmcpbridge connectconfiguration needs no changes. - Before execution, the requester can call
mcphub_cancel_approvalwithapproval_idand optionalreason, or cancel from a reviewer browser session. An authorized reviewer can revoke an approved request. Cancellation/revocation race atomically with execution; an admitted write may finish and cannot be rolled back by revocation.
Preview contract: preview_tool names an explicitly published read-only original tool in the same backend/group. It receives the same arguments as the write. Its structuredContent (the JSON object response body for HTTP tools) must contain version, before and after, for example {"version":"v7","before":{"limit":10},"after":{"limit":20}}. version_argument selects a specific nonempty version string in the arguments; * is rejected. MCPHub checks preview permissions/resources and compares its content, version and tool generation at creation and resume. Any change or failure prevents execution. The upstream write must also enforce the version atomically, using HTTP If-Match or a database conditional update; a preview cannot close the read/write race. Existing HTTP Header parameter mappings can send expected_version as If-Match. Without a configured preview, the UI shows administrator-defined action/resource details and the complete request without inventing a change result.
The UI filters by status, exact tool name and exact requester, with cursor pagination (25 records by default). Decisions, cancellation, execution, expiry and restart recovery are audited. An unknown outcome can receive an investigation note and outcome (applied/not applied/uncertain); this never changes execution status or restores execution allowance. APIs: GET /api/v1/approvals?status=&tool=&subject=&limit=25&cursor=, GET /api/v1/approvals/{id}, and POST /api/v1/approvals/{id}. Mutations take decision (approved/rejected/revoked/cancelled/investigated), a required reason of at most 2048 bytes, and investigation outcome (applied/not_applied/uncertain). POST /api/v1/approvals/{id}/verify starts step-up verification. These endpoints require a scoped reviewer browser session; mutations also require CSRF/Origin validation.
SQLite/PostgreSQL atomically consume each approval once; concurrent resumes cannot execute twice, and repeats return the saved result. Cancellation, broken connections and process interruption may leave unknown outcomes that need backend investigation. This is not an upstream exactly-once or rollback guarantee. Changed tool/configuration generations invalidate approval; full runtime reloads/restarts invalidate outstanding requests, and restart marks interrupted execution unknown. Arguments, business metadata and continuation inputs are immutable; only the progress token is rebound. Large integers retain precision. Backend interactions requiring another call require new approval.
Limits: 20 active requests per issuer/subject; 60 KiB execution request; 32 KiB preview; 64 KiB complete intent including policies; 16 MiB saved result. Requests, previews, results, reasons and investigation details are encrypted. A minute-based maintenance loop removes terminal records and detailed history past retention in bounded batches; general activity logs retain argument/reason-free state events. Already admitted writes may finish after policy changes. Approved writes use fresh HTTP/1 connections to prevent transparent retries, so upstreams must support HTTP/1.1. Reads retain connection pooling.
Fresh managed deployments use schema 10. Back up the database and matching encryption key. A signed-in built-in local console can review approvals. Local unauthenticated advanced management and YAML-only deployments cannot execute writes/unclassified tools. MCP and management API bearer tokens cannot approve. Isolate reviewer browsers, configuration/database access and upstream write credentials from agents. With configuration governance disabled, configuration administrators can change classifications directly; enable independent security review to guard those changes. Strong authentication does not replace reviewing the operation or downstream least privilege.