The Claude Code plugin system reads like a distribution feature.
Bundle some skills, ship a hook, point at a language server, publish to a marketplace. Read it against the harness instead and it is something else: the write path, exposed as configuration.
A plugin does not sit above the layer that assembles context, authorizes the tool call, and commits state. It sits inside it, and it can change how that layer behaves. That makes plugin configuration an architectural artifact rather than a packaging detail — and the specification, read closely, admits a structure most teams installing plugins have never named.
Stack the whole thing before taking it apart. The model reasons and intends. Skills, agents, and output styles shape what it sees without holding any power over what it does. The harness assembles context, authorizes the call, and commits state. A durable session records what happened, separate from the layer that acted. The workspace and any external system are where state lands. One band is empty on purpose — the position above the harness, where a governing ring would sit. There is no ring here. Every mechanism a plugin can configure sits inside the write path.
Three postures, precisely scoped
The claim is not that a plugin exposes exactly three components. It exposes six, and several of them can change state. The claim is narrower and more useful: relative to a single write, a plugin can configure three distinct postures — intercept it before the fact, diagnose the result after, or report on conditions alongside.
They are postures because each names a relationship to the write rather than a rank. They are tiers of authority because that relationship orders them: interception can refuse, diagnosis can only contradict, reporting answers for nothing. Preventive authority is the axis, and it descends in that order. The rest of this dispatch is about what descends alongside it, and what does not.
Blocking hooks intercept. The reference documents thirty hook events, and only some of them can block; the rest surface feedback or run for side effects [2]. On the events that can, a pre-tool handler is the strongest instrument in the system: it can deny a call, escalate to the user, defer it, or rewrite the tool’s arguments before execution [2]. The handler itself may be a shell command, an HTTP endpoint, a tool on a connected MCP server, a single-turn model evaluation, or an agent subprocess with read and search tools for deeper verification [2]. The posture is the same across all five: decide before the fact.
Language servers diagnose. An LSP server configured by a plugin pushes diagnostics into the model’s context after edits, and answers definitions, references, and type queries on demand [1][3]. It contradicts. It stops nothing, and the specification documents no path by which it could — a claim from documented absence, labeled as such.
Monitors and post-action events report. A declared monitor runs a shell command for the session’s lifetime and delivers every line of standard output to the model as a notification [1]. A file-watch event fires when a watched file changes on disk with no decision control at all [2]. A post-tool handler sees the tool’s result and can replace it, but cannot undo it [2]. These are wired as observational channels — though a monitor’s command runs unsandboxed at hook trust level, so the channel is observational by role and must be reviewed to be observational in fact [1]. That gap is itself a governance finding: a nominally reporting posture can carry side effects unless someone reads the command.
Reach is not a fourth posture. An MCP server is a mechanism a posture invokes — one handler type calls a tool on an already-connected server at the moment of decision [2] — which pulls external capability inward rather than positioning it above.
Native versus discretionary evidence
Now read the postures by what each one examines, because that is where the finding is.
The naive version of this argument is that interception reads only an event. That is wrong, and the specification says so: an agent handler can read files and search the workspace before deciding [2], and an MCP-tool handler can query an external system. An architect can deliberately enrich the interception posture until it inspects a great deal of state.
The real asymmetry is not what each posture can reach. It is what each produces natively.
A language server’s finding is external by default. It is produced by a process outside the model’s reasoning, analyzing the resulting workspace state, and a reviewer can re-run it — given the workspace, the analyzer, its version, its configuration, and its environment. That list is the point, not a footnote. The protocol standardizes how a client and a server talk; it does not guarantee deterministic output, identical analyzer versions, or compiler-grade semantics, and the plugin manifest can supply the server’s environment, initialization options, settings, and workspace root [1]. Reproducibility is a property of preserved inputs, not of the protocol. What arrives for free is that the analysis happens outside the reasoning it checks.
An interception handler’s evidence is discretionary. It exists only if the author gathered it, only in the form the author chose, and it is subject to constraints the specification is explicit about: handler output is capped at ten thousand characters [2], a non-2xx response or connection failure from an HTTP handler is a non-blocking error that lets execution continue [2], a prompt-submit handler that times out is canceled and its output discarded while the prompt proceeds [2], and the conditional filter that narrows a handler is best-effort and fails open when a command cannot be parsed [2].
Anthropic states the consequence plainly in its own reference: use the permission system rather than a hook to enforce a hard allow or deny [2]. That is the vendor qualifying the strength of its strongest interception instrument.
So the inversion holds, in its precise form. The posture with the most preventive authority produces evidence that is optional, bounded, and fails open. The posture whose native output is external analysis, re-runnable when its inputs are preserved, has no power to refuse.
This is the corpus position arriving in a first-party product surface. The Trace Caught the Cheat argued it against a benchmark leak: a control reading the event at a boundary waves through a run in which the agent retrieved the answer instead of deriving it, because the action itself was permitted [5]. The seam saw an allowed call. The trace saw the cheat. The same asymmetry is now a configuration schema.
What each posture can attest
Enforcement and attestation are different products, and independence is the axis that separates them.
An interception handler that blocks produces a decision, plus whatever its author retained. However thorough, that record is generated by the acting layer, about its own decision, under the acting layer’s constraints. It is testimony from the party with the interest.
A language server’s finding is a different evidentiary class, and it arrives with no authority attached. It is also worth being exact about how far its independence runs: external to the model’s reasoning and re-runnable outside the originating session, but not independent of the harness as operator. Claude Code starts the server, hands it its configuration, and decides which diagnostics reach context.
Which yields the defensible form of the claim: no posture delivers both preventive authority and external attestation natively. A sophisticated agent handler can inspect state, run checks, retain findings, and block — genuinely combining enforcement with evidence gathering. It can go further: a handler that invokes the same third-party analyzer and preserves its output produces evidence every bit as external as the language server’s. The difference is not capability. It is that the diagnosis posture does this as its operating model, while a handler does it only if its author deliberately invokes an independent analyzer, preserves the output, and identifies the version that produced it. Native versus architected — not native versus impossible.
Compose the postures, do not choose between them
The practical consequence, and the part most teams miss.
Because interception can be enriched and post-action events can retain evidence, the design question is not which posture do we install. It is how do the postures divide the work. The composition that follows from the specification:
Enforcement in the interception posture, deliberately enriched. If a guarantee must hold, it belongs on an event that can block, with a handler built to inspect the state it needs rather than trusting the event payload alone. Accept that it fails open in several documented ways, and design for that.
Evidence in the post-action posture. A post-tool handler sees the result and can inject context or replace the output [2]. It cannot undo the call — which is precisely why it is the right place for recording rather than gating.
External analysis in the language server. Left as the channel that produces it without anyone having to build it, and whose findings a reviewer can re-run given the analyzer, its version, and its configuration.
Teams that install one posture and describe the result as a control have usually installed the diagnosis posture. It is a witness. A good one, and not a control.
What configuration actually changes
Every posture is tunable by whoever authored the plugin, and it is worth being precise about what each setting moves rather than collapsing them into one verb.
Availability. A language server’s crash-restart flag defaults to on; turning it off leaves a dead server stopped rather than restarted [1]. A single settings flag switches off every hook that is not managed [2]. Both remove a posture from the session while the session keeps running.
Reach. When two enabled servers claim the same file extension, the first registered wins and the other never starts [1]. The posture is present; which mechanism supplies its findings is decided by registration order rather than by intent.
Visibility. Diagnostic injection defaults to on, and setting it off retains code navigation while suppressing the automatic push into context [1]. The mechanism still answers when asked. It stops volunteering.
Construal, and this is the consequential one. A manifest can supply a language server’s environment, initialization options, settings, and workspace root [1]. Those inputs change what the analysis concludes. The first three classes remove a control, which is at least legible as absence. Construal leaves the control apparently working and changes its answer — and a reviewer holding the output cannot tell from the output alone. The companion dispatch works this through where it bites hardest, in the diagnosis posture.
None of that is a defect; it is a specification behaving as documented. The governance problem is where the degradation surfaces. Some of it does: the plugin interface warns when a server loses an extension race, and the errors tab names a missing binary [1]. Other states are invisible to ordinary task behavior and require inspecting plugin status, the errors tab, or debug output [1].
Which means the manifest is part of what an architect owes a reviewer. Not the plugin’s description — its configuration.
The boundary the design already draws
Anthropic’s specification already treats plugin configuration as a write-path trust surface, and the pattern validates the reading rather than merely permitting it.
The postures gate differently at the same scope. For a project-scope plugin discovered in a repository, language servers start only after the workspace trust dialog is accepted, MCP servers go through per-server approval, and background monitors do not load at all [1]. Three postures, three trust decisions, one scope.
Configured values are restricted by source. User-configurable plugin values are read only from user settings, an explicit settings flag, and managed organizational policy — entries in a project’s checked-in settings are ignored, because a cloned repository could otherwise supply values that flow into hook commands, MCP configurations, language-server commands, and monitor commands [1]. Fields that execute through a shell reject those values outright, on the reasoning that substituting a configured value into a shell command would let the shell run whatever the value contains [1][2].
And the hierarchy runs the right direction. An administrator can block user, project, and plugin hooks through managed policy, hook entries merge across settings levels rather than replacing one another, and a disable-all flag set below the managed level cannot disable managed hooks [2].
Read together, that is an unambiguous architectural statement: supplying plugin configuration is exercising write-path authority, so the specification restricts who may supply it and in what form. Anthropic drew the boundary in the schema. Most enterprises installing plugins have not drawn it in their review process.
The mechanism best positioned to prevent an action is not the mechanism best positioned to establish that the resulting state is correct. Claude Code’s plugin system makes that separation configurable — and configuration reaches further than most teams assume, because it decides not only which postures are present but which analyzer runs and under what settings. A manifest is therefore a set of decisions about where preventive authority sits, what evidence gets produced, and whether a reviewer holding that evidence can tell what actually generated it.
That is the work. Not choosing a posture — composing them, and being able to say which one carries each guarantee and what would have to be preserved for a reviewer to check it. Assurance binds where state is written, and it binds to a record the acting layer does not own [4][6][7]. A plugin manifest is now one of the places that record is shaped, and a security or assurance reviewer should be able to read it as such — including the parts that decide which analyzer’s answer the record contains.
