feat(marketplace): make marketplace the core extension surface#2581
Draft
reachjalil wants to merge 12 commits into
Draft
feat(marketplace): make marketplace the core extension surface#2581reachjalil wants to merge 12 commits into
reachjalil wants to merge 12 commits into
Conversation
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
|
@reachjalil is attempting to deploy a commit to the Different AI Team on Vercel. A member of the Team first needs to authorize it. |
16af0fc to
c955efb
Compare
POST /v1/plugins/:pluginId/server-instances is policy-visible to the internal MCP principal (tag Plugins), but its body carries credential material (apiKey, oauthClient, config field values). Apply the same boundary as POST /v1/mcp-connections: the agent may configure secretless instances, but secret material must be entered in the dashboard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GET /v1/marketplaces is member-reachable and lazily seeds the default marketplaces, so granting the caller a personal manager role on every built-in/preset plugin let any member escalate to editing directory plugins: configuring server instances (org credentials), deleting them, and flipping org-wide skill enablement. Seeding now writes directory config objects directly as system rows (org-wide viewer grant only, no creator ownership, no caller role checks), and short-circuits once every preset holds a system membership so the member-hot list path stays a pure read. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Import-reuse resolves connections org-wide by URL, and the binding upsert re-pointed the existing row at the importing plugin — so a second plugin importing the same server URL silently stole the first plugin's configured instance (its item showed zero instances, and governance attribution moved). A binding owned by another plugin is now returned untouched, surfaced to the import caller as reusedFromPluginId; only same-plugin re-imports update the row in place. This also removes the old fall-through that could double-insert against the unique connection index when the post-update re-select returned nothing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
POST /v1/mcp-connections probes non-OAuth servers before accepting them; the marketplace configure flow did not, so a typo'd template URL or a rejected API key produced an instance that read as Connected (api-key presence) and only failed later at agent call time with nothing pointing back at the misconfiguration. configurePluginServerInstance now runs the same connectExternalMcp probe for apikey/none instances, deletes the just-created connection on failure with a 400 that carries the real cause, and stamps connectedAt for both auth types on success. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bun's mock.module replaces a module for the whole test process, so the new store tests' db/access/url-guard mocks leaked into files that ran after them (mcp-url-guard assertions failed on the full suite). Mocks now delegate to the eagerly-captured real implementations except while the owning file's tests run, keeping the suite order-independent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The hub could browse marketplaces but not manage them: no way to create one (the empty state promised it), no rename/archive, and no way to compose a marketplace from the plugin library. Adds a New-marketplace dialog on Browse, Edit/Archive on the detail header, and add/remove plugin composition backed by the existing membership endpoints. Seeded catalogs (OpenWork Marketplace, Directory, Anthropic starter, Your organization) are looked up by name by the lazy seeder, so renaming or archiving them would just respawn a copy — they render a Managed by OpenWork badge instead, and the three curated catalogs also lock composition. Verified live: create -> compose -> edit -> archive round trip, managed guard on the Directory, zero console errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Marketplace as the core extension surface
Makes Marketplaces the admin surface for publishing and configuring extensions. The model: plugins define pure resources (skills, agents, MCP server templates — never credentials), marketplaces compose, expose, and configure them, and External MCP Connections keep sole credential ownership.
/mcp/agentstill exposes exactlysearch_capabilities/execute_capability;Your Connectionsis unchanged.What's in here
plugin_mcp_server_instancebinding table (queryable plugin⇄connection link, N instances per template),config_object.den_skill_id, encryptedexternal_mcp_connection.config_values. Migration0032(renumber if another DB PR lands first) + idempotentbackfill:plugin-bindingsscript.GET /plugins/:id/server-templates,POST/DELETE /plugins/:id/server-instances(configure a template into a connection),PUT /config-objects/:id/status(skill enablement, enforced in search and execute),POST /marketplaces/wrap-standalone-connections.configFields(bearer / header / query / OAuth-client placements) materialized into the credential layer at configure time; runtime header/query injection in the MCP transport. Non-OAuth instances are reachability-probed at configure time and cleaned up on failure./dashboard/mcp-connectionsstays as a compatibility path.pluginId/serverKey/instanceLabel; plugin-bound connections stop rendering as standalone rows.Security notes for review
isAgentServerInstanceSecretWrite, tested inmcp-agent-config-policy.test.ts).plugin-system-seed-access.test.tspins zero writes + no personal grants).reusedFromPluginIdinstead of re-pointing the row.Validation
tsc --noEmit+ build: den-api, den-web, desktop — clean.bun test(den-api): 16 new/extended tests pass; full-suite failure set is byte-identical to the pre-PR base (pre-existing, MySQL-dependent). New test mocks are file-scoped (test/support/scoped-module-mocks.ts) so the suite is order-independent.pnpm fraimz --flow marketplace-core-extension-surface --stack den— PASSED (evals/results/2026-07-08T14-43-27-557Z/fraimz.html): directory template → instance, API-key template without plugin-owned secrets, GitHub skill import → capability search/execute, skill disable → capability removal.Reviewer notes
dev.