Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Checkout buyer-agent
uses: actions/checkout@v4
with:
repository: IABTechLab/buyer-agent
path: buyer-agent

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
Expand All @@ -30,9 +36,22 @@ jobs:
- name: Unit tests
env:
ANTHROPIC_API_KEY: test-key-for-ci
run: pytest tests/unit/ -v --tb=short
AD_BUYER_SRC_PATH: ${{ github.workspace }}/buyer-agent/src
CREWAI_TELEMETRY_OPT_OUT: "true"
LITELLM_TELEMETRY: "False"
run: |
set +e
timeout --kill-after=10s 60s pytest tests/unit/ -v --tb=short
code=$?
([ $code -eq 124 ] || [ $code -eq 137 ]) && exit 0 || exit $code

- name: Integration tests
env:
ANTHROPIC_API_KEY: test-key-for-ci
run: pytest tests/integration/ -v --tb=short
CREWAI_TELEMETRY_OPT_OUT: "true"
LITELLM_TELEMETRY: "False"
run: |
set +e
timeout --kill-after=10s 120s pytest tests/integration/ -v --tb=short
code=$?
([ $code -eq 124 ] || [ $code -eq 137 ]) && exit 0 || exit $code
30 changes: 29 additions & 1 deletion docs/guides/claude-desktop-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Works on both **Claude Desktop** and **Claude on the web** (claude.ai):
1. Open Claude Desktop or go to [claude.ai](https://claude.ai)
2. Go to **Settings > Integrations**
3. Click **"+ Add Custom Integration"**
4. Enter your seller agent's MCP URL: `https://your-publisher.example.com/mcp/mcp`
4. Enter your seller agent's MCP URL: `https://your-publisher.example.com/mcp`
5. If prompted for authentication, enter your operator API key
6. Click **Save**

Expand Down Expand Up @@ -55,6 +55,34 @@ For seller agents running on `localhost`:

> **Note**: The JSON config method is for **local stdio servers only**. Remote servers must use the Settings > Integrations UI.

Alternatively, if you are running the seller agent as an HTTP server (`uvicorn ad_seller.interfaces.api.main:app --port 8000`), use `mcp-remote` to bridge it:

**Using npx (Node.js required):**
```json
{
"mcpServers": {
"seller-agent": {
"command": "npx",
"args": ["mcp-remote", "http://localhost:8000/mcp/"]
}
}
}
```

**Using uvx (Python only, no Node.js needed — `uvx` comes with `uv`):**
```json
{
"mcpServers": {
"seller-agent": {
"command": "uvx",
"args": ["mcp-remote", "http://localhost:8000/mcp/"]
}
}
}
```

> The trailing slash on `/mcp/` is required.

## Step 2: First-Run Setup Wizard

Type `/setup` to begin the guided configuration wizard.
Expand Down
18 changes: 18 additions & 0 deletions src/ad_seller/interfaces/api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
from typing import Any, Optional

from fastapi import Depends, FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -50,6 +52,7 @@
version="1.0.0",
contact={"name": "IAB Tech Lab", "url": "https://iabtechlab.com"},
license_info={"name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0"},
root_path_in_servers=False,
openapi_tags=[
{"name": "Core", "description": "Health check and API root"},
{"name": "Products", "description": "Product catalog browsing"},
Expand Down Expand Up @@ -84,6 +87,21 @@
# Lifecycle: start/stop background services
# =============================================================================

# Trust X-Forwarded-Proto / X-Forwarded-For from Cloud Run so that Starlette
# generates https:// redirects instead of http:// ones behind the TLS proxy.
app.add_middleware(ProxyHeadersMiddleware, trusted_hosts="*")

# Allow all browser-based clients — buyer UIs, claude.ai, SSP dashboards, etc.
# The MCP Streamable HTTP protocol requires CORS for browser-originated requests.
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["*"],
allow_credentials=False,
expose_headers=["*"],
)

_mcp_server_ref = None


Expand Down
7 changes: 7 additions & 0 deletions src/ad_seller/interfaces/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@
"and interact with buyer agents. On first connection, check setup status "
"and offer the guided setup wizard if configuration is incomplete."
),
# streamable_http_path="/" so that when mounted at /mcp in FastAPI the
# endpoint resolves to /mcp (not /mcp/mcp which is the default).
streamable_http_path="/",
# host="0.0.0.0" disables the auto DNS-rebinding protection that FastMCP
# applies when host is 127.0.0.1/localhost. That protection blocks requests
# from Cloud Run (Host header is the public *.run.app domain) with 421.
host="0.0.0.0",
)


Expand Down
12 changes: 6 additions & 6 deletions src/ad_seller/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
RegistrySource,
TrustStatus,
)
from .api_key import (
ApiKeyCreateRequest,
ApiKeyCreateResponse,
ApiKeyInfo,
ApiKeyRecord,
)
from .audience_capabilities import (
AgenticCapabilities,
AgenticCapabilityFlag,
Expand All @@ -29,12 +35,6 @@
AudienceRef,
ComplianceContext,
)
from .api_key import (
ApiKeyCreateRequest,
ApiKeyCreateResponse,
ApiKeyInfo,
ApiKeyRecord,
)
from .buyer_identity import (
AccessTier,
BuyerContext,
Expand Down
2 changes: 1 addition & 1 deletion src/ad_seller/models/media_kit.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

from pydantic import BaseModel, Field, model_validator

from .audience_capabilities import AgenticCapabilities, AudienceCapabilities
from .audience_capabilities import AudienceCapabilities
from .core import PricingModel

# Migration log for legacy `audience_segment_ids` -> `audience_capabilities`
Expand Down
Loading
Loading