@auggieteo/dsh-mcp-adapter 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [v0.4.2] - 2026-09-28
8
+
9
+ ### Fixed
10
+
11
+ - Settings > MCP loads again on DSH 0.1.7 instead of showing `Could not load MCP status: transport failure for /mcp-adapter/overview: HTTP 405`. The status RPC was a dedicated `connection.rpc.handle('/mcp-adapter', …)` channel, but DSH 0.1.7 mounts such channels with `owner.webServer.register` on the connection provider's own fiber, and cordis 4 refuses that un-injected property read — the channel silently never registered and the SPA static fallback answered every POST with 405 (the `mcp-adapter` plugin row still reads `active`; only a Host console line shows the failure). The RPC surface now mounts each endpoint as an exact Fetch route on the shared `/api` channel through the documented `connection.fetch.register` seam — the platform still applies browser authentication and the origin fence before the handler runs — and the Client calls `connection.rpc.call('/api', 'mcp-adapter/<endpoint>')`; envelopes and the page are otherwise unchanged. `test/rpc-fetch-routes.test.js` pins the mount and the dispose/remount lifecycle through a real cordis fiber, which the old `rpc.handle` fakes could not see. [ADR 0011](docs/adr/0011-rpc-on-shared-api-fetch-routes.md).
12
+
13
+ ### Changed
14
+
15
+ - The Settings MCP nav icon fixes itself. The shell hard-codes nav icons by section id and the `settings.section` slot carries no icon option, so v0.4.1 patched the installed shell bundle by hand — and a DSH upgrade replaced the patched file, bringing the gear back. The patch core moved to `src/host/nav-icon.js` and the Adapter now applies it at every activation: after any install or DSH upgrade the icon returns on the next `dsh web` start. Only the install the process actually runs from is patched (located via the CLI entry, `DSH_ROOT` overrides), the insert stays idempotent behind the `// dsh-mcp-adapter` marker, and every failure warns instead of throwing. `scripts/patch-dsh-settings-nav-icon.mjs` remains for manual runs and `--revert` and now ships with the package. [ADR 0012](docs/adr/0012-automatic-nav-icon-patch.md).
16
+
17
+ ## [v0.4.1] - 2026-09-28
18
+
19
+ ### Fixed
20
+
21
+ - The Host entry no longer dies on start with `Error: cannot get property "config" without inject`. v0.4.0 read the Adapter's Loader-entry Config from `ctx.config`, but cordis 4 has no `config` service or accessor — the context proxy refuses any property the plugin did not declare in `inject`, so every DSH start that mounted the `mcp-adapter` entry threw inside `createMcpConfigScope` and the Adapter never came up. The validated Config is the second `apply` argument (`apply(ctx, config)`, the shape the shipped DSH plugins use), and its volatile fields are references the Loader commits through in place, so the scope stays live for the whole activation. `createMcpConfigScope(ctx, config)` now takes it explicitly; `test/settings.test.js` pins the contract against a real cordis fiber and its fakes refuse a `config` context property the way the proxy does. [ADR 0010](docs/adr/0010-loader-entry-config-model.md) records the correction.
22
+
7
23
  ## [v0.4.0] - 2026-09-27
8
24
 
9
25
  ### Added
package/CONTEXT.md CHANGED
@@ -33,7 +33,7 @@ The standard `mcpServers`-shaped JSON stored as the Adapter's profile Loader-ent
33
33
  _Avoid_: manifest, registry file
34
34
 
35
35
  **Entry Config**:
36
- The DSH 0.1.7 configuration model: a plugin's `Config` schema validated by the cordis Loader into `ctx.config`, persisted in the profile composition. Fields marked volatile are edited live through the settings forms and commit into the running plugin without a restart.
36
+ The DSH 0.1.7 configuration model: a plugin's `Config` schema validated by the cordis Loader and handed to the plugin's `apply` as its second argument — cordis has no `config` service, so `ctx.config` is not readable — persisted in the profile composition. Fields marked volatile are edited live through the settings forms and commit into the running plugin's references without a restart.
37
37
  _Avoid_: settings namespace, settings document
38
38
 
39
39
  **Lazy Lifecycle**:
package/README.md CHANGED
@@ -66,7 +66,7 @@ Then restart DSH. The Settings panel (`⌘,` / sidebar foot) gains an **MCP** se
66
66
 
67
67
  | DSH release | Support |
68
68
  | --- | --- |
69
- | 0.1.7-rc.1 and newer 0.1.x | Supported. Configuration is the plugin's Loader-entry Config: the Host reads `ctx.config` and live-committed volatile updates, writes go through the settings service, and the page reads the `configForms` service and writes through the `remote.settings` face mounted by the `@deepseek-ai/dsh-api-remotes` client bundle. |
69
+ | 0.1.7-rc.1 and newer 0.1.x | Supported. Configuration is the plugin's Loader-entry Config: the Host reads the validated Config cordis passes to `apply` (there is no `ctx.config`) plus its live-committed volatile updates, writes go through the settings service, and the page reads the `configForms` service and writes through the `remote.settings` face mounted by the `@deepseek-ai/dsh-api-remotes` client bundle. |
70
70
  | Older 0.1.x | Unsupported. Those releases require the registered-namespace settings model this plugin used through v0.3.x; upgrade DSH or stay on that release line. |
71
71
 
72
72
  Upgrading from v0.3.x on DSH 0.1.7: at the first start after the upgrade, the Adapter imports the legacy `mcp:` section of the profile's `settings.yaml` into the `mcp-adapter` entry once. A `mcp-adapter.legacy-imported` marker records the import; anything the import refuses to accept (for example an invalid section) stays in `settings.yaml.imported` and a Host warning names the manual copy path. See [ADR 0010](./docs/adr/0010-loader-entry-config-model.md).
@@ -157,6 +157,10 @@ JSON import accepts the standard `{ "mcpServers": { ... } }` shape. Import repla
157
157
 
158
158
  Every page write carries the latest entry revision. Field edits use path mutations.
159
159
 
160
+ The status surface talks to the Host through exact Fetch routes on the shared `/api` channel (`POST /api/mcp-adapter/<endpoint>`), authenticated by the platform like every other `/api` request ([ADR 0011](./docs/adr/0011-rpc-on-shared-api-fetch-routes.md)). If the page instead shows `Could not load MCP status: … HTTP 405`, the installed Adapter predates v0.4.2: its dedicated RPC channel cannot mount on DSH 0.1.7. Upgrade and restart DSH.
161
+
162
+ The sidebar's MCP row shows a link icon. The icon map lives in the DSH settings shell and the `settings.section` slot carries no icon option, so the Adapter patches one marked branch into the installed shell bundle at every start — after a DSH upgrade the icon returns by itself, and if it does not, restart `dsh web` and refresh the page. `scripts/patch-dsh-settings-nav-icon.mjs` wraps the same patch for manual runs (`--icon <Name>`, `--revert`). See [ADR 0012](./docs/adr/0012-automatic-nav-icon-patch.md).
163
+
160
164
  ### Server lifecycle
161
165
 
162
166
  Each Server's `lifecycle` setting controls when it connects and whether it idles out. `lazy` remains the default.
@@ -253,6 +257,7 @@ src/host/ host half: plain ESM, no build step
253
257
  src/client/ client half: JSX, bundled to lib/client.js
254
258
  build.mjs esbuild wrapper producing the loader-compatible bundle
255
259
  lib/client.js build artifact (gitignored), required at runtime
260
+ scripts/ CLI over the Settings nav-icon patch core
256
261
  docs/adr/ architecture decisions
257
262
  docs/verification/ end-to-end verification records
258
263
  CONTEXT.md project vocabulary
@@ -1,13 +1,15 @@
1
1
  # Configuration is the plugin's Loader-entry Config (DSH 0.1.7)
2
2
 
3
- DSH 0.1.7 removed the settings model the Adapter was built on. `ctx.settings.register(namespace, schema)` — the `SettingsProvider` seam where plugins registered their own namespace — is gone: `@deepseek-ai/dsh-settings` now exports a single `SettingsForms` service that projects the cordis **Loader entries' Config schemas** into editable profile forms (verified against the 0.1.7-rc.2 packages). Host plugins `export const Config`; the Loader validates the entry's profile config into `ctx.config`; fields marked `.volatile()` (schemastery `~3.18.4`) resolve to cosmokit volatile references, and a config edit touching only volatile fields is committed into the running fiber's references by cordis-plugin-loader without a restart, emitting `loader/volatile-update` — everything else restarts the entry. `SettingsForms` reads and writes by **profile entry id** (ours is `mcp-adapter`, the id `cordis.patch.yml` registers), refuses writes to non-volatile paths, and `configure({ auto })` decides whether the Settings shell auto-generates a form page for the entry. On the client `settingsScope` is replaced by the `configForms` service — `get(entryId)` answers a per-entry form controller (`{ status, value, base, user, revision, writable, mode }` snapshots, `subscribe`, `mutate`) and `describe()` a shared document mirror — while the `remote.settings` wire face (positional args, flat `{ ok, value | error }` envelope, `settings/document-updated` invalidation) and the `settings.section` slot (now owned by `dsh-client-ui-settings-general`) survive. This supersedes [ADR 0002](./0002-config-in-dsh-settings-namespace.md) (where global Config lives) and completes [ADR 0009](./0009-dual-generation-settings-api.md) (whose predicted third shape is exactly this one).
3
+ DSH 0.1.7 removed the settings model the Adapter was built on. `ctx.settings.register(namespace, schema)` — the `SettingsProvider` seam where plugins registered their own namespace — is gone: `@deepseek-ai/dsh-settings` now exports a single `SettingsForms` service that projects the cordis **Loader entries' Config schemas** into editable profile forms (verified against the 0.1.7-rc.2 packages). Host plugins `export const Config`; the Loader validates the entry's profile config and cordis hands the validated object to the plugin as the **second `apply` argument** (`new runtime.callback(ctx, fiber.config)`) — there is no `config` service or accessor, so reading `ctx.config` throws `cannot get property "config" without inject`; fields marked `.volatile()` (schemastery `~3.18.4`) resolve to cosmokit volatile references, and a config edit touching only volatile fields is committed into the running fiber's references by cordis-plugin-loader without a restart, emitting `loader/volatile-update` — everything else restarts the entry. `SettingsForms` reads and writes by **profile entry id** (ours is `mcp-adapter`, the id `cordis.patch.yml` registers), refuses writes to non-volatile paths, and `configure({ auto })` decides whether the Settings shell auto-generates a form page for the entry. On the client `settingsScope` is replaced by the `configForms` service — `get(entryId)` answers a per-entry form controller (`{ status, value, base, user, revision, writable, mode }` snapshots, `subscribe`, `mutate`) and `describe()` a shared document mirror — while the `remote.settings` wire face (positional args, flat `{ ok, value | error }` envelope, `settings/document-updated` invalidation) and the `settings.section` slot (now owned by `dsh-client-ui-settings-general`) survive. This supersedes [ADR 0002](./0002-config-in-dsh-settings-namespace.md) (where global Config lives) and completes [ADR 0009](./0009-dual-generation-settings-api.md) (whose predicted third shape is exactly this one).
4
4
 
5
- The Adapter's Config becomes its entry Config: `src/host/index.js` exports `Config = z.object({ mcpServers: …volatile(), skillInstall: …volatile() })`. Both marks sit on the top-level fields because schemastery rejects volatile fields below a dict or beneath another volatile field, and SettingsForms only accepts writes on schema-declared volatile paths. `MCP_SETTINGS_NAMESPACE` changes from `'mcp'` to `'mcp-adapter'`: under 0.1.7 the namespace identity is the entry id, so the client's `configForms.get()`, the `remote.settings` writes, and the host's `settings.update()` all address the entry. `createMcpConfigScope(ctx)` wraps the fiber in the same `{ get, watch, update, mutate }` surface the old scope provided — `get()` unwraps the volatile refs off `ctx.config`, `watch` notifies `(next, previous)` on `loader/volatile-update` when the value actually changed, and the write methods forward to `ctx.get('settings')` — so the workspace layer, manager, promotions, OAuth, and commands consume the port unchanged.
5
+ The Adapter's Config becomes its entry Config: `src/host/index.js` exports `Config = z.object({ mcpServers: …volatile(), skillInstall: …volatile() })`. Both marks sit on the top-level fields because schemastery rejects volatile fields below a dict or beneath another volatile field, and SettingsForms only accepts writes on schema-declared volatile paths. `MCP_SETTINGS_NAMESPACE` changes from `'mcp'` to `'mcp-adapter'`: under 0.1.7 the namespace identity is the entry id, so the client's `configForms.get()`, the `remote.settings` writes, and the host's `settings.update()` all address the entry. `createMcpConfigScope(ctx, config)` wraps the entry Config in the same `{ get, watch, update, mutate }` surface the old scope provided — `get()` unwraps the volatile refs off the Config object cordis passed to `apply`, `watch` notifies `(next, previous)` on `loader/volatile-update` when the value actually changed, and the write methods forward to `ctx.get('settings')` — so the workspace layer, manager, promotions, OAuth, and commands consume the port unchanged. The scope holds the object for the whole activation, which is sound because a volatile commit writes through the references inside it (cosmokit freezes them; the shared `Symbol.for('cosmokit.volatile.write')` member is the only writer) and any ordinary edit replaces `fiber.config` and restarts the entry, so a fresh `apply` receives the new object.
6
6
 
7
- Considered options: keeping any legacy generation alongside 0.1.7 was rejected because the model replaced both faces at once — supporting 0.1.2/0.1.5 too means two parallel settings subsystems in Host and client (register-scope plus volatile-Config, `settingsScope` plus `configForms`) and doubled generation fakes; the release is 0.1.7-only, deleting the `connection.api.settings` fallback, the legacy fakes, and the dead `@deepseek-ai/dsh-client-runtime` entry (last published for 0.1.1) from `dsh.client.inject`. Restarting the whole entry on every form edit (the platform default for non-volatile fields) was rejected for `mcpServers` because a single keystroke in a Server dialog would drop every live connection; live volatile commits plus `loader/volatile-update` keep the manager reconciling exactly as it watched the old scope. The alternative to the scope facade — rewriting every consumer against `ctx.config` refs — was rejected as a wide diff with no behavior benefit.
7
+ Considered options: keeping any legacy generation alongside 0.1.7 was rejected because the model replaced both faces at once — supporting 0.1.2/0.1.5 too means two parallel settings subsystems in Host and client (register-scope plus volatile-Config, `settingsScope` plus `configForms`) and doubled generation fakes; the release is 0.1.7-only, deleting the `connection.api.settings` fallback, the legacy fakes, and the dead `@deepseek-ai/dsh-client-runtime` entry (last published for 0.1.1) from `dsh.client.inject`. Restarting the whole entry on every form edit (the platform default for non-volatile fields) was rejected for `mcpServers` because a single keystroke in a Server dialog would drop every live connection; live volatile commits plus `loader/volatile-update` keep the manager reconciling exactly as it watched the old scope. The alternative to the scope facade — rewriting every consumer against the entry Config refs — was rejected as a wide diff with no behavior benefit.
8
8
 
9
9
  Cross-field validation had to move. The old `SettingsProvider` ran `validateMcpSettings` before every persist; the Config write path validates only the schema, so structure errors now surface as a failed entry start, and `validateMcpSettings` runs inside the scope's `get()` instead: a section that passes the schema but fails the cross-field rules serves an empty server list with one deduplicated Host warning, so a hand-edited profile can never brick the connection manager. The client writes keep the direct `remote.settings` wrapper rather than the form controller's queue so conflict messages still reach the page as text, and the controller reports a mirrored-but-schema-invalid entry (whose form stays `loading` by contract) as an `invalid` state instead of spinning.
10
10
 
11
11
  Legacy data needs one explicit bridge. 0.1.7 renames the profile's `settings.yaml` to `settings.yaml.imported` and imports only sections whose name matches an entry id — the Adapter's legacy `mcp` section under its `mcp-adapter` entry would be silently stranded. After the Loader settles (the platform import runs first and skips the mismatched name), if the entry is untouched — no user section, no non-default servers — the section is validated against the Config schema and written once through `settings.update`, and an `mcp-adapter.legacy-imported` marker file in the profile home records the import so a later deliberate "remove every Server" never resurrects the old list. Every failure warns with manual copy instructions and never throws.
12
12
 
13
13
  Consequences: peers widen to `@deepseek-ai/dsh-settings` `^0.1.7-rc.1` and `@deepseek-ai/schemastery` `^3.18.4`; `yaml` becomes a runtime dependency of the migration module. Global Servers persist in the active profile composition (through Settings > MCP or the profile document) instead of a settings document, and README/CONTEXT vocabulary follows. `test/client-apply.test.js` collapses to a single 0.1.7 generation fake pinning ns `mcp-adapter` on the wire; `test/legacy-import.test.js` locks the migration. The nav-icon patch script targets 0.1.7's renamed `*Medium` icon members: its `MODELS_BRANCH` pattern now pins only the `Icon` prefix and the default icon is `IconLinkOutlineMedium`.
14
+
15
+ Correction (v0.4.1): this ADR originally said the Loader validates the entry Config *into `ctx.config`*, and the shipped v0.4.0 entry acted on that — it died on start with `cannot get property "config" without inject`. cordis 4 resolves every context property through a proxy that refuses anything the plugin did not declare in `inject`, and there is no `config` service or accessor to declare, so `ctx.config` is unreachable by construction. The validated Config is the second `apply` argument (`apply(ctx, config)`), which is how the shipped DSH plugins take it. `createMcpConfigScope` now receives that argument and `src/host/index.js` threads it through; the test fakes refuse a `config` context property the way the cordis proxy does, and a probe mounted through a real cordis fiber pins the contract, so the assumption cannot come back unnoticed.
@@ -0,0 +1,9 @@
1
+ # Settings RPC mounts as exact Fetch routes on the shared /api channel
2
+
3
+ `Settings > MCP` died on every load with `Could not load MCP status: transport failure for /mcp-adapter/overview: HTTP 405`. The status RPC was mounted as a dedicated channel: `ctx.connection.rpc.handle('/mcp-adapter', handler)`. In DSH 0.1.7 — byte-identical in rc.1 and rc.2, so not an rc regression, broken since the v0.4.0 port — `HostConnectionService.rpc.handle` ends in `register()`, which runs `owner.effect(() => owner.webServer.register(route))` where `owner` is the connection service's **own provider fiber** (the `client-connection` plugin context, whose `inject` never declares `webServer`). cordis 4 resolves every context property through a proxy that refuses an undeclared read (`cannot get property "webServer" without inject`), so the effect throws, the inner fiber dies at `state=3` with the failure visible only as a Host `ctx.logger.error` (the plugin row still reads `active`), and the prefix route is never registered. DSH's `dsh-host-frontend-static` fallback then answers the browser's `POST /mcp-adapter/overview` with 405. DSH's own plugins only use `rpc.intercept('/api', …)` — a path that never reads `webServer` — which is why `rpc.handle` stayed broken upstream. Reproduced in-process against a real cordis 4 fiber: the handle-based mount registers zero routes.
4
+
5
+ The Adapter mounts each Settings RPC endpoint instead as an **exact Fetch route** through the documented public seam `connection.fetch.register({ path, methods, requestBody, fetch })` (`ConnectionFetchRoute`: "One exact Fetch route on the shared API channel … owned by a Host feature"). Paths are `/api/mcp-adapter/<endpoint>` for the eight endpoints (`status`, `catalog`, `overview`, `layers`, `oauth-login`, `oauth-logout`, `oauth-status`, `reconnect`), `methods: ['POST']`, `requestBody: 'buffered'`. The platform's shared `/api` handler applies admission — browser authentication and the Host/origin fence — before the fetch, and `registerFetchRoute` only mutates the provider's live route map: it never touches `webServer`, so nothing depends on the broken path. Each route speaks the connection envelope so the Client's unchanged `connection.rpc.call('/api', 'mcp-adapter/<endpoint>')` validates the reply exactly as for platform endpoints: decode the `client-request` (`type`, string `rpcId`, matching `method`; malformed JSON or a non-`client-request` body ⇒ 400), dispatch to the endpoint handler, and answer `server-response` with the same `rpcId` and the unchanged `{ ok, value | error: { code, message, details } }` result shape. Registration is one `ctx.effect` per route: a partial failure rolls the earlier routes back with the fiber, and an entry restart disposes every route before re-registering — the cordis test pins that dispose/remount leaves no stale registration and no duplicate-collision. `test/rpc-fetch-routes.test.js` mounts `installMcpManagerRpc` through a real cordis fiber and a faithful `HostConnectionService.fetch` model, because the older fakes called `rpc.handle` directly and could not see the platform defect.
6
+
7
+ Considered options: declaring `webServer` in the caller's `inject` was ruled out by experiment — the refused read happens on the provider's fiber, which the caller cannot annotate. `rpc.intercept('/api', …)` was ruled out because the shared channel admits exactly one interceptor and `dsh-api-gateway` owns it (`registerInterceptor` throws otherwise). Mounting our own `webServer` prefix route was rejected for re-implementing platform internals — the node→Fetch bridge, admission, and envelope framing — which drift on every DSH release. Patching `dsh-client-connection` in place (the nav-icon-script pattern) was rejected as patching platform RPC internals: riskier, and unnecessary once the designed seam works. Upstream should still fix `rpc.handle`; this mount keeps working either way, because `fetch.register` is the documented API.
8
+
9
+ Consequences: the Settings wire URL becomes `POST /api/mcp-adapter/overview`; unauthenticated probes now get the platform's 401 instead of 405, non-POST methods fall through to the gateway interceptor (404), `MCP_RPC_CHANNEL` is `'/api'` on the client with the `mcp-adapter/` endpoint prefix, and the host exports `MCP_RPC_ROUTE_PREFIX` / `MCP_RPC_ENDPOINTS` in place of the old channel constant. [ADR 0010](./0010-loader-entry-config-model.md) stays accurate; only the RPC mount moved. See also [ADR 0012](./0012-automatic-nav-icon-patch.md) for the second half of the same 405-and-gear report.
@@ -0,0 +1,9 @@
1
+ # The Settings nav icon patch applies itself at every activation
2
+
3
+ The Settings sidebar picks its icon from a hard-coded `navIcon(id)` map inside the shell bundle `@deepseek-ai/dsh-client-ui-settings-general`; an unknown section id — ours is `mcp` — gets the generic settings gear. The `settings.section` slot's register options are only `id`, `order`, and `label`: there is no icon option, so a plugin cannot express the icon through the slot contract. v0.4.1 shipped the workaround as a manual script (`scripts/patch-dsh-settings-nav-icon.mjs`) that inserts one marked branch into the installed shell bundle — but a DSH upgrade replaces that bundle, so after the 0.1.7-rc.2 upgrade the gear was back and fixing it was an unremembered manual step.
4
+
5
+ The patch core moved to `src/host/nav-icon.js` (shipped with the package) and `apply()` calls `installMcpNavIconPatch(ctx)` at every activation, so a fresh install and every DSH upgrade self-heal on the next start. The hook locates the **running** DSH install from `process.argv[1]`: a global `dsh` runs through a bin symlink whose argv path stays the symlink, so the lookup resolves the real path first, then walks up to the nearest `package.json` naming `@deepseek-ai/dsh`; the `DSH_ROOT` environment variable overrides. The insert is idempotent via the `// dsh-mcp-adapter` marker (the per-boot cost is one file read). Two safety properties: the activation hook patches only the install it is running in — it never falls back to the npm global root, so a headless run or test process whose entry is not a DSH CLI touches nothing on disk — and every failure (missing file, pattern drift in a future build, read-only install) is a `ctx.logger.warn`, never a throw: Settings chrome must not break the Adapter. `scripts/patch-dsh-settings-nav-icon.mjs` remains as the CLI over the same core, with the npm-global-root candidate and `--revert` for manual use, and `scripts/` joined the package `files` list so an installed copy can revert itself. A patched file only takes effect once the server serves fresh bundles, so the logged guidance — and the README — say to restart `dsh web` and refresh when the icon does not appear.
6
+
7
+ Considered options: npm `postinstall` was rejected because it runs only at plugin install — it cannot cover DSH upgrades, which is exactly the case that broke — and package managers gate build scripts, making it unreliable. Asking for an icon register option in the `settings.section` slot is the right upstream change but not something this release can ship. Patching the client-modules combo cache was rejected: combos are content-hashed at boot, so the file patch plus a restart is the simpler correct layering.
8
+
9
+ Consequences: the default icon stays `IconLinkOutlineMedium` (the `--icon <Name>` knob chooses another primitive); `test/nav-icon.test.js` pins insert idempotence, revert round trip, pattern-drift rejection, argv-root discovery through a symlink, and the warn-only failure paths; and the ADR 0010 note about the script targeting `*Medium` icon members carries over unchanged. See also [ADR 0011](./0011-rpc-on-shared-api-fetch-routes.md) for the other half of the same report.
package/lib/client.js CHANGED
@@ -44,7 +44,8 @@ var import_react = __toESM(require("react"), 1);
44
44
 
45
45
  // src/client/settings-controller.js
46
46
  var MCP_SETTINGS_NAMESPACE = "mcp-adapter";
47
- var MCP_RPC_CHANNEL = "/mcp-adapter";
47
+ var MCP_RPC_CHANNEL = "/api";
48
+ var MCP_RPC_ENDPOINT_PREFIX = "mcp-adapter/";
48
49
  var INVALID_SETTINGS_MESSAGE = "The saved MCP configuration failed validation. Fix or remove the invalid fields in the profile, then reopen Settings.";
49
50
  var SERVER_FIELDS = /* @__PURE__ */ new Set([
50
51
  "command",
@@ -1537,7 +1538,12 @@ function apply(ctx) {
1537
1538
  scope,
1538
1539
  describe,
1539
1540
  settingsApi: createSettingsApi(ctx),
1540
- rpc: (endpoint, payload, signal) => ctx.connection.rpc.call(MCP_RPC_CHANNEL, endpoint, payload, signal)
1541
+ rpc: (endpoint, payload, signal) => ctx.connection.rpc.call(
1542
+ MCP_RPC_CHANNEL,
1543
+ `${MCP_RPC_ENDPOINT_PREFIX}${endpoint}`,
1544
+ payload,
1545
+ signal
1546
+ )
1541
1547
  });
1542
1548
  ctx.effect(() => installMcpSettingsStyles(), "mcp-adapter: Settings styles");
1543
1549
  ctx.effect(() => () => controller.dispose(), "mcp-adapter: Settings controller");