You deploy a change to your storefront. The JSON still parses. The linter is happy. Tests pass.
Two weeks later you notice AI agents have been silently skipping your store, because a capability schema endpoint started returning a 404 and nothing anywhere told you.
This is the failure mode of agentic commerce readiness: it degrades quietly. There is no 500 page, no error rate spike, no angry customer email. Agents just move on to a merchant whose profile resolves.
The fix is the same as for every other class of quality problem in software. Stop checking it by hand. Put it in CI.
What actually breaks
UCP profiles are not simple documents. They carry namespace bindings, HTTPS requirements, signing keys, and extension chains that reference capabilities elsewhere in the file. A profile can be perfectly valid JSON and still be broken in ways that matter:
-
UCP_NS_ORIGIN_MISMATCH- your declared namespace does not match the origin serving it -
UCP_ENDPOINT_NOT_HTTPS- agents require HTTPS, without exception -
UCP_MISSING_SIGNING_KEYS- required before order and fulfillment capabilities will be trusted -
UCP_SCHEMA_FETCH_FAILED- a capability schema URL returns an error -
UCP_ORPHANED_EXTENSION- an extension points at a capability that no longer exists
Every one of these survives JSON.parse(). Every one of these makes you invisible to some agent. And the last two are exactly the kind of thing a refactor breaks without touching the profile file at all, because the breakage lives at the other end of a URL.
That is the argument for CI. Not "validate once at launch" - validate on every push, against the environment you are about to ship.
The GitHub Action
If you are on GitHub, this is four lines of YAML:
- uses: Nolpak14/ucp-validate-action@v1
with:
domain: 'yourstore.com'
fail-on-grade: 'C'
The action runs four-level validation (structural, rules, network, SDK compliance) against the live domain and scores AI readiness. fail-on-grade fails the build at that grade or worse; fail-on-score does the same against a 0-100 number. Use whichever matches how your team talks about quality.
It exposes score, grade, ucp-found, passed, and result-json as outputs, so you can branch on the result rather than just pass or fail:
- uses: Nolpak14/ucp-validate-action@v1
id: ucp
with:
domain: 'yourstore.com'
- run: echo "UCP score: ${{ steps.ucp.outputs.score }}"
- if: steps.ucp.outputs.ucp-found == 'false'
run: echo "::warning::No UCP profile at /.well-known/ucp"
On pull requests it posts a comment with the score, a category breakdown, and any validation issues with their error codes, updating in place rather than piling up duplicates. Set comment: 'false' if you would rather have step summaries only.
The pattern that actually pays: gate the deploy
Validating on push is useful. Gating the deploy on it is what stops a broken profile reaching production:
name: Deploy
on:
push:
branches: [main]
jobs:
validate-ucp:
runs-on: ubuntu-latest
steps:
- uses: Nolpak14/ucp-validate-action@v1
with:
domain: 'staging.yourstore.com'
fail-on-grade: 'C'
deploy:
needs: validate-ucp # deploy only runs if validation passed
runs-on: ubuntu-latest
steps:
- run: echo "Deploying to production..."
Point it at staging. If staging's profile does not clear your bar, the deploy job never runs. You catch the regression before customers see it, and before agents quietly reroute around you.
Not on GitHub? Use the CLI
The validator ships as an npm package, so it drops into any runner with Node.js 20+. It exits non-zero when validation fails, which is all a CI system needs.
GitLab CI:
ucp-validation:
image: node:20-alpine
script:
- npx -p @ucptools/validator ucp-validate validate --remote yourstore.com
rules:
- if: $CI_COMMIT_BRANCH == "main"
CircleCI:
jobs:
ucp-validation:
docker:
- image: cimg/node:20.11
steps:
- run:
name: Validate UCP profile
command: npx -p @ucptools/validator ucp-validate validate --remote yourstore.com
Or wire it into your test suite so it runs wherever your tests already run - Jenkins, Bitbucket Pipelines, a Makefile, a pre-commit hook:
npm install --save-dev @ucptools/validator
{
"scripts": {
"test:ucp": "ucp-validate validate --remote yourstore.com"
}
}
You can also validate the file before it ever ships, which is faster and catches structural errors without a network round trip:
npx -p @ucptools/validator ucp-validate validate --file ./public/.well-known/ucp
The other protocol: ACP readiness
UCP is not the only spec agents care about. The Agentic Commerce Protocol - OpenAI and Stripe's standard, the one behind checkout inside ChatGPT - has its own readiness surface: platform detection, payment integration, policy endpoints, security posture.
Worth being precise about how this runs, because it is different: the GitHub Action and the CLI check UCP. ACP readiness is a public REST endpoint. No key, no install, so it works in any CI that has curl and jq.
curl -sX POST https://ucptools.dev/api/acp-check \
-H 'Content-Type: application/json' \
-d '{"domain":"yourstore.com"}' > acp.json
SCORE=$(jq -r '.score' acp.json)
GRADE=$(jq -r '.grade' acp.json)
echo "ACP readiness: $GRADE ($SCORE/100)"
[ "$SCORE" -ge 70 ] || {
jq -r '.recommendations[]? | "[\(.priority)] \(.title) - \(.action)"' acp.json
exit 1
}
One trap worth knowing: the endpoint answers 200 OK even when a domain fails, with "ok": false in the body. A reflexive curl --fail gate would pass every grade, including an F. Read .score or .grade from the response, not the HTTP status.
The response also carries score_breakdown across platform, payment, policies, and security, plus recommendations as structured objects with priority, title, description, and action - which is why the snippet above prints something a developer can act on rather than a wall of JSON.
Where to set the bar
Resist the urge to start at fail-on-grade: 'A'. You will spend a week fighting your own pipeline and then disable the check, which is worse than never adding it.
Start where you are. Run the validator once, take the grade it gives you, and set the threshold one notch below. Now the build fails only on regression, which is the thing you actually care about. Ratchet the bar upward as you fix the backlog. A gate that fires on real regressions and stays quiet otherwise is a gate people keep.
For ACP, 70 is a reasonable opening threshold. For UCP, fail-on-grade: 'C' catches the profile-level breakage without blocking on Schema.org polish.
Getting started
- Add the action, or the
npxone-liner, to a workflow you already run - Point it at staging, not production
- Set a threshold one notch below your current grade
- Add the ACP curl step if you care about ChatGPT checkout
- Make the deploy job
needs:the validation job
Copy-paste recipes for GitHub Actions, GitLab CI, CircleCI, npx, and the ACP endpoint live at ucptools.dev/ci. If you would rather call validation from your own code than from a CI step, the API and npm reference covers the public REST endpoints and the typed package.
The point is not the score. The point is that a profile which silently stopped resolving should break your build, not your revenue.
UCPtools is an independent community tool. UCP is an open standard by Google and Shopify; ACP is an open standard by OpenAI and Stripe. We are not affiliated with any of them.
Top comments (0)