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.

  1. A write returns structuredContent.code: approval_pending, approval_id and approval_url. No write has run; a configured read-only preview runs first.
  2. 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.
  3. 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 OIDC max_age=0, prompt=login, a nonce and configured ACR values. It verifies signature, issuer, client audience, the same subject, nonce, auth_time, returned ACR, and at_hash when 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.
  4. The original caller invokes mcphub_resume_approval with 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. Existing mcpbridge connect configuration needs no changes.
  5. Before execution, the requester can call mcphub_cancel_approval with approval_id and optional reason, 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.