diff --git a/docs/discovery/implementation-guide.md b/docs/discovery/implementation-guide.md new file mode 100644 index 0000000000..9ca7a7ddf1 --- /dev/null +++ b/docs/discovery/implementation-guide.md @@ -0,0 +1,115 @@ +# Discovery Protocol Implementation Guide + +This guide helps publishers implement the AdCP Discovery Protocol. + +## Quick Start + +1. Create a JSON file mapping agent types to MCP endpoints +2. Serve it at `https://yourdomain.com/.well-known/adcp.json` +3. That's it! + +## Example Discovery Document + +```json +{ + "sales": "https://salesagent.yourdomain.com/mcp" +} +``` + +## Implementation Options + +### Option 1: Static File + +Create a file named `adcp.json` in your `.well-known` directory: + +```json +{ + "sales": "https://api.yourdomain.com/sales/mcp", + "signals": "https://api.yourdomain.com/analytics/mcp" +} +``` + +### Option 2: Nginx Configuration + +```nginx +location = /.well-known/adcp.json { + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + return 200 '{"sales": "https://salesagent.example.com/mcp"}'; +} +``` + +### Option 3: Dynamic Response + +```javascript +// Express.js +app.get('/.well-known/adcp.json', (req, res) => { + res.json({ + sales: "https://api.example.com/sales/mcp" + }); +}); +``` + +## Requirements + +- **HTTPS**: Both discovery and MCP endpoints must use HTTPS +- **Content-Type**: Must be `application/json` +- **CORS**: Add headers for browser-based access: + ``` + Access-Control-Allow-Origin: * + Access-Control-Allow-Methods: GET + ``` + +## Testing Your Implementation + +```bash +# Test discovery +curl https://yourdomain.com/.well-known/adcp.json + +# Expected response +{ + "sales": "https://api.yourdomain.com/mcp" +} +``` + +## Common Issues + +### 404 Not Found +- Check the path is exactly `/.well-known/adcp.json` +- Ensure your web server serves the file + +### CORS Errors +- Add the required CORS headers +- Test from a browser console + +### Invalid JSON +- Validate your JSON syntax +- Use double quotes for strings + +## Examples + +### Single Agent +```json +{ + "signals": "https://signals.scope3.com/mcp" +} +``` + +### Multiple Agents +```json +{ + "sales": "https://api.example.com/sales/mcp", + "curation": "https://api.example.com/curation/mcp", + "signals": "https://api.example.com/signals/mcp" +} +``` + +## Next Steps + +Once your discovery endpoint is working: + +1. Implement your MCP server at the specified endpoint +2. Handle MCP authentication and capabilities +3. Test with an AdCP orchestrator + +For help with MCP implementation, see the [MCP documentation](https://modelcontextprotocol.io). \ No newline at end of file diff --git a/docs/discovery/protocol.md b/docs/discovery/protocol.md new file mode 100644 index 0000000000..062886c4e6 --- /dev/null +++ b/docs/discovery/protocol.md @@ -0,0 +1,150 @@ +# AdCP Agent Discovery Protocol + +The AdCP Agent Discovery Protocol enables orchestrators to automatically discover MCP endpoints for advertising agents. + +## Overview + +The discovery protocol uses the `.well-known` URI pattern (RFC 8615) to provide a standardized location for AdCP agent endpoints at: + +``` +https://example.com/.well-known/adcp.json +``` + +## Discovery Document Format + +The discovery document is a simple JSON object mapping agent types to their MCP endpoints: + +```json +{ + "sales": "https://salesagent.example.com/mcp", + "signals": "https://signals.example.com/mcp", + "curation": "https://curation.example.com/mcp" +} +``` + +That's it. No complex configuration needed. + +## Agent Types + +- **sales**: Media buying and inventory management +- **signals**: Analytics and performance data +- **curation**: Content and audience curation + +## Examples + +### Scope3 Signals Agent + +```json +{ + "signals": "https://signals.scope3.com/mcp" +} +``` + +### Publisher with Multiple Agents + +```json +{ + "sales": "https://api.publisher.com/adcp/sales/mcp", + "signals": "https://api.publisher.com/adcp/analytics/mcp" +} +``` + +### Full-Service Platform + +```json +{ + "sales": "https://platform.example.com/mcp/sales", + "curation": "https://platform.example.com/mcp/curation", + "signals": "https://platform.example.com/mcp/signals" +} +``` + +## Implementation Requirements + +### For Publishers + +1. Create a JSON file with your agent endpoints +2. Serve it at `/.well-known/adcp.json` with `Content-Type: application/json` +3. Use HTTPS with valid certificates +4. Add CORS headers for browser-based orchestrators: + ``` + Access-Control-Allow-Origin: * + Access-Control-Allow-Methods: GET + ``` + +### For Orchestrators + +1. GET the discovery document from `/.well-known/adcp.json` +2. Parse the JSON to find agent endpoints +3. Connect to the MCP endpoints directly +4. Handle authentication as specified by the MCP protocol + +## MCP Connection + +Once you have the MCP endpoint from discovery, connect using the standard MCP protocol. The MCP server will provide: + +- Available tools and capabilities +- Authentication requirements +- Protocol version information +- Any other metadata needed + +## Why So Simple? + +- **Publishers** only need to maintain a static JSON file +- **No duplication** - capabilities and auth are handled by MCP +- **Always current** - no risk of discovery data being out of sync +- **Easy to implement** - can be a static file on any web server + +## Security Considerations + +- Always use HTTPS for both discovery and MCP endpoints +- Validate SSL certificates +- The discovery document is public - don't include secrets +- Authentication happens at the MCP layer, not discovery + +## Quick Implementation + +### Static File (Nginx) + +```nginx +location = /.well-known/adcp.json { + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + return 200 '{"sales": "https://api.example.com/mcp"}'; +} +``` + +### Express.js + +```javascript +app.get('/.well-known/adcp.json', (req, res) => { + res.json({ + sales: "https://api.example.com/sales/mcp", + signals: "https://api.example.com/signals/mcp" + }); +}); +``` + +### Static File + +Just create a file at `/.well-known/adcp.json` on your web server: + +```json +{ + "sales": "https://salesagent.mycompany.com/mcp" +} +``` + +## Testing + +```bash +# Discover agents +curl https://example.com/.well-known/adcp.json + +# Response +{ + "sales": "https://api.example.com/mcp" +} + +# Connect to the MCP endpoint directly for full capabilities +``` \ No newline at end of file diff --git a/docs/intro.md b/docs/intro.md index c9d9e01fc1..dc2d75039b 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -109,6 +109,11 @@ The AI assistant will: ## Available Protocols +### 🔍 [Discovery Protocol](./discovery/protocol) +**Status**: RFC/v1.0 + +Automatically discover AdCP agents using `.well-known/adcp.json` endpoints. + ### 🎯 [Signals Activation Protocol](./signals/overview) **Status**: RFC/v0.1 diff --git a/docusaurus.config.ts b/docusaurus.config.ts index fe043a25d3..0dc194c7f2 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -93,8 +93,8 @@ const config: Config = { to: '/docs/intro', }, { - label: 'Audience Protocol', - to: '/docs/audience/overview', + label: 'Signals Protocol', + to: '/docs/signals/overview', }, ], }, diff --git a/sidebars.ts b/sidebars.ts index 9cecc1f3c6..9b7ebe4f65 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -17,10 +17,10 @@ const sidebars: SidebarsConfig = { 'intro', { type: 'category', - label: 'Audience', + label: 'Signals', items: [ - 'audience/overview', - 'audience/specification', + 'signals/overview', + 'signals/specification', ], }, { @@ -71,6 +71,14 @@ const sidebars: SidebarsConfig = { }, ], }, + { + type: 'category', + label: 'Discovery', + items: [ + 'discovery/protocol', + 'discovery/implementation-guide', + ], + }, { type: 'category', label: 'Reference', diff --git a/src/discovery/client.ts b/src/discovery/client.ts new file mode 100644 index 0000000000..843fedeaf8 --- /dev/null +++ b/src/discovery/client.ts @@ -0,0 +1,86 @@ +/** + * AdCP Discovery Client + * + * Simple client for discovering AdCP agent endpoints + * using the .well-known/adcp.json endpoint. + */ + +import { isDiscoveryDocument, type DiscoveryDocument, type DiscoveryResult } from '../types/discovery'; + +/** + * Discovers AdCP agents at a given domain + */ +export async function discoverAgents( + domain: string, + options: RequestInit = {} +): Promise { + // Ensure domain has protocol + const url = domain.startsWith('http') + ? `${domain}/.well-known/adcp.json` + : `https://${domain}/.well-known/adcp.json`; + + try { + const response = await fetch(url, { + method: 'GET', + headers: { + 'Accept': 'application/json', + ...options.headers, + }, + ...options, + }); + + if (!response.ok) { + return { + success: false, + error: `HTTP ${response.status}: ${response.statusText}`, + }; + } + + const data = await response.json(); + + if (!isDiscoveryDocument(data)) { + return { + success: false, + error: 'Invalid discovery document format', + }; + } + + return { + success: true, + endpoints: data, + }; + + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }; + } +} + +/** + * Example usage + */ +export async function example() { + // Discover Scope3's agents + const result = await discoverAgents('scope3.com'); + + if (result.success === true) { + if (result.endpoints.signals) { + console.log(`Scope3 signals agent: ${result.endpoints.signals}`); + // Now connect to the MCP endpoint directly + } + } else { + console.error(`Discovery failed: ${result.error}`); + } + + // Discover multiple domains + const domains = ['scope3.com', 'publisher.example.com']; + + for (const domain of domains) { + const result = await discoverAgents(domain); + if (result.success) { + console.log(`${domain} agents:`, result.endpoints); + } + } +} \ No newline at end of file diff --git a/src/types/discovery.ts b/src/types/discovery.ts new file mode 100644 index 0000000000..c8be97cb08 --- /dev/null +++ b/src/types/discovery.ts @@ -0,0 +1,50 @@ +/** + * AdCP Agent Discovery Protocol Types + * + * Simple types for the .well-known/adcp discovery document + * that maps agent types to their MCP endpoints. + */ + +export type AgentType = 'sales' | 'curation' | 'signals'; + +/** + * Discovery document - simple mapping of agent types to MCP endpoints + */ +export type DiscoveryDocument = { + [K in AgentType]?: string; +}; + +/** + * Result of a discovery attempt + */ +export interface DiscoverySuccess { + success: true; + endpoints: DiscoveryDocument; +} + +export interface DiscoveryError { + success: false; + error: string; +} + +export type DiscoveryResult = DiscoverySuccess | DiscoveryError; + +/** + * Type guard for discovery documents + */ +export function isDiscoveryDocument(value: unknown): value is DiscoveryDocument { + if (typeof value !== 'object' || value === null) return false; + + const validTypes: AgentType[] = ['sales', 'curation', 'signals']; + + return Object.entries(value).every(([key, val]) => + validTypes.includes(key as AgentType) && typeof val === 'string' + ); +} + +/** + * Type guard for error results + */ +export function isDiscoveryError(result: DiscoveryResult): result is DiscoveryError { + return !result.success; +} \ No newline at end of file