Commit cc7ca6c
authored
feat(plugins): plugins, MCP, and skills management on the provider-plugin model (#344)
Builds the client-facing management surface for provider plugins,
user-owned custom MCP servers, and skills on top of the provider-plugin
model that landed with CODE-23/403/432 (which was backend-only: no wire
contract, no UI, `managementCapabilities` hardcoded all-false).
Supersedes the closed #261 / CODE-382~387 batch — that design invented a
parallel "capability unit + connector" concept in the same paths. Design
doc: [Plugins / MCP / Skills
Management](https://linear.app/arcbox/document/plugins-mcp-skills-management-design-bdf5c992be29).
## What ships
**Wire (protocol 67; minimum compatible 67)**
- `plugin.list.get`/`result` carrying `Plugin[]`, standalone skills, and
`providerStatus[]` — so the UI can tell "this agent has no plugins" from
"its CLI failed".
- `plugin.set-enabled`/`plugin.updated` and
`skill.set-enabled`/`skill.updated`, both replying with the re-read
entity so clients patch one cache entry instead of re-running discovery.
- `config.get`/`config.set` gain custom MCP servers: masked reads
(`envKeys`/`headerKeys`, never values) and per-key patch ops.
- `session.started` gains optional `mcpWarnings`.
**Provider management, honestly capability-gated**
- Claude plugin enable/disable via `claude plugin enable|disable -s
<scope>`; Codex enable/disable via `config/value/write` on
`plugins."<id>".enabled`.
- Codex plugin install/uninstall via app-server `plugin/install` and
`plugin/uninstall`; Claude installation remains unimplemented by this
adapter.
- Per-skill toggles: Claude writes `skillOverrides` in settings.json
(what the TUI `/skills` dialog writes; read-modify-write preserving
every other key and any finer `name-only`/`user-invocable-only` tier the
user set), Codex calls `skills/config/write`.
- Both providers **blind-write** — a nonexistent plugin/skill still
reports success — so every write is followed by a re-read, and that
readback is the only success check. Covered by tests.
**Custom (BYO) MCP servers**
- Persisted in `~/.linkcode/config.json` (0600, per-entry tolerant
parse), injected into MCP-capable sessions' `StartOptions.mcpServers`
alongside the existing simulator endpoint, with
`agent-unsupported`/`name-conflict` warnings instead of silent drops.
- Writes are patch ops with per-key secret semantics (blank = keep,
typed = replace, removed = delete). A whole-array replace would clobber
real secrets with mask placeholders, so it is structurally impossible
here. `buildCustomMcpPatch` is the highest-value unit test in the batch.
**Settings page** — one "Plugins & Skills" category on desktop and
webview, four tabs: **Plugins** (installed only, provider-grouped,
capability-gated switches), **Market** (uninstalled marketplace
listings), **MCP** (custom servers + read-only plugin-provided ones),
**Skills** (plugin + standalone, per-skill switches). Manual refresh
only — discovery is a real CLI shell-out.
## Verification
- `pnpm check:ci` + `pnpm test` green on the current head (2341 tests).
- Dev-mock Playwright pass, 17/17: card rendering, toggle round-trip,
secret never reaching the DOM, blank-save preserving stored secrets,
add/edit/remove, empty states.
- Live against a real daemon before the latest rebase (then wire 64):
469 real plugins + 70 skills discovered; `claude plugin disable/enable`
round-trips on the real CLI; a real skill toggle writes `skillOverrides`
and restoring it removes the key; `~/.claude/settings.json` diffed
byte-identical to a pre-test backup afterwards.
Two real bugs were found by that live pass and are fixed here rather
than left for later:
- **Discovery was broken for both providers on any current install**
(CODE-505). claude's `available[].source` became a union (bare path
string in 52 of 275 entries, object with its own `source` discriminator
in the rest) and its `version` is `null` for 262 of 275; codex omits
`version` entirely while the schema required the key present. One bad
entry failed the whole array, so the page was empty everywhere.
Regression fixtures now cover every observed shape.
- Two secret-row inputs inside one base-ui `Field` both rendered
`name="secrets.0.value"` (Field owns a single control), silently
dropping the key field from the form.
## Deliberately out of scope
Managed/HQ connectors and OAuth (CODE-94/96/340 untouched); plugin
update and Claude install/uninstall (Codex install/uninstall ships
here); Claude's `name-only`/`user-invocable-only` skill tiers have no
wire representation yet (preserved, not coarsened); retrofitting the
same masking onto `accounts`, which still round-trips plaintext.
The Market list renders a bounded 60 entries per provider with a visible
"showing 60 of 273 — narrow it down with search" note rather than
truncating silently; virtualizing it is a follow-up if browsing the full
catalog matters.
Closes CODE-487, CODE-488, CODE-490, CODE-491, CODE-492, CODE-493,
CODE-494, CODE-495, CODE-496, CODE-497, CODE-502, CODE-503, CODE-504,
CODE-505.80 files changed
Lines changed: 7477 additions & 223 deletions
File tree
- apps
- daemon
- src
- __tests__
- secrets
- sim
- desktop/src/renderer/src/settings
- webview/src
- routes/settings
- packages
- client
- core
- src
- client
- tests/integration
- sdk/src
- workbench/src
- mock
- data
- settings
- plugins
- __tests__
- surface
- foundation/schema
- src
- model
- __tests__
- wire
- tests/contract/wire
- host
- agent-adapter
- src
- __tests__
- plugins
- engine/src
- __tests__
- agent
- plugin
- session
- simulator
- wire
- presentation
- i18n/src/locales
- ui/src/shell
- plugins
- __tests__
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
32 | 33 | | |
33 | 34 | | |
34 | | - | |
35 | | - | |
36 | | - | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
37 | 38 | | |
38 | 39 | | |
39 | 40 | | |
40 | | - | |
41 | | - | |
42 | | - | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
43 | 47 | | |
44 | 48 | | |
45 | 49 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
| 20 | + | |
19 | 21 | | |
20 | 22 | | |
21 | 23 | | |
| |||
62 | 64 | | |
63 | 65 | | |
64 | 66 | | |
65 | | - | |
| 67 | + | |
66 | 68 | | |
67 | 69 | | |
68 | 70 | | |
| |||
267 | 269 | | |
268 | 270 | | |
269 | 271 | | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
270 | 456 | | |
271 | 457 | | |
272 | 458 | | |
| |||
295 | 481 | | |
296 | 482 | | |
297 | 483 | | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
298 | 558 | | |
299 | 559 | | |
300 | 560 | | |
| |||
0 commit comments