fix: sync MCP marketplace with extensions catalog (#645)

Update the extensions dependency to the commit that removes deprecated MCP entries, assert those entries stay absent from the marketplace, and keep legacy installed servers visible/searchable in the MCP settings UI.

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Engel Nyst
2026-05-20 01:56:08 +02:00
committed by GitHub
parent 7b9c2fec33
commit 1a6e629fda
5 changed files with 50 additions and 6 deletions
+1 -1
View File
@@ -400,7 +400,7 @@ return new ConversationClient(getAgentServerClientOptions()).someMethod(...);
- Custom secrets are NOT auto-attached by the agent-server. `POST /api/conversations` only persists what the client sends in `request.secrets`; the persisted secrets store (`/api/settings/secrets`) is never read at conversation-start. `buildStartConversationRequestWithEncryptedSettings` enumerates `SecretsService.getSecrets()` and turns each entry into a `LookupSecret` whose `url` points back at `/api/settings/secrets/{name}` and whose `headers` carry `X-Session-API-Key` for auth. Pre-1.21.x agent-server SDKs would silently drop that header during validation when `secrets_encrypted=true` (the cipher in the validation context tried to `cipher.decrypt(plaintext_session_key)`, failed, and the validator removed the header — the conversation runtime then got 401s for every saved secret). The SDK fix preserves plaintext header values when decryption fails; if you still see saved secrets unavailable inside a conversation, verify the running agent-server bundles a `LookupSecret._validate_secrets` that falls back to plaintext on decrypt failure.
- MCP page layout: MCP is a **top-level** nav entry at `/mcp` (rendered by `src/routes/mcp.tsx`), shown right below "Skills" in `src/components/features/sidebar/sidebar.tsx`. The legacy `/settings/mcp` route still works as a redirect via `src/routes/mcp-settings-redirect.tsx`, and `src/routes/mcp-settings.tsx` re-exports the new page so the published `MCPSettings` library symbol (in `src/components/settings/index.ts`) keeps the same shape. Marketplace catalog data and MCP logo mappings live in `@openhands/extensions/mcps`; the Slack catalog entry there should point at `https://github.com/zencoderai/slack-mcp-server` and use `@zencoderai/slack-mcp-server`. `src/utils/mcp-marketplace-utils.ts` matches installed servers back to catalog entries. Tavily is a regular stdio MCP entry (`tavily-mcp` + `TAVILY_API_KEY`), not a special built-in sentinel anymore. Components are colocated under `src/components/features/mcp-page/` and reuse the existing `MCPServerForm` for the "Add custom server" / edit flow.
- MCP page layout: MCP is a **top-level** nav entry at `/mcp` (rendered by `src/routes/mcp.tsx`), shown right below "Skills" in `src/components/features/sidebar/sidebar.tsx`. The legacy `/settings/mcp` route still works as a redirect via `src/routes/mcp-settings-redirect.tsx`, and `src/routes/mcp-settings.tsx` re-exports the new page so the published `MCPSettings` library symbol (in `src/components/settings/index.ts`) keeps the same shape. Marketplace catalog data and MCP logo mappings live in `@openhands/extensions/mcps`; the Slack catalog entry there should point at `https://github.com/zencoderai/slack-mcp-server` and use `@zencoderai/slack-mcp-server`. Deprecated marketplace entries removed upstream (for example GitLab / Google Maps / Postgres / Puppeteer / SQLite) should disappear from the marketplace grid, but existing installed servers with those names must still render in the Installed section and stay searchable/editable/deletable via the raw server `name` / `command` fallback in `src/utils/mcp-marketplace-utils.ts` + `InstalledServerCard`. Tavily is a regular stdio MCP entry (`tavily-mcp` + `TAVILY_API_KEY`), not a special built-in sentinel anymore. Components are colocated under `src/components/features/mcp-page/` and reuse the existing `MCPServerForm` for the "Add custom server" / edit flow.
- Library packaging notes:
- Public npm entrypoints now come from `src/index.ts` → `src/lib/index.ts`, with domain barrels under `src/components/{conversation,terminal,browser,files,settings,sidebar}/index.ts`.
@@ -31,6 +31,16 @@ describe("OpenHands extensions catalogs", () => {
);
});
it("drops deprecated MCP entries that no longer have maintained replacements", () => {
const catalogIds = new Set(MCP_CATALOG.map((entry) => entry.id));
expect(catalogIds.has("gitlab")).toBe(false);
expect(catalogIds.has("google-maps")).toBe(false);
expect(catalogIds.has("postgres")).toBe(false);
expect(catalogIds.has("puppeteer")).toBe(false);
expect(catalogIds.has("sqlite")).toBe(false);
});
it("loads recommended automations from @openhands/extensions", () => {
expect(AUTOMATION_CATALOG.length).toBeGreaterThan(0);
+35 -1
View File
@@ -107,10 +107,44 @@ describe("MCPPage", () => {
screen.queryByTestId("mcp-marketplace-card-github"),
).not.toBeInTheDocument();
expect(
screen.queryByTestId("mcp-marketplace-card-postgres"),
screen.queryByTestId("mcp-marketplace-card-gitlab"),
).not.toBeInTheDocument();
});
it("keeps installed legacy servers visible even after their marketplace entries are removed", async () => {
vi.spyOn(SettingsService, "getSettings").mockResolvedValue(
buildSettings({
agent_settings: {
...MOCK_DEFAULT_USER_SETTINGS.agent_settings,
mcp_config: {
mcpServers: {
gitlab: {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-gitlab"],
},
},
},
},
}),
);
renderPage();
await screen.findByTestId("mcp-installed-list");
expect(screen.getByText("gitlab")).toBeInTheDocument();
expect(
screen.queryByTestId("mcp-marketplace-card-gitlab"),
).not.toBeInTheDocument();
const search = screen.getByTestId("mcp-search-input");
fireEvent.change(search, { target: { value: "gitlab" } });
await waitFor(() => {
expect(screen.getByText("gitlab")).toBeInTheDocument();
});
expect(screen.getByTestId("mcp-marketplace-empty")).toBeInTheDocument();
});
it("shows a search-empty state when the query matches nothing", async () => {
vi.spyOn(SettingsService, "getSettings").mockResolvedValue(buildSettings());
+3 -3
View File
@@ -12,7 +12,7 @@
"@heroui/react": "2.8.10",
"@microlink/react-json-view": "1.31.20",
"@monaco-editor/react": "4.7.0",
"@openhands/extensions": "git+https://github.com/OpenHands/extensions.git#c3f896acc55d8ee8717ab40f236960e71c2d477d",
"@openhands/extensions": "git+https://github.com/OpenHands/extensions.git#7b33f64ffccf95f7ccd2ec640aeae84ab1cc2c75",
"@openhands/typescript-client": "git+https://github.com/OpenHands/typescript-client.git#v0.6.0",
"@react-router/node": "7.14.2",
"@react-router/serve": "7.14.2",
@@ -3441,8 +3441,8 @@
},
"node_modules/@openhands/extensions": {
"version": "0.0.0",
"resolved": "git+https://github.com/OpenHands/extensions.git#c3f896acc55d8ee8717ab40f236960e71c2d477d",
"integrity": "sha512-PzLYaZA9ys8is9dNqNDDAExkz40DJ7t1sXUUrUUdha6vrO1/J4xqspK4YLOp0V5i4DewP3U9Rzqi2nFC/bVIMQ==",
"resolved": "git+https://github.com/OpenHands/extensions.git#7b33f64ffccf95f7ccd2ec640aeae84ab1cc2c75",
"integrity": "sha512-vozWq4MJiKoColb7zvXYgtxmWJsk7LdjdRH9SQebheHDOKvDAn7xy+sM7d0mEFRCk3QDEaVyR5HMH9w/1HH25Q==",
"license": "MIT",
"engines": {
"node": ">=18.20.0"
+1 -1
View File
@@ -23,7 +23,7 @@
"@heroui/react": "2.8.10",
"@microlink/react-json-view": "1.31.20",
"@monaco-editor/react": "4.7.0",
"@openhands/extensions": "git+https://github.com/OpenHands/extensions.git#c3f896acc55d8ee8717ab40f236960e71c2d477d",
"@openhands/extensions": "git+https://github.com/OpenHands/extensions.git#7b33f64ffccf95f7ccd2ec640aeae84ab1cc2c75",
"@openhands/typescript-client": "git+https://github.com/OpenHands/typescript-client.git#v0.6.0",
"@react-router/node": "7.14.2",
"@react-router/serve": "7.14.2",