feat(plugins): MCP plugin management, agent injection, and custom MCP import#261
feat(plugins): MCP plugin management, agent injection, and custom MCP import#261lucas77778 wants to merge 24 commits into
Conversation
This reverts commit 2833562.
This reverts commit 7b57c75.
This reverts commit 36da7b2.
This reverts commit 64df55c.
… service A plugin now composes one or more MCP servers; each server names its service and resolves its credential through the global service->binding map instead of a per-unit connector reference. Wire protocol 45.
…n warnings AGENT_MCP_CAPABLE replaces the resolver-private agent set so clients read the same truth. The resolver skips a declaredly expired local credential with the new expired-credential warning reason (wire 47), and start diagnostics now live on LiveSession so session.attach replays them to late attachers.
Deduped by unit+service+reason so attach replays never stack while distinct reasons for one dependency all stay visible.
Pure derivation from catalog + public config, mirroring the resolver's verdict vocabulary; the dev mock now fails invalid connector operations with the daemon's sanitized public message.
Card list of catalog units on a new Tools tab in both apps: localized label/description, enable switch writing PluginConfigSet.units, and per-server status badges derived from the plugin view model.
…ns (CODE-93) claude: Options.mcpServers record (spawn-time --mcp-config; merges with the user's own MCP config). codex: thread/start|resume config.mcp_servers (no type tag, headers->http_headers; startup failures surface as recoverable errors). opencode: per-session spawn Config.mcp (local/remote, env->environment, explicit enabled). Live-verified on claude 2.1.218: an injected stdio MCP server's tool was called end to end through a LinkCode session.
Adds a BYO MCP tier alongside the catalog: CustomMcpServer ({id,
enabled, server}) in the plugin config, injected into agent sessions
through the same resolved StartOptions.mcpServers path. Credentials are
inline and opaque to LinkCode — the public projection masks env/header
values to key lists, and a custom name colliding with a catalog server
is rejected at write time. The resolver shares the capability gate and
client-name precedence with catalog servers; plugin-warning gained a
custom-server source. A Tools-tab section imports/toggles/removes them.
Wire protocol 48.
Live-verified on claude 2.1.218: a custom stdio server imported through
the SDK was injected and its tool called end to end; the webview form
adds a server, shows only the masked credential key, and removes it.
…path Adversarial review found the name-collision invariant lived only in the engine's config.set guard: the dev-mock reimplementation skipped it (breaking engine parity), and nothing defended a bad-state config.json at resolution. The resolver now skips a custom server whose name is already taken (client / catalog / earlier custom) so it can never silently clobber another in the adapter's name-keyed MCP config, and the dev-mock now rejects colliding names like the daemon.
Greptile SummaryAdds end-to-end MCP plugin and custom-server management across the daemon, wire protocol, clients, settings UI, and supported agent adapters.
Confidence Score: 4/5The PR needs a fix before merging because the custom-server form can silently save unintended credentials when duplicate environment-variable or header keys are entered. Duplicate credential keys pass form validation and are collapsed by last-write-wins parsing, causing accepted user input to be silently discarded before the MCP server configuration is persisted. packages/client/workbench/src/settings/plugins/custom-server-dialog.tsx
What T-Rex did
Important Files Changed
Sequence DiagramsequenceDiagram
participant U as Tools UI
participant C as Client SDK
participant D as Daemon
participant E as Engine resolver
participant A as Agent adapter
U->>C: Add or configure MCP server
C->>D: config.set
D->>D: Validate and persist plugin config
D-->>C: Public masked config
C-->>U: Render tool status
E->>D: Read enabled plugin configuration
E->>E: Resolve bindings and warnings
E->>A: Start session with mcpServers
Reviews (2): Last reviewed commit: "fix(plugins): validate custom MCP creden..." | Re-trigger Greptile |
| if (trimmed === '') continue; | ||
| const at = trimmed.indexOf(separator); | ||
| if (at <= 0) continue; | ||
| pairs[trimmed.slice(0, at).trim()] = trimmed.slice(at + 1).trim(); |
There was a problem hiding this comment.
Duplicate credential keys overwrite silently
When a user enters the same environment-variable or HTTP-header key more than once, every line passes validation but parsePairs assigns them to the same object property. The later value silently replaces the earlier one, causing the imported MCP server to authenticate with unintended credentials or start with incorrect configuration despite the form reporting success.
Artifacts
Repro: focused CustomServerDialog Vitest harness
- Evidence file captured while the check ran.
Repro: focused Vitest configuration
- Evidence file captured while the check ran.
Repro: passing runtime output showing no validation error and last-write-wins onSubmit payload
- The full command output behind this check.
Repro: Playwright script exercising the real webview mock form
- Evidence file captured while the check ran.
Repro: successful Playwright execution log
- The full command output behind this check.
- The flow T-Rex ran, recorded end to end.
Before: Custom Server Empty Form
- What the screen looked like at this point in the check.
- The flow T-Rex ran, recorded end to end.
Duplicate Credential UI Repro Poster
- What the screen looked like at this point in the check.
Duplicate Credential UI Populated
- What the screen looked like at this point in the check.
Duplicate Credential UI Submitted
- What the screen looked like at this point in the check.
Builds out the Connector/plugin batch on the daemon and clients: a redesigned plugin schema, MCP injection into agent sessions, the Tools settings surface, and user-imported custom MCP servers.
What's in here
Schema redesign (CODE-382) — a plugin composes 1..N MCP servers (
McpPluginDescriptor.servers[]), and connector association moved from a per-unit binding to a global per-serviceserviceBindingsmap. Secrets never cross the wire (masked public projections).MCP capability + warnings (CODE-385) —
AGENT_MCP_CAPABLElives in@linkcode/schema(engine and clients read one truth); the resolver skips declaredly-expired credentials (expired-credential); plugin warnings are kept onLiveSessionand replayed onsession.attach; client-core folds them intoConversationViewModel.pluginWarnings.Data plane + Tools UI (CODE-386 / CODE-387) — workbench view models (
pluginUnitViews/pluginServiceViews/customServerViews), and a card-based Tools tab in desktop + webview: capability units with enablement + per-server status badges.MCP injection into agents (CODE-93) — claude / codex / opencode now consume
StartOptions.mcpServers(each via its own SDK channel; pi excluded by the capability matrix). Verified live on claude 2.1.218 — an injected stdio MCP server's tool was called end to end.Custom MCP import (CODE-407) — users add their own MCP servers on the Tools tab. Credentials are inline and masked to key-only on
config.get; the resolver injects enabled ones, sharing the capability gate and client-name precedence; name-uniqueness is enforced at write time, at resolution, and mirrored in the dev mock.Also merges
master(telemetry batch, chat-card refactor, CODE-388/391 data-plane work). Wire protocol 43 → 48 — daemon and every client must rebuild/restart together.Verification
pnpm check:ciclean, 1846 vitest passing. Live E2E: MCP injection (catalog + custom) drove a real claude session end to end; the Tools tab add/mask/remove flow verified in webview via Playwright. Two adversarial review passes (workflow) — confirmed findings fixed (expired-credential injection, fold dedup, dev-mock name-collision parity, resolver dedup defense-in-depth).Notes for review
packages/foundation/schema/src/wire/message.tscomments (44 collision → 46, then 47/48).serverreplacement for a future edit form).