Skip to main content
A Marketplace tool is a self-contained package. Its installable logic, runtime, settings schema, chat actions, UI declarations, tests, and author documentation belong in that package—not in apps/web.
Do not add an if (toolId === 'my-tool'), a static optional-tool import, a dedicated web API route, or a React component to apps/web for a new Marketplace tool. The host discovers installed manifests, extensions, agent actions, settings fields, and UI surfaces dynamically.

Package checklist

Create one directory under packages/marketplace-tools/<tool-id>/:
The package is the deletion boundary: uninstalling it removes its optional behavior without changing the web application or the project’s package.json.

Define the manifest

Use manifest v3 whenever the tool has a runtime, UI surface, credentials, or usage metering.
configSchema creates the installed-tool settings card. password fields are never prefilled from a package or committed to source control. runtime creates the runtime selector, while usage can add a manifest-driven usage section. The manifest’s emoji, icon, description, tags, and install count make up the Marketplace card.

Add an agent action

Export TOOL_EXTENSION and AGENT_TOOLS from the package entrypoint. The chat route loads every installed action through this contract; it does not know your tool id, parameter schema, or handler.
Agent action names must be globally unique. The host validates registrations and rejects an action that collides with a first-party or another installed action.

Credentials, environment variables, and limits

There are two distinct configuration surfaces: Put rate limiting and quota checks in the tool runtime, before expensive work begins. Return 429 with a retry hint when appropriate. For paid or limited runtimes, declare provider-neutral billing.meters; reserve quota atomically, and have the runtime consume an entitlement rather than calling Stripe directly. A future payment service can update the entitlement without rewriting tool logic.

UI that travels with the tool

Declare each surface in ui.surfaces; available slots are data-indexing, settings, chat-result, and asset-actions. Keep browser code in the package’s UI entrypoint and request sandboxed-iframe isolation for untrusted code. For chat output, return the host’s versioned, declarative result UI shape (for example, a ui.kind: 'citations' result) rather than adding a tool-name switch in the chat component. Use a package-owned src/ui.ts to keep surface ids, input/result contracts, and UI versions next to the tool logic. A surface must degrade gracefully when it is not rendered; chat answers must still be understandable as text.

Test before publishing

Test the manifest, client contract, credential failure, rate-limit response, and every agent action. If the tool has a runtime, add a lightweight health check and a test for its entitlement/quota state. Next: publish or share a private tool.