Skip to content

feat(openapi): generated MCP/Python contract types + drift CI (#37 phase 2)#48

Merged
linfangw merged 3 commits into
mainfrom
feat/issue-46-openapi-type-gen
May 18, 2026
Merged

feat(openapi): generated MCP/Python contract types + drift CI (#37 phase 2)#48
linfangw merged 3 commits into
mainfrom
feat/issue-46-openapi-type-gen

Conversation

@linfangw
Copy link
Copy Markdown
Contributor

Closes #46. Implements Phase 2 of #37 (follow-up to #44 / Phase 1).

What

Generate checked-in type artifacts from the website-mirrored docs/openapi/qveris-public-api.openapi.json, as a contract reference — the hand-written public MCP/SDK models are untouched.

  • MCP / TypeScript: openapi-typescript@7.4.4 (pinned devDep) → packages/mcp/src/generated/openapi.d.ts. Regenerate with npm --prefix packages/mcp run gen:openapi.
  • Python SDK: datamodel-code-generator==0.26.3 (pinned dev extra) → packages/python-sdk/qveris/generated/openapi_models.py (pydantic v2, py3.8 target) + generated/__init__.py documenting regeneration.
  • CI: .github/workflows/openapi-types.yml re-runs both pinned generators and fails on git diff --exit-code, path-scoped to docs/openapi/** + the generators (mirrors openapi-contract.yml from ci: OpenAPI contract validation (#37 phase 1) #44).

Non-goals (Phase 3 / #47)

No adoption of the generated types into runtime code. MCP, CLI, and Python SDK public APIs are unchanged and backward compatible.

Verification

  • Byte-reproducible: regenerating both from the checked-in spec yields an empty git diff (so the drift gate is stable).
  • packages/mcp/src/generated/openapi.d.ts typechecks under tsc --strict --skipLibCheck.
  • openapi_models.py passes py_compile.
  • Pinned generator versions guarantee CI regen matches the checked-in artifacts.

…se 2)

Implements phase 2 of #37 / closes #46. Generated artifacts are a contract
reference; hand-written MCP/SDK public models are unchanged (no runtime or
package behavior change — backward compatible).

- MCP/TS: `openapi-typescript@7.4.4` (pinned devDep) -> checked-in
  packages/mcp/src/generated/openapi.d.ts; `npm run gen:openapi` regenerates.
- Python SDK: `datamodel-code-generator==0.26.3` (pinned dev extra) ->
  checked-in packages/python-sdk/qveris/generated/openapi_models.py
  (pydantic v2, py3.8 target) + package __init__ documenting regeneration.
- CI: .github/workflows/openapi-types.yml regenerates both with the pinned
  generators and fails on `git diff --exit-code`, path-scoped to
  docs/openapi/** + the generators (mirrors openapi-contract.yml from #44).

Verified: artifacts are byte-reproducible from the checked-in spec
(empty diff on regen), the .d.ts typechecks under tsc --strict, and the
Python models pass py_compile.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces automated generation of OpenAPI contract models for both the MCP package and the Python SDK. In the MCP package, a new gen:openapi script and the openapi-typescript dependency were added to generate TypeScript definitions. For the Python SDK, datamodel-code-generator was integrated to produce Pydantic models, accompanied by documentation on regeneration and CI drift prevention. Review feedback suggests appending a formatting step to the TypeScript generation script to ensure the output adheres to the project's 2-space indentation standard.

Comment thread packages/mcp/package.json Outdated
linfangw and others added 2 commits May 18, 2026 14:54
* feat(contract): adopt generated OpenAPI types — CLI + Python guards (#37 phase 3)

Implements phase 3 of #37 / closes #47. First low-risk adoption step;
public MCP/CLI/Python SDK APIs unchanged (additive tests + CI only).

- CLI (JS/MJS): packages/cli/test/openapi-contract.test.mjs asserts every
  endpoint+method the CLI calls (kept in sync with src/client/api.mjs:
  POST /search, /tools/by-ids, /tools/execute; GET /auth/credits,
  /auth/usage/history/v2, /auth/credits/ledger) exists in the mirrored
  public OpenAPI spec — the issue's "CLI contract tests before type gen".
- Python SDK: tests/test_openapi_contract.py starts *consuming* the
  generated qveris.generated.openapi_models as a drift guard (core contract
  models present + pydantic + smoke roundtrip). Hand-written qveris.types
  stays the public surface; field alignment stays gradual by design.
- CI: .github/workflows/contract-tests.yml runs both on PRs (the existing
  cli/python *-publish workflows only fire on release tags, so without this
  the guards would not gate PRs).

Verified locally: CLI `node --test` 7/7 pass; Python pytest 11/11 pass.
Builds on #46/#48 (needs the generated artifacts) — merge after #48.

* fix(contract): load generated models standalone + robust shape check (#47)

CI python-contract failed: importing qveris.generated.openapi_models pulled
qveris/__init__.py (httpx not installed in the minimal contract job). Load the
generated file by path so the guard only needs pydantic. Replace the
instantiation smoke with a model_fields shape assertion (the generated file's
__future__ annotations + constrained types need model_rebuild() to validate;
the field map is the stable contract signal regardless).
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@linfangw linfangw merged commit cafdd40 into main May 18, 2026
3 checks passed
@linfangw linfangw deleted the feat/issue-46-openapi-type-gen branch May 18, 2026 07:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci/types: OpenAPI-derived type generation (#37 phase 2)

2 participants