Skip to content

feat(mcp): generate config packs for MCP clients #645

feat(mcp): generate config packs for MCP clients

feat(mcp): generate config packs for MCP clients #645

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
# Cancel superseded runs on the same ref (e.g. rapid PR pushes); never cancel
# on main so every landed commit is fully verified (issue #474).
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
# fail-fast disabled so every Python cell reports its own status even
# when one version fails — important now that the matrix spans the full
# supported range (issue #339).
fail-fast: false
matrix:
# Supported range 3.10–3.13 inclusive (issue #339). All cells are
# gating: a release is only claimed as supported once its cell is green.
# 3.14 is deliberately excluded for now — the heavy dev/adapter stack
# (crewai, mem0ai, fastmcp, langgraph, langchain-core) still caps at
# Requires-Python <3.14, so the [dev,langchain] suite cannot resolve on
# 3.14. Tracked for re-add once those upstreams ship 3.14 wheels.
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip packages
uses: actions/cache@v5
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}
restore-keys: |
${{ runner.os }}-pip-${{ matrix.python-version }}-
${{ runner.os }}-pip-
- name: Install dependencies
run: pip install -e ".[dev,langchain]"
- name: Format check
run: ruff format --check src/ tests/ examples/ scripts/
- name: Lint
run: ruff check src/ tests/ examples/ scripts/
- name: Type check
# examples/ and scripts/ are type-checked alongside src/ — they are the
# most-copied code in the repo and back the gating CI scripts (#539).
run: mypy src/ examples/ scripts/
- name: Test
run: pytest --cov=contextweaver --cov-report=term-missing -q
- name: Examples
run: make example
- name: Demo
run: make demo
- name: "Generated-artifact drift gate (gating, issues #522/#518)"
if: ${{ matrix.python-version == '3.12' }}
# One gate over every committed generated artifact — schemas,
# scorecards, recorded demos, llms.txt, the context-rot SVG, and the
# public-API manifest — via the shared harness (issue #522). The
# artifacts are deterministic and interpreter-independent, so one matrix
# cell enforces them. Runs before the benchmark step below, which
# overwrites benchmarks/results/latest.json with this runner's numbers
# (the scorecard gate reads the committed file).
run: make drift-check
- name: "Module-size convention gate (gating, issue #456)"
if: ${{ matrix.python-version == '3.12' }}
run: make module-size-check
- name: "Doc-snippet execution gate (gating, issue #526)"
if: ${{ matrix.python-version == '3.12' }}
run: make doc-snippets-check
- name: README version drift check (gating, issue #347)
# Fails when a tracked README version reference (the "Current package
# version" line or the comparison-table self-reference) lags the
# version in pyproject.toml. Stdlib-only; no install required.
run: python scripts/check_readme_version.py
- name: Security-policy drift check (gating, issue #691)
# Fails when SECURITY.md's supported-version table drifts from the
# package version in pyproject.toml, or when a relative link it
# references no longer resolves. Stdlib-only; no install required.
run: python scripts/check_security_policy.py
- name: Weaver-spec conformance
# Round-trip + JSON-Schema validation against the canonical contracts
# published at https://weaver-spec.dev/contracts/v0/. Gating because
# weaver_contracts + jsonschema ship in [dev] (issues #143, #145).
run: |
mkdir -p .weaver-schemas
for s in routing_decision choice_card selectable_item frame; do
curl -fsSL --retry 3 --retry-delay 2 "https://raw.githubusercontent.com/dgenio/weaver-spec/main/contracts/json/${s}.schema.json" \
-o ".weaver-schemas/${s}.schema.json"
done
python scripts/weaver_spec_conformance.py --schemas-dir .weaver-schemas
- name: "Sidecar HTTP smoke (non-gating, issue #678)"
if: ${{ matrix.python-version == '3.12' }}
continue-on-error: true
# Spins the stdlib HTTP sidecar up in-process and drives /v1/route +
# /v1/compact over loopback. Deterministic, credential-free, no network.
run: make sidecar-smoke
- name: "Smoke evaluation (non-gating, issue #392)"
if: ${{ matrix.python-version == '3.12' }}
continue-on-error: true
# Deterministic and credential-free by default. Keep this informational:
# it exercises the evaluation path continuously but is not a release gate.
run: python benchmarks/smoke_eval.py
- name: Benchmark (informational, non-gating)
continue-on-error: true
run: python benchmarks/benchmark.py
floor-deps:
# Floor-deps proof (issue #356). A normal CI run resolves the *latest*
# dependencies, so it never proves the declared ``>=`` lower bounds are
# truthful. This job installs the *minimum* declared direct versions via
# ``uv pip install --resolution lowest-direct`` and runs the full suite on
# Python 3.10 (the floor interpreter). Gating: if a declared floor is
# secretly too low, this job goes red and the floor must be bumped (e.g.
# the ``pytest-asyncio>=0.23.8`` bump that landed with this job).
name: Floor deps (lowest-direct, 3.10)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install minimum declared dependency versions
# ``--resolution lowest-direct`` pins each *direct* dependency to its
# declared floor while letting transitive deps resolve normally — the
# canonical way to prove ``>=X`` bounds without hand-maintaining a
# constraints-min.txt.
run: uv pip install --system --resolution lowest-direct -e ".[dev,langchain]"
- name: Test (floor versions)
# No ``--cov`` here: this job exists to prove the *runtime* dependency
# floors are truthful (the suite passes at the declared minimums), not
# to measure coverage — the matrix ``test`` job does that at latest
# deps. Running coverage here also trips a spurious
# statement-vs-branch combine error from subprocess-spawning tests.
run: pytest -q
tool-run-smoke:
# Zero-install distribution check (#437). Build the wheel once per OS,
# then run its console entry point in isolated uvx / pipx environments.
# This catches packaging or script-entry regressions that an editable
# install can hide.
name: Tool-run smoke (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install pipx
run: python -m pip install pipx
- name: Build wheel
run: uv build --wheel
- name: Locate wheel
id: wheel
shell: bash
run: echo "path=$(find dist -name '*.whl' -print -quit)" >> "$GITHUB_OUTPUT"
- name: Smoke test via uvx
run: |
uvx --isolated --no-config --from "${{ steps.wheel.outputs.path }}" \
contextweaver demo --scenario killer
uvx --isolated --no-config --from "${{ steps.wheel.outputs.path }}" \
contextweaver mcp serve \
--catalog examples/architectures/mcp_context_gateway/real_catalogs/filesystem.json \
--dry-run
- name: Smoke test via pipx
run: |
pipx run --no-cache --spec "${{ steps.wheel.outputs.path }}" \
contextweaver demo --scenario killer
pipx run --no-cache --spec "${{ steps.wheel.outputs.path }}" \
contextweaver mcp serve \
--catalog examples/architectures/mcp_context_gateway/real_catalogs/filesystem.json \
--dry-run
benchmark-comment:
# Sticky regression-feedback comment (issue #211). Runs on a single
# Python version after the matrix test job so we only post one comment
# per PR push, not one per matrix cell. Continues on error: comment
# failure must not gate the PR.
name: Post benchmark delta PR comment
needs: test
runs-on: ubuntu-latest
timeout-minutes: 20
if: ${{ github.event_name == 'pull_request' }}
permissions:
pull-requests: write
contents: read
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install -e ".[dev]"
- name: Generate delta
id: delta
continue-on-error: true
run: |
python benchmarks/benchmark.py --matrix --output benchmarks/results/head.json
cp benchmarks/results/latest.json benchmarks/results/base.json
python scripts/benchmark_delta.py \
--base benchmarks/results/base.json \
--head benchmarks/results/head.json \
--output benchmarks/results/delta.md
- name: Find existing comment
# ``peter-evans/find-comment`` scans the PR's comments for ours via
# the HTML marker the delta renderer plants at the top of the body
# (``scripts/benchmark_delta.py:COMMENT_MARKER``). Returns the
# comment id when found, empty otherwise.
id: find
if: ${{ steps.delta.outcome == 'success' }}
uses: peter-evans/find-comment@v3
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: "github-actions[bot]"
body-includes: "<!-- contextweaver:benchmark-delta -->"
- name: Post or update sticky comment
# Sticky semantics (#211): one comment per PR, updated in place on
# every push. Falls through silently if the delta step failed —
# benchmark feedback never gates the PR.
if: ${{ steps.delta.outcome == 'success' && hashFiles('benchmarks/results/delta.md') != '' }}
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.find.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body-path: benchmarks/results/delta.md
edit-mode: replace
docs-build:
# Gate the docs build on PRs (issue #474). docs.yml only builds+deploys on
# push to main, so a malformed docstring or broken nav could land on main
# and break the auto-generated API reference before anyone saw it. This job
# builds the site (no deploy) with the same command docs.yml uses, so PR
# docs breakage fails here first. (Not --strict: the repo has pre-existing
# cross-page anchor warnings tracked separately; --strict can be adopted
# once those are cleaned up.)
name: Docs build
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install package and docs dependencies
run: pip install -e ".[docs]"
- name: Build docs
run: mkdocs build --clean