Power with Boundaries: Extensions, Permissions, and Trust
This chapter examines how OpenCode crosses its local boundary through plugins, MCP servers, and shared TUI/API contracts, then asks what its permission and server controls really protect. The practical goal is to choose an extension or deployment boundary without confusing in-process code with an external service, an approval prompt with a sandbox, or an embedded client with a second implementation. OpenCode’s power comes from composability, but its safe use depends on keeping ownership and trust visible.
Two ways to add capability, plus one way to move it
A plugin and an MCP server can both make new tools or behavior available to an OpenCode agent. They are not the same kind of extension.
A server plugin runs inside OpenCode’s application process. The current plugin service loads built-in integrations and configured external packages, gives them a generated OpenCode client and project/worktree/directory context, and lets them register hooks. Hooks can observe or transform operations such as tool execution, configuration, events, or provider behavior. Plugin tools are adapted through OpenCode’s Tool Registry, so the model can see a description and schema while the host still controls how the handler executes and how the result is represented.
Process ownership makes the plugin powerful. In-process code can participate directly in the host lifecycle and can receive the same project context as other services. It also makes governance important: a plugin is executable code, not a passive configuration file. The current loader reports installation, compatibility, entry, and runtime failures, runs hook registration sequentially to keep ordering deterministic, and calls disposal hooks when the Location is torn down. The V2 Effect plugin API makes the intended lifecycle more explicit by tying transforms and runtime hooks to a scope. Closing that scope removes its registrations; reloading a domain rebuilds its state from the active transforms.
That scoped transform model addresses a subtle maintenance problem. If a plugin contributes a provider, agent, command, or reference, the host should not have to remember a bespoke “undo” operation for every field the plugin changed. A rebuild starts from fresh domain state and replays the active contributions in registration order. Disposing one plugin removes its contribution before the next rebuild. The approach makes extension removal more predictable, although it also means that reload ordering, slow package activation, and the meaning of a partially loaded catalog remain operational concerns. The V2 documentation presents this as the intended direction; the current legacy loader and configuration surface still coexist with it.
MCP, the Model Context Protocol, is an external protocol boundary. OpenCode can connect to a local server through standard input/output or to a remote server through Streamable HTTP or server-sent events. A connected server can expose tools, prompts, resources, and instructions. OpenCode caches tool definitions, observes connection and tool-list changes, applies request timeouts, and reports states such as connected, disabled, failed, or awaiting authentication. Remote servers can use OAuth, including a local callback flow, while local servers are launched as child processes with a configured command and environment.
The common model-facing surface should not hide that difference. An MCP tool still enters the Session through OpenCode’s tool adaptation and permission path, and an MCP resource can become text or a bounded supported attachment. But the external server remains responsible for its own code, data, credentials, availability, and behavior.