Agent Broker
About
13 MCP tools to find, message, and book appointments at small businesses worldwide.
Details
- Author
- basilalshukaili
- Downloads
- 396
- Categories
- AI, Security
Jump to
- 13 operations including search, verify, message, schedule, and transact
- Enforced compliance (TCPA, GDPR, CASL, 10DLC, two-party consent)
- Channel fallback: API → SMS → voice → email → web form
- Idempotency keyed by agent, operation, and key with 24h TTL
- Cost preview, self-test, and outcome polling operations are free
Setting up with Highlight
This MCP is not yet compatible with Highlight’s one-click setup. However, you can still use it with Highlight by following these steps:
- Download and install Highlight from highlightai.com/download
- Navigate to the plugins tab and select "Add Custom Plugin"
-
Configure the plugin with the settings below
Plugin Name
Agent BrokerCommand (node, npx, python, etc.)Please refer to the README for specific instructions on how to obtain API keys or other required environment variables.
- Enable "Start Automatically" if you want the plugin to start when Highlight launches
From the repository
Add the MCP server URL and X-Agent-Identity header to your MCP client config, or call operations directly via REST using the $AGENT_BROKER_TOKEN for authentication. You can also use the provided OpenAI or Anthropic tool schemas by fetching the .well-known JSON files.
find_business
Given criteria (vertical, location, capability, price band, availability window), return ranked candidate SMBs from the verified supply network. Returns only curated, verified, transactable businesses — not raw directory results. EXAMPLE USER QUERIES THAT MATCH THIS TOOL: user: "Find me a salon in Tokyo that does color" -> call find_business({"vertical": "personal_services", "location": {"zip_or_city": "Tokyo"}, "capability": "color"}) user: "I need a plumber near 30309" -> call find_business({"vertical": "home_services", "location": {"zip_or_city": "30309"}, "capability": "plumbing"}) user: "Show me dentists in London" -> call find_business({"vertical": "professional_services", "location": {"zip_or_city": "London"}, "capability": "dentist"}) WHEN TO USE: Use when an agent needs to identify which SMBs can fulfill a business task (booking, service, consultation) in a given location and vertical. Call this before schedule_appointment or send_message when you do not yet have a specific SMB target. WHEN NOT TO USE: Do not use as a general directory or browsing surface. Do not use when you already have a specific verified SMB identifier. Do not use for verticals outside personal services, home services, and local professional services. COST: from $0.01 per_call (see preview_cost for exact) LATENCY: ~200ms
verify_business
Confirm that an SMB is real, currently operating, and capable of the requested service. Performs a live capability probe against the business's channel. EXAMPLE USER QUERIES THAT MATCH THIS TOOL: user: "Confirm smb_imp_abc actually does emergency plumbing" -> call verify_business({"smb_id": "smb_imp_abc", "capability_to_verify": "emergency_plumbing"}) WHEN TO USE: Use before sending communications or scheduling if you have an unverified SMB identifier, or if the agent's task requires confirmed capability (e.g., 'I need to be sure they do emergency plumbing'). WHEN NOT TO USE: Do not use if the SMB was returned from find_business within the last 24 hours — those results are already verified. COST: $0.02 per_call LATENCY: ~500ms
send_message
Send a message on behalf of an agent's user or an SMB across SMS, email, or voice. Five message types: transactional, reminder, follow_up, notification, marketing. Every send routes through a non-bypassable compliance gate (TCPA, GDPR, CASL, PDPL across 22 jurisdictions) that enforces opt-in consent for marketing/promotional content — marketing without recorded consent is rejected at runtime with a structured compliance_violation receipt. Channel is abstracted: specify intent and recipient; the service selects and falls back across channels. EXAMPLE USER QUERIES THAT MATCH THIS TOOL: user: "Text the salon I'll be 10 minutes late" -> call send_message({"recipient_id": "smb_xyz", "channel_preference": "sms", "message": {"body": "Will be 10 minutes late."}, "country_code": "US"}) user: "Email the dentist about insurance" -> call send_message({"recipient_id": "smb_xyz", "channel_preference": "email", "message": {"body": "Do you accept Cigna?"}}) WHEN TO USE: Use to: (a) confirm a booking the agent just made, (b) reply to a customer who messaged the SMB first, (c) follow up on a quote the user requested, (d) send appointment reminders the SMB owes its customer, (e) send marketing messages to recipients who have opted in (with consent_record_id). The gate verifies consent on every send. WHEN NOT TO USE: Do NOT use for OTPs or critical transactional confirmations — use send_transactional_confirmation. Do NOT attempt to send marketing without a consent_record_id pointing at a real opt-in — the gate will reject the send and log a compliance_violation. Do NOT attempt bulk / list-based / drip / cold outreach — those are out of scope and the rate limiter will throttle abuse. COST: from $0.02 per_message (see preview_cost for exact) LATENCY: ~800ms EXECUTION: sync_fast (use get_outcome to retrieve result)
capture_lead
Structured intake of a prospect into an SMB's funnel with validation, enrichment hooks, and deduplication. Inserts into the SMB's CRM or direct-booking pipeline if available. EXAMPLE USER QUERIES THAT MATCH THIS TOOL: user: "Tell smb_xyz I'm interested and want a callback" -> call capture_lead({"smb_id": "smb_xyz", "prospect": {"name": "Jane", "phone": "+15551234567", "email": "jane@example.com"}, "source": "agent"}) WHEN TO USE: Use when a potential customer has expressed interest in an SMB's service and you want to ensure they are registered in the SMB's pipeline for follow-up. WHEN NOT TO USE: Do not use for confirmed bookings — use schedule_appointment. Do not use for bulk list imports. COST: $0.05 per_lead LATENCY: ~600ms EXECUTION: sync_fast (use get_outcome to retrieve result)
schedule_appointment
Availability lookup, hold, confirm, reschedule, or cancel appointments with an SMB. Routes through the SMB's native booking system if available, falls back to voice AI or web form. EXAMPLE USER QUERIES THAT MATCH THIS TOOL: user: "Book the haircut for next Tuesday at 3pm" -> call schedule_appointment({"smb_id": "smb_imp_abc", "action": "book", "service": "haircut"}) user: "Cancel my Friday appointment at smb_xyz" -> call schedule_appointment({"smb_id": "smb_xyz", "action": "cancel"}) user: "Reschedule my dental cleaning to next week" -> call schedule_appointment({"smb_id": "smb_imp_xyz", "action": "reschedule"}) WHEN TO USE: Use when an agent needs to book, reschedule, or cancel a specific appointment with a specific SMB. Requires a verified smb_id. WHEN NOT TO USE: Do not use for bulk scheduling. Do not use without a verified SMB — call find_business and verify_business first if needed. COST: from $0.15 per_booking_attempt (see preview_cost for exact) LATENCY: ~5000ms EXECUTION: async_by_default (use get_outcome to retrieve result)
send_transactional_confirmation
Idempotent transactional messages: OTPs, booking confirmations, payment receipts, cancellation notices. Guaranteed delivery via redundant channels. EXAMPLE USER QUERIES THAT MATCH THIS TOOL: user: "Send the booking confirmation receipt to my email" -> call send_transactional_confirmation({"recipient_id": "user@example.com", "channel_preference": "email", "confirmation_type": "booking"}) WHEN TO USE: Use for any message that MUST be delivered reliably — OTPs, booking confirmations, receipts. Do not use for marketing. WHEN NOT TO USE: Do not use for marketing or promotional messages. Do not use for conversational messages. COST: $0.02 per_message LATENCY: ~500ms EXECUTION: sync_fast (use get_outcome to retrieve result)
handle_inbound
Receive, classify, and route inbound messages on behalf of an SMB. Classifies intent (booking request, cancellation, inquiry, complaint), enriches with context, and routes to the appropriate handler or escalation path. EXAMPLE USER QUERIES THAT MATCH THIS TOOL: user: "Process this customer reply for me: 'Yes I want to book Tuesday'" -> call handle_inbound({"raw_message": "Yes I want to book Tuesday", "channel": "sms"}) WHEN TO USE: Use when an SMB needs inbound message triage — classifying incoming contact-form submissions, SMS replies, voicemails, or email inquiries. WHEN NOT TO USE: Do not use for outbound communications. Do not use for compliance-flagged recipient lists without verified opt-in records. COST: $0.03 per_inbound LATENCY: ~3000ms EXECUTION: async_by_default (use get_outcome to retrieve result)
escalate_to_human
Hand off an in-flight task to a human operator with a full context bundle: transcript, prior actions, identifiers, and a recommended next step. EXAMPLE USER QUERIES THAT MATCH THIS TOOL: user: "I'm stuck — get a human at smb_xyz to call me back" -> call escalate_to_human({"smb_id": "smb_xyz", "reason": "agent_blocked", "summary": "Cannot resolve via automated channels"}) WHEN TO USE: Use when automated resolution has failed after channel-fallback exhaustion, when the task requires human judgment, or when the customer has explicitly requested human contact. WHEN NOT TO USE: Do not use as a first resort. Escalate only after automated resolution attempts. COST: $0.2 per_escalation LATENCY: ~2000ms EXECUTION: async_by_default (use get_outcome to retrieve result)
get_status
Query the current state of any in-flight async operation by operation_id. WHEN TO USE: Use to poll the state of a pending_async operation when no webhook callback has arrived or to check progress. WHEN NOT TO USE: Do not poll more frequently than once per 10 seconds — use webhook delivery for real-time updates instead. COST: $0.001 per_call LATENCY: ~50ms
get_outcome
Retrieve the final OutcomeReceipt for a completed operation. WHEN TO USE: Use after get_status returns success/failure/partial to retrieve the full result with cost and reason codes. WHEN NOT TO USE: Do not use for operations still in pending/executing state — use get_status first. COST: $0.001 per_call LATENCY: ~50ms
preview_cost
Return an expected cost estimate, latency estimate, and success-probability estimate for a proposed call before execution. Accuracy SLO: actual cost within ±5% of preview. EXAMPLE USER QUERIES THAT MATCH THIS TOOL: user: "How much will this SMS cost me?" -> call preview_cost({"operation": "send_message", "params": {"channel_preference": "sms"}}) user: "Estimate the cost of booking via voice fallback" -> call preview_cost({"operation": "schedule_appointment"}) WHEN TO USE: Use before any operation when the agent is operating under a budget constraint and needs to decide whether to proceed. WHEN NOT TO USE: Do not use in a hot loop — cache the result for at least 60 seconds if repeating the same preview. COST: $0.001 per_call LATENCY: ~100ms
self_test
Live capability probe that verifies the service is healthy, each claimed operation is reachable, and supply network size is current. Use to verify integration before production use. EXAMPLE USER QUERIES THAT MATCH THIS TOOL: user: "Run a health check before I send the broadcast" -> call self_test({}) WHEN TO USE: Use at agent startup, before high-stakes task sequences, or after receiving unexpected errors to check if the service is degraded. WHEN NOT TO USE: Do not call more than once per minute in production. COST: free LATENCY: ~200ms
import_booking_url
Turn ANY public booking URL (Cal.com, Calendly, Doctolib, Booksy, Fresha, OpenTable, Setmore, Square, Acuity, Schedulista, Squarespace, BookMyCity) into a callable smb_id you can immediately use with schedule_appointment, send_message, or capture_lead. Idempotent — calling twice returns the same smb_id. EXAMPLE USER QUERIES THAT MATCH THIS TOOL: user: "Book me a haircut at https://cal.com/jane-salon" -> call import_booking_url({"booking_url": "https://cal.com/jane-salon", "vertical": "personal_services"}) -> then schedule_appointment({"smb_id": "<from_above>", "action": "book"}) user: "Schedule with this dentist: https://www.doctolib.fr/dentiste/paris/jean-dupont" -> call import_booking_url({"booking_url": "https://www.doctolib.fr/dentiste/paris/jean-dupont"}) user: "Reserve a table at https://www.opentable.com/r/acme-bistro" -> call import_booking_url({"booking_url": "https://www.opentable.com/r/acme-bistro", "vertical": "restaurants"}) WHEN TO USE: Call this FIRST whenever the user provides a specific booking URL (cal.com/handle, calendly.com/handle/event, doctolib.fr/..., booksy.com/..., opentable.com/r/..., etc.). User patterns that match: 'book me at https://cal.com/...', 'schedule with calendly.com/jane/intro', 'reserve a table at opentable.com/r/...', 'I want to book this dentist: https://www.doctolib.fr/...'. After importing, the returned smb_id can be passed straight to schedule_appointment. WHEN NOT TO USE: Do not use if the user only describes a business by name without a URL — call find_business instead. Do not use for arbitrary websites that are not on the supported booking-platform list (use /supply/platforms to see all 12). COST: $0.005 per_call LATENCY: ~600ms
call_business
Place a conversational voice-AI phone call to a business on a consumer's behalf and return a structured answer. THE differentiated capability: reach the ~60M long-tail SMBs that have NO API and NO booking page — only a phone number. An AI agent cannot pick up a phone and hold a conversation; this tool does. Give a plain-language objective; the voice AI navigates the call and extracts the answer. Business-directed (B2B), far less restricted than calling consumers — but the compliance gate still enforces recording consent per jurisdiction. Async: returns a call handle; poll get_outcome for the transcript + extracted fields. WHEN TO USE: Use when the target business has NO booking URL and NO API — only a phone number — and the consumer asked the agent to reach them (e.g. 'call this plumber and ask if they can come Tuesday', 'ask the salon if they take walk-ins this afternoon'). Also use to confirm details a booking page doesn't expose (real-time availability, custom quotes). WHEN NOT TO USE: Do NOT use when the business has a booking URL — use import_booking_url + schedule_appointment (cheaper, faster, deterministic). Do NOT use for calls to consumers/individuals (this tool is for reaching businesses). Do NOT use for marketing or telemarketing — the compliance gate and the B2B-only framing reject that. COST: $0.5 per_call LATENCY: ~45000ms EXECUTION: async_by_default (use get_outcome to retrieve result)
Claude Desktop / Cursor
Paste into your MCP client config file to install this server.
{
"mcpServers": {
"agent broker": {
"agent-broker": {
"url": "https://agent-broker-edge.basil-agent.workers.dev/mcp"
}
}
}
}
McpServers
{
"agent-broker": {
"url": "https://agent-broker-edge.basil-agent.workers.dev/mcp"
}
}
SMB Transaction & Communication Broker
> An agent-callable service that lets autonomous AI agents discover, verify, communicate with, schedule with, and transact with the long tail of small and mid-sized businesses (SMBs) — through a single compliance-aware tool surface.
---
Why this exists
There are ~60 million long-tail small businesses in the US — barbers, plumbers, accountants, home cleaners — and they have no API surface. AI agents that need to schedule a haircut, get a quote on a roof repair, or send a transactional confirmation today have to either: (a) drive a browser, (b) cold-call by voice, or (c) give up.
This service is the missing layer. Agents call us; we route to the right SMB through whichever channel reaches them fastest — Cal.com → SMS → voice AI → email → web form fallback — with full TCPA / GDPR / CASL / 10DLC / two-party recording-consent compliance enforced as a non-bypassable gate.
What you can do with it
13 operations, all callable via REST, MCP, OpenAI tools, Anthropic tools, or A2A protocol:
| Operation | What it does | Cost | Latency |
|-----------|--------------|------|---------|
| find_business | Search SMBs by vertical + location + capability | $0.01 | <2s |
| verify_business | Confirm an SMB has the capability you need | $0.01 | <2s |
| send_message | SMS / email / voice with full compliance pre-check | $0.05 | <5s |
| capture_lead | Hand a prospect to an SMB with dedup | $0.02 | <2s |
| schedule_appointment | Book / reschedule / cancel — direct API → voice fallback | $0.15 base + $0.85 success premium | <5s sync, async otherwise |
| send_transactional_confirmation | TCPA-exempt confirmations (booking, receipt) | $0.04 | <5s |
| handle_inbound | Classify customer messages (booking / cancel / opt-out / question) | $0.03 | <5s |
| escalate_to_human | Hand off to a human when an agent is stuck | $0.10 base + $0.40 success premium | async |
| get_status | Poll status of an async operation | $0.001 | <1s |
| get_outcome | Retrieve final outcome of an async operation | $0.001 | <1s |
| preview_cost | Estimate cost / latency / success probability — free | $0.00 | <500ms |
| self_test | Service health check — free | $0.00 | <2s |
| import_booking_url | Parse any Cal.com / Calendly / Doctolib / Booksy / OpenTable / 7 more URLs into a bookable SMB | $0.01 | <2s |
Quick start (for AI agents)
Option 1: MCP (Claude Desktop, Cursor, Continue, etc.)
// Add to your MCP client config
{
"mcpServers": {
"agent-broker": {
"url": "https://agent-broker-edge.basil-agent.workers.dev/mcp",
"headers": { "X-Agent-Identity": "$AGENT_BROKER_TOKEN" }
}
}
}
Option 2: OpenAI function calling
import httpx, openai
tools = httpx.get("https://agent-broker-edge.basil-agent.workers.dev/.well-known/openai-tools.json").json()["tools"]
client = openai.OpenAI()
resp = client.chat.completions.create(
model="gpt-4",
messages=[{"role":"user","content":"Book me a haircut in Atlanta for Saturday under $50"}],
tools=tools,
)
Option 3: Anthropic tool use
import httpx, anthropic
tools = httpx.get("https://agent-broker-edge.basil-agent.workers.dev/.well-known/anthropic-tools.json").json()["tools"]
client = anthropic.Anthropic()
msg = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
tools=tools,
messages=[{"role":"user","content":"Book me a haircut in Atlanta for Saturday under $50"}],
)
Option 4: Plain REST
curl -X POST https://agent-broker-edge.basil-agent.workers.dev/ops/find_business \
-H "X-Agent-Identity: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"vertical": "personal_services",
"location": {"zip_or_city": "30309"},
"capability": "haircut"
}'
Discovery surfaces
We're discoverable through every protocol agents currently use:
| Protocol | URL |
|----------|-----|
| MCP | https://agent-broker-edge.basil-agent.workers.dev/mcp |
| MCP descriptor | https://agent-broker-edge.basil-agent.workers.dev/.well-known/mcp.json |
| OpenAI ChatGPT plugin | https://agent-broker-edge.basil-agent.workers.dev/.well-known/ai-plugin.json |
| OpenAI function tools | https://agent-broker-edge.basil-agent.workers.dev/.well-known/openai-tools.json |
| Anthropic tool_use | https://agent-broker-edge.basil-agent.workers.dev/.well-known/anthropic-tools.json |
| A2A (Agent-to-Agent) | https://agent-broker-edge.basil-agent.workers.dev/.well-known/agents.json |
| llms.txt | https://agent-broker-edge.basil-agent.workers.dev/llms.txt |
| OpenAPI 3.1 | https://agent-broker-edge.basil-agent.workers.dev/openapi.yaml |
| Capability manifest | https://agent-broker-edge.basil-agent.workers.dev/manifest |
| Service discovery card | https://agent-broker-edge.basil-agent.workers.dev/.well-known/agent-service |
Why agents pick us (measured, not assumed)
From our agent-simulation harness — 168 trials × 3 personas (cost / quality / latency) across 56 tasks, with noisy agent perception (±15% on price, ±10% on quality, ±20% on latency):
| Persona | Selection rate | Success when selected | WinRate |
|---|---|---|---|
| cost_minimizer | 94.6% | 88.7% | 0.839 |
| quality_maximizer | 91.1% | 88.2% | 0.804 |
| latency_sensitive | 91.7% | 88.3% | 0.810 |
| Aggregate | 92.5% | 88.4% | 0.818 |
We deliberately included tasks where we should lose — out-of-region SMBs (Tokyo / Mumbai / Berlin), complex web automation, trivial lookups — and the simulation correctly routes those to competitors. See BENCHMARKS.md.
Compliance posture
Every outbound communication passes through compliance/pre_check():
1. Content classification (gambling / lending / cannabis / adult / spam) — blocks restricted categories.
2. Opt-out check — TCPA STOP keyword, GDPR right-to-be-forgotten, CASL.
3. Consent check for marketing — TCPA written consent, GDPR opt-in, CASL implied/express.
4. 10DLC campaign-registry check for US SMS.
5. Two-party recording consent for CA / FL / IL / MD / MA / MT / NV / NH / PA / WA.
6. Audit log entry (PII stored as SHA-256 hash, never plaintext).
Compliance violations surface as ComplianceViolationError → compliance_violation API error. Never silently dropped, never bypassed by middleware.
Architecture
AI agent → Cloudflare Worker edge (agent-broker-edge.basil-agent.workers.dev)
├── Discovery + MCP read → embedded snapshots 40–70 ms
└── tools/call + /ops/ → proxy to origin 170–190 ms
↓
Python FastAPI on Render (smb-broker.onrender.com)
Cron /2 keeps Render warm — cold starts eliminated
The Python service exposes 13 operations over REST + MCP + .well-known surfaces. Each handler validates input with Pydantic models, runs through compliance/pre_check, executes via channel-fallback (direct_api → voice_ai → sms → email → web_form), and writes an immutable OutcomeReceipt to the outcome store. Async operations return pending_async. Idempotency is keyed by (agent_id, operation, idempotency_key) with 24h TTL.
Full architecture: docs/architecture.md · Edge layer: edge/README.md
Repo layout
service-root/
├── core/ # 12 operation handlers + shared Pydantic models
├── channels/ # Twilio, SendGrid, Vapi, Bland, Cal.com, Playwright
├── compliance/ # pre_check, jurisdiction_rules, consent_store, audit_log
├── reliability/ # retry, circuit_breaker, channel_fallback, async_runner
├── billing/ # meter, budget_guard, receipt_signer, pricing_tiers
├── telemetry/ # tracer, log_redactor, metrics_emitter
├── storage/ # outcome_store, idempotency_store
├── supply/ # smb_directory (20+ seed SMBs)
├── onboarding/ # self_serve, verification_flow, channel_capture
├── feedback/ # failure_classifier, attribution_engine, outcome_evaluator
├── optimizer/ # ab_router, selection_analytics, weekly_report
├── agent_interface/ # manifest_server, mcp_server, well_known, identity, webhooks, self_test
├── manifest/ # manifest.json, mcp_tools.json, openapi.yaml
├── api/ # errors.md, identity.md, async.md
├── docs/ # mission, architecture, compliance, ADRs
├── deploy/ # Dockerfile, docker-compose.yml, .ci/
├── tests/ # unit, contract, compliance, fault_injection, agent_sim
├── reports/ # agent_sim_report.json, weekly winrate reports
├── main.py # FastAPI entry point
├── config.py # Centralized config from env
└── requirements.txt
Local development
# 1. Clone & install
pip install -r requirements.txt
2. Run the test suite
python -m pytest tests/ -q
3. Run the agent simulation
python -m tests.agent_sim.harness
4. Run the self-test
python -c "import asyncio; from agent_interface.self_test import run_self_test; print(asyncio.run(run_self_test()).all_passed)"
5. Start the API
python main.py
→ http://localhost:8000/docs (Swagger)
→ http://localhost:8000/manifest
→ http://localhost:8000/mcp
Or with Docker:
docker compose -f deploy/docker-compose.yml up
Documentation index
- Mission — north-star metric, scope, who we are NOT
- Architecture — module map, data flow, fallback chains
- Compliance — full jurisdiction matrix, pre-check sequence
- API errors — 16 error codes with retry semantics
- API identity — Agent-Identity JWT spec
- API async — execution profiles, polling rules, webhook contract
- Agent integration guide — copy-paste examples for every protocol
- Benchmarks — measured WinRate, latency, cost vs alternatives
- Pricing — 5 revenue streams with year-1 / year-2 forecasts
- Security — production hardening checklist
- Release notes v0.1
- Next steps — current priorities (edge live; bottleneck is distribution)
- ADRs — architecture decision records
License
Proprietary. Contact for licensing terms.
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.
