Skip to content

AdCP Testing Framework with Authentication, Hierarchical Logging & Security Features#1

Merged
bokelley merged 4 commits intomainfrom
foundation
Sep 4, 2025
Merged

AdCP Testing Framework with Authentication, Hierarchical Logging & Security Features#1
bokelley merged 4 commits intomainfrom
foundation

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented Sep 4, 2025

Summary

  • Complete AdCP Testing Framework for MCP and A2A protocols
  • Fixed authentication issues with Bearer token support
  • Implemented hierarchical logging for better debug visibility
  • Added comprehensive security features and resource management

Key Features

  • Protocol Support: Full MCP and A2A agent communication
  • Authentication: Bearer token and x-adcp-auth header support
  • Hierarchical Logging: Groups multi-step MCP operations into single expandable entries
  • Security: SSRF protection, token sanitization, rate limiting, security headers
  • Resource Management: Client pooling, concurrent request limiting, memory management
  • Error Handling: Standardized error responses with classification

Test Plan

  • MCP agent authentication and product retrieval
  • A2A agent authentication and product retrieval
  • Hierarchical debug log display in UI
  • Token sanitization in logs
  • Rate limiting functionality
  • Client connection cleanup
  • Security header validation

🤖 Generated with Claude Code

bokelley and others added 4 commits September 4, 2025 06:24
…rkers

Complete migration and enhancement of AdCP Testing Framework:

• **Protocol Support**: Full A2A and MCP protocol implementations
• **Real Agent Integration**: Production-ready agents (adcp-sales-agent.fly.dev)
• **Fixed MCP Protocol**: Proper JSON-RPC 2.0, SSE parsing, session management
• **Removed Authentication**: Streamlined for public testing deployment
• **Production Ready**: Fly.io deployment with Docker, health checks
• **Enhanced UI**: Migrated original sales-agents.html with debug panel
• **API Endpoints**: RESTful testing API for automation
• **Security Cleanup**: Removed hardcoded tokens and secrets

Key Features:
- Web UI for interactive testing at localhost:3000
- API endpoints for automated testing
- Support for both MCP and A2A protocols
- Real-time debug logging with request/response capture
- Production deployment configuration for Fly.io
- Comprehensive documentation and deployment guides

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

Co-Authored-By: Claude <noreply@anthropic.com>
- Add proper authentication headers (x-adcp-auth + Authorization Bearer) for MCP agents
- Fix MCP promoted_offering parameter default value to avoid "cannot be empty" errors
- Implement proper MCP response parsing to extract products from nested JSON structure
- Fix duplicate product extraction by prioritizing content[].text over structuredContent
- Format debug logs to show clean JSON instead of raw Server-Sent Events format
- Both A2A and MCP agents now successfully authenticate and return product data

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

Co-Authored-By: Claude <noreply@anthropic.com>
Frontend Updates:
- Update index.html displayDebugLogs() to handle operation logs
- Add displayOperationLog() and displayLegacyPairs() functions
- Add comprehensive CSS styling for operation display
- Update sales-agents.html with hierarchical debug logs
- Maintain backward compatibility with legacy log format

Backend Updates:
- OperationLogger class tracks multi-step MCP operations
- Groups initialize → notifications/initialized → tools/list → tools/call
- Returns structured operation logs with step-by-step timing
- Preserves raw protocol logs for detailed debugging

UI Experience:
- Single expandable operation instead of 6-7 separate calls
- Protocol steps with individual timing breakdowns
- Rich metadata display (agent, duration, status)
- Clean visual hierarchy with color-coded status

Both debug panels now show logical operations that can be expanded
to see detailed protocol steps when needed for debugging.

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

Co-Authored-By: Claude <noreply@anthropic.com>
- SSRF protection with IP whitelist validation
- Token sanitization in logs and responses
- Client connection pooling and lifecycle management
- Rate limiting with per-IP tracking
- Concurrent request limiting with queue management
- Security headers (CSP, HSTS, XSS protection, etc.)
- Request size validation and unique request IDs
- Standardized error handling with classification
- Memory management for debug logs with rotation

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

Co-Authored-By: Claude <noreply@anthropic.com>
@bokelley bokelley merged commit 4a522ed into main Sep 4, 2025
1 check passed
tescoboy pushed a commit to tescoboy/adcp-client that referenced this pull request Apr 14, 2026
Addresses review feedback:
- Removed standalone Implementation Traps section
- Folded accepted traps (adcontextprotocol#1-adcontextprotocol#6) into existing Common Mistakes table
- Dropped traps adcontextprotocol#7 (Accept header), adcontextprotocol#8 (Zod bug), adcontextprotocol#9 (pin version) per reviewer guidance
tescoboy pushed a commit to tescoboy/adcp-client that referenced this pull request Apr 14, 2026
…tprotocol#3) in Common Mistakes table

Removes traps adcontextprotocol#1, adcontextprotocol#4, adcontextprotocol#5, adcontextprotocol#6 per reviewer feedback:

adcontextprotocol#1 and adcontextprotocol#6 have accuracy issues
adcontextprotocol#4 and adcontextprotocol#5 to be filed as GitHub Issues (may be Addie-specific)
bokelley added a commit that referenced this pull request Apr 15, 2026
The #1 DX blocker: Zod schemas use .nullish() (null | undefined) but
TypeScript types used ? (undefined only). Every handler echoing
params.context hit type errors.

Fix: generate-types.ts now adds | null to optional TypeScript properties,
matching Zod .nullish() behavior. 25 internal call sites updated with
?? undefined to strip null where needed.

Also fixes ZodIntersection schemas (get_signals, etc.) that lost .shape
after regeneration — extractShape() traverses intersection _def.right.

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

* feat: createAdcpServer — declarative server builder with domain-grouped handlers

Domain registration that wires handler types → input schemas → Zod validation
→ response formatting. Auto-generates get_adcp_capabilities from registered tools.

- Domain-grouped handlers: mediaBuy, signals, creative, governance, accounts,
  eventTracking, sponsoredIntelligence
- resolveAccount middleware: auto-resolves AccountReference on tools with account
  field, returns ACCOUNT_NOT_FOUND on failure
- Response builder auto-application: productsResponse, mediaBuyResponse (with
  revision/confirmed_at defaults), getSignalsResponse, etc.
- Tool annotations: readOnlyHint, destructiveHint, idempotentHint set per tool
- Tool coherence warnings: create_media_buy without get_products, etc.
- Unknown handler key warnings: catches typos like getProduct vs getProducts
- Handler error catching: try/catch returns SERVICE_UNAVAILABLE on unhandled errors
- checkGovernance composable helper: seller calls explicitly in financial handlers
- governanceDeniedError convenience: converts denial to COMPLIANCE_UNSATISFIED

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: add changeset for createAdcpServer

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: pluggable state store for domain object persistence

AdcpStateStore interface with InMemoryStateStore (default) and
PostgresStateStore (production) implementations. Handlers receive
ctx.store for persisting media buys, accounts, creatives, etc.

- Generic document store: get/put/delete/list by collection + id
- InMemoryStateStore: nested Maps, filtering, pagination
- PostgresStateStore: JSONB table, containment queries, keyset pagination
- Shared across all domain handlers via ctx.store
- getAdcpStateMigration() for postgres table setup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: state store review fixes — cursor bug, patch, copy semantics, limit cap

- Fix InMemoryStateStore cursor tracking (was broken after filtering)
- Add patch() to interface and both implementations (JSONB || merge for postgres)
- get() returns a shallow copy (prevents mutation of store internals)
- list() returns copies and caps limit at 500
- Validate cursor format in PostgresStateStore
- Add tests for cursor roundtrip, patch, copy semantics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: update all skills and guides for createAdcpServer framework

Updated 8 SKILL.md files, BUILD-AN-AGENT.md, and llms.txt to reference
createAdcpServer with domain-grouped handlers, ctx.store for state
persistence, and auto-applied response builders.

- Seller, signals, creative, governance, SI, brand rights, retail media,
  generative seller skills all show createAdcpServer patterns
- BUILD-AN-AGENT.md leads with createAdcpServer, demotes createTaskCapableServer
  to advanced/escape-hatch section
- llms.txt Quick Start updated with createAdcpServer example
- Common Mistakes tables updated across all skills
- SDK Quick Reference tables updated across all skills

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update_media_buy hasAccount → true (adcp#2179)

update_media_buy now has a required account field per adcp#2174.
This enables automated account resolution on the most financially
sensitive mutation tool.

preview_creative flattening (adcp#2175) will be picked up when
schemas are regenerated from the updated spec.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: derive hasAccount from schema, validate governance response, fix JSDoc

- hasAccount no longer hand-coded in TOOL_META — derived from Zod schema
  at init time via 'account' in schema.shape. Eliminates drift bugs
  (get_creative_delivery and get_signals were wrong).
- checkGovernance validates required fields before casting response
- Module JSDoc updated to remove resolveGovernance (now composable helper)
- genericResponse includes tool name instead of "OK"
- Add test for required account field on create_media_buy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: use TOOL_REQUEST_SCHEMAS as single source of truth for tool schemas

Replace 40+ individual schema imports and hand-coded TOOL_META.schema
with TOOL_REQUEST_SCHEMAS from #540. Schema and hasAccount are now
derived from the canonical map at registration time.

- Eliminates dual source of truth (review item #5)
- hasAccount derived from schema ('account' in schema.shape)
- Tools not in TOOL_META still register if present in TOOL_REQUEST_SCHEMAS
- TOOL_META now only contains response builders and annotations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: context passthrough in createAdcpServer handlers and capabilities

- Handler wrapper auto-injects params.context into response data before
  response builder wraps it. Handlers don't need to echo context manually.
- get_adcp_capabilities registered with actual schema (not empty {}) so
  context from request is received and echoed.
- Storyboard results: 5/6 signal_marketplace steps pass (remaining failure
  is signal_id null/undefined schema mismatch tracked in #542).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: align TypeScript types with Zod nullish, regenerate schemas

The #1 DX blocker: Zod schemas use .nullish() (null | undefined) but
TypeScript types used ? (undefined only). Every handler echoing
params.context hit type errors.

Fix: generate-types.ts now adds | null to optional TypeScript properties,
matching Zod .nullish() behavior. 25 internal call sites updated with
?? undefined to strip null where needed.

Also fixes ZodIntersection schemas (get_signals, etc.) that lost .shape
after regeneration — extractShape() traverses intersection _def.right.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove nullish workaround, fix intersection root cause

Root cause fixes for the two schema generation issues:

1. **Nullish removed**: postProcessForNullish was converting every
   .optional() to .nullish(), making Zod produce null|undefined while
   TypeScript types only had undefined. Removed the post-processor —
   Zod schemas now use .optional() matching TypeScript. Also removed
   alignOptionalWithNullish (TypeScript workaround) and
   postProcessLazyTypeAnnotations (cascading nullish fix).

2. **Intersections fixed**: postProcessRecordIntersections already
   handles most cases. The 5 remaining schemas (get_signals, etc.)
   are now clean z.object() with .shape. Only comply_test_controller
   remains a union (by design — multiple scenario types).

Reverted extractShape() workaround in createAdcpServer — schemas
have .shape directly now.

0 nullish, 2877 optional. Types and schemas match. 2972 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use GOVERNANCE_DENIED instead of COMPLIANCE_UNSATISFIED

GOVERNANCE_DENIED (adcp#2194) is specific to governance agent denials.
COMPLIANCE_UNSATISFIED is broader (content standards, brand safety).
governanceDeniedError() now uses the correct code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: regen schemas from adcp#2194, add preview_creative to createAdcpServer

Pull in adcp#2194: GOVERNANCE_DENIED error code, flatten comply_test_controller,
context/ext on all schemas, require signal_id.

- preview_creative now flat z.object() — added to CreativeHandlers, TOOL_META,
  TOOL_REQUEST_SCHEMAS, and CREATIVE_ENTRIES
- Removed old PreviewCreative variant exports (no longer needed)
- Updated discriminated union tests for flat schema behavior
- signal_id now required on signals (should fix storyboard provenance check)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: fix CI — prettier formatting and regenerate agent docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: broken resolveAccount example in seller SKILL.md

resolveAccount callback referenced ctx.store which is not in scope.
Fixed to use stateStore (declared outside createAgent factory).
Added InMemoryStateStore import and declaration to make example
copy-paste correct.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (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