Skip to content

feat(dist): registry manifests + discoverability for the MCP bridge#7

Merged
abrichr merged 2 commits into
mainfrom
feat/distribution-and-discoverability
Jul 21, 2026
Merged

feat(dist): registry manifests + discoverability for the MCP bridge#7
abrichr merged 2 commits into
mainfrom
feat/distribution-and-discoverability

Conversation

@abrichr

@abrichr abrichr commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

PART 1 of the MCP + Agent Skills bridge (the openadapt-agent serve MCP server and emit-skill emitter over governed openadapt-flow bundles) is already complete on main (PR #2) and verified here end-to-end against the real flow CLI: emit-skill produces flow's skill folder + MCP/halt appendix; tools/list exposes list_workflows/get_workflow/get_run_report + run_<slug>; and a real governed run_demo_triage returns a structured refused (exit 2, real gate coverage report) — never a fabricated success.

This PR adds PART 2: distribution & discoverability, which was the actual gap.

What's added

  • server.json — official MCP registry manifest (PyPI package, uvx runtime hint, stdio transport, required --bundles, secret OPENADAPT_BUNDLE_KEY). Validated against the live 2025-12-11/server.schema.json.
  • smithery.yaml — Smithery stdio startCommand with a bundlesDir/allowRun/bundleKey config schema and a commandFunction (verified by evaluating it in node).
  • llms.txt — link-first summary for AI assistants (GEO).
  • docs/DISTRIBUTION.md — the public-capability-vs-private-bundle distinction, canonical registry metadata block, and a step-by-step founder-action submission plan (PyPI publish, official registry, Smithery, mcp.so, Glama, PulseMCP, Agent Skills directories). These mint public first-party identities and are deliberately not automated.
  • READMEuvx quickstart + "Install as an MCP server (registries)" section; cross-links docs.openadapt.ai.
  • tests/test_distribution.py — pins version consistency (server.json ↔ pyproject ↔ __version__), asserts registry-launch is read-only by default (execution requires operator --allow-run), guards the smithery wiring and the OPENADAPT_BUNDLE_KEY-is-secret invariant. Adds pyyaml to the dev extra.

Key design decision

Read-only by default; private bundles never shipped. A one-click registry install exposes only inspection tools; run_* requires the operator to add --allow-run. A user's compiled workflow bundle is their private artifact, supplied at launch via --bundles, and is never embedded in the package, server.json, or any registry listing.

Verification

  • ruff check src tests clean; pytest 35 passed (28 existing + 7 new).
  • server.json validated against the live MCP registry schema.
  • Bridge re-verified end-to-end against the real openadapt-flow CLI (emit-skill, MCP tools/list, governed run → structured refused).

Founder actions (not performed here)

Publishing to PyPI and submitting to each registry are outward-facing founder actions listed in docs/DISTRIBUTION.md §3. Pick the final release version (e.g. 2.0.0rc1) at publish time — the CI guard keeps the three version fields in lockstep.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM

abrichr and others added 2 commits July 21, 2026 00:50
Make the openadapt-agent MCP server easily installable and discoverable,
and document the public-capability vs private-bundle boundary. PART 1 (the
bridge itself) is already complete on main; this adds the distribution
surface.

- server.json: official MCP registry manifest (PyPI package, uvx runtime
  hint, stdio transport, --bundles arg, OPENADAPT_BUNDLE_KEY secret).
  Validated against the live 2025-12-11 server.schema.json.
- smithery.yaml: Smithery stdio startCommand with a bundlesDir/allowRun/
  bundleKey configSchema and a commandFunction (verified in node).
- llms.txt: link-first summary for AI assistants.
- docs/DISTRIBUTION.md: public-vs-private-server distinction, canonical
  registry metadata, and a founder-action submission plan (PyPI, official
  registry, Smithery, mcp.so, Glama, PulseMCP, Agent Skills directories).
- README: uvx quickstart + "Install as an MCP server (registries)"
  section; cross-links docs.openadapt.ai.
- tests/test_distribution.py: pin version consistency across server.json /
  pyproject / __version__, assert the registry launch is read-only by
  default (execution requires operator --allow-run), and guard the
  smithery wiring. Adds pyyaml to the dev extra.

Read-only-by-default is a security invariant: a one-click registry
install exposes only inspection tools; run_* requires --allow-run. A
user's compiled bundle is their private artifact and is never embedded in
the package or any registry listing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM
…ecrets)

Add a tag/release-gated release pipeline so the founder-action publish
plan is as automated as is safely possible, without ever firing a publish
from a normal push/PR.

.github/workflows/release.yml:
- validate job (runs on PRs, workflow_dispatch, and every trigger): builds
  sdist+wheel, runs the license/package-boundary gate on the BUILT
  archives, `twine check`, validates server.json against its live schema,
  and runs the version-consistency guard. No publish -> the pipeline is
  testable via PR/dispatch.
- pypi-publish job (ONLY on a vX.Y.Z tag or a published Release): asserts
  the tag matches the package version, then uploads via PyPI Trusted
  Publishing (OIDC) in the `pypi` environment. Token fallback wired behind
  a repo variable and fails LOUD (naming PYPI_API_TOKEN) if selected-unset.
- mcp-registry-publish job (tag/release only): waits for the new version
  to be live on PyPI (the registry validates package existence), then
  `mcp-publisher login github-oidc` + publish. PAT fallback wired and
  fails loud (naming MCP_GITHUB_TOKEN) if selected-unset.
- concurrency guard; least-privilege permissions; id-token only where OIDC
  is used.

scripts/check_release_artifacts.py: the license/boundary gate. Inspects
the actual wheel+sdist and fails if any member is a bundle (workflow.json),
an .enc artifact, run outputs, a key/.env/db, or a non-code wheel payload.
Enforces AGENTS.md 4.1/4.2 (never ship a user's bundle or crown-jewel
data). Verified to pass on the real archives and to reject an injected
bundle.

docs/DISTRIBUTION.md: rewrote section 3 into AUTOMATED (fires on release)
vs founder one-time actions, with the exact repo config. OIDC path needs
ZERO repo secrets: one-time PyPI Trusted Publisher config + a `pypi` GH
environment; MCP registry is authorized by the repo's own OIDC identity.
Optional token fallbacks name their required secret. Smithery claim /
mcp.so / PulseMCP / Agent Skills listings remain one-time manual actions
(no publish API); Glama auto-indexes.

README: note the automated, secret-free release pipeline.

No publish is triggered from this branch. actionlint clean; 35 tests pass;
ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM
@abrichr

abrichr commented Jul 21, 2026

Copy link
Copy Markdown
Member Author

Update: release automation added (commit 58aef48)

Automated the founder-action publish plan as far as is safe — no publish ever fires from a push/PR, only on a deliberate vX.Y.Z tag or a published GitHub Release.

New .github/workflows/release.yml:

  • validate (runs on PRs, workflow_dispatch, and every trigger): builds sdist+wheel, runs the license/package-boundary gate on the built archives, twine check, server.json live-schema validation, and the version-consistency guard. Dry run — no publish. This ran green on this very PR (see the "Build + validate (dry run, no publish)" check); the two publish jobs correctly show skipping.
  • pypi-publish (tag/release only): asserts tag == package version, then PyPI Trusted Publishing (OIDC) in the pypi environment.
  • mcp-registry-publish (tag/release only): waits for the version to be live on PyPI, then mcp-publisher login github-oidc + publish.

New scripts/check_release_artifacts.py: the license/boundary gate — inspects the actual wheel+sdist and fails if any member is a bundle (workflow.json), .enc, run output, key/.env/db, or a non-code wheel payload. Verified to pass on the real archives and to reject an injected bundle.

Secrets: NONE in the OIDC path. One-time founder setup: register the PyPI Trusted Publisher (OpenAdaptAI/openadapt-agent/release.yml/env pypi) and create the pypi GitHub environment; the MCP registry is authorized by the repo's own OIDC identity. Optional token fallbacks (PYPI_API_TOKEN behind vars.PYPI_PUBLISH_METHOD=token; MCP_GITHUB_TOKEN behind vars.MCP_PUBLISH_METHOD=token) fail loud, naming the missing secret.

Still manual (one-time claims, no publish API): Smithery "Add Server" (then it auto-reads smithery.yaml on push), mcp.so + PulseMCP submission forms, Agent Skills first-party listing. Glama auto-indexes.

actionlint clean; 35 tests pass; ruff clean.

@abrichr
abrichr merged commit 37ae5a8 into main Jul 21, 2026
10 checks passed
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.

1 participant