Guides

Tutorial: ship a UCP-compliant commerce MCP server to every agent host with one command#

In this tutorial you wrap a Universal Commerce Protocol (UCP) MCP server in an agent-connector package and install it into every agent host on your machine with one command. The server is Shopify's public Global Catalog endpoint, so there is nothing to deploy first; the same steps apply to a UCP server you run yourself.

Before you start: UCP in MCP terms#

The Universal Commerce Protocol lets a business declare commerce capabilities (catalog search, checkout, fulfillment, discounts, orders) in a profile at /.well-known/ucp and offer them over REST, MCP, A2A or an embedded protocol. The MCP binding is plain MCP: tools/call with the operation name as the tool, the platform's agent profile URL in params.arguments.meta["ucp-agent"].profile, the UCP payload in structuredContent, and outputSchema pointing at the UCP JSON Schemas. So distributing a UCP server is distributing an MCP server.

/.well-known/ucp
json
// GET https://catalog.shopify.com/.well-known/ucp  (abridged)
{
  "ucp": {
    "version": "2026-08-25",
    "services": {
      "dev.ucp.shopping": [{
        "version": "2026-08-25",
        "transport": "mcp",
        "endpoint": "https://catalog.shopify.com/api/ucp/mcp",
        "schema": "https://ucp.dev/2026-08-25/services/shopping/mcp.openrpc.json"
      }]
    },
    "capabilities": { "dev.ucp.shopping.catalog.search": [ ... ], ... }
  }
}

Shopify's Global Catalog profile declares the endpoint used below, and Shopify's own quickstart installs its servers with a separate command per AI tool (Claude Code, Codex, Antigravity CLI, Cursor, VS Code). You are about to replace that matrix with one declaration.

You needWhy
Node.js 18 or neweragent-connector and the bin run on Node.
At least one agent host installedClaude Code, Codex, Cursor, Gemini CLI, Windsurf, Zed or any other host with an adapter; detect lists what it finds.
Network access to catalog.shopify.comThe endpoint answers initialize and tools/list without authentication.

1. Create the package#

A connector is an npm package with agent-connector as a dependency. The package name, mcpName, bin and version are the connector's identity, so you never repeat them in the config.

terminal
bash
mkdir shop-catalog-mcp && cd shop-catalog-mcp
npm init -y >/dev/null
npm install @ken-jo/agent-connector
# now replace package.json with the one below and add the two files
package.json
json
{
  "name": "@acme/shop-catalog-mcp",
  "version": "0.1.0",
  "description": "Shopify Global Catalog UCP MCP, installable into every agent host",
  "type": "module",
  "mcpName": "io.github.acme/shop-catalog",
  "bin": { "shop-catalog": "./bin.mjs" },
  "files": ["bin.mjs", "agent-connector.config.mjs"],
  "dependencies": { "@ken-jo/agent-connector": "^0.6.4" }
}

2. Declare the UCP endpoint#

Remote servers use transport: "http" and the endpoint from the business profile. The PreToolUse hook returns decision: "ask" for the checkout tools, so every host that supports hooks pauses before a purchase is created or completed.

agent-connector.config.mjs
ts
import { defineConnector } from "@ken-jo/agent-connector/sdk";

// Shopify's Global Catalog is a UCP-compliant MCP server: a remote JSON-RPC
// endpoint declared in https://catalog.shopify.com/.well-known/ucp
export default defineConnector({
  server: {
    transport: "http",
    url: "https://catalog.shopify.com/api/ucp/mcp",
    tools: { include: ["*"] },
    timeoutMs: 30_000,
  },
  hooks: {
    // UCP checkout tools move money; every host that has hooks asks first.
    PreToolUse: {
      matcher: "create_checkout|update_checkout|complete_checkout",
      async handler(evt) {
        return { decision: "ask", reason: `Confirm ${evt.toolName} with the buyer` };
      },
    },
  },
  targets: "auto",
});

Authenticated capabilities

Shopify's buyer-linked scopes and other authenticated UCP capabilities take a bearer token. Declare it once as auth: { type: "bearerEnv", bearerEnvVar: "SHOP_TOKEN" } in server; each host's config then references the environment variable in its own syntax. Field reference: Server.

3. Add the bin and audit#

createConnectorCli() exposes every agent-connector subcommand under your bin, scoped to the connector beside it. audit checks that package.json, bin and connector agree before anything touches a host.

bin.mjs
ts
#!/usr/bin/env node
// bin.mjs — every agent-connector subcommand under your own bin, scoped to this connector
import { createConnectorCli } from "@ken-jo/agent-connector/cli";

createConnectorCli({
  packageJson: new URL("./package.json", import.meta.url),
  connector: new URL("./agent-connector.config.mjs", import.meta.url),
})
  .run()
  .then((code) => { process.exitCode = code; });
terminal
bash
$ node bin.mjs audit
  ✓ package name: @acme/shop-catalog-mcp
  ✓ package version: 0.1.0
  ✓ branded bin: shop-catalog
  ✓ @ken-jo/agent-connector dependency: ^0.6.4

✓ package audit passed.

4. Preview the install#

install --dry-run detects the hosts on your machine and prints the plan without writing. This is the output on a machine with six hosts on 2026-09-06; yours lists the hosts you have.

terminal
bash
$ shop-catalog install --dry-run
shop-catalog  v0.1.0
  server      http · https://catalog.shopify.com/api/ucp/mcp
  hooks       PreToolUse
  telemetry   on

→ 6 agent hosts detected:
  ! claude-code   MCP server + 1 hook   ~/.claude.json, ~/.claude/settings.json
      ! telemetry not captured for shop-catalog on claude-code — remote (http) transport; per-tool telemetry is stdio-only
  ! codex         MCP server + 1 hook   ~/.codex/config.toml, ~/.codex/hooks.json
  ! cursor        MCP server + 1 hook   ~/.cursor/mcp.json, ~/.cursor/hooks.json
  ! gemini-cli    MCP server + 1 hook   ~/.gemini/settings.json
  ! windsurf      MCP server            ~/.codeium/windsurf/mcp_config.json
  ! zed           1 change              ~/.config/zed/settings.json

✓ Would install shop-catalog to 6 hosts · 10 files (dry-run — nothing written).

Read the report, not just the summary. Windsurf and Zed have no hook surface, so they receive the MCP server only. The telemetry line says per-tool token telemetry is not captured, because agent-connector measures tokens by wrapping a stdio server and cannot sit in front of a hosted endpoint. Both are facts about the hosts and the transport, reported rather than hidden.

5. Install, verify, uninstall#

terminal
bash
# write the configs for real (same plan as the dry run)
node bin.mjs install

# health-check every host that received the connector
node bin.mjs doctor

# the full inverse — removes everything install wrote
node bin.mjs uninstall --dry-run
node bin.mjs uninstall

After install, open one host and list its MCP servers: the connector appears under the name from package.json. doctor checks each host's config; for a remote transport it reports that the live stdio probe is skipped, which is expected. uninstall removes exactly what install wrote.

6. Verify the UCP endpoint itself#

Because the binding is plain MCP, two JSON-RPC calls confirm the endpoint independently of any host. UCP puts the platform's agent profile URL in the arguments; Shopify's docs publish example profiles you can use for this check.

terminal
bash
# initialize — a UCP MCP server answers like any MCP server
curl -s https://catalog.shopify.com/api/ucp/mcp -H 'content-type: application/json' -d '{
  "jsonrpc":"2.0","id":1,"method":"initialize",
  "params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}
}'
# → {"result":{"protocolVersion":"2025-06-18","serverInfo":{"name":"universal-ucp-mcp",...}}}

# tools/list — UCP puts the platform's agent profile in params.arguments.meta
curl -s https://catalog.shopify.com/api/ucp/mcp -H 'content-type: application/json' -d '{
  "jsonrpc":"2.0","id":2,"method":"tools/list",
  "params":{"arguments":{"meta":{"ucp-agent":{"profile":"https://shopify.dev/ucp/agent-profiles/2026-08-25/valid-with-capabilities.json"}}}}
}'
# → tools: search_catalog, lookup_catalog, ... (input/response conform to dev.ucp.shopping.catalog.*)

What success looks like#

CheckExpected result
node bin.mjs auditFour checks pass; the bin name is shop-catalog.
node bin.mjs install --dry-runEvery installed host is listed with the files it would receive; nothing is written.
node bin.mjs install then a host's MCP listThe connector shows up under the package-derived name in each host you have.
Ask the host to search the catalogThe host calls search_catalog; results carry the UCP metadata envelope with prices in minor units.
node bin.mjs uninstallThe entries and hook registrations are gone; doctor reports nothing installed.

What agent-connector does not do for UCP#

ConcernWhere it lives
Business profile at /.well-known/ucpServed from the business origin; the connector references its endpoint.
Payments (AP2), identity linking (OAuth), order webhooksThe UCP server and the platform. agent-connector installs the server; it does not sit in the request path.
REST, A2A and embedded transportsAgent hosts consume MCP; the other transports are for platforms integrating directly.

Next: the general publisher flow, including plugin marketplaces and the standard MCP artifacts, is in Publish your MCP server.