fix(schema): publish UCP annotated schemas#125
Conversation
Remove the pre-generated spec/ directory (95 JSON files, ~7,200 lines) and resolve schemas at documentation build time using the ucp-schema CLI. Changes: - Delete spec/ directory and generation scripts (generate_schemas.py, schema_utils.py, validate_specs.py) - Update main.py to call `ucp-schema resolve` with caching - Add polymorphic type detection for correct anchor generation - Update CI workflow to run `ucp-schema lint source/` - Update CONTRIBUTING.md with new development workflow The source/ directory is now the single source of truth. Schema resolution happens on-demand during mkdocs build, with results cached per-schema. Trade-offs: - (+) Single source of truth in source/ directory - (+) No drift between source and generated schemas - (+) 7,200 fewer lines to maintain - (+) Simpler workflow (no generate step before build) - (-) Runtime dependency on ucp-schema CLI
Rewrites $id and $ref URLs to include version path so schemas resolve
correctly after mike deploys to /{version}/ paths.
Version handling:
- Date config (YYYY-MM-DD): used for both URL path and version field
- Non-date config (e.g., 'draft'): URL uses literal, version field
gets today's date (publish date while marking as draft)
Changes:
- Add _rewrite_version_urls() to inject version into $id/$ref URLs
- Add _set_schema_version() to update capability version fields
- Remove redundant _publish_versioned_schemas() (was duplicating output)
- Set ucp_version to 'draft' for main branch builds
Example output for draft build:
$id: https://ucp.dev/draft/schemas/shopping/checkout.json
version: 2026-01-26
$ref: https://ucp.dev/draft/schemas/shopping/types/buyer.json
hooks.py: - Restructure version logic to make invariant explicit (url_version = ucp_version always when config exists) - Replace string slicing with removeprefix() for clarity main.py: - Remove duplicate _resolve_with_ucp_schema() inner function - Delegate to module-level _resolve_schema() instead
Remove silent fallbacks that would render raw JSON with ucp_request annotations when ucp-schema CLI is unavailable or fails. Now displays visible error messages in docs with install instructions. Previously, if ucp-schema wasn't installed, the build would "succeed" but produce incorrect documentation with unprocessed annotations.
drewolson-google
left a comment
There was a problem hiding this comment.
This effectively solves the schema versioning problem, great work.
Two things to discuss at TC:
-
This requires yet another language toolchain to be locally installed when working on UCP. We would now require
python,node, andrust, along with other tools for running linting and other checks mirroring Github actions. Let's make sure the benefit of introducing another toolchain outweighs the costs. -
This makes ucp-schema a direct dependency of working on UCP. This means that we'll need to effectively be able to contribute to this tool if there are issues or bugs. While it removes a large amount of code from this repository, it does spread some of that logic of two repositories for the future contributors to this project.
.github/workflows/docs.yml
Outdated
| if: steps.spec_files_changed.outputs.any_changed == 'true' | ||
| if: steps.source_files_changed.outputs.any_changed == 'true' | ||
| run: | | ||
| chmod +x scripts/ci_check_models.sh |
There was a problem hiding this comment.
We should be able to check the executable bit into git so we don't have to run this every time.
|
@drewolson-google ack and +1 on toolchain. I reached for Rust to make validator a self-contained binary that you can wget and not worry about installing an entire ecosystem — e.g. build artifacts that can be used directly. It makes authoring the validator harder, but that's pain and complexity that's localized to the ucp-schema devs, that everyone else does not and won't need to feel. If we go forward with this path, I'd also suggest we adopt ucp-schema as official tool under UCP org -- happy to contribute and move it to the official repo. |
hooks.py
Outdated
|
|
||
| def _set_schema_version(data, version): | ||
| """Set version field in capability schemas (top-level 'version' property).""" | ||
| if "version" in data: |
There was a problem hiding this comment.
Would it make sense to remove version from the source file completely, and add it here for all files?
Remove version from source schema files. Build now injects version based on branch config (date-based) or today's date (non-date branches). Condition changed from "has version" to "has name" - named entities (capabilities, services, handlers) require version per entity meta-schema.
Integrate uv dependency management (PR #126) with ucp-schema toolchain: - CONTRIBUTING.md: merged uv + ucp-schema development workflow - Deleted generate_schemas.py, validate_specs.py (replaced by ucp-schema) - Deleted requirements-docs.txt (replaced by uv sync) Additional fixes: - playground.md: versioned URLs pointing to 2026-01-23 - spec docs: removed hardcoded Version lines (now in URL/header only)
The ci_check_models.sh script was designed for the old split-schema workflow (generate_schemas.py → spec/). It validated that datamodel-code-generator could parse the pre-split schemas. We need to rethink this in light of new shape. Removing, we can bring it back in separate PR.
- Disable VALIDATE_SPELL_CODESPELL in super-linter (cspell already
runs via spellcheck.yaml with better acronym handling)
- Fix typo: wrappper → wrapper in .gitignore
The generate_ts_schema_types.js script still referenced the removed spec/ directory. Updated to use source/ which is where schemas now live after the migration in Universal-Commerce-Protocol#125. Fixes Universal-Commerce-Protocol#150 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The generate_ts_schema_types.js script still referenced the removed spec/ directory. Updated to use source/ which is where schemas now live after the migration in Universal-Commerce-Protocol#125. Fixes Universal-Commerce-Protocol#150
Our contract is that UCP schemas are self-describing. We define and advertise one URL in capability metadata, which agents and servers can fetch and validate against. At the moment, this contract is broken.
A schema yields different shapes based on operation (e.g., create vs update vs complete) and direction (request vs response). We encode these rules via
ucp_request/ucp_responseannotations in oursource/*schemas, but then run a preprocessor that splits them into N permutations based on operation × direction (e.g.,checkout.create_req.jsonmaps to create + request). The schema URL we advertise in UCP metadata doesn't contain all the necessary information, and we've defined a convention that off-the-shelf schema validators are not aware of and would need to be taught to understand. As is, this makes UCP validation very hard to implement.Proposed desired state
By count and burden: servers > agents > validators. If we require that servers dynamically resolve and return operation-specific schema refs, that's complexity multiplied across every UCP business / implementer. Same for agents, who would need to vary their profile per operation — clunky. We can eliminate and shift all of this complexity into validators: restore the single file contract, a ref to which can be passed in both directions by servers+agents, and lean on UCP-aware tooling.
Recommendation
ucp_request/ucp_responseannotations intact/spec/schemas/directoryPrototype validator: ucp-schema
The validator is written in Rust for portability & re-use. We can use it for our build process, and we can provide it as official binary for validating schemas for capability developers, and for agent+server developers to validate payloads on the wire — see docs on above repo for linting, resolving, validation, etc.
This PR
Big diff, but mostly deletions and simplification of the build pipeline in favor of using the shared ucp-schema validator.
ucp-schema, deprecates Python preprocessorsourceschemas with annotations intact (non-breaking: standard validators ignore unknown keywords)ucp-schema lint+ucp-schema validateBig picture changes
1. Runtime schema resolution via
ucp-schemaCLI (main.py)ucp-schema resolveduring mkdocs build2. Versioned schema publishing (
hooks.py)/schemas/{version}/during build$idand$refURLs to include version pathversionfield in capability metadata2026-01-26) vs. named versions (draft)3. CI integration (
.github/workflows/docs.yml)ucp-schemaviacargo installucp-schema lint source/before buildBefore/After
Single schema URL, versioned. All site-generation markdown logic remains functional & intact.
Type of change
Please delete options that are not relevant.
Checklist