Skip to content

Feature/plugin bundle phase1 - #260

Open
liyangbing wants to merge 2 commits into
mainfrom
feature/plugin-bundle-phase1
Open

Feature/plugin bundle phase1#260
liyangbing wants to merge 2 commits into
mainfrom
feature/plugin-bundle-phase1

Conversation

@liyangbing

Copy link
Copy Markdown
Collaborator

What & why
Plugin Phase 1: Server Tools. Bundles that declare a server_entry now get their tools exposed as an MCP server the daemon spawns on demand. This is the core value of the plugin system — agents can call custom business tools defined by FDEs, without touching engine code or writing full MCP servers by hand.

Builds on Phase 0 (KindBundle + Skill injection, merged in feature/plugin-bundle-phase0).

How
Plugin Host (server/plugin-host/): A lightweight Node.js process speaking MCP stdio protocol (JSON-RPC 2.0). On startup it scans --plugins-dir for the named plugin, loads its
index.js
, and calls the default export with a PluginServerContext that provides ctx.tools.define(). All registered tools are then available via tools/list and tools/call.

Go resolver: resolveBundleCapability now returns a bundleResolution struct. When ServerEntry is non-empty and PARSAR_PLUGIN_HOST_PATH is configured, it emits a standard {command: "node", args: [...]} MCP server entry — the daemon handles it identically to any other MCP capability. No new spawn logic needed.

CLI: parsar plugin add copies plugin files to ~/.parsar/plugins// before the API call (if copy fails, no orphan DB record). parsar plugin remove cleans up the disk directory after API deletion.

Trade-offs:

One plugin-host process per bundle per prompt (not a shared long-lived process). Simple and stateless — matches how MCP servers work today. Phase 2 may revisit if FDEs need shared state across tools.
Directory name strips @scope/ prefix — two differently-scoped plugins with the same base name would collide. Acceptable for single-workspace enterprise deployment; documented as known constraint.
Verification
make check-go (sqlc drift + all Go unit tests)
make check-web (tsc typecheck + design lint)
make check-cli (CLI + opencode-plugin typechecks)
make check-hygiene
make check-store (requires Postgres; no schema changes in this PR)
Manual smoke: installed hotel-ops plugin → bound to agent → asked "please hotel price" → agent called suggest_pricing → returned occupancy-based pricing (screenshot in thread)
Notes for reviewers
bundleNameToDirName (Go) and pluginDirName (CLI) are intentionally duplicated with cross-reference comments. A shared package felt premature for 4 lines of logic.
server/plugin-host/ has no node_modules — it uses only Node.js built-ins (no external dependencies).
The PARSAR_PLUGIN_HOST_PATH env var is opt-in. When unset, bundles with server_entry are silently skipped with a warning log. Existing deployments are unaffected.
Phase 0 left two web typecheck errors (missing i18n key, MarketplaceTab type mismatch) — fixed here as drive-by.

Introduce a new capability kind 'bundle' that packages skills (and in
later phases: server tools, client UI, hooks) as a single deployable
plugin unit. This is Phase 0 of the Plugin platform architecture.

Changes:
- canonical: add KindBundle constant, BundleSpec struct with inline
  skills, Validate/UnmarshalJSON branches, ErrInvalidBundle sentinel
- render: all 4 targets (ClaudeCode/OpenCode/Codex/Pi) support KindBundle
- agentdaemon: resolveBundleCapability extracts inline skills and injects
  them as append-mode system prompts
- store: normalizeCapabilityType and validateImportSpecPreCommit accept
  'bundle'
- dev routes: POST .../capabilities/plugins/install endpoint for CLI
- dev routes: isListedCapabilityType includes 'bundle' so bundles appear
  in workspace capability listings
- CLI: 'parsar plugin add/list/remove' subcommands that read a local
  plugin directory manifest, embed skill content, and call the server API
- frontend: add Plugin tab to capability type filter, CapabilityTypeBadge
  handles 'bundle' type
- examples: customer-service-skin demo plugin (skill-only, changes Agent
  persona to professional customer service style)

Verified end-to-end: install plugin via API -> bind to Agent -> new
conversation shows customer-service persona in responses (system prompt
injection confirmed in server logs).
Plugin bundles with a server_entry now get their tools exposed as an
MCP server that the daemon spawns on demand.

Architecture:
- server/plugin-host/: Node.js MCP server (JSON-RPC 2.0 stdio)
  loads plugin server modules and exposes ctx.tools.define API
- resolveBundleCapability emits {command:'node', args:[...]} when
  ServerEntry is non-empty and PARSAR_PLUGIN_HOST_PATH is configured
- CLI 'parsar plugin add' copies server files to ~/.parsar/plugins/
- CLI 'parsar plugin remove' cleans up on-disk files

New env var: PARSAR_PLUGIN_HOST_PATH (absolute path to plugin-host/index.js)

Example: examples/plugins/hotel-ops/ with check_room_status and
suggest_pricing tools, validated end-to-end.

Also fixes Phase 0 web typecheck errors (missing i18n key for bundle
type, MarketplaceTab type narrowing).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant