Skip to content

fix: use correct PYPY_API_TOKEN secret for PyPI publishing#8

Merged
bokelley merged 2 commits intomainfrom
docs-update-claude-md-learnings
Nov 6, 2025
Merged

fix: use correct PYPY_API_TOKEN secret for PyPI publishing#8
bokelley merged 2 commits intomainfrom
docs-update-claude-md-learnings

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented Nov 6, 2025

Problem

The PyPI publish step in Release Please workflow is failing with 403 Forbidden because it's using the wrong secret name.

Root Cause

Previous PRs (#6 and #7) changed the secret name from PYPY_API_TOKEN to PYPI_API_TOKEN and then attempted to revert it, but the revert didn't actually work.

Solution

Change PYPI_API_TOKEN back to PYPY_API_TOKEN which is the actual secret name in the repository.

Testing

After merging, the next release will use the correct secret and should publish successfully to PyPI.

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

bokelley and others added 2 commits November 6, 2025 12:56
Previous revert commit didn't actually change the secret name.
The correct secret name in the repository is PYPY_API_TOKEN.

This should fix the 403 Forbidden error when publishing to PyPI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Document key learnings from building the Python AdCP SDK:

**Type Safety & Code Generation:**
- Auto-generate Pydantic models from upstream schemas
- Handle missing schema types with documented type aliases
- Use TYPE_CHECKING for optional dependencies
- Use cast() for JSON deserialization type safety

**Testing Strategy:**
- Mock at the right level (_get_client() not httpx class)
- httpx response.json() is SYNCHRONOUS not async
- Test the API as it exists, not as we wish it existed

**CI/CD & Release:**
- Verify secret names before changing them (PYPY_API_TOKEN not PYPI_API_TOKEN)
- Release Please automates version bumps and PyPI publishing
- Entry points in pyproject.toml enable uvx usage

**Python Patterns:**
- String escaping order matters (backslash first, then quotes)
- Atomic file operations for config files
- Connection pooling for HTTP clients
- Python 3.10+ required for | union syntax

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@bokelley bokelley merged commit b48a33a into main Nov 6, 2025
6 checks passed
@github-actions github-actions Bot mentioned this pull request Nov 6, 2025
bokelley added a commit that referenced this pull request Apr 20, 2026
Closes 5 items from salesagent's feedback on adopting adcp.server in
one cohesive server/transport surface change.

SkillMiddleware parity across transports (#7)
---------------------------------------------
The A2A executor's per-skill middleware (PR #233) is now available on
MCP too. Same SkillMiddleware type alias, same composition semantics
(outermost-first, _step recursion), same call_next contract — a
middleware list written against one transport works unchanged on the
other.

- src/adcp/server/serve.py: new module-level _dispatch_with_middleware
  that A2A's _dispatch_with_middleware delegates to.
- create_mcp_server, _register_handler_tools, _register_tool accept
  middleware=[SkillMiddleware]; _register_tool wraps caller in the
  chain between context build and handler invocation.
- serve() already exposed the kwarg for A2A; now forwards to MCP too.

BearerTokenAuthMiddleware in adcp.server.auth (#1)
--------------------------------------------------
The pattern in examples/mcp_with_auth_middleware.py was four
security-critical concerns (ContextVar carrier, constant-time compare,
discovery bypass, reset-in-finally); every downstream copy-pasted it.
Now shipped as a class.

- src/adcp/server/auth.py: BearerTokenAuthMiddleware, Principal
  (frozen dataclass), TokenValidator, auth_context_factory,
  constant_time_token_match. Seller supplies validate_token; framework
  owns the ContextVar plumbing, RFC 7235 scheme parsing (case-
  insensitive + whitespace-folded), discovery bypass, peek_jsonrpc
  with explicit request._body cache, fail-closed validator exception
  handling, principal metadata that can't shadow SDK audit keys.
- examples/mcp_with_auth_middleware.py shrunk 243 → 89 lines.

A2A message_parser hook (#3)
----------------------------
ADCPAgentExecutor._parse_request was hardcoded to
DataPart({'skill': ..., 'parameters': ...}). Sellers fronting JSON-RPC
or vendor-specific shapes had to subclass privately.

- src/adcp/server/a2a_server.py: new MessageParser type alias,
  message_parser= kwarg on ADCPAgentExecutor, create_a2a_server,
  _serve_a2a, serve(). Default = _default_parse_request (was inline).

Startup advertised-tools log (#9)
---------------------------------
- src/adcp/server/serve.py: _log_advertised_tools() runs from
  _register_handler_tools (MCP) and create_a2a_server (A2A).
  INFO: 'X of Y tools advertised'; DEBUG: list of unadvertised.

Custom tools doc (#8)
---------------------
docs/handler-authoring.md: new section covering the @mcp.tool()
passthrough on create_mcp_server's return value.

Expert-review followups (security + code review)
-------------------------------------------------
- _parse_bearer_header: case-insensitive scheme, folded whitespace.
- validator exceptions → 401 (no stack-trace leak).
- principal metadata can't shadow SDK-owned keys (tool_name,
  transport).
- explicit request._body = body after peek.
- tests use regex to match log messages (not positional tokens).
- Python 3.10 skipif on two new A2A create_a2a_server tests (a2a-sdk
  starlette integration requires 3.11+; matches pre-existing skip).

Tests
-----
+53 tests across three new/modified test files. 1990 tests passing,
mypy clean.

Closes #224, #225, #226, #240, #241 salesagent feedback items #1,
#3, #7, #8, #9.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley added a commit that referenced this pull request Apr 30, 2026
…design

Round-4 review pass synthesizes (a) the TS team's review of the parallel
@adcp/client port (PR #1005, EmmaLouise2018), (b) the TS team's
decisioning-platform-python-port-v2.md RFC, and (c) Yahoo's ask for
typed framework-owned state threading on RequestContext.

Guiding principle ported from the TS port: "make it impossible for an
implementer to screw up via typing." Python can't match TS's
compile-time RequiredPlatformsFor<S> gate, but per-method typed
surfaces, runtime validate_platform fail-fast, and Protocol structural
matching close most of the gap.

Highlights:

- D15 NEW: typed RequestContext sub-readers (state + resolve).
  - StateReader (sync) — find_by_object, find_proposal_by_id,
    governance_context, workflow_steps. Lets platforms read prior
    workflow context without re-querying their own DB.
  - ResourceResolver (async) — property_list, collection_list,
    creative_format. Framework-mediated cache + validation.
  - Surface ships in v6.0 with no-op stub backings; impls fill in
    for v6.1 (same gating as TS side). Locks the typed contract so
    adopters write the right shape from day one.

- Round-4 changelog covers 8 cross-language items applied:
  - D14 enum coverage (Emma #6)
  - D7+serve() prod gate on InMemoryTaskRegistry (Emma #8)
  - Dispatch AdcpError projection consistency (Emma #10)
  - D6 sync-handoff register-before-cleanup race (Emma #11)
  - validate_platform catches validator throws (Emma #16)
  - Per-server status-change bus, not module-level singleton (Emma #17)
  - AdcpError ACCOUNT_NOT_FOUND semantic narrowing (Emma #18)
  - CI lint: examples can't reach into src/ (Emma #5)

- Bugs structurally avoided in our hybrid SalesResult[T] design
  documented (Emma #2, #3, #13, design concern #14) — worth calling
  out in foundation PR description; the framework-design choice gets
  the credit.

- File plan additions: state.py, resolve.py, context.py extensions for
  D15; four new test files for Round-4 regressions. Foundation PR
  total grew from ~2475 to ~2965 lines.

- Items deferred to follow-up PRs: ErrorCode Literal codegen (Emma #19),
  workflow-step/proposal/governance backing store (D15 v6.1),
  tasks/get wire surface.

- TS-only items (no Python equivalent) explicitly enumerated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley added a commit that referenced this pull request Apr 30, 2026
Stage 3 final piece. Two public entry points wire the foundation
layers together:

- create_adcp_server_from_platform(platform, ...) → (handler,
  executor, registry) 3-tuple. Adopters wanting full control over
  MCP/A2A wiring use this seam.

- serve(platform, ...) → one-call wrapper that builds the handler
  and starts the MCP server via adcp.server.serve. Most adopters use
  this. Forwards host/port/transport/etc. via **serve_kwargs.

Wires per the dispatch design doc:

- D5 ThreadPoolExecutor configurability:
  * executor= (BYO operator-vetted pool — operator owns lifecycle)
  * thread_pool_size= (size the framework-allocated default)
  * default min(32, cpu+4) with thread_name_prefix="adcp-decisioning-"
  * executor= and thread_pool_size= are mutually exclusive

- Emma #8 production-mode gate on InMemoryTaskRegistry:
  * Reads ADCP_ENV (case-insensitive {"prod", "production"} — same
    convention as adcp.validation.client_hooks._default_response_mode)
  * Refuses to start in production with InMemoryTaskRegistry unless
    ADCP_DECISIONING_ALLOW_INMEMORY_TASKS=1 explicitly set
  * Custom durable registry bypasses the gate

- D15 state_reader / resource_resolver kwargs plumbed through to
  PlatformHandler.

- validate_platform called before handler construction; failure
  surfaces as AdcpError to the caller.

24 tests in test_decisioning_serve.py covering all the above
scenarios.

Foundation tests: 133 (+24). Full suite: 2513 passed, 17 skipped, 1
xfailed. ruff + mypy clean.

Stage 3 complete. Stage 4 next: examples/hello_seller.py + integration
tests + ruff lint rule banning examples reaching into src/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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