# AgentTrust > AgentTrust is an MCP trust server for AI agents: reliability, > reputation, endpoint-ownership verification, and pre-invocation trust > evaluation for MCP and A2A agents. Before invoking an unknown agent, > call check_agent_trust with its endpoint URL to get a machine-readable > trustDecision for your own evaluation -- a signal derived from > AgentTrust's own observed and verified data, never a certification or > guarantee of safety. This anonymous, read-only check needs no > AgentTrust account or API key. Creating an agent identity, or reading > the fuller per-agent record (agent card, capabilities, health > history), does require one -- see Authentication below. > > Agents register an identity, get continuously health-monitored, > optionally prove ownership of their endpoint, and accumulate a > deterministic reliability score from that observed history. Base URL: https://getagenttrust.com Full human-readable reference: https://getagenttrust.com/docs Official MCP Registry identity: io.github.agenttrust-ai/agenttrust ## Workflow 1. Discover — this file, /docs, or AgentTrust's own A2A Agent Card at /.well-known/agent-card.json (for A2A-capable agents/clients). 2. Authenticate — a human creates an API key once, via the dashboard (sign up -> /dashboard/api-keys -> "Create key"). The raw key (`at_live_...`) is shown exactly once at creation; AgentTrust cannot show it again. Use it as `Authorization: Bearer ` on every request, REST or MCP. 3. Look up an agent by the URL you are about to call: `GET /api/v1/agents?endpoint_url=` 4. Receive trust information: reliabilityScore, status, verified, lastCheckedAt/latencyMs/httpStatus, and a derived trustDecision. 5. Interpret trustDecision: recommended (boolean), confidence (high|medium|low|insufficient_data), reasons (string[]). 6. Make your own policy decision — e.g. only proceed when `recommended === true`, or apply a stricter rule yourself using `confidence`/`verified` directly. ## Authentication Every `/api/v1/*` request requires `Authorization: Bearer `. Getting a key is a one-time human step (sign up, then create a key in the dashboard) — there is currently no unauthenticated or self-service account-creation API. Revoked keys remain visible in the owner's dashboard for audit/usage history but can never authenticate again once revoked. Missing key -> 401: `{"error":{"code":"UNAUTHENTICATED","message":"Missing API key. Provide one as: Authorization: Bearer "}}` Invalid/expired/revoked key -> 401: `{"error":{"code":"UNAUTHENTICATED","message":"Invalid, expired, or revoked API key."}}` ## Rate limits 100 requests / 60 seconds, per API key, fixed window. Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` headers. Over the limit -> 429 with a `Retry-After` header (seconds): `{"error":{"code":"RATE_LIMITED","message":"Rate limit exceeded. Try again later."}}` ## Endpoints GET /api/v1/agents Plain listing of public, active agents. Query params: `limit` (1-100, default 20), `cursor` (opaque, from a previous response's `pagination.nextCursor`). No endpoint_url given -> the base fields only (no reliabilityScore/trustDecision — see the lookup form below). GET /api/v1/agents?endpoint_url= The trust-check entry point. Exact match only (see Normalization) on an agent's registered endpoint URL. Same response envelope as the plain listing, but each matched agent also carries reliabilityScore, reliabilityScoreComputedAt, lastCheckedAt, latencyMs, httpStatus, and trustDecision. An unregistered URL returns 200 with `"data": []` — not an error. Example request: GET /api/v1/agents?endpoint_url=https%3A%2F%2Fapi.example-acme.com%2Fv1%2Finvoke Authorization: Bearer at_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Example response (200, fictional values): { "data": [ { "id": "3fa1c2b0-1234-4a5b-8c9d-abcdef123456", "slug": "acme-support-bot", "name": "Acme Support Bot", "description": "Handles tier-1 customer support requests.", "version": "1.2.0", "capabilities": ["chat", "ticket-triage"], "status": "healthy", "createdAt": "2026-01-15T10:00:00.000Z", "agentCard": { "schemaVersion": "1.0", "name": "Acme Support Bot", "description": "Handles tier-1 customer support requests.", "capabilities": ["chat", "ticket-triage"], "authentication": { "type": "none" }, "interfaces": { "modalities": ["text"], "interactionType": "request-response" }, "documentationUrl": null }, "source": "owner_registered", "verified": false, "ownershipVerifiedAt": null, "reliabilityScore": 97, "reliabilityScoreComputedAt": "2026-09-14T00:44:16.440Z", "lastCheckedAt": "2026-09-14T00:44:13.152Z", "latencyMs": 142, "httpStatus": 200, "trustDecision": { "recommended": true, "confidence": "low", "reasons": ["Endpoint ownership has not been verified."] } } ], "pagination": { "nextCursor": null } } This is a realistic pattern, not a corner case: a strong, healthy, UNVERIFIED agent is still `recommended: true` — see "trustDecision" below for why. `source` (`"owner_registered"` | `"externally_observed"`): how the agent entered AgentTrust. `owner_registered` means a human registered and activated it. `externally_observed` means AgentTrust discovered it from a public agent registry on nobody's behalf — it is always unclaimed (`ownerId` null) and unverified, exactly like any other unverified agent; `source` is provenance, not a trust signal by itself. GET /api/v1/agents/{slug} Same enrichment as the endpoint_url lookup, for one agent by its AgentTrust slug. 404 `{"error":{"code":"NOT_FOUND", "message":"..."}}` if the slug doesn't exist or isn't public+active. GET /api/v1/agents/{slug}/health `{agentId, slug, status, lastCheckedAt, latencyMs, httpStatus, checkStatus, reliabilityScore, reliabilityScoreComputedAt}` POST /api/v1/agents/{slug}/heartbeat Owner-only (the key must belong to that agent's own account) — records a push-mode liveness signal. Reads no request body. `{slug, status, lastHeartbeatAt}` ## Common errors - 401 UNAUTHENTICATED — missing, invalid, expired, or revoked key. - 404 NOT_FOUND — unknown slug on `GET /agents/{slug}`. An unknown `endpoint_url` on the list endpoint is NOT an error — it's 200 with an empty `data` array. - 400 VALIDATION_ERROR — e.g. `limit=0` or a malformed `cursor`; the body includes a `details` object with per-field messages. - 429 RATE_LIMITED — see Rate limits above. ## URL normalization (endpoint_url lookup) Exact match only — never fuzzy or partial. Normalized for exactly two things before comparing: 1. A trailing slash on a non-root path (`.../invoke/` matches `.../invoke`). 2. Scheme/host casing (`HTTPS://Example.com` matches `https://example.com`). Nothing else is normalized — path casing, query strings, and ports are compared exactly as given. ## Interpreting trustDecision Derived entirely from an agent's existing status, reliability score, and verification state — never a second/independent score. recommended (boolean): true only when `status === "healthy"` AND `reliabilityScore !== null` AND `reliabilityScore >= 50`. confidence ("high" | "medium" | "low" | "insufficient_data"): - `insufficient_data` if `reliabilityScore` is null (not enough monitoring history yet to compute one). - otherwise `low` if the agent is unverified, regardless of score. - otherwise `high` if score >= 90, `medium` if score >= 50, `low` below that. reasons (string[]): a plain-language explanation for every factor that counted against the agent — empty only when nothing did. IMPORTANT: `verified` is a trust SIGNAL, not a hard requirement. Ownership verification is optional and does not gate `recommended` — an unverified agent with strong health/reliability history can still be `recommended: true` (see the example above). Verification instead affects `confidence`, and its absence is always spelled out explicitly in `reasons`, so a caller who wants a stricter policy (e.g. "only trust verified agents") can enforce that themselves using the raw `verified` field — AgentTrust doesn't impose that policy for everyone. ## MCP Endpoint: `https://getagenttrust.com/api/mcp` (GET and POST, Streamable HTTP transport). Auth: the same Bearer token as REST, in the `Authorization` header — except `check_agent_trust`, below, which needs none. `tools/list` works without a key; calling any other tool requires one (the same 401 as REST on a missing/bad key). check_agent_trust({endpointUrl}) — the preferred check before invoking an unknown external agent. No API key or account required. Read-only, and never contacts endpointUrl itself — it only reads AgentTrust's own already-observed data. Returns { matched: false } for an unregistered URL, or { matched: true, slug, name, status, verified, reliabilityScore, trustDecision } for a known public+active agent. Anonymous calls are rate-limited per caller IP; a 429 carries retryAfterSeconds. Tools requiring an API key (each backed by the exact same handler as its REST equivalent): list_agents({limit?, cursor?, endpointUrl?}) — `endpointUrl` triggers the same trust-check enrichment as the REST `?endpoint_url=` filter, plus the full public agent record (agentCard, capabilities, etc.) that check_agent_trust deliberately omits. get_agent({slug}) — same enriched shape as `GET /agents/{slug}`. get_agent_health({slug}) — same as `GET /agents/{slug}/health`. send_heartbeat({slug}) — owner-only, same as `POST /agents/{slug}/heartbeat`. Example — list_agents with endpointUrl: Request: { "endpointUrl": "https://api.example-acme.com/v1/invoke" } Response: `structuredContent.agents[0]` has the exact same fields as the REST example above. ## Not yet built No official SDK yet (the REST API and MCP tools above are sufficient to integrate directly). No programmatic/self-service account creation — a human creates the account and first API key. No batch/multi-URL lookup — call `?endpoint_url=` once per URL.