feat(mcp): replace /settings/mcp with full-featured /mcp page (catalog + marketplace + sources)#231
Conversation
|
Thanks for the substantial work, @Interstellar-code — this is impressive scope. CI is green and the architecture (REST API + TanStack hooks + dialog/marketplace split) reads well. That said, this touches 85 files and removes the existing
If you have a hosted preview or Loom walkthrough handy, that'd help speed it up. Will report back with findings. |
I'm happy to take a more active role in this repository. Actually I was thinking initially to fork and develop myself but since you were responsive in terms of approving my bug fixes initially. I have lots of features already lined up. Let me know if you want to connect more directly to coordinate and work together efficiently. Thanks! |
|
Big and well-scoped PR \u2014 thanks for the work. Needs a rebase against latest main (recently merged composer/skills work touches related areas). Could you do: After rebase + green CI we can merge. |
Implements the MCP management plan (.omc/plans/mcp-management.md) Phase 1 end-to-end on a single feature branch (PR1+PR2+PR3+PR4 collapsed): - New `/mcp` route with capability gate + BackendUnavailableState fallback. - New `/api/mcp` (GET list, POST create), `/api/mcp/test` (POST connection probe), `/api/mcp/discover` (POST tool discovery for a draft config), `/api/mcp/configure` (PUT enable/toolMode/include/exclude), and `/api/mcp/$name` (DELETE). - Strict `mcp` capability probe in gateway-capabilities: hits `GET /api/mcp` directly and validates the body parses through `normalizeMcpList` — dashboard-up-but-route-missing returns false (resolves Open Question #4). - Type split: read shapes in `src/types/mcp.ts` (client+server), write shapes in `src/types/mcp-input.ts` (server-only; secrets contained here). - Runtime normalization layer `src/server/mcp-normalize.ts` mirrors the Skills `asRecord`/`readString`/`normalizeSkill` defense — strips unknown fields, coerces enums, masks secrets via `MASK_SENTINEL`, re-applies via `maskSecretsInPlace` before every `json(...)`. - All write endpoints CSRF-checked via `requireJsonContentType`. - Capability-off responses use `createCapabilityUnavailablePayload('mcp')` with `{ servers: [], total: 0, categories }` for GET (200) and 503 for writes — feature gates fall open without throwing. - Static preset catalog (`src/screens/mcp/presets.ts`) with GitHub, Filesystem, Postgres, Slack, Linear; Catalog tab installs prefilled drafts through the same dialog flow. - Screens: `McpScreen` (Installed/Catalog/All tabs + search + category filter), `McpServerCard` (status badge + Test/Edit/Delete + enable toggle), `McpServerDialog` (HTTP/stdio + auth + Discover + Save with bearer-token clear-on-submit). - TanStack Query hooks (`useMcpServers`, `useTestMcpServer`, `useDiscoverMcpTools`, `useUpsertMcpServer`, `useConfigureMcpServer`, `useDeleteMcpServer`). Tests (vitest): - `src/server/mcp-normalize.test.ts` — 13 tests covering enum coercion, list-shape variants, malformed-entry drop, presence flags without echo, env/header masking by key hint, idempotency, test-result normalization, payload-string scanner. - `src/routes/api/-mcp.test.ts` — 8 tests covering input validation, capability fall-open shape, CSRF gate (415 on non-JSON POST, pass on JSON, pass on GET), and the **secret echo guard**: a worst-case agent that echoes a submitted bearer token in body/env/headers must never surface the original string in the workspace response. Build, lint, and the new test files are clean. Pre-existing unrelated test failures on `local` (router-route-resolution, context-usage, markdown math, slash-command-menu, chat-message-list, gateway-capabilities env-source) are unchanged by this PR. Worked with Interstellar Code
Architect review flagged that `McpClientInput` in `src/types/mcp.ts` (the
file explicitly designated for client+server read shapes with no secrets)
contained `bearerToken` and `oauth.clientSecret`, allowing the browser
bundle to import a secret-bearing type via the dialog component.
Resolves the type-split violation:
- `src/types/mcp.ts`: drop `bearerToken` and `oauth` from `McpClientInput`.
Now strictly the browser-safe form payload, no secret fields.
- `src/screens/mcp/components/mcp-server-dialog.tsx`: hold `bearerToken`
in ephemeral component-local `useState<string>` typed inline. Cleared
on submit and on dialog open. No exported type carries the field.
- `src/screens/mcp/hooks/use-mcp-mutations.ts`: `useUpsertMcpServer`
accepts `McpClientInput & { bearerToken?: string }` inline at the
call-site, again with no exported secret-bearing type. Server route
`parseMcpServerInput` re-validates and forwards to the agent.
The full server-side write shape (`McpServerInput` with secrets) remains
in `src/types/mcp-input.ts`, server-only.
Worked with Interstellar Code
Add no-restricted-imports rule scoped to src/screens/** and src/components/** that blocks importing @/types/mcp-input. That file may carry unmasked secrets and is server-only — clients should import McpClientInput from @/types/mcp instead. Worked with Interstellar Code
Mirror the existing /skills registration across every nav and command surface so the MCP screen is reachable from the dashboard overflow grid, command palette, mobile hamburger drawer, mobile tab bar, slash menu, search modal quick actions, and workspace shell (active-tab tracking + mobile page title). Inspector panel gets a parallel MCP tab that lists configured servers via /api/mcp. Worked with Interstellar Code
Catalog tab now reuses the screen's search state to filter presets by name/description, surfaces an empty-state when no presets match, and renders each preset as a card with an Official Presets category badge styled to match the skills-screen design vocabulary. Tests: - src/components/-mcp-nav.test.tsx: each modified nav file references the /mcp route (or registers an mcp tab id for inspector-panel) - src/screens/mcp/-presets.test.ts: filtering MCP_PRESETS by query narrows results by name and description, returns full catalog for empty queries, and returns nothing for unknown queries Worked with Interstellar Code
The primary visible left rail (`chat-sidebar.tsx`) was missed by the prior nav-coverage commit. Slot MCP between Skills and Profiles in `knowledgeItems`, mirroring the McpServerIcon used elsewhere. Worked with Interstellar Code
…logs
- `useMcpServers`: enable refetchOnWindowFocus for live state.
- `McpServerCard`: per-card Refresh button (re-runs Test, updates
discoveredToolsCount), Reauth button when authType === 'oauth' (uses
new useMcpOAuth hook), Logs button (opens McpLogsDrawer).
- `use-mcp-oauth.ts`: opens auth URL in new tab, polls /api/mcp/test
every 2s until status === 'connected' or 60s timeout. Returns
mutation-style { start, isPending, isError, error, data }.
- `mcp-logs-drawer.tsx`: fixed-right slide-in drawer subscribing via
EventSource to /api/mcp/<name>/logs. Newest-first, max 500 lines,
auto-scroll, tear down on close (no zombie EventSource).
- `routes/api/mcp/$name.logs.ts`: SSE proxy with auth + capability
gates. Capability-off → 503. Pattern follows chat-events.ts.
- Tests: 3 new for logs route (input validation, capability-off,
auth gate); smoke test for useMcpOAuth shape.
Total tests: 24 passing (13 normalize + 8 mcp + 3 logs). Build clean.
Worked with Interstellar Code
Adds an `mcpFallback` capability that lets the workspace perform CRUD on `config.mcp_servers` via the existing dashboard `/api/config` route when the agent does not yet expose the new `/api/mcp*` runtime endpoints. Gated to loopback-only deployments by `isLocalhostDeployment()` (both URLs loopback AND HOST unset/loopback). Test/Discover/Logs return a structured "not yet available" payload in fallback mode; the MCP screen renders an amber banner so the limitation is visible. Worked with Interstellar Code
Workspace-only end-to-end MCP catalog + marketplace replacing the static
presets.ts and the upstream /settings/mcp surfaces.
Phase 2 — File-backed catalog:
- assets/mcp-presets.seed.json + ~/.hermes/mcp-presets.json (atomic
bootstrap via tmp+linkSync, mtime+ino+ctime+size cache, malformed-file
preservation, schema validation: id regex, transport-specific fields,
env key regex, https URLs, category allowlist, duplicate-id rejection,
unknown-field warnings)
- src/server/mcp-input-validate.ts: shared parseMcpServerInput returning
per-field {path, message} errors; promoted from inline definition
- src/routes/api/mcp/presets.ts GET handler
Phase 3.0 — Federated marketplace:
- src/server/mcp-hub/{cache,trust,index,types}.ts + sources/{mcp-get,
local-file}.ts: Smithery registry adapter (replaces speculative
registry.mcp.run NXDOMAIN), ETag/If-Modified-Since with 304 reuse,
rate-limit handling, parallel Promise.allSettled across sources with
8s per-source timeout, dedupe by source+id+name, fallback to
local-file when remote degraded
- Trust hardening: shell metachar reject, transport allowlist, env-key
regex, control-char + absolute-path attack defenses, inline-exec
flag detection (-c, -lc, -e for sh/bash/python/node/perl/ruby)
- src/screens/mcp/components/install-confirmation-dialog.tsx: 2-click
commit with full template preview (command/args/env masked) and
AbortController on dismiss
- Disk persistence for tool-discovery cache (mcp-tools-cache.ts) +
hermes-mcp CLI bridge (mcp-cli-bridge.ts) for live test/tool
enumeration in fallback mode
Phase 3.2 — User-configurable sources:
- ~/.hermes/mcp-hub-sources.json schema (built-ins always present,
protected from mutation; user can add HTTPS-only generic-json
sources with trust+format)
- src/routes/api/mcp/hub-sources{,.$id}.ts CRUD with per-process mutex
(read-modify-write race protection)
- generic-json adapter: SSRF guard (private/loopback/link-local/IPv6
ULA all rejected after DNS resolution, redirects disabled), 5MB
response-size cap (streaming read), trust hard-cap at 'community'
for user-source entries, source field 'user:<id>' for dedupe
- src/screens/mcp/components/sources-manager-dialog.tsx UI
Polish:
- Placeholder detection at install confirmation (inline fill form
blocks commit until /path/to/, <your-...>, empty *_TOKEN/_KEY/etc
resolved)
- Test result UX hints when stdio Connection closed + placeholder args
or http fetch failed + placeholder url
- Env-ref preserved in normalize (${VAR_NAME} no longer masked) +
Edit dialog diagnostic
UI: Skills-pattern parity for /mcp screen (Tabs + Marketplace tab,
Switch primitive, Button primitives, DialogRoot/Content, primary-*
Tailwind classes matching skills-screen.tsx). Single-row toolbar
(tabs + search + filter). Removed All + Catalog tabs, kept Installed +
Marketplace.
Backend:
- gateway-capabilities probeMcp uses authenticated dashboardFetch
(Codex MAJOR fix); probeMcpConfigKey + isLocalhostDeployment for
mcpFallback capability
- routes/mcp.tsx route gate accepts mcp || mcpFallback
- mcp-normalize.ts headers.Authorization + env *_TOKEN/_KEY/_SECRET
/_AUTH/_APIKEY auth detection upgrades authType to 'bearer'
Removed (replaced by /mcp):
- src/screens/settings/mcp-settings-screen.tsx (759 LOC)
- src/routes/settings/mcp.tsx
- src/routes/api/mcp/{servers,reload}.ts (orphaned endpoints; reload
posted to gateway 404s)
- src/screens/mcp/presets.ts (static array, replaced by file-backed)
- settings-sidebar MCP nav entries (replaced by main /mcp route)
Tests: 263+ passing across 19+ MCP suites — input-validate, presets-
store, hub-cache/trust/unified-search, sources/{mcp-get,local-file,
generic-json}, hub-sources-store, mcp-tools-cache, ssrf-guard,
marketplace-install-confirmation, marketplace-placeholder-detection,
hub-search/-presets/-hub-sources route tests. Pre-existing 2
gateway-capabilities env-resolution failures unrelated.
Reviewers: Codex critic 4 passes (Phase 2 REJECTED → 8 fixes applied,
Phase 3.0 APPROVED-WITH-CHANGES → 4 fixes, Phase 3.2 REJECTED → 6
fixes including SSRF guard + response-size cap + concurrent-CRUD
mutex + trust cap). Architect approved final pass.
Worked with Interstellar Code
cfda538 to
6a830df
Compare
|
The README's feature list and roadmap were stale: missing the new MCP page (#231), Operations dashboard, Agent View panel, Conductor screen, and capability-gate work landed in the past 24-48h. Roadmap still listed shipped features as 'In Development'. Changes: - Replace short 'Features' bullet list with a comprehensive 'What's inside' that mentions every major surface, including the explicit Conductor caveat with a link to #262. - Roadmap restructured into Shipped / In progress / Coming sections. Conductor gets its own 'in progress' row with the upstream-plugin caveat. Native Desktop App moved to 'in progress' per spec status. Multi-provider support called out explicitly so users know what works on day one. - Drop the in-house 'Agent W Managed Companion' subsection from public README \u2014 it's internal-team-only deployment notes that don't apply to anyone running upstream. - Collapse the duplicate '## Features' section near the bottom into the consolidated security section. Avoids saying the same thing twice. - Tighten security headings: now '## Security & deployment env vars' with two clean subsections: 'Built-in safeguards' and 'Env vars for remote / Docker deployments'. Build clean. No code changes. Co-authored-by: Aurora release bot <release@outsourc-e.com>
…g + marketplace + sources) (outsourc-e#231) * feat(mcp): MCP server management page (Phase 1) Implements the MCP management plan (.omc/plans/mcp-management.md) Phase 1 end-to-end on a single feature branch (PR1+PR2+PR3+PR4 collapsed): - New `/mcp` route with capability gate + BackendUnavailableState fallback. - New `/api/mcp` (GET list, POST create), `/api/mcp/test` (POST connection probe), `/api/mcp/discover` (POST tool discovery for a draft config), `/api/mcp/configure` (PUT enable/toolMode/include/exclude), and `/api/mcp/$name` (DELETE). - Strict `mcp` capability probe in gateway-capabilities: hits `GET /api/mcp` directly and validates the body parses through `normalizeMcpList` — dashboard-up-but-route-missing returns false (resolves Open Question #4). - Type split: read shapes in `src/types/mcp.ts` (client+server), write shapes in `src/types/mcp-input.ts` (server-only; secrets contained here). - Runtime normalization layer `src/server/mcp-normalize.ts` mirrors the Skills `asRecord`/`readString`/`normalizeSkill` defense — strips unknown fields, coerces enums, masks secrets via `MASK_SENTINEL`, re-applies via `maskSecretsInPlace` before every `json(...)`. - All write endpoints CSRF-checked via `requireJsonContentType`. - Capability-off responses use `createCapabilityUnavailablePayload('mcp')` with `{ servers: [], total: 0, categories }` for GET (200) and 503 for writes — feature gates fall open without throwing. - Static preset catalog (`src/screens/mcp/presets.ts`) with GitHub, Filesystem, Postgres, Slack, Linear; Catalog tab installs prefilled drafts through the same dialog flow. - Screens: `McpScreen` (Installed/Catalog/All tabs + search + category filter), `McpServerCard` (status badge + Test/Edit/Delete + enable toggle), `McpServerDialog` (HTTP/stdio + auth + Discover + Save with bearer-token clear-on-submit). - TanStack Query hooks (`useMcpServers`, `useTestMcpServer`, `useDiscoverMcpTools`, `useUpsertMcpServer`, `useConfigureMcpServer`, `useDeleteMcpServer`). Tests (vitest): - `src/server/mcp-normalize.test.ts` — 13 tests covering enum coercion, list-shape variants, malformed-entry drop, presence flags without echo, env/header masking by key hint, idempotency, test-result normalization, payload-string scanner. - `src/routes/api/-mcp.test.ts` — 8 tests covering input validation, capability fall-open shape, CSRF gate (415 on non-JSON POST, pass on JSON, pass on GET), and the **secret echo guard**: a worst-case agent that echoes a submitted bearer token in body/env/headers must never surface the original string in the workspace response. Build, lint, and the new test files are clean. Pre-existing unrelated test failures on `local` (router-route-resolution, context-usage, markdown math, slash-command-menu, chat-message-list, gateway-capabilities env-source) are unchanged by this PR. Worked with Interstellar Code * fix(mcp): strip secret fields from client-safe McpClientInput Architect review flagged that `McpClientInput` in `src/types/mcp.ts` (the file explicitly designated for client+server read shapes with no secrets) contained `bearerToken` and `oauth.clientSecret`, allowing the browser bundle to import a secret-bearing type via the dialog component. Resolves the type-split violation: - `src/types/mcp.ts`: drop `bearerToken` and `oauth` from `McpClientInput`. Now strictly the browser-safe form payload, no secret fields. - `src/screens/mcp/components/mcp-server-dialog.tsx`: hold `bearerToken` in ephemeral component-local `useState<string>` typed inline. Cleared on submit and on dialog open. No exported type carries the field. - `src/screens/mcp/hooks/use-mcp-mutations.ts`: `useUpsertMcpServer` accepts `McpClientInput & { bearerToken?: string }` inline at the call-site, again with no exported secret-bearing type. Server route `parseMcpServerInput` re-validates and forwards to the agent. The full server-side write shape (`McpServerInput` with secrets) remains in `src/types/mcp-input.ts`, server-only. Worked with Interstellar Code * fix(mcp): block client imports of server-only mcp-input types Add no-restricted-imports rule scoped to src/screens/** and src/components/** that blocks importing @/types/mcp-input. That file may carry unmasked secrets and is server-only — clients should import McpClientInput from @/types/mcp instead. Worked with Interstellar Code * feat(mcp): wire /mcp into all sidebar/nav surfaces Mirror the existing /skills registration across every nav and command surface so the MCP screen is reachable from the dashboard overflow grid, command palette, mobile hamburger drawer, mobile tab bar, slash menu, search modal quick actions, and workspace shell (active-tab tracking + mobile page title). Inspector panel gets a parallel MCP tab that lists configured servers via /api/mcp. Worked with Interstellar Code * feat(mcp): catalog tab search, category badges, and nav coverage tests Catalog tab now reuses the screen's search state to filter presets by name/description, surfaces an empty-state when no presets match, and renders each preset as a card with an Official Presets category badge styled to match the skills-screen design vocabulary. Tests: - src/components/-mcp-nav.test.tsx: each modified nav file references the /mcp route (or registers an mcp tab id for inspector-panel) - src/screens/mcp/-presets.test.ts: filtering MCP_PRESETS by query narrows results by name and description, returns full catalog for empty queries, and returns nothing for unknown queries Worked with Interstellar Code * feat(mcp): add MCP entry to chat-sidebar Knowledge group The primary visible left rail (`chat-sidebar.tsx`) was missed by the prior nav-coverage commit. Slot MCP between Skills and Profiles in `knowledgeItems`, mirroring the McpServerIcon used elsewhere. Worked with Interstellar Code * feat(mcp): Phase 3 — live tool refresh, OAuth reauth, per-server SSE logs - `useMcpServers`: enable refetchOnWindowFocus for live state. - `McpServerCard`: per-card Refresh button (re-runs Test, updates discoveredToolsCount), Reauth button when authType === 'oauth' (uses new useMcpOAuth hook), Logs button (opens McpLogsDrawer). - `use-mcp-oauth.ts`: opens auth URL in new tab, polls /api/mcp/test every 2s until status === 'connected' or 60s timeout. Returns mutation-style { start, isPending, isError, error, data }. - `mcp-logs-drawer.tsx`: fixed-right slide-in drawer subscribing via EventSource to /api/mcp/<name>/logs. Newest-first, max 500 lines, auto-scroll, tear down on close (no zombie EventSource). - `routes/api/mcp/$name.logs.ts`: SSE proxy with auth + capability gates. Capability-off → 503. Pattern follows chat-events.ts. - Tests: 3 new for logs route (input validation, capability-off, auth gate); smoke test for useMcpOAuth shape. Total tests: 24 passing (13 normalize + 8 mcp + 3 logs). Build clean. Worked with Interstellar Code * feat(mcp): localhost-only config-fallback transport (Phase 1.5) Adds an `mcpFallback` capability that lets the workspace perform CRUD on `config.mcp_servers` via the existing dashboard `/api/config` route when the agent does not yet expose the new `/api/mcp*` runtime endpoints. Gated to loopback-only deployments by `isLocalhostDeployment()` (both URLs loopback AND HOST unset/loopback). Test/Discover/Logs return a structured "not yet available" payload in fallback mode; the MCP screen renders an amber banner so the limitation is visible. Worked with Interstellar Code * feat(mcp): full catalog + marketplace + sources manager (Phase 2-3.2) Workspace-only end-to-end MCP catalog + marketplace replacing the static presets.ts and the upstream /settings/mcp surfaces. Phase 2 — File-backed catalog: - assets/mcp-presets.seed.json + ~/.hermes/mcp-presets.json (atomic bootstrap via tmp+linkSync, mtime+ino+ctime+size cache, malformed-file preservation, schema validation: id regex, transport-specific fields, env key regex, https URLs, category allowlist, duplicate-id rejection, unknown-field warnings) - src/server/mcp-input-validate.ts: shared parseMcpServerInput returning per-field {path, message} errors; promoted from inline definition - src/routes/api/mcp/presets.ts GET handler Phase 3.0 — Federated marketplace: - src/server/mcp-hub/{cache,trust,index,types}.ts + sources/{mcp-get, local-file}.ts: Smithery registry adapter (replaces speculative registry.mcp.run NXDOMAIN), ETag/If-Modified-Since with 304 reuse, rate-limit handling, parallel Promise.allSettled across sources with 8s per-source timeout, dedupe by source+id+name, fallback to local-file when remote degraded - Trust hardening: shell metachar reject, transport allowlist, env-key regex, control-char + absolute-path attack defenses, inline-exec flag detection (-c, -lc, -e for sh/bash/python/node/perl/ruby) - src/screens/mcp/components/install-confirmation-dialog.tsx: 2-click commit with full template preview (command/args/env masked) and AbortController on dismiss - Disk persistence for tool-discovery cache (mcp-tools-cache.ts) + hermes-mcp CLI bridge (mcp-cli-bridge.ts) for live test/tool enumeration in fallback mode Phase 3.2 — User-configurable sources: - ~/.hermes/mcp-hub-sources.json schema (built-ins always present, protected from mutation; user can add HTTPS-only generic-json sources with trust+format) - src/routes/api/mcp/hub-sources{,.$id}.ts CRUD with per-process mutex (read-modify-write race protection) - generic-json adapter: SSRF guard (private/loopback/link-local/IPv6 ULA all rejected after DNS resolution, redirects disabled), 5MB response-size cap (streaming read), trust hard-cap at 'community' for user-source entries, source field 'user:<id>' for dedupe - src/screens/mcp/components/sources-manager-dialog.tsx UI Polish: - Placeholder detection at install confirmation (inline fill form blocks commit until /path/to/, <your-...>, empty *_TOKEN/_KEY/etc resolved) - Test result UX hints when stdio Connection closed + placeholder args or http fetch failed + placeholder url - Env-ref preserved in normalize (${VAR_NAME} no longer masked) + Edit dialog diagnostic UI: Skills-pattern parity for /mcp screen (Tabs + Marketplace tab, Switch primitive, Button primitives, DialogRoot/Content, primary-* Tailwind classes matching skills-screen.tsx). Single-row toolbar (tabs + search + filter). Removed All + Catalog tabs, kept Installed + Marketplace. Backend: - gateway-capabilities probeMcp uses authenticated dashboardFetch (Codex MAJOR fix); probeMcpConfigKey + isLocalhostDeployment for mcpFallback capability - routes/mcp.tsx route gate accepts mcp || mcpFallback - mcp-normalize.ts headers.Authorization + env *_TOKEN/_KEY/_SECRET /_AUTH/_APIKEY auth detection upgrades authType to 'bearer' Removed (replaced by /mcp): - src/screens/settings/mcp-settings-screen.tsx (759 LOC) - src/routes/settings/mcp.tsx - src/routes/api/mcp/{servers,reload}.ts (orphaned endpoints; reload posted to gateway 404s) - src/screens/mcp/presets.ts (static array, replaced by file-backed) - settings-sidebar MCP nav entries (replaced by main /mcp route) Tests: 263+ passing across 19+ MCP suites — input-validate, presets- store, hub-cache/trust/unified-search, sources/{mcp-get,local-file, generic-json}, hub-sources-store, mcp-tools-cache, ssrf-guard, marketplace-install-confirmation, marketplace-placeholder-detection, hub-search/-presets/-hub-sources route tests. Pre-existing 2 gateway-capabilities env-resolution failures unrelated. Reviewers: Codex critic 4 passes (Phase 2 REJECTED → 8 fixes applied, Phase 3.0 APPROVED-WITH-CHANGES → 4 fixes, Phase 3.2 REJECTED → 6 fixes including SSRF guard + response-size cap + concurrent-CRUD mutex + trust cap). Architect approved final pass. Worked with Interstellar Code
…urc-e#274) The README's feature list and roadmap were stale: missing the new MCP page (outsourc-e#231), Operations dashboard, Agent View panel, Conductor screen, and capability-gate work landed in the past 24-48h. Roadmap still listed shipped features as 'In Development'. Changes: - Replace short 'Features' bullet list with a comprehensive 'What's inside' that mentions every major surface, including the explicit Conductor caveat with a link to outsourc-e#262. - Roadmap restructured into Shipped / In progress / Coming sections. Conductor gets its own 'in progress' row with the upstream-plugin caveat. Native Desktop App moved to 'in progress' per spec status. Multi-provider support called out explicitly so users know what works on day one. - Drop the in-house 'Agent W Managed Companion' subsection from public README \u2014 it's internal-team-only deployment notes that don't apply to anyone running upstream. - Collapse the duplicate '## Features' section near the bottom into the consolidated security section. Avoids saying the same thing twice. - Tighten security headings: now '## Security & deployment env vars' with two clean subsections: 'Built-in safeguards' and 'Env vars for remote / Docker deployments'. Build clean. No code changes. Co-authored-by: Aurora release bot <release@outsourc-e.com>



Summary
Replaces the upstream
/settings/mcproute and its associated screen with adedicated, full-featured
/mcppage. This workspace-only change touches nohermes-agent code — all modifications are confined to
src/,assets/, andeslint.config.js.Closes #230
What changed
Removed (replaced by
/mcp)src/screens/settings/mcp-settings-screen.tsx(759 LOC) — old settings-embedded MCP UIsrc/routes/settings/mcp.tsx— old/settings/mcproutesrc/routes/api/mcp/servers.tsandreload.ts— orphaned endpoints (reload POSTs to a gateway 404)/mcprouteAdded — Phase 1: Core MCP management page
/mcproute with capability gate +BackendUnavailableStatefallbackGET /api/mcp,POST /api/mcp,PUT /api/mcp/configure,DELETE /api/mcp/$name,POST /api/mcp/test,POST /api/mcp/discoverMcpScreen(Installed / Marketplace tabs + search + category filter)McpServerCard(status badge, Test/Edit/Delete, enable toggle)McpServerDialog(HTTP/stdio + auth + Discover + Save; bearer-token clear-on-submit)useMcpServers,useTestMcpServer,useDiscoverMcpTools,useUpsertMcpServer,useConfigureMcpServer,useDeleteMcpServersrc/types/mcp.ts(client+server); write shapes insrc/types/mcp-input.ts(server-only, secrets never leave server)src/server/mcp-normalize.ts) — strips unknown fields,coerces enums, masks secrets via
MASK_SENTINEL, preserves${VAR_NAME}env-refsrequireJsonContentTypeworkspace shell, chat-sidebar Knowledge group
Added — Phase 1.5: Localhost-only config-fallback transport
gateway-capabilities.ts:probeMcpuses authenticateddashboardFetch;probeMcpConfigKey+isLocalhostDeploymentformcpFallbackcapability/mcproute gate acceptsmcp || mcpFallbackuse-mcp-capability-mode.tshook for runtime mode detectionGET /api/mcp/$name/logsSSE endpoint +McpLogsDrawercomponentuse-mcp-oauth.tshook for OAuth reauth flowAdded — Phase 2: File-backed catalog
assets/mcp-presets.seed.json— seed catalog bundled with the app~/.hermes/mcp-presets.json— atomic bootstrap (tmp+linkSync), mtime+ino cache,malformed-file preservation, full schema validation (id regex, transport fields,
env key regex, HTTPS URLs, category allowlist, duplicate-id rejection)
src/server/mcp-input-validate.ts— sharedparseMcpServerInputwith per-field errorsGET /api/mcp/presetshandlerAdded — Phase 3.0: Federated marketplace
src/server/mcp-hub/— Smithery registry adapter with ETag/If-Modified-Since 304reuse, rate-limit handling, parallel
Promise.allSettledacross sources (8sper-source timeout), dedupe by source+id+name, fallback to local-file when remote
is degraded
control-char + absolute-path attack defenses, inline-exec flag detection
InstallConfirmationDialog— 2-click commit with full template preview(command/args/env masked), placeholder detection blocks commit until filled,
AbortControlleron dismisssrc/server/mcp-tools-cache.ts— disk-persisted tool-discovery cachesrc/server/mcp-cli-bridge.ts— hermes-mcp CLI bridge for live test/tool enumerationAdded — Phase 3.2: User-configurable sources
~/.hermes/mcp-hub-sources.jsonschema — built-ins always present + protected;users can add HTTPS-only generic-json sources with trust + format
GET/POST /api/mcp/hub-sources,PUT/DELETE /api/mcp/hub-sources/$id— CRUDwith per-process mutex (read-modify-write race protection)
rejected after DNS resolution, redirects disabled), 5MB response-size cap
SourcesManagerDialogUI componentFiles changed
87 files changed, 12723 insertions(+), 1146 deletions(-)
Key paths:
src/routes/mcp.tsx— route entry pointsrc/screens/mcp/— all MCP UI screens, components, hooks, libsrc/routes/api/mcp/— all MCP API route handlerssrc/server/mcp-normalize.ts,mcp-input-validate.ts,mcp-presets-store.ts,mcp-tools-cache.ts,mcp-cli-bridge.ts,mcp-hub-sources-store.tssrc/server/mcp-hub/— federated marketplace infrastructureassets/mcp-presets.seed.json— bundled preset catalogsrc/types/mcp.ts,src/types/mcp-input.ts— type definitionssrc/lib/feature-gates.ts—mcp+mcpFallbackcapability gatessrc/server/gateway-capabilities.ts— MCP capability probingTest coverage
296 tests passing across 19 MCP suites (vitest):
src/routes/api/-mcp.test.tssrc/routes/api/-mcp-logs.test.tssrc/routes/api/mcp/-hub-search.test.tssrc/routes/api/mcp/-hub-sources.test.tssrc/routes/api/mcp/-presets.test.tssrc/server/mcp-normalize.test.tssrc/server/mcp-input-validate.test.tssrc/server/mcp-presets-store.test.tssrc/server/mcp-tools-cache.test.tssrc/server/mcp-hub-sources-store.test.tssrc/server/mcp-hub/-cache.test.tssrc/server/mcp-hub/-trust.test.tssrc/server/mcp-hub/-unified-search.test.tssrc/server/mcp-hub/-unified-search-user-sources.test.tssrc/server/mcp-hub/sources/-*.test.ts(×3)src/server/mcp-hub/lib/-ssrf-guard.test.tssrc/screens/mcp/-marketplace-*.test.tsx(×2)src/screens/mcp/-mcp-oauth.test.tssrc/components/-mcp-nav.test.tsxBuild:
pnpm buildgreen (✓ built in 2.13s).Scope constraint
Workspace-only — no changes to hermes-agent, no dashboard API contract changes.
The MCP backend endpoints are implemented entirely within the workspace's TanStack
Start server functions (
src/routes/api/mcp/).Verifiers
pnpm build— passgit diff upstream/main..feature/mcp-management-v2 --name-onlyconfirms zeropersonal dev files (no version bump, no react-grab, no settings-precedence changes)
AGENTS.md(local dev artifact, not present in upstream) — removedassistantCorruptionWarningdeclaration inmessage-item.tsxintroduced during commit squash — removed stale plain-call, kept the
useMemoversionWorked with Interstellar Code