Skip to content

feat(marketplace): make marketplace the core extension surface#2581

Draft
reachjalil wants to merge 12 commits into
different-ai:devfrom
reachjalil:feature/marketplace-core-extension-surface
Draft

feat(marketplace): make marketplace the core extension surface#2581
reachjalil wants to merge 12 commits into
different-ai:devfrom
reachjalil:feature/marketplace-core-extension-surface

Conversation

@reachjalil

@reachjalil reachjalil commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

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/agent still exposes exactly search_capabilities / execute_capability; Your Connections is unchanged.

What's in here

Area Change
Data model plugin_mcp_server_instance binding table (queryable plugin⇄connection link, N instances per template), config_object.den_skill_id, encrypted external_mcp_connection.config_values. Migration 0032 (renumber if another DB PR lands first) + idempotent backfill:plugin-bindings script.
Plugin API 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.
MCP templates Declarative 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.
Directory Lazy "OpenWork Directory" marketplace derived from the existing MCP presets — presets become configurable items.
Den Web hub Browse + Configured servers tabs; create / edit / archive marketplaces; compose plugins (add/remove from the library); per-item instance configuration + skill toggles; GitHub import; legacy-wrap CTA. Seeded catalogs show a "Managed by OpenWork" badge (rename/archive would fight the lazy seeder). /dashboard/mcp-connections stays as a compatibility path.
Desktop Connection responses carry pluginId/serverKey/instanceLabel; plugin-bound connections stop rendering as standalone rows.

Security notes for review

  • Secrets (API keys, OAuth clients, header/query values) live only on External MCP Connections and are entered by humans: the internal MCP principal is rejected on every secret-carrying write (isAgentServerInstanceSecretWrite, tested in mcp-agent-config-policy.test.ts).
  • Lazy marketplace seeding runs on the member-reachable list endpoint and grants the caller nothing — seed rows are system-owned, org-wide viewer only, with a short-circuit so the hot path is a pure read (plugin-system-seed-access.test.ts pins zero writes + no personal grants).
  • Instance bindings stay with the plugin that configured them; import-reuse of the same server URL reports reusedFromPluginId instead 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.
  • Manual UI pass on the live stack: create → compose → configure → edit → archive round trip; managed guard on seeded catalogs; zero console errors.

Reviewer notes

@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openwork-landing Ready Ready Preview, Comment, Open in v0 Jul 8, 2026 3:25pm

@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown
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.

reachjalil and others added 5 commits July 8, 2026 07:21
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>
@reachjalil reachjalil changed the title Draft proposal: Marketplace as core extension surface feat(marketplace): make marketplace the core extension surface Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant