DEV Community

Cover image for Valid JSON Valid UCP: 10 Checks That Should Fail Your Build
Peter
Peter

Posted on

Valid JSON Valid UCP: 10 Checks That Should Fail Your Build

Your UCP manifest passes JSON.parse(). Your linter is happy. But Google AI Mode, Gemini, and every other AI shopping agent that hits /.well-known/ucp silently moves on to the next store.

Why? Because valid JSON is not valid UCP. The protocol has structural, security, and network requirements that go beyond syntax. Miss any of them, and you're invisible.

This post covers the 10 checks that actually break AI agent discovery — not the optional capabilities you can safely skip. Each check includes its rule ID, what breaks, and a concrete fix.

The Discovery Hierarchy

When an AI agent visits your store, it follows a strict sequence:

1. FETCH  — Can I reach /.well-known/ucp over HTTPS?
2. PARSE  — Is the response valid UCP (not just valid JSON)?
3. VERIFY — Are endpoints reachable, schemas fetchable, keys present?
4. NEGOTIATE — Do our capabilities intersect?
Enter fullscreen mode Exit fullscreen mode

Fail at level 1, and nothing else matters. Fail at level 2, and the agent can't understand your profile. Levels 3 and 4 are where most subtle bugs hide.

Google's own Merchant UCP guide says capabilities are opt-in: "select the set of capabilities & extensions you want to support." Missing Cart or Catalog is fine. Missing a fetchable endpoint is fatal.

The 10 Discovery-Critical Checks

Level 1: Fetchable

1. UCP_PROFILE_FETCH_FAILED — Manifest unreachable

The single most common failure. The agent sends:

curl -s -o /dev/null -w "%{http_code}" https://yourstore.com/.well-known/ucp
Enter fullscreen mode Exit fullscreen mode

If this returns anything other than 200 with Content-Type: application/json, you're invisible.

Common causes:

  • CDN/reverse proxy strips /.well-known/ paths
  • Squarespace/Wix can't serve arbitrary well-known paths natively
  • Docker/nginx misconfiguration blocks the route
  • SSL certificate issues (agents require valid HTTPS)

Fix: Verify the path is routable in your web server config. For platforms that can't serve /.well-known/, use Shopify's ucp-proxy or a Cloudflare Worker.

2. UCP_ENDPOINT_NOT_HTTPS — HTTP endpoint in profile

Every URL in your UCP profile must use HTTPS. No exceptions.

// BAD
"endpoint": "http://api.store.com/ucp"

// GOOD
"endpoint": "https://api.store.com/ucp"
Enter fullscreen mode Exit fullscreen mode

This applies to service endpoints, spec URLs, and schema URLs. Agents won't downgrade to HTTP.

Level 2: Parseable

3. UCP_MISSING_ROOT — No ucp object

The profile must have a root ucp key. Serving raw capabilities without the wrapper object fails parsing immediately.

// BAD  looks right, isn't
{
  "version": "2026-01-11",
  "capabilities": [...]
}

// GOOD  wrapped in ucp object
{
  "ucp": {
    "version": "2026-01-11",
    "services": {...},
    "capabilities": [...]
  }
}
Enter fullscreen mode Exit fullscreen mode

4. UCP_MISSING_VERSION / UCP_INVALID_VERSION_FORMAT — Bad version

UCP uses date-based versioning: YYYY-MM-DD. Not semver. Not integers.

// BAD
"version": "1.0"
"version": "2026"
"version": "January 2026"

// GOOD
"version": "2026-01-11"
Enter fullscreen mode Exit fullscreen mode

Agents use the version to negotiate protocol compatibility. No version = no negotiation.

5. UCP_MISSING_SERVICES — No services object

Services define your transport bindings (REST, MCP, A2A). Without them, the agent has no API to call.

"services": {
  "dev.ucp.shopping": {
    "version": "2026-01-11",
    "spec": "https://ucp.dev/specification/shopping/",
    "rest": {
      "schema": "https://ucp.dev/services/shopping/rest.openapi.json",
      "endpoint": "https://yourstore.com/api/ucp"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

6. UCP_MISSING_CAPABILITIES — No capabilities array

Capabilities tell the agent what your store supports (checkout, catalog, fulfillment). An empty or missing array means: "I support nothing."

Level 3: Verifiable

7. UCP_NS_ORIGIN_MISMATCH — Schema URL spoofing

This is a security check. UCP uses reverse-domain naming (dev.ucp.shopping.checkout), and the spec/schema URLs must match the namespace authority.

// BAD  capability claims dev.ucp.* but schema is on random domain
{
  "name": "dev.ucp.shopping.checkout",
  "schema": "https://evil.com/fake-checkout.json"  // FAILS
}

// GOOD  dev.ucp.* schemas come from ucp.dev
{
  "name": "dev.ucp.shopping.checkout",
  "schema": "https://ucp.dev/schemas/shopping/checkout.json"
}
Enter fullscreen mode Exit fullscreen mode

This prevents a store from claiming official UCP capabilities while pointing to modified schemas.

8. UCP_ORPHANED_EXTENSION — Extension without parent

Extensions like Fulfillment or Discount must reference a parent capability via extends. If the parent isn't in your capabilities array, the extension is orphaned and gets pruned during negotiation.

// BAD  fulfillment extends checkout, but checkout isn't listed
"capabilities": [
  {
    "name": "dev.ucp.shopping.fulfillment",
    "extends": "dev.ucp.shopping.checkout"
  }
]

// GOOD  parent capability is present
"capabilities": [
  { "name": "dev.ucp.shopping.checkout", ... },
  {
    "name": "dev.ucp.shopping.fulfillment",
    "extends": "dev.ucp.shopping.checkout",
    ...
  }
]
Enter fullscreen mode Exit fullscreen mode

9. UCP_MISSING_SIGNING_KEYS — No webhook verification keys

UCP requires JWK public keys for webhook signature verification. Without them, the agent can't trust your store's callbacks.

"signing_keys": [
  {
    "kid": "key-1",
    "kty": "EC",
    "crv": "P-256",
    "x": "f83OJ3D2xF1Bg8vub9tLe1gH...",
    "y": "x_FEzRu9m36HLN_tue659LNp...",
    "use": "sig",
    "alg": "ES256"
  }
]
Enter fullscreen mode Exit fullscreen mode

Generate a key pair with:

npx @ucptools/validator generate-keys --algorithm ES256
Enter fullscreen mode Exit fullscreen mode

10. UCP_ENDPOINT_TRAILING_SLASH — Trailing slash in URL

Small but real. Trailing slashes in endpoint URLs cause path resolution bugs when agents append checkout paths.

// BAD
"endpoint": "https://api.store.com/ucp/"

// GOOD
"endpoint": "https://api.store.com/ucp"
Enter fullscreen mode Exit fullscreen mode

What About Cart, Catalog, and Identity Linking?

Cart and Catalog are draft specs (published March 19, 2026). Identity Linking is stable. But none of them are discovery-critical.

Google's guide is explicit: capabilities are opt-in. A valid UCP profile with only Checkout is fully functional for AI agent commerce. Your validator should:

  • Error on the 10 checks above — these break discovery
  • Warn on Cart/Catalog schema drift — draft specs change
  • Error on Identity Linking config issues — stable spec, strict validation
  • Ignore missing optional capabilities — not a failure

Running These Checks in CI

Install the validator:

npm install @ucptools/validator
Enter fullscreen mode Exit fullscreen mode

Validate in your build pipeline:

import { validateProfile } from '@ucptools/validator';

const profile = JSON.parse(fs.readFileSync('.well-known/ucp', 'utf-8'));
const report = validateProfile(profile);

if (!report.ok) {
  console.error('UCP validation failed:');
  report.issues
    .filter(i => i.severity === 'error')
    .forEach(i => console.error(`  ${i.code}: ${i.message}`));
  process.exit(1);
}
Enter fullscreen mode Exit fullscreen mode

Or validate a live domain:

npx @ucptools/validator validate --url yourstore.com
Enter fullscreen mode Exit fullscreen mode

Each issue comes with a rule ID (like UCP_ENDPOINT_NOT_HTTPS), a JSON path (like $.ucp.services.dev.ucp.shopping.rest.endpoint), and a fix hint. Designed for CI output, not just dashboards.

A GitHub Action wrapper (ucptools/validate-action@v1) is coming this week — validate on every PR, fail on discovery-critical errors, post results as PR comments.

TL;DR

Check Rule ID Level What Breaks
Manifest unreachable UCP_PROFILE_FETCH_FAILED Fetch Everything
HTTP endpoint UCP_ENDPOINT_NOT_HTTPS Fetch Agent refuses connection
No ucp root object UCP_MISSING_ROOT Parse Profile unparseable
Bad version format UCP_INVALID_VERSION_FORMAT Parse No negotiation possible
No services UCP_MISSING_SERVICES Parse No API to call
No capabilities UCP_MISSING_CAPABILITIES Parse "Store supports nothing"
Schema origin mismatch UCP_NS_ORIGIN_MISMATCH Verify Security rejection
Orphaned extension UCP_ORPHANED_EXTENSION Verify Extension silently pruned
Missing signing keys UCP_MISSING_SIGNING_KEYS Verify Webhooks untrusted
Trailing slash UCP_ENDPOINT_TRAILING_SLASH Verify Path resolution bugs

Capabilities are opt-in. Discovery is not.


Free validation at ucptools.dev. npm package: @ucptools/validator. CLI, API, and CI integration included.

Top comments (0)