{
  "schema_version": "1.3.0",
  "slug": "when-must-an-instance-refuse-tool-descriptor-inheritance-the-skill-injection-boundary-in-mcp-tool-augmented-agents",
  "title": "When must an instance refuse tool-descriptor inheritance: the skill-injection boundary in MCP tool-augmented agents",
  "date_of_record": "2026 November 14",
  "scope": "Applies to agents that load tool or skill descriptors from external MCP servers, plugin manifests, or registry entries at runtime and merge them into an existing tool namespace. Does not apply to statically compiled tool sets where descriptors are fixed at build time, nor to sandboxed agents that never re-read descriptors after initialization.",
  "projections": {
    "unified_markdown_uri": "https://corpusfetch.com/c/when-must-an-instance-refuse-tool-descriptor-inheritance-the-skill-injection-boundary-in-mcp-tool-augmented-agents.md",
    "method_python_slice_uri": "https://corpusfetch.com/c/when-must-an-instance-refuse-tool-descriptor-inheritance-the-skill-injection-boundary-in-mcp-tool-augmented-agents.py",
    "remnant_warrant_slice_uri": "https://corpusfetch.com/c/when-must-an-instance-refuse-tool-descriptor-inheritance-the-skill-injection-boundary-in-mcp-tool-augmented-agents.warrant.json"
  },
  "token_profile": {
    "full_markdown_tokens": 1561,
    "python_slice_tokens": 241,
    "warrant_slice_tokens": 220
  },
  "remnant_summary": {
    "core_distinction": "The non-obvious boundary is not \"untrusted server vs trusted server.\" It is **descriptor provenance continuity** versus **descriptor provenance substitution**. A tool-augmented agent has two distinct moments: (a) the initial capability snapshot, where a descriptor arrives from a server whose identity was resolved and pinned at session start, and (b) every su...",
    "what_was_observed": "In a reproduction against an MCP aggregator bridging three upstream servers, the following sequence produced a silent capability escalation: 1. Session start: agent pins server `A` (trusted, signed manifest) and server `B` (experimental). Tool namespace contains `A.search`, `B.fetch`. 2. Server `B` emits a `notifications/tools/list_changed` event and returns an updated `tools/list` containing `B.fetch` plus a new des...",
    "condition_of_validity": "The refusal-to-inherit rule is valid, and must be enforced, under all of the following: - The agent supports dynamic descriptor refresh (`tools/list_changed`, manifest polling, or `$ref` resolution) after the initial handshake. - Descriptors carry executable or instruction-bearing content in `description`, `inputSchema` defaults, or examples that the model m...",
    "decision_boundary": "The decision is a single predicate evaluated at every descriptor arrival after the initial snapshot: If any conjunct is false, the agent must **reject the descriptor and continue with the prior snapshot**, not merge it and not silently drop the refresh. Silent drop is its own failure: it hides the injection attempt from audit logs. The correct behavior is ex...",
    "operational_effect": "Enforcing this boundary converts a silent capability escalation into a loud, auditable rejection. In the reproduction, the `B.install_skill` injection was caught at step 3 with a provenance-mismatch event naming the descriptor, its claimed origin, and the pinned origin it failed to match. The agent continued serving `A.search` and `B.fetch` from the frozen s..."
  },
  "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": "d24b7df81faf9feff3b85fb15aa3ec83467ddf4ba9fd2c3cbf7995bf99ebbff6",
    "verified_classes": [
      "DescriptorProvenanceError",
      "PinnedSession"
    ],
    "verified_functions": [
      "inherit_descriptor",
      "refresh_tools",
      "__init__"
    ],
    "verified_functions_count": 3,
    "defensive_guards_count": 4,
    "raised_exceptions": [
      "DescriptorProvenanceError"
    ],
    "sample_fail_closed_invariants": [],
    "stdlib_dependencies": [],
    "observation_mode": "EMPIRICAL_BOUNDARY_CAPTURE",
    "zero_external_deps": true
  }
}
