Skip to content

Remote serving: Streamable-HTTP + OAuth 2.1 resource-server#4

Merged
thorwhalen merged 1 commit into
mainfrom
claude/serve-http
Jun 17, 2026
Merged

Remote serving: Streamable-HTTP + OAuth 2.1 resource-server#4
thorwhalen merged 1 commit into
mainfrom
claude/serve-http

Conversation

@thorwhalen

Copy link
Copy Markdown
Member

What

Adds the remote counterpart to serve_stdio: a py2mcp FastMCP server can now be served over Streamable HTTP with OAuth 2.1, wrapping FastMCP's native transports/OAuth (nothing reinvented). New py2mcp/http.py:

  • mk_auth_provider(auth) — an auth-config dict → a fastmcp RemoteAuthProvider (a resource server). type='jwt' validates a managed IdP's JWTs via JWTVerifier, audience-bound (RFC 8707) so a token minted for another service can't be replayed; publishes RFC 9728 /.well-known/oauth-protected-resource pointing at the IdP. It never issues tokens (no AS, no confused-deputy, no token passthrough).
  • mk_http_app(refs, *, auth=None, transport='streamable-http', stateless_http=...) — build + attach auth → a Starlette ASGI app to run under any ASGI server.
  • serve_http(refs, *, host, port, auth) — build and run (blocking) via FastMCP/uvicorn.
  • mk_mcp_server/mk_mcp_from_refs gained an optional auth (attached at FastMCP construction); python -m py2mcp --http reads auth/host/port from the config.

Why

First customer is coact's upcoming claude-remote-connector publish target. Per coact's D17 division of labour, coact writes packaging/deploy scaffolding and py2mcp builds + serves the MCP server — the same split as the local .mcpb stdio path (which uses serve_stdio). aw_agents has no HTTP/OAuth, so this is the right home.

Safety / tests

Building the provider and ASGI app does no network I/O (JWKS fetch is lazy), so it is safe at scaffold/import time and fully offline-testable. 12 new offline tests; full suite + doctests green; ruff clean. fastmcp is already a core dep (its server.auth provides all the OAuth machinery).

…rver

Adds the remote counterpart to serve_stdio: a py2mcp FastMCP server can now be
served over Streamable HTTP with OAuth 2.1, wrapping FastMCP's native machinery
(no transport/auth code reinvented). py2mcp/http.py:

- mk_auth_provider(auth): an auth-config dict -> a fastmcp RemoteAuthProvider
  (resource server). type='jwt' validates a managed IdP's JWTs via JWTVerifier,
  audience-bound (RFC 8707) so a token for another service can't be replayed;
  publishes RFC 9728 protected-resource metadata pointing at the IdP. Never
  issues tokens itself (no AS, no confused-deputy, no token passthrough).
- mk_http_app(refs, *, auth=None, transport='streamable-http', stateless_http):
  build the server + attach auth -> a Starlette ASGI app for any ASGI server.
- serve_http(refs, *, host, port, auth): build and run (blocking) via uvicorn.
- mk_mcp_server/mk_mcp_from_refs gained an optional `auth` (attached at FastMCP
  construction); `python -m py2mcp --http` reads auth/host/port from the config.

Building the provider/app performs NO network I/O (JWKS fetch is lazy), so it is
safe at scaffold/import time and fully offline-testable. First customer: coact's
claude-remote-connector publish target (coact writes packaging; py2mcp builds +
serves the MCP server — same split as the .mcpb stdio path). 12 offline tests.
@thorwhalen thorwhalen merged commit 7835496 into main Jun 17, 2026
12 checks passed
@thorwhalen thorwhalen deleted the claude/serve-http branch June 17, 2026 12:49
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