PUBLISH P3: remote claude.ai connector (Streamable-HTTP + OAuth 2.1) - #38
Merged
Conversation
The second PUBLISH target (D19): a claude.ai *custom connector* is a REMOTE MCP server reached from Anthropic's cloud over HTTPS + OAuth 2.1 — a separate surface from the local stdio .mcpb. `claude-remote-connector` (coact/publish_remote.py) SCAFFOLDS a deployable service you host: - emits server/app.py (an ASGI app via py2mcp.http.mk_http_app), a connector_config.json, requirements.txt, a DEPLOY.md guide, and a Dockerfile. - the auth block is a resource-server (type=jwt): validate a managed IdP's JWTs, audience-bound (RFC 8707), never issue tokens, never forward them upstream. - no IdP/connector-url given -> clearly-marked placeholders + a loud warning (never a silently-unauthenticated config); a remote connector MUST require OAuth. - coact writes packaging; py2mcp.http builds + serves the MCP server (D17). The build is pure stdlib; py2mcp/fastmcp/uvicorn are runtime-only (missing -> warning). `import coact` pulls in none of them (isolation-tested). - IntegrationSpec.auth='oauth2.1' / deployment='remote-http' go live; same draft/ empty guards as .mcpb. CLI `coact publish --target claude-remote-connector [--connector-url --idp-issuer]`; exports publish_remote; DECISIONS D19; skill + README updated. 12 offline tests (one dev-only test execs the generated app.py to prove it builds a real authed ASGI app). The py2mcp http layer has its own upstream PR. Full suite: 405 passed.
Adversarial security review (8 confirmed / 8 refuted); all confirmed fixed: - [high] CLI --connector-url/--idp-issuer were forwarded unconditionally and crashed the default claude-local-mcpb target with a raw TypeError. They now raise a clear error unless --target claude-remote-connector. - [high] upstream py2mcp fix (separate PR): JWTVerifier skips audience validation when audience is None; mk_auth_provider now REQUIRES audience (RFC 8707, the confused-deputy defense — fail-closed). coact always supplies one, but the pin is bumped accordingly. - [low] a tools-less spec (resources/prompts only) now gets its own clear message instead of a misleading "0 proposed tool(s)" design-draft message. - [low] scaffold pins py2mcp>=0.1.5 (the release that actually ships py2mcp.http; 0.1.4 predated it) in all three places. - tests added: CLI flag routing (reject-on-wrong-target + forward-on-remote), the py2mcp-absent warning branch, placeholder audience-binding content, and preview truncation. Full suite: 410 passed, 4 skipped; ruff clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds the remote claude.ai connector publish target (DECISIONS D19). A
claude.ai custom connector is a remote MCP server reached from Anthropic's
cloud over HTTPS + OAuth 2.1 — a separate surface from the local stdio
.mcpb.claude-remote-connector(coact/publish_remote.py) scaffolds a deployableservice you host.
coact publish mypkg.tools:summarize --target claude-remote-connector \ --name my-conn --dest ./out \ --connector-url https://my-conn.example.com --idp-issuer https://my-idp.example.com # → out/my-conn-connector/ (server/app.py, connector_config.json, requirements.txt, DEPLOY.md, Dockerfile)Design
.mcpbpacks a bundle (you own the install). D8/D13.http.mk_http_app/serve_http(added upstream — py2mcp#4, merged, 0.1.4), wrapping FastMCP. Building the scaffold is pure stdlib;py2mcp/fastmcp/uvicornare runtime-only (missing → warning).import coactpulls in none of them (isolation-tested).authistype: jwt— validate a managed IdP's JWTs (JWTVerifier→RemoteAuthProvider), audience-bound (RFC 8707), never an authorization server, never forwards the inbound token upstream.DEPLOY.mdstates the rules.IntegrationSpec.auth='oauth2.1'/deployment='remote-http'go live; same draft/empty/unsafe-name guards as.mcpb.Tests
405 passed, 4 skipped; ruff clean. 12 offline tests assert the emitted files; a dev-only test (skipped in bare CI) execs the generated
server/app.pyto prove it builds a real authed ASGI app. Reviewed by an adversarial security-focused multi-agent pass over the OAuth wiring.