A2A (Agent-to-Agent) is the standard Google opened in 2025 — one agent discovers another agent's capabilities, identity, and skill catalogue. AIDE's proto-a2a-agent-card check feeds this manifest into your discoverability score.
What AIDE actually checks
proto-a2a-agent-card validates four things in order:
- Does
https://<domain>/.well-known/agent.jsonreturn HTTP 200? - Does the JSON match the A2A v0.2 schema — required
name,url,version,capabilities,skills? - Does
skillscontain at least oneid+descriptionpair? - Is the
endpointURL reachable at scan time?
Steps 3 and 4 also feed proto-agent-skills — neither check tops out without the other.
Minimum Agent Card
{
"name": "AIDE Scan Agent",
"description": "Agent that runs AI-readiness scans and returns a score.",
"url": "https://api.aide.tr",
"version": "1.0.0",
"documentationUrl": "https://aide.tr/docs",
"provider": {
"organization": "AIDE",
"url": "https://aide.tr"
},
"capabilities": {
"streaming": true,
"pushNotifications": true,
"stateTransitionHistory": true
},
"authentication": {
"schemes": ["bearer"]
},
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["application/json"],
"skills": [
{
"id": "scan_url",
"name": "Scan a site",
"description": "Tests a URL against 25+ standards and returns an AI-readiness score 0–100.",
"tags": ["seo", "ai-readiness", "audit"],
"examples": [
"Scan https://example.com",
"How AI-ready is stripe.com?"
],
"inputModes": ["text/plain"],
"outputModes": ["application/json"]
}
]
}
Critical fields:
| Field | Why | AIDE impact |
|---|---|---|
| name + description | The dispatcher reads these to understand purpose | Missing → parse error → FAIL |
| url | A2A endpoint base URL | Must be HTTPS, otherwise WARNING |
| capabilities.streaming | Required for long-running tasks | Affects proto-agent-skills score |
| authentication.schemes | Which auth mechanism the endpoint expects | Unknown scheme → WARNING |
| skills[].id + description | Agents embed this pair to pick the right skill | Empty → skill is undiscoverable |
Step-by-step setup
1. Generate the JSON
A static public/.well-known/agent.json is the fastest path. But version + skills change often, so template-driven generation stays cleaner:
# scripts/gen_agent_card.py
import json
from pathlib import Path
CARD = {
"name": "AIDE Scan Agent",
"description": "Agent that runs AI-readiness scans and returns a score.",
"url": "https://api.aide.tr",
"version": "1.0.0",
"capabilities": {"streaming": True, "pushNotifications": True, "stateTransitionHistory": True},
"authentication": {"schemes": ["bearer"]},
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["application/json"],
"skills": [...], # load from a separate skills.yaml
}
Path("apps/web/public/.well-known/agent.json").write_text(
json.dumps(CARD, indent=2, ensure_ascii=False)
)
Wire this into CI build — when skills change, the manifest can't drift.
2. Serve it via Caddy/Nginx
Next.js serves public/ natively, and .well-known lives under it. But some frameworks reserve that path. To be safe at the proxy:
aide.tr {
handle_path /.well-known/* {
root * /opt/aide/apps/web/public/.well-known
file_server
header Content-Type application/json
header Cache-Control "public, max-age=300"
}
# ... other handlers
}
5-minute cache — dispatchers don't poll the manifest often, but you don't want to delay refreshes either.
3. Content type + CORS
The A2A manifest must allow CORS — remote agents may fetch from a browser:
header /.well-known/agent.json Access-Control-Allow-Origin "*"
header /.well-known/agent.json Access-Control-Allow-Methods "GET, OPTIONS"
Content-Type must be application/json, never text/plain.
Skills design: common mistakes
| Mistake | Symptom | Fix |
|---|---|---|
| One mega-skill (do_anything) | Dispatcher can't decide when to call it | Split skills by task (scan_url, get_score_history, compare_sites) |
| examples empty or abstract | Embedding match is weak; skill never picked | Provide ≥3 concrete user-language examples ("Can you scan X?") |
| Random tags | Discovery filters miss you | Use a standardized tag taxonomy (seo, audit, accessibility, security) |
| inputModes only JSON | Voice agents can't reach you | Add text/plain, optionally audio/wav |
Authentication scheme choice
A2A v0.2 recognises three schemes:
none— Read-only public APIs. AIDE accepts this but it's rare in production.bearer— API key or OAuth access token. Most common.oauth2— Full OAuth flow. Pair it withproto-oauth-discoveryso agent dispatchers can complete the dance.
AIDE scores all three equally; the test is the declared scheme is enforced. If the manifest says bearer but the endpoint serves anonymous calls → WARNING.
A2A vs MCP — which one?
These aren't substitutes:
- MCP = an agent invokes a tool (like a function call). Semi-imperative.
- A2A = an agent delegates to another agent. Fully-autonomous, semi-async.
Most products publish both:
- MCP server → for Claude Desktop / IDE integrations
- Agent Card → for other agents to discover you (Google Vertex AI Agent Builder, OpenAI Swarm, etc.)
AIDE scores them separately — your ai_ready score won't top out if either is missing.
Verification
After publishing:
curl -s https://your-site.com/.well-known/agent.json | jq '.skills | length'
Should return ≥1. Then run an AIDE scan:
curl -X POST https://api.aide.tr/v1/scans \
-H 'Content-Type: application/json' \
-d '{"url":"https://your-site.com","profile":"ai_ready"}'
Both proto-a2a-agent-card and proto-agent-skills should report PASS.
Related resources
- A2A Protocol Spec
- Agent Card Schema
- AIDE check details:
/learn/proto-a2a-agent-card,/learn/proto-agent-skills