Plugins and MCP Servers
Load project or user plugins in @namzu/sdk, register namespaced tools, execute hooks, and mount plugin-managed stdio MCP servers.
The plugin runtime is the SDK's project- and user-scoped extension system. It does three practical things today:
- loads namespaced tool modules
- runs hook modules over runtime phases
- starts stdio MCP servers declared by plugins and adapts their tools into the local tool registry
1. What the Plugin Runtime Owns Today
The public plugin surface is centered on:
| Export | Responsibility |
|---|---|
discoverPlugins() / discoverAllPluginDirs() | Find plugin directories |
loadPluginManifest() | Read and validate plugin.json |
PluginLifecycleManager | Install, enable, disable, and uninstall plugins |
PluginResolver | Resolve namespaced plugin components |
PluginRegistry | Hold installed plugin definitions and statuses |
The runtime currently supports these manifest contribution types:
toolshooksmcpServers
The runtime currently rejects these manifest contribution types at enable time:
skillsconnectorspersonas
That fail-fast behavior is intentional. It is better to deny unsupported contributions clearly than to half-load a plugin and leave the runtime in an ambiguous state.
2. Discovery Paths and Manifest File
The plugin discovery constants point at:
- project scope:
<workingDirectory>/.namzu/plugins/<plugin-name>/plugin.json - user scope:
~/.namzu/plugins/<plugin-name>/plugin.json
Minimal manifest example:
Manifest rules that matter operationally:
namemust be lowercase kebab-caseplugin.jsonis validated eagerly when loaded- contribution arrays are capped by plugin-level limits in the SDK constants
3. Bootstrap the Plugin Runtime
This gives you one important invariant:
- installation records the plugin definition
- enabling loads and registers the contributions
Those are intentionally separate lifecycle steps.
4. Tool Modules
A plugin tool module must export a tools array:
When enabled, plugin tools are registered as deferred and namespaced:
- manifest name
docs-tools - tool name
summarize_workspace - final registered name
docs-tools:summarize_workspace
That namespacing keeps plugin contributions from colliding with local or built-in tools.
5. Hook Modules
A plugin hook module must export a hooks array:
Hook events currently available:
run_startrun_endpre_tool_usepost_tool_usepre_llm_callpost_llm_calliteration_startiteration_end
6. Hook Ordering and Flow Control
PluginLifecycleManager.executeHooks() has explicit ordering semantics:
pre_*hooks run in registration orderpost_*hooks run in reverse ordermodifyactions compose, so each later hook sees the previous modified inputerrorandskipshort-circuit further hook executionresumeandretryalso stop further hook execution
The default hook timeout is five seconds unless you override hookTimeoutMs.
That means plugin hooks should be fast, bounded, and deliberate. They are runtime controls, not background jobs.
7. Plugin-Managed MCP Servers
Plugin manifests can declare mcpServers, but the runtime shape is important:
- each manifest entry becomes an
MCPClient - the transport is stdio-based today
- the runtime calls
listTools()on the remote MCP server - discovered remote tools are adapted into deferred, namespaced local tools
Example names:
- plugin name
fs-plugin - MCP server name
fs - remote tool
read_file - final tool name
fs-plugin:mcp__fs__read_file
That naming scheme is intentional and collision-resistant.
8. What Plugin mcpServers Do Not Do
The current plugin runtime does not automatically:
- expose MCP resources or templates as local docs or tool surfaces
- use
http-ssetransport from plugin manifests - host inbound MCP servers for other clients
The runtime path today is specifically:
- spawn a local stdio MCP server process
- connect as an MCP client
- adapt remote tools into local deferred Namzu tools
9. Disable and Uninstall Behavior
Plugin shutdown behavior is intentionally ordered:
- disconnect plugin-managed MCP clients
- unregister namespaced tools
- remove hook handlers
- update plugin status
This matters because it prevents new remote MCP calls from reaching a client while the tool surface is being torn down.
10. Resolve Namespaced Plugin Components
PluginResolver helps when your app needs to reason about namespaced tool names:
This is useful for:
- admin UIs
- plugin attribution in logs
- filtering or grouping tools by plugin
11. Common Mistakes
| Mistake | Why it hurts |
|---|---|
| assuming plugin tools are active immediately | plugin tools are registered as deferred by default |
assuming skills, connectors, or personas contributions already work | the runtime rejects those contribution types today |
assuming plugin mcpServers can be configured as HTTP/SSE endpoints | manifest-driven plugin MCP currently uses stdio transport only |
| forgetting tool names are namespaced | direct activation or filtering by bare tool name will miss plugin tools |