Version: 0.2.0 (Draft) Status: Draft Authors: Yoann Arzu Last Updated: 2025-12-29
The Entity Discovery Protocol (EDP) defines a standard way for AI agents to discover which MCP (Model Context Protocol) servers can be used to interact with a given real-world entity (business, organization, or service provider).
While MCP defines how agents communicate with servers, and Server Cards (SEP-1649) describe what MCP servers can do, EDP answers a different question: "Which MCP servers serve this entity?"
| Term | Definition |
|---|---|
| Entity | A real-world business, organization, or service that can be interacted with via MCPs |
| MCP Provider | An organization that operates MCP servers on behalf of entities |
| Entity Card | A JSON document declaring which MCPs an entity delegates to |
| EDP Registry | A service that indexes entities and provides resolution APIs |
| Resolution | The process of finding which MCPs serve a given entity |
| Verification Level | A measure of trust in the entity-MCP association |
Entity Cards are intentionally minimal. They only declare MCP associations, not business metadata.
Business information (name, address, hours, etc.) should live on the entity's website using existing standards (Schema.org, Open Graph, etc.). EDP registries enrich entries by crawling these sources.
This separation ensures:
- Single responsibility: Entity Cards do one thing well
- No sync issues: Business info stays current on the source website
- Low friction: Easy to publish and maintain
Entity Cards SHOULD be published at:
https://{entity-domain}/.well-known/entity-card.json
This follows RFC 8615 for well-known URIs.
{
"schema_version": "0.2.0",
"domain": "string (required)",
"entities": [
{
"name": "string (required)",
"path": "string (optional)",
"location": {
"city": "string (optional)",
"country": "string (optional, ISO 3166-1 alpha-2)",
"coordinates": {
"lat": "number (optional)",
"lng": "number (optional)"
}
},
"mcps": [
{
"provider": "string (required)",
"endpoint": "string (required, URL)",
"entity_id": "string (optional)",
"capabilities": ["string (optional)"],
"priority": "number (optional, default: 0)",
"verification": {
"method": "string (optional)",
"signature": "string (optional)",
"issued_at": "string (optional, ISO 8601)",
"expires_at": "string (optional, ISO 8601)"
}
}
]
}
]
}A single domain can serve multiple entities (e.g., a restaurant chain with multiple locations). Each entity is identified by its path within the domain.
For example, acme-bistro.com may have:
/paris- Acme Bistro Paris/lyon- Acme Bistro Lyon
Each entity can have different MCP providers or entity_ids at the same provider.
The domain field MUST match the domain hosting the Entity Card.
For example, if the Entity Card is hosted at:
https://example-restaurant.com/.well-known/entity-card.json
Then the domain field MUST be example-restaurant.com.
Registries SHOULD reject Entity Cards where the domain does not match the hosting domain. This prevents impersonation attacks where a malicious site claims to be another business.
| Field | Type | Required | Description |
|---|---|---|---|
schema_version |
string | Yes | Version of the Entity Card schema (e.g., "0.2.0") |
domain |
string | Yes | The domain publishing this Entity Card |
entities |
array | Yes | List of entities at this domain |
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Human-readable name of the entity |
path |
string | No | Path within domain identifying this entity (e.g., "/paris") |
location |
object | No | Geographic location information |
mcps |
array | Yes | List of MCP associations for this entity |
| Field | Type | Required | Description |
|---|---|---|---|
city |
string | No | City where entity is located |
country |
string | No | ISO 3166-1 alpha-2 country code |
coordinates |
object | No | GPS coordinates (lat/lng) |
| Field | Type | Required | Description |
|---|---|---|---|
provider |
string | Yes | Identifier of the MCP provider |
endpoint |
string | Yes | MCP server endpoint URL (must be HTTPS) |
entity_id |
string | No | Entity's identifier at this provider |
capabilities |
array | No | List of capability identifiers |
priority |
number | No | Priority for capability conflicts (higher = preferred) |
verification |
object | No | Verification information |
Capabilities describe what actions are possible via an MCP. Recommended values:
Booking & Scheduling
reservations— Book appointments or tablesavailability— Check available slotscancellation— Cancel bookings
Commerce
ordering— Place orderspayments— Process paymentscatalog— Browse products/services
Information
menu— View menus/offeringshours— Operating hourscontact— Contact information
Communication
messaging— Send messagesnotifications— Receive updates
Capabilities are extensible. Providers may define custom capabilities using a namespaced format: provider:capability (e.g., acme:custom-feature).
MCP providers can register entities they serve with EDP registries, enabling bulk discovery without requiring each entity to publish its own Entity Card.
{
"provider": {
"id": "string (required)",
"name": "string (required)",
"endpoint": "string (required, URL)",
"public_key": "string (optional, PEM)",
"capabilities": ["string"]
},
"entities": [
{
"entity_id": "string (required)",
"domain": "string (optional)",
"name": "string (required)",
"category": "string (optional)",
"location": {
"city": "string (optional)",
"country": "string (optional, ISO 3166-1 alpha-2)",
"coordinates": {
"lat": "number",
"lng": "number"
}
},
"capabilities": ["string"]
}
],
"signature": "string (optional)"
}Note: Provider registration includes entity metadata because the provider is the authoritative source for entities they serve. This is different from Entity Cards, which only declare associations.
Important: Provider Registration is based on trust. A malicious provider could falsely claim to serve entities it doesn't actually serve. No technical mechanism can prevent this.
Mitigations:
- Registries SHOULD only accept registrations from verified, trusted providers (business partnerships)
- For higher assurance, registries SHOULD cross-reference with Entity Cards published by businesses
- Provider fraud is ultimately a legal/contractual issue, not a technical one
This is similar to Certificate Authorities in TLS: if a CA lies, the system fails. The protection is reputational and legal, not cryptographic.
| Level | Source | What it proves |
|---|---|---|
| 0 | Provider Registration only | Provider claims to serve this entity |
| 1 | Entity Card only | Business claims to delegate to this MCP |
| 2 | Both match | Mutual agreement between business and provider |
The entity appears in the registry via Provider Registration only. No Entity Card exists.
Trust model: Trust the provider entirely.
Use case: Bulk onboarding, low-risk interactions, bootstrapping.
Risk: Provider could falsely claim entities.
The business publishes an Entity Card at /.well-known/entity-card.json.
Trust model: Trust the business. Publishing to .well-known/ proves domain control—no additional verification needed.
Use case: Businesses without provider partnerships, self-hosted MCPs.
Risk: Business could claim to delegate to an MCP that doesn't actually serve them (MCP will reject unauthorized requests).
Both sources agree:
- Provider Registration declares the entity, AND
- Entity Card published by business points to the same provider
Trust model: Both parties independently confirm the relationship. An attacker would need to compromise both sides.
Use case: Production, bookings, sensitive actions.
How registries verify:
- Provider registers entity with
domain: "example-restaurant.com" - Registry fetches
https://example-restaurant.com/.well-known/entity-card.json - Entity Card lists the same provider → Level 2 confirmed
For additional assurance (non-repudiation, legal evidence), the provider can sign a JWT that the business publishes in their Entity Card.
This is optional for Level 2 but recommended for:
- Payment processing
- Legal compliance
- Audit trails
{
"header": {
"alg": "ES256",
"typ": "JWT",
"kid": "provider-key-2025"
},
"payload": {
"iss": "provider-id",
"sub": "entity-domain.com",
"entity_id": "entity-id-at-provider",
"capabilities": ["reservations", "menu"],
"iat": 1735344000,
"exp": 1766880000
}
}{
"schema_version": "0.2.0",
"domain": "example-restaurant.com",
"entities": [
{
"name": "Example Restaurant",
"mcps": [
{
"provider": "booking-provider",
"endpoint": "https://mcp.booking-provider.com",
"entity_id": "example-001",
"verification": {
"method": "signed_jwt",
"signature": "eyJhbGciOiJFUzI1NiIs...",
"issued_at": "2025-12-29T00:00:00Z",
"expires_at": "2026-12-29T00:00:00Z"
}
}
]
}
]
}The registry:
- Fetches the provider's public key (from registry or JWKS endpoint)
- Verifies the JWT signature
- Checks
issmatches the declared provider - Checks
submatches the entity's domain - Checks expiration
EDP registries SHOULD implement these endpoints:
GET /v1/resolve?query={search_query}&location={location}Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | Yes | Search query (name, keywords) |
location |
string | No | Location context (city, coordinates) |
capabilities |
string | No | Filter by capabilities (comma-separated) |
min_verification |
number | No | Minimum verification level (0-2) |
Response:
{
"results": [
{
"domain": "example-restaurant.com",
"entities": [
{
"name": "Example Restaurant Paris",
"path": "/paris",
"category": "restaurant",
"location": {
"city": "Paris",
"country": "FR",
"coordinates": { "lat": 48.8534, "lng": 2.3328 }
},
"verification_level": 2,
"mcps": [
{
"provider": "booking-provider",
"endpoint": "https://mcp.booking-provider.com",
"entity_id": "example-paris-001",
"capabilities": ["reservations", "menu"],
"verification": {
"level": 2,
"method": "signed_jwt",
"valid": true,
"expires_at": "2026-12-29T00:00:00Z"
}
}
]
}
],
"confidence": 0.95
}
]
}Note: Entity metadata (name, category, location) in the response is enriched by the registry, not taken directly from the Entity Card.
GET /v1/resolve/domain/{domain}
GET /v1/resolve/domain/{domain}?path={path}Returns all entities for a domain. Use the optional path parameter to filter to a specific entity.
Response:
{
"domain": "acme-bistro.com",
"entities": [
{
"name": "Acme Bistro Paris",
"path": "/paris",
"location": { "city": "Paris", "country": "FR" },
"verification_level": 2,
"mcps": [...]
},
{
"name": "Acme Bistro Lyon",
"path": "/lyon",
"location": { "city": "Lyon", "country": "FR" },
"verification_level": 1,
"mcps": [...]
}
]
}GET /v1/nearby?lat={latitude}&lng={longitude}&radius={meters}Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
lat |
number | Yes | Latitude |
lng |
number | Yes | Longitude |
radius |
number | No | Radius in meters (default: 1000) |
capabilities |
string | No | Filter by capabilities |
An attacker could publish an Entity Card claiming to be a legitimate business.
Mitigations:
- Verification levels (Section 4)
- MCP-side authorization (the MCP rejects unauthorized requests)
- Registry moderation
An attacker could claim a fake MCP endpoint.
Mitigations:
- Bidirectional signatures (Level 2)
- Provider registration with EDP registries
- HTTPS only for endpoints
Entity Cards may become outdated.
Mitigations:
- Regular crawling by registries
- Provider-side updates via registration API
- Expiration on signed claims
Entity Cards are public by design (they're published on websites).
Recommendations:
- Only declare public MCP associations
- Sensitive entity_ids can be opaque tokens
- Create
/.well-known/entity-card.json - List MCPs you delegate to
- (Optional) Obtain signed claims from providers for Level 2 verification
- Register with EDP registries
- Provide a public key for signature verification
- Generate signed JWTs for your customers
- Keep entity lists updated via registration API
- Crawl
.well-known/entity-card.jsonfiles - Accept provider registrations
- Enrich entries with business metadata (Schema.org, Google Business, etc.)
- Implement verification for all levels
- Provide resolution APIs
- Query EDP registries to resolve entities
- Check verification levels based on action sensitivity
- Cache results appropriately
- Handle multiple MCPs by capability matching
{
"schema_version": "0.2.0",
"domain": "example-restaurant.com",
"entities": [
{
"name": "Example Restaurant",
"mcps": [
{
"provider": "booking-provider",
"endpoint": "https://mcp.booking-provider.com",
"entity_id": "example-001"
}
]
}
]
}{
"schema_version": "0.2.0",
"domain": "acme-bistro.com",
"entities": [
{
"name": "Acme Bistro Paris",
"path": "/paris",
"location": {
"city": "Paris",
"country": "FR",
"coordinates": { "lat": 48.8566, "lng": 2.3522 }
},
"mcps": [
{
"provider": "booking-provider",
"endpoint": "https://mcp.booking-provider.com",
"entity_id": "acme-paris-001",
"capabilities": ["reservations", "availability", "menu"],
"priority": 10
},
{
"provider": "delivery-provider",
"endpoint": "https://mcp.delivery-provider.com",
"entity_id": "acme-del-paris-001",
"capabilities": ["ordering", "delivery_tracking"],
"priority": 5
}
]
},
{
"name": "Acme Bistro Lyon",
"path": "/lyon",
"location": {
"city": "Lyon",
"country": "FR"
},
"mcps": [
{
"provider": "booking-provider",
"endpoint": "https://mcp.booking-provider.com",
"entity_id": "acme-lyon-001",
"capabilities": ["reservations", "availability", "menu"]
}
]
}
]
}{
"schema_version": "0.2.0",
"domain": "example-restaurant.com",
"entities": [
{
"name": "Example Restaurant Downtown",
"path": "/downtown",
"mcps": [
{
"provider": "booking-provider",
"endpoint": "https://mcp.booking-provider.com",
"entity_id": "example-downtown-001",
"capabilities": ["reservations", "menu"],
"verification": {
"method": "signed_jwt",
"signature": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
"issued_at": "2025-12-29T00:00:00Z",
"expires_at": "2026-12-29T00:00:00Z"
}
}
]
}
]
}- Breaking change: Add multi-entity support via
entities[]array - Move MCPs from root level to per-entity
- Add
name,path, andlocationfields to entities - Update Resolution API to return
entities[]array - One domain can now serve multiple locations/entities
- Initial draft specification
- Minimal Entity Card format (no business metadata)
- MCP Provider registration
- Verification levels (0-2)
- Resolution API