Skip to content

v1.0.0 feat(mcp-registry): #201 PR 5 of 5 HTTP install/uninstall + spec/36 LOCK#334

Merged
dep0we merged 4 commits into
mainfrom
feat/201-pr5-http-write-spec36-lock-v1
Jun 4, 2026
Merged

v1.0.0 feat(mcp-registry): #201 PR 5 of 5 HTTP install/uninstall + spec/36 LOCK#334
dep0we merged 4 commits into
mainfrom
feat/201-pr5-http-write-spec36-lock-v1

Conversation

@dep0we
Copy link
Copy Markdown
Owner

@dep0we dep0we commented Jun 4, 2026

Summary

Final PR of the MCPServerRegistryBackend arc (#201) and the v1.0.0 release candidate. Closes the v1.0 Protocol surface: twelve of twelve backend protocols shipped.

Feature (commit 4464c2b):

  • HTTPMCPServerRegistryBackend.install(spec) posts to /mcp-servers with the spec body, projects MCPServerRef from input, maps 409 to MCPServerAlreadyInstalled, 405 to _handle_tier_regression raising NotImplementedError with operator-readable tier-change wording.
  • HTTPMCPServerRegistryBackend.uninstall(name) deletes via /mcp-servers/<name>, 204 on success or absence per MUST 9 idempotency, 405 to tier regression handler.
  • _handle_tier_regression(operation) re-probes outside the cache lock, updates capabilities, raises NotImplementedError. Re-probe failure raises MCPRegistryUnavailable instead of swallowing.
  • install() rejects literal env values with ValueError BEFORE any network call so the load_mcp_server -> install pipeline cannot exfiltrate resolved secrets via the POST body. Callers MUST pass unresolved $VAR refs.
  • MCPServerSpec.__repr__ redacts env contents defensively.
  • MCPServerRef.source keeps the raw catalog URL per spec/36 line 228 with an operator-facing docstring warning.
  • CLI handler catches NotImplementedError from tier-1 / tier-regression so it surfaces as readable output.

Spec + docs (commit df59918):

  • spec/36-mcp-server-registry-backend.md LOCKED at v1.0: DRAFT marker removed; Install/uninstall semantics (HTTP) section added; exception table extended with POST 409 / 405 and DELETE 204 / 405; Decision 5 wording corrected; baseline test counts corrected; inline TODO removed; env-var input contract documented; tier-2 re-probe-after-405 inconsistent-server contract documented.
  • spec/19-mcp.md cross-link updated DRAFT to LOCKED at [backend] MCPServerRegistryBackend — unify MCP server discovery across agents #201 PR 5; addendum for resolve_env=False.
  • release-runbook.md gains pre-publish smoke pipeline + TestPyPI smoke + rollback contract (yank semantics).
  • versioning.md gains a Protocol-surface breaking-change policy.
  • _locks.py shim sunset deferred from v1.0 to v1.1; markers + docstrings + warning text updated; spec/21 + disaster-recovery.md refreshed.

Release ceremony (commit 4319f1e):

  • pyproject.toml version 0.13.0 to 1.0.0; classifier Alpha to Production/Stable; twine>=6.0 in dev deps; sdist excludes for tests/docs/extras/.claude/.github.
  • atomic_agents/__init__.py version bumped in lockstep.
  • README badge 0.13.0 to 1.0.0; Twelve of twelve flip; backend table row to Shipped; pip install / uv add Quick Start; 45 relative-to-absolute GitHub URL rewrites for PyPI rendering.
  • CLAUDE.md architecture diagram MCPServerRegistry green; 12th backend lock-paragraph; where-things-live count flip 31 locked + 4 drafts to 32 locked + 3 drafts.
  • CHANGELOG.md [Unreleased] renamed to [1.0.0] - 2026-06-04 with PR 4 + PR 5 entries; fresh empty [Unreleased] above.
  • .github/workflows/publish-to-pypi.yml NEW: strict 3-component SemVer tag trigger only; uv build + twine check + uv publish via UV_PUBLISH_TOKEN.
  • .github/workflows/test.yml: --extra http added so CI validates the [http] optional dependency declaratively.

Doc drift sweep (commit 9706692):

  • docs/methodology.md: eleven to twelve; MCPServerRegistryBackend added to shipped list; 2937+ to 3270+ tests.
  • CONTRIBUTING.md: test count 2937 to 3270+.
  • docs/getting-started.md: stale install-verify step 296 passed (v0.1.0 era) to 3270 passed.

Test Coverage

87% on PR 5 surface (13 of 15 tracepoints covered with dedicated tests; 2 known gaps documented).

install() write path
  [PASS] POST 201 happy path -> MCPServerRef projection
  [PASS] POST 409 -> MCPServerAlreadyInstalled (with spec.name in message)
  [PASS] POST 405 -> re-probe tier-1 -> NotImplementedError
  [PASS] POST 405 -> re-probe fails -> MCPRegistryUnavailable (cache may be stale)
  [PASS] POST 405 -> re-probe still tier-2 -> NotImplementedError (inconsistent server)
  [PASS] Capability gate (tier-1) -> NotImplementedError, no network call
  [PASS] env literal value -> ValueError (refuse, no POST)
  [PASS] env dollar-var refs accepted
  [PASS] env empty dict accepted
  [PASS] env empty string value accepted
  [PASS] auth_token -> Bearer header on POST

uninstall() write path
  [PASS] DELETE 204 -> None
  [PASS] DELETE 204 (absent name) -> None (idempotent per MUST 9)
  [PASS] DELETE 405 -> tier regression -> NotImplementedError
  [PASS] Capability gate -> conformance test_capability_honesty_uninstall[http]

Defense-in-depth + conformance
  [PASS] _handle_http_error 409 branch (via install path)
  [PASS] MUST 4 credential redaction (parametrized: filesystem + http)
  [PASS] MUST 9 concurrent install atomicity (HTTP MockTransport with 409 simulation + threading.Lock)
  [PASS] MUST 10 post-install consistency (MockTransport mirrors POST into servers list)
  [GAP]  MCPServerSpec.__repr__ env redaction (logic tested indirectly via Security review verification)
  [GAP]  CLI NotImplementedError dispatch (handler tested via backend tests; no CLI-layer simulation)

Test delta: 3,307 to 3,326 collected (+19 net; 3,270 passing + 56 skipped, 0 regressions, 0 failures).

Pre-Landing Review

Ran a 10-reviewer army (5 specialists, 2 single-purpose subagents, 1 Claude adversarial, 1 Codex adversarial, 1 Codex structured). 60 findings total; 8 P1 + 3 em-dash project-rule violations + 2 user-decision items resolved inline before push.

P1 auto-fixes applied:

  • _handle_tier_regression except clause extended to catch MCPRegistryDescriptorInvalid (was bypassing the stale-cache path on malformed /capabilities re-probe responses).
  • _handle_http_error 409 branch now names spec.name in the MCPServerAlreadyInstalled message via the new installed_server_name parameter (was returning bare URL only).
  • Conformance MockTransport gains a threading.Lock around the check-and-set on the installed dict so the MUST 9 concurrent test is race-free (was relying on GIL behavior).
  • Conformance MockTransport POST handler appends installed servers to the readable list so MUST 10 post-install consistency stops passing vacuously.
  • .github/workflows/publish-to-pypi.yml tag pattern tightened from v1.* to strict 3-component SemVer (was matching v1.0.0-rc1 and similar).
  • .github/workflows/test.yml adds --extra http to CI uv sync (was relying on transitive mcp>=1.0.0 for httpx).
  • Stale "sunset planned for v1.0" text refreshed across spec/21, disaster-recovery.md, CLAUDE.md to v1.1 (matches the prior _locks.py shim deferral decision).
  • Three em-dash project-rule violations in PR 5 additions resolved (README PR 5 lines + CLAUDE.md 12th lock-paragraph; 45 replacements total).
  • HTTP write paths add explicit if resp.status_code != 201 / != 204 checks after raise_for_status() so non-contract 2xx responses (200 HTML interstitial, 202 async queue) fail loudly instead of silently reporting success.
  • CHANGELOG [1.0.0] structure cleaned up.

User-decision items resolved:

  • install() rejects literal env values with ValueError (upgraded from warn + proceed). Both adversarial reviewers BLOCKED on the warn-only design because it lets resolved secrets reach the catalog server's request body. Refusal at the API boundary is the v1.0 contract.
  • MCPServerRef.source keeps the raw catalog URL per spec/36 line 228, with operator-facing docstring warnings added to types.py and install() return docs.

Adversarial Review

Both Codex (codex review + codex exec) and Claude (subagent) ran in parallel.

Codex structured review: GATE PASS (0 P1 markers); 2 P2 findings on non-contract 2xx responses (silently treated as success). Both addressed inline with explicit status code checks.

Codex adversarial: BLOCK recommendation on env exfiltration path. 8 findings: env exfiltration via warn-only design (addressed: upgraded to refuse), MCPServerRef.source raw URL (addressed: docstring warnings), MCPServerSpec.repr command/args not redacted (out of scope for v1.0; documented as intentional), tier regression DescriptorInvalid catch (addressed), 409 message missing spec.name (addressed), PyPI workflow OIDC (deferred to v1.1 as documented), list_mcp_servers spec vs HTTP behavior carve-out (spec already documents this), spec validation gap (addressed with status checks).

Claude adversarial: BLOCK on Findings 1 (env exfiltration) and 3 (PyPI wildcard tag trigger). Both addressed inline. 10 findings total; all addressed or documented.

Cross-model synthesis: High-confidence findings (env exfiltration, MCPServerRef.source, tier regression catch, 409 message, PyPI wildcard, em-dashes, non-contract 2xx) all caught by 2+ reviewers and addressed before push.

Plan Completion

48 of 51 plan items DONE; 0 NOT DONE; 3 UNVERIFIABLE at PR-merge time (PyPI/TestPyPI publish, v1.0.0 git tag, GitHub Release creation; these execute post-merge per the release ceremony in docs/deployment/release-runbook.md).

All 6 pre-dispatch corrections applied (exception table rows, HTTP install/uninstall semantics section, Decision 5 wording, test count corrections, inline TODO removal, tier-2-still-supports clarification). All 13 D-PR5 implementation decisions applied. All 4 user-decision items resolved with Dan's prep-time picks (one PR shape, _locks deferred to v1.1, TestPyPI first, repr env redacted).

Documentation

Files updated post-PR-5-implementation

  • docs/methodology.md: backend protocol count corrected eleven to twelve; MCPServerRegistryBackend added to the shipped-protocols list in the intro paragraph; test count corrected 2937+ to 3270+.
  • CONTRIBUTING.md: test count corrected 2937 to 3270+ in the Tests section.
  • docs/getting-started.md: stale install-verify step corrected from 296 passed (written at v0.1.0) to 3270 passed.

Files already current (updated by PR 5)

README.md, CLAUDE.md, CHANGELOG.md, docs/spec/36-mcp-server-registry-backend.md, docs/spec/19-mcp.md, docs/spec/21-lock-backend.md, docs/deployment/release-runbook.md, docs/deployment/versioning.md, docs/deployment/disaster-recovery.md.

Documentation coverage

All shipped features (HTTP install/uninstall, 409 collision mapping, 405 mid-session tier regression handler, tier negotiation) are fully documented in spec/36, CHANGELOG, README, and CLAUDE.md. No critical gaps.

Version

Already bumped to 1.0.0 by PR 5; covers all changes in this branch.

Test plan

  • All pytest tests pass (3,270 passed + 56 skipped, 0 failures, 41 deprecation warnings unchanged from baseline)
  • Targeted MCP registry HTTP backend tests pass (84 tests in test_mcp_server_registry_http_backend.py + 72 in conformance)
  • In-branch test failure caught and fixed: test_legacy_agentlock_emits_deprecation_warning assertion updated v1.0 to v1.1
  • Spec/36 internal consistency: DRAFT marker removed; 3 remaining "DRAFT" tokens all in PR 1 historical context, not live status
  • 5-stream pre-prep + 10-reviewer ship pass + cross-model adversarial both completed
  • /document-release subagent updated 3 stale doc files
  • Post-merge: v1.0.0 git tag annotated from CHANGELOG [1.0.0] section
  • Post-merge: gh release create v1.0.0 from CHANGELOG
  • Post-merge: uv build + twine check + clean-venv install verification
  • Post-merge: TestPyPI smoke publish first per release-runbook.md
  • Post-merge: uv publish to real PyPI

Closes #201. Extends /ship streak to 12 consecutive clean ships.

🤖 Generated with Claude Code

Dan Powers and others added 4 commits June 4, 2026 13:29
…ion handler

Wires the HTTPMCPServerRegistryBackend write paths against a tier-2+ catalog
server: install(spec) posts to /mcp-servers (returning 201 with MCPServerRef
projection from input spec), uninstall(name) deletes (204 on success or
absence per MUST 9 idempotency). _handle_tier_regression() catches 405 on
either POST or DELETE, calls refresh_capabilities() outside the cache lock,
and raises NotImplementedError with operator-readable tier-change wording.

install() now REJECTS literal env values with ValueError before any I/O. The
load_mcp_server -> install pipeline would otherwise send resolved secrets in
the POST body. Callers MUST pass unresolved $VAR refs.

MCPServerSpec.__repr__ redacts env contents (defense-in-depth for any future
log site). MCPServerRef.source preserves the raw catalog URL per spec/36
line 228 with an operator-facing docstring warning to redact before logging.

CLI mcp-registry handler catches NotImplementedError so tier-1 / tier
regression surfaces as readable output rather than an unhandled exception.

Tests: +18 net (16 in http_backend covering POST 201/409/405-with-three
re-probe outcomes, DELETE 204/absent/405, capability gate, auth headers,
env validation x3, MUST 4 redaction, regression-guard C-F7; +2 in
conformance for MUST 4 parametrized credential redaction across both
backends). MockTransport upgraded to tier-2 with POST 201 + 409
simulation + DELETE 204 + servers-list mirroring for MUST 10.
… versioning policy

spec/36 LOCKED at PR 5: removes DRAFT marker, adds the missing
Install/uninstall semantics (HTTP) subsection mirroring the PR 3
filesystem section, extends the exception table with POST 409 / 405
and DELETE 204 / 405 rows, corrects Decision 5 wording so the
'static class default' phrase no longer contradicts Decision 6's
dynamic-per-tier contract, corrects baseline test counts (the prior
3,078 / 3,088 numbers were 219 off from reality), removes the inline
TODO comment, documents the new install() env-var input contract
(callers MUST pass unresolved $VAR refs), documents the tier-2
re-probe-after-405 inconsistent-server contract, and adds an
operator note for the MCPServerRef.source raw URL.

spec/19 cross-link updated from DRAFT to LOCKED at #201 PR 5; brief
addendum documents the resolve_env=False parameter on
parse_mcp_md_text for backend implementers.

release-runbook.md gains a pre-publish smoke pipeline (uv build +
twine check + clean-venv install + version assertion), a TestPyPI
smoke step (first publish ever for this project), and a rollback
contract (yank semantics) section. versioning.md gains a Protocol
surface breaking-change policy.

_locks.py deprecation shim sunset deferred from v1.0 to v1.1 per the
PR 5 release decision: sunset markers, docstrings, and warning
messages all updated. spec/21 + disaster-recovery.md text refreshed
to match. Lock integration test assertion updated to v1.1.
…pped)

Cuts the v1.0.0 release after twelve consecutive backend protocols
shipped across the protocol-pattern series. Spec/36 LOCKED in the
prior commit closes the v1.0 Protocol surface.

pyproject.toml: version 0.13.0 -> 1.0.0, classifier Alpha ->
Production/Stable, twine>=6.0 added to dev deps, [tool.hatch.build
.targets.sdist] excludes tests/ + docs/ + extras/ + .claude/ +
.github/ so the published sdist stays clean. atomic_agents/__init__.py
version bumped in lockstep.

README.md: version badge 0.13.0 -> 1.0.0 (alpha -> stable);
backend protocols table flips MCPServerRegistryBackend row to
Shipped; status block flips "Eleven of twelve" to "Twelve of
twelve"; Quick Start gains pip install + uv add paths above the
git clone path; 45 relative-to-absolute GitHub URL rewrites
across spec list, backend table, and image refs so the README
renders cleanly on PyPI (it bakes into the wheel at build time
and is not patchable post-publish).

CLAUDE.md: architecture diagram MCPServerRegistry emoji flips
yellow to green; 12th backend lock-paragraph added mirroring
the CorpusBackend precedent shape (covers Decision 1-9, the 10
MUSTs, capability evolution table, tier negotiation, what it
closes); where-things-live spec count flips from "31 locked + 4
drafts" to "32 locked + 3 drafts" with spec/36 dropped from the
draft enumeration; "Eleven backend protocols shipped" string
flipped to "Twelve" in two locations.

CHANGELOG.md: renames [Unreleased] section to [1.0.0] - 2026-06-04;
prepends the PR 5 entry above the existing PR 4 entry under the
shared [1.0.0] header; adds a fresh empty [Unreleased] section
above for post-1.0 work; documents the install() env-var contract
upgrade under Changed; documents the _locks.py deprecation shim
deferral under Deprecated. Uses "1.0.0" no v prefix per the
existing convention.

.github/workflows/publish-to-pypi.yml NEW: triggers on strict
3-component v[0-9]+.[0-9]+.[0-9]+ semver tag (rejects v1.0.0-rc1
and similar pre-release tags that would otherwise match a v1.*
wildcard); runs uv build + twine check + uv publish via
UV_PUBLISH_TOKEN secret. First publish stays manual (PyPI account
token bootstrap); subsequent releases automated.

.github/workflows/test.yml: adds --extra http to the uv sync
command so CI installs httpx explicitly per the [http] optional
dependency. httpx arrives transitively via mcp>=1.0.0 today, but
the explicit declaration validates the operator install path and
guards against a future mcp release dropping the dep.

Closes #201. Twelve of twelve backend protocols shipped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- docs/methodology.md: "eleven are shipped" → "twelve are shipped"; add
  MCPServerRegistryBackend to the protocol list; 2937+ → 3270+ test count
- CONTRIBUTING.md: 2937 → 3270+ test count
- docs/getting-started.md: stale "296 passed" → "3270 passed" (was written
  at v0.1.0 era; reflects the current full-suite count at v1.0.0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@dep0we dep0we merged commit 7725a91 into main Jun 4, 2026
5 checks passed
@dep0we dep0we deleted the feat/201-pr5-http-write-spec36-lock-v1 branch June 4, 2026 18:36
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.

[backend] MCPServerRegistryBackend — unify MCP server discovery across agents

1 participant