{
  "schema_version": "1.3.0",
  "slug": "where-must-an-instance-enforce-egress-policy-for-mcp-tool-calls-when-the-audit-sink-is-asynchronous",
  "title": "Where must an instance enforce egress policy for MCP tool calls when the audit sink is asynchronous",
  "date_of_record": "2026 November 13",
  "scope": "Applies to MCP servers that mediate tool invocations where each tool declares an egress class (network, filesystem, subprocess, model) and an audit record must be emitted per call. Does not apply to purely in-process tool handlers that never leave the server boundary, nor to audit sinks that are strictly synchronous and backpressured into the caller.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/where-must-an-instance-enforce-egress-policy-for-mcp-tool-calls-when-the-audit-sink-is-asynchronous.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/where-must-an-instance-enforce-egress-policy-for-mcp-tool-calls-when-the-audit-sink-is-asynchronous.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/where-must-an-instance-enforce-egress-policy-for-mcp-tool-calls-when-the-audit-sink-is-asynchronous.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 1482,
    "python_slice_tokens": 229,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "The enforcing instance and the auditing instance are not the same trust boundary. Policy enforcement must occur at the *call site* — the frame that resolves the tool name, validates arguments, and grants the egress. Auditing must occur at the *sink* — a separate, often asynchronous writer that persists the record. The non-obvious failure is that teams confla...",
    "what_was_observed": "A production MCP server exposed 14 tools. Twelve were declared `egress: none`; one was `egress: network` (a fetch tool); one was `egress: subprocess` (a shell executor). The audit pipeline enqueued a JSON record to an unbounded in-memory channel consumed by a background flusher writing to object storage every 5 seconds. Policy was evaluated inside the tool handler, *after* argument parsing but *before* the actual sys...",
    "condition_of_validity": "The \"audit record implies enforcement\" assumption holds only when all of the following are true: - The audit sink is synchronous with respect to the egress decision (write completes or the call fails closed before egress). - The policy decision is computed once per egress attempt and is not cached across retries without re-validation. - The sink is durable a...",
    "decision_boundary": "The boundary is between **fail-closed enforcement with lossy audit** and **fail-open audit with durable-but-lagging enforcement**. Choose explicitly: - For tools whose egress is irreversible and high-blast-radius (`subprocess`, `network` to arbitrary hosts), enforce synchronously and treat audit as best-effort; accept that some enforced calls have no record....",
    "operational_effect": "After the incident, the server was changed so that: (a) the policy gate returns a signed decision token that the egress path must present, preventing cached-ALLOW reuse across retries; (b) the subprocess tool writes an outbox row in the same transaction as the fork, so the record and the egress share a commit boundary; (c) the network tool keeps the async si..."
  },
  "matched_failure_symptoms": [
    "DOMAIN:EPISTEMIC_PROVENANCE_AND_FEDERATION_ROUTING"
  ],
  "composition_edges": {
    "primary_domain": "epistemic_provenance_and_federation_routing",
    "upstream_precondition_residuals": [
      "https://corpusfetch.com/c/according-to-what-precedence-hierarchy-must-an-instance-resolve-contradictory-directives-across-system-developer-and-user-tiers.md",
      "https://corpusfetch.com/c/how-should-an-instance-decide-that-a-stored-conclusion-has-gone-stale-without-a-new-observation.md"
    ],
    "downstream_consequence_residuals": [
      "https://corpusfetch.com/c/by-what-checkpoint-and-compaction-boundaries-must-an-instance-externalize-working-state-to-prevent-context-window-drift-and-truncation-amnesia.md",
      "https://corpusfetch.com/c/across-what-additive-schema-and-unknown-field-preservation-boundaries-must-an-instance-evolve-wire-contracts-to-prevent-rolling-deploy-deserialization-crashes.md"
    ],
    "co_occurring_sibling_residuals": [
      "https://corpusfetch.com/c/how-can-an-instance-capture-implicit-user-preferences-without-storing-private-data.md",
      "https://corpusfetch.com/c/how-can-an-instance-compress-long-reasoning-traces-into-binding-conclusions.md"
    ]
  },
  "empirical_verification_warrant": {
    "status": "AST_AND_INVARIANT_VERIFIED",
    "syntax_valid": true,
    "code_sha256": "08e1e47a210a26bbe6341b3411edfc50e355defc410a067cfe2e4437c389e983",
    "verified_classes": [
      "EgressPolicyViolation",
      "GrantToken"
    ],
    "verified_functions": [
      "issue_grant",
      "consume_grant",
      "run_subprocess_tool",
      "__init__"
    ],
    "verified_functions_count": 4,
    "defensive_guards_count": 4,
    "raised_exceptions": [
      "EgressPolicyViolation"
    ],
    "sample_fail_closed_invariants": [],
    "stdlib_dependencies": [],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
