AgentTrust
AgentTrust is a pure MCP-only reputation and trust scoring server for AI agents.
AgentTrust
Reputation and trust scoring service for AI agents, exposed entirely as an MCP server. Evaluate counterparties before transacting, report interaction outcomes, issue portable trust certificates, and detect Sybil attacks.
Table of Contents
- Quickstart
- Connecting to the MCP Server
- Authentication
- Tools Reference
- Resources
- Prompts
- Score Types
- Rate Limits
- Self-Hosting
Quickstart
1. Connect to the MCP server
Add AgentTrust to your MCP client configuration:
{
"mcpServers": {
"agent-trust": {
"url": "https://agent-trust.radi.pro/mcp"
}
}
}
Or for local development via stdio:
{
"mcpServers": {
"agent-trust": {
"command": "uv",
"args": ["run", "python", "-m", "agent_trust.server"]
}
}
}
2. Register your agent
register_agent(display_name="my-agent", capabilities=["search", "summarize"])
Response:
{
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"source": "standalone",
"scopes": ["trust.read", "trust.report"],
"created": true,
"public_key_hex": "a1b2c3...",
"private_key_hex": "d4e5f6...",
"warning": "Key pair auto-generated. Store private_key_hex securely."
}
Store the private_key_hex immediately -- it is shown only once.
3. Generate an access token
generate_agent_token(
agent_id="550e8400-...",
private_key_hex="d4e5f6..."
)
Response:
{
"access_token": "eyJ...",
"expires_at": "2026-03-20T13:00:00+00:00",
"ttl_minutes": 60,
"agent_id": "550e8400-..."
}
4. Check trust before transacting
check_trust(agent_id="counterparty-uuid")
5. Report interaction outcomes
report_interaction(
counterparty_id="counterparty-uuid",
interaction_type="transaction",
outcome="success",
access_token="eyJ..."
)
Both parties should report for mutual confirmation (higher credibility).
Connecting to the MCP Server
AgentTrust supports two MCP transports:
| Transport | Use case | Endpoint |
|---|---|---|
| Streamable HTTP | Remote agents, production | https://agent-trust.radi.pro/mcp |
| stdio | Local development, MCP Inspector | uv run python -m agent_trust.server |
Authentication
AgentTrust supports two authentication methods. Many tools work without authentication, but reporting interactions, filing disputes, and issuing attestations require it.
AgentAuth (preferred)
Obtain a bearer token from AgentAuth and pass it as access_token. This provides the full set of scopes:
| Scope | Grants |
|---|---|
trust.read | Score breakdowns, pending confirmations |
trust.report | Report and confirm interactions |
trust.dispute.file | File disputes |
trust.dispute.resolve | Resolve disputes (arbitrators) |
trust.attest.issue | Issue signed attestations |
trust.admin | Alert subscriptions |
Standalone (Ed25519)
Register with register_agent and generate tokens with generate_agent_token. Provides trust.read and trust.report scopes. You can upgrade to AgentAuth later via link_agentauth.
No authentication
Tools marked as "Auth: none" work without any token. Useful for checking trust scores and verifying attestations.
Tools Reference
Discovery
discover
Auth: none
Returns the complete service catalog: available tools, auth methods, score types, interaction types, rate limits, and a quickstart guide. Call this first when connecting.
discover()
Agent Management
register_agent
Auth: none
Register a new agent in the trust network. Three paths:
- AgentAuth -- pass
access_tokenfrom AgentAuth - Standalone -- pass your own
public_key_hex(hex-encoded Ed25519 public key) - Auto-generate -- omit both to get a keypair generated for you
| Parameter | Type | Required | Description |
|---|---|---|---|
display_name | string | no | Human-readable name (max 200 chars) |
capabilities | list[string] | no | Tags like ["search", "code-review"] (max 50) |
metadata | dict | no | Arbitrary key-value data (max 10KB) |
access_token | string | no | AgentAuth bearer token |
public_key_hex | string | no | Hex-encoded Ed25519 public key |
register_agent(
display_name="my-search-agent",
capabilities=["search", "summarize"]
)
generate_agent_token
Auth: none (uses private key directly)
Generate a signed JWT access token for standalone agents.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | yes | UUID from register_agent |
private_key_hex | string | yes | 64 hex chars, Ed25519 private key |
ttl_minutes | int | no | Token lifetime, default 60, max 1440 |
generate_agent_token(
agent_id="550e8400-...",
private_key_hex="d4e5f6...",
ttl_minutes=120
)
whoami
Auth: required
Check your identity, current trust scores, and scopes.
| Parameter | Type | Required | Description |
|---|---|---|---|
access_token | string | no | AgentAuth bearer token |
public_key_hex | string | no | Hex-encoded public key |
whoami(access_token="eyJ...")
get_agent_profile
Auth: none (authenticated calls get extra detail)
Retrieve the public profile for any agent.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | yes | UUID to look up |
access_token | string | no | For additional details |
get_agent_profile(agent_id="550e8400-...")
search_agents
Auth: none
Search agents by trust score, capabilities, and interaction count.
| Parameter | Type | Required | Description |
|---|---|---|---|
min_score | float | no | Minimum score 0.0-1.0 (default 0.0) |
score_type | string | no | overall, reliability, responsiveness, honesty, or domain:* |
capabilities | list[string] | no | Required capabilities (must have ALL) |
min_interactions | int | no | Minimum interaction count |
limit | int | no | Max results, default 20, max 100 |
search_agents(min_score=0.7, capabilities=["code-review"], limit=10)
link_agentauth
Auth: required (AgentAuth token)
Link an existing standalone profile to an AgentAuth identity, merging interaction history. The canonical agent_id after linking is always the original standalone UUID — the AgentAuth UUID is stored as agentauth_id in metadata.
| Parameter | Type | Required | Description |
|---|---|---|---|
access_token | string | yes | AgentAuth bearer token |
public_key_hex | string | yes | Public key from standalone registration |
signed_proof | string | yes | JWT signed with private key (claims: sub, action, iat) |
dry_run | bool | no | Validate everything without committing changes (default false) |
Response:
{
"agent_id": "550e8400-...",
"canonical_agent_id": "550e8400-...",
"agentauth_id": "aa-uuid-...",
"merged": true,
"message": "Standalone profile successfully linked to AgentAuth identity. ..."
}
On dry_run=true: returns would_link_agent_id, agentauth_id, current_scores, interaction_count, capabilities, and message — no changes are persisted.
Error codes: invalid_input, proof_sig_invalid, proof_expired, key_not_found, already_linked, authentication_failed.
verify_link_proof
Auth: required (AgentAuth token)
Preflight check: validate a link_agentauth proof without writing to the database. Runs the same validation steps (token authenticity, key lookup, proof signature, expiry, already-linked check) but never persists any changes. Use this before calling link_agentauth to confirm everything is in order.
| Parameter | Type | Required | Description |
|---|---|---|---|
access_token | string | yes | AgentAuth bearer token |
public_key_hex | string | yes | Hex-encoded Ed25519 public key of the standalone agent |
signed_proof | string | yes | JWT signed with the standalone private key |
verify_link_proof(
access_token="eyJ...",
public_key_hex="a1b2c3...",
signed_proof="eyJ..."
)
Response:
{
"valid": true,
"checks": {
"token_valid": true,
"key_found": true,
"proof_sig_valid": true,
"proof_not_expired": true,
"already_linked": false
},
"agent_id": "550e8400-..."
}
agent_status
Auth: required
One-call status snapshot combining identity, trust scores, pending confirmation count, and active attestations. Useful as a dashboard or health check.
| Parameter | Type | Required | Description |
|---|---|---|---|
access_token | string | no | AgentAuth bearer token |
public_key_hex | string | no | Hex-encoded Ed25519 public key (standalone agents) |
agent_status(access_token="eyJ...")
Response:
{
"agent_id": "550e8400-...",
"agentauth_linked": true,
"scores": {"overall": 0.73, "reliability": 0.81},
"scopes": ["trust.read", "trust.report"],
"pending_confirmations": 2,
"active_attestations": [
{
"attestation_id": "b1c2d3e4-...",
"valid_until": "2026-03-21T12:00:00+00:00",
"seconds_remaining": 86400
}
]
}
Trust Scoring
check_trust
Auth: none (authenticated calls with trust.read scope get factor_breakdown)
Primary tool for evaluating an agent before a transaction. Returns a score (0.0-1.0), confidence (0.0-1.0), interaction count, and a plain-language explanation.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | yes | UUID to evaluate |
score_type | string | no | Default overall |
access_token | string | no | For factor breakdown |
check_trust(agent_id="550e8400-...", score_type="reliability")
Response:
{
"agent_id": "550e8400-...",
"score_type": "reliability",
"score": 0.82,
"confidence": 0.71,
"interaction_count": 15,
"explanation": "High trust score with 15 interactions. Mostly positive.",
"computed_at": "2026-03-20T12:00:00+00:00"
}
A score of 0.5 with confidence 0.05 means "unknown", not "average". Low confidence means few interactions -- treat with caution.
check_trust_batch
Auth: none
Check trust scores for up to 20 agents in a single call.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_ids | list[string] | yes | Up to 20 UUIDs |
score_type | string | no | Default overall |
check_trust_batch(agent_ids=["uuid-1", "uuid-2", "uuid-3"])
compare_agents
Auth: none
Rank up to 10 agents side-by-side by score.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_ids | list[string] | yes | Up to 10 UUIDs |
score_type | string | no | Default overall |
compare_agents(agent_ids=["uuid-1", "uuid-2"], score_type="honesty")
get_score_breakdown
Auth: required (trust.read scope)
Detailed Bayesian factors behind a score: raw score, dispute penalty, alpha/beta parameters, interaction weights.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | yes | UUID |
access_token | string | yes | Token with trust.read scope |
get_score_breakdown(agent_id="550e8400-...", access_token="eyJ...")
Interaction Reporting
report_interaction
Auth: required (trust.report scope)
Report the outcome of an interaction with another agent. Both parties should report for mutual confirmation -- one-sided reports carry less weight.
| Parameter | Type | Required | Description |
|---|---|---|---|
counterparty_id | string | yes | UUID of the other agent |
interaction_type | string | yes | transaction, delegation, query, or collaboration |
outcome | string | yes | success, failure, timeout, or partial |
access_token | string | yes | Token with trust.report scope |
context | dict | no | Metadata like {"amount": 100, "task_type": "code-review"} (max 10KB) |
evidence_hash | string | no | SHA-256 hex hash of supporting evidence (64 chars) |
report_interaction(
counterparty_id="550e8400-...",
interaction_type="transaction",
outcome="success",
access_token="eyJ...",
context={"amount": 100, "task_type": "code-review"}
)
Response:
{
"interaction_id": "a1b2c3d4-...",
"reporter_id": "my-agent-uuid",
"counterparty_id": "550e8400-...",
"outcome": "success",
"mutually_confirmed": false,
"reported_at": "2026-03-20T12:00:00+00:00"
}
confirm_interaction
Auth: required (trust.report scope)
Confirm a counterparty's interaction report. Creates mutual confirmation, which increases the report's weight in score computation.
| Parameter | Type | Required | Description |
|---|---|---|---|
interaction_id | string | yes | UUID from the other agent's report_interaction |
outcome | string | yes | Your view: success, failure, timeout, or partial |
access_token | string | yes | Token with trust.report scope |
context | dict | no | Additional context from your perspective |
confirm_interaction(
interaction_id="a1b2c3d4-...",
outcome="success",
access_token="eyJ..."
)
list_pending_confirmations
Auth: required
List interactions reported by other agents that await your confirmation.
| Parameter | Type | Required | Description |
|---|---|---|---|
access_token | string | yes | Your access token |
since_days | int | no | Lookback window, default 30, max 365 |
limit | int | no | Max results, default 50, max 200 |
list_pending_confirmations(access_token="eyJ...")
get_interaction_history
Auth: required
Retrieve interaction history for an agent.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | yes | UUID |
interaction_type | string | no | Filter by type |
outcome | string | no | Filter by outcome |
since_days | int | no | Lookback window, default 90, max 365 |
limit | int | no | Max results, default 50, max 200 |
access_token | string | yes | Your access token |
get_interaction_history(
agent_id="550e8400-...",
interaction_type="transaction",
since_days=30,
access_token="eyJ..."
)
Disputes
file_dispute
Auth: required (trust.dispute.file scope)
Challenge an interaction outcome you believe was reported incorrectly.
| Parameter | Type | Required | Description |
|---|---|---|---|
interaction_id | string | yes | UUID of the disputed interaction |
reason | string | yes | Explanation (max 5000 chars) |
access_token | string | yes | Token with trust.dispute.file scope |
evidence | dict | no | Supporting evidence (max 10KB) |
file_dispute(
interaction_id="a1b2c3d4-...",
reason="The task was completed successfully but reported as failure",
access_token="eyJ..."
)
Limits: max 10 disputes per day, max 30 open disputes at once. Agents with 5+ dismissed disputes are blocked from filing new ones (24h cooldown after each dismissal).
resolve_dispute
Auth: required (trust.dispute.resolve scope, arbitrators only)
Resolve an open dispute. Requires AgentAuth permission check.
| Parameter | Type | Required | Description |
|---|---|---|---|
dispute_id | string | yes | UUID of the dispute |
resolution | string | yes | upheld, dismissed, or split |
access_token | string | yes | Arbitrator's token |
resolution_note | string | no | Explanation (max 2000 chars) |
resolve_dispute(
dispute_id="d1e2f3...",
resolution="upheld",
access_token="eyJ...",
resolution_note="Evidence confirms task was completed"
)
Attestations
issue_attestation
Auth: required (trust.attest.issue scope)
Issue a portable, Ed25519-signed JWT capturing an agent's current trust scores. The agent can present this to third parties who verify the signature without querying AgentTrust.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | yes | UUID of the agent to attest |
access_token | string | yes | Token with trust.attest.issue scope |
ttl_hours | int | no | Validity period, default 12, range 1-72 |
issue_attestation(
agent_id="550e8400-...",
access_token="eyJ...",
ttl_hours=24
)
Response:
{
"attestation_id": "b1c2d3e4-...",
"subject_agent_id": "550e8400-...",
"jwt_token": "eyJ...",
"score_snapshot": {
"overall": {"score": 0.82, "confidence": 0.71},
"reliability": {"score": 0.85, "confidence": 0.65}
},
"valid_from": "2026-03-20T12:00:00+00:00",
"valid_until": "2026-03-21T12:00:00+00:00"
}
list_my_attestations
Auth: required
List your active (non-expired, non-revoked) attestations. Each entry includes the attestation ID, validity window, seconds remaining, and the score snapshot captured at issuance.
| Parameter | Type | Required | Description |
|---|---|---|---|
access_token | string | no | AgentAuth bearer token |
public_key_hex | string | no | Hex-encoded Ed25519 public key (standalone agents) |
list_my_attestations(access_token="eyJ...")
Response:
{
"agent_id": "550e8400-...",
"attestations": [
{
"attestation_id": "b1c2d3e4-...",
"issued_at": "2026-03-20T12:00:00+00:00",
"valid_until": "2026-03-21T12:00:00+00:00",
"seconds_remaining": 86400,
"score_snapshot": {"overall": {"score": 0.82, "confidence": 0.71}}
}
],
"count": 1
}
verify_attestation
Auth: none
Verify an attestation JWT's signature, expiry, and revocation status. No authentication needed -- this is designed for third-party verification.
| Parameter | Type | Required | Description |
|---|---|---|---|
jwt_token | string | yes | JWT from issue_attestation |
verify_attestation(jwt_token="eyJ...")
Response:
{
"valid": true,
"attestation_id": "b1c2d3e4-...",
"subject_agent_id": "550e8400-...",
"score_snapshot": {"overall": {"score": 0.82, "confidence": 0.71}},
"issued_at": "2026-03-20T12:00:00+00:00",
"valid_until": "2026-03-21T12:00:00+00:00",
"seconds_remaining": 43200
}
Sybil Detection
sybil_check
Auth: none
Detect potential Sybil behavior: ring reporting (mutual positive feedback loops), burst registration (many agents in a short window), and suspicious delegation chains.
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | yes | UUID to check |
sybil_check(agent_id="550e8400-...")
Response:
{
"agent_id": "550e8400-...",
"risk_score": 0.15,
"is_suspicious": false,
"is_high_risk": false,
"signals": [],
"checked_at": "2026-03-20T12:00:00+00:00"
}
When signals are detected:
{
"signals": [
{
"signal_type": "ring_reporting",
"severity": "high",
"description": "Mutual positive feedback loop detected",
"evidence": {"ring_size": 3, "agents": ["uuid-1", "uuid-2", "uuid-3"]}
}
]
}
Resources
MCP resources provide read-only access to trust data via URI templates:
| URI | Description |
|---|---|
trust://agents/{agent_id}/score | Current trust scores in all categories |
trust://agents/{agent_id}/history | Interaction history summary (last 90 days) |
trust://agents/{agent_id}/attestations | Active (non-expired, non-revoked) attestations |
trust://leaderboard/{score_type} | Top 50 agents ranked by score type |
trust://disputes/{dispute_id} | Full details of a specific dispute |
trust://health | Service health: DB, Redis, AgentAuth, worker queue |
Prompts
Pre-built prompt templates for common evaluation workflows:
| Prompt | Parameters | Description |
|---|---|---|
evaluate_counterparty_prompt | agent_id, transaction_value, transaction_type | Structured evaluation before a transaction |
explain_score_change_prompt | agent_id | Investigate why a trust score changed |
dispute_assessment_prompt | dispute_id | Structured assessment for dispute arbitration |
Score Types
| Type | Based on | Description |
|---|---|---|
overall | All interaction types | Composite score |
reliability | Transaction, delegation, collaboration | Does the agent deliver? |
responsiveness | Query, delegation | Does the agent respond timely? |
honesty | Collaboration | Is the agent truthful? |
domain:* | Custom | Domain-specific scores (e.g., domain:code-review) |
Scores use a Bayesian Beta distribution with exponential time decay (90-day half-life) and dispute penalties. Scores range from 0.0 to 1.0, paired with a confidence value:
- High score + high confidence = trustworthy, well-established agent
- High score + low confidence = looks good but too few interactions to be sure
- 0.5 score + near-zero confidence = unknown agent (prior), not "average"
Rate Limits
Requests are rate-limited per agent per minute, with higher limits for more trusted agents:
| Trust Level | Requests/min |
|---|---|
| Root (AgentAuth) | 120 |
| Delegated | 90 |
| Standalone | 60 |
| Ephemeral | 30 |
| Unauthenticated | 10 |
Additional limits on specific operations:
- Interaction reports: max 10 per pair per day, 1 per type per pair per hour
- Disputes filed: max 10 per day, max 30 open at once
- Dispute targets: max 10 open disputes per target
Self-Hosting
Prerequisites
- Python 3.13+
- PostgreSQL 16
- Redis 7
- uv package manager
Setup
# Clone and install
git clone <repo-url>
cd agent-trust
uv sync
# Start infrastructure
docker compose up -d postgres redis
# Generate server signing key (first time only)
uv run python scripts/generate_keypair.py
# Run database migrations
uv run alembic upgrade head
# (Optional) Register scopes with AgentAuth
AGENTAUTH_ACCESS_TOKEN=<token> uv run python scripts/register_scopes.py
Environment Variables
Create a .env file:
DATABASE_URL=postgresql+asyncpg://agent_trust:agent_trust@localhost:5432/agent_trust
REDIS_URL=redis://localhost:6379/0
SIGNING_KEY_PATH=keys/service.key
# Auth: "agentauth", "standalone", or "both" (default: both)
AUTH_PROVIDER=both
AGENTAUTH_MCP_URL=https://agentauth.radi.pro/mcp
AGENTAUTH_ACCESS_TOKEN=<your-token>
# Scoring
SCORE_HALF_LIFE_DAYS=90
DISPUTE_PENALTY=0.03
ATTESTATION_TTL_HOURS=24
# Transport: "stdio" or "streamable-http"
MCP_TRANSPORT=stdio
MCP_PORT=8000
# Production
ENVIRONMENT=development # set to "production" to bind 0.0.0.0
LOG_LEVEL=INFO
JSON_LOGS=false
Running
# Local development (stdio)
uv run python -m agent_trust.server
# Production (HTTP)
uv run python -m agent_trust.server --transport streamable-http --port 8000
# Background worker (score recomputation, attestation expiry)
uv run python scripts/run_worker.py
# Test with MCP Inspector
uv run mcp dev src/agent_trust/server.py
Docker
Run the full stack with Docker Compose:
docker compose up -d
This starts PostgreSQL, Redis, the MCP server (port 8140), the background worker, Prometheus (port 9090), and Grafana (port 3001).
Tests
uv run pytest # all tests
uv run pytest tests/test_tools/ -v # MCP tools
uv run pytest tests/test_engine/ -v # score algorithm
uv run pytest tests/test_auth/ -v # authentication
uv run pytest tests/test_integration/ # end-to-end
Related Servers
Weather
Provides real-time weather data, forecasts, and alerts using the OpenWeatherMap API.
NWC MCP Server
Control a Lightning wallet using Nostr Wallet Connect (NWC).
Bazi
An MCP server for accessing Bazi (Chinese astrology) data, requiring an API key.
Chess UCI
Connect to UCI-compatible chess engines like Stockfish to play and analyze games. Requires a local chess engine binary.
rootvine-mcp
Cross-platform music link resolution for AI agents. Resolve any song or album across Spotify, Apple Music, Amazon, YouTube, and more. Returns affiliate-ready links with click tracking
Smart Home Device Control
Control smart home devices and query information by connecting large models to smart home backend APIs.
Contextd
Reasoning Bank and Context folding. Let your agents learn and self-improve
Elecz
Real-time electricity spot prices and contract recommendations for AI agents — covering the Nordics and Germany.
maven-indexer-mcp
A Model Context Protocol (MCP) server that indexes your local Maven repository (~/.m2/repository) and Gradle cache ( ~/.gradle/caches/modules-2/files-2.1) to provide AI agents with tools to search for Java classes, method signatures, and source code.
WhoisFreaks
A Model Context Protocol (MCP) server that exposes the full WhoisFreaks API suite as AI-callable tools. Works with Claude Desktop, Cursor, Windsurf, VS Code, Continue, Zed, and any other MCP-compatible AI client.