---
title: "MCP Server for Company Identity"
summary: "Entity Bind's MCP server gives an agent eleven tools over stdio or streamable HTTP. Tenancy comes from the caller's own key, and an abstention costs nothing."
canonical_url: https://entitybind.com/mcp
last_updated: 2026-10-15
copy: full
---

# MCP Server for Company Identity

> Entity Bind's MCP server gives an agent eleven tools over stdio or streamable HTTP. Tenancy comes from the caller's own key, and an abstention costs nothing.

Canonical page: https://entitybind.com/mcp
Last updated: 2026-10-15

## Eleven tools your agent can call, and a refusal it can act on

A resolve over MCP is the same resolve, at the same price, against the same rate-limit bucket.

Your agent is about to raise a purchase order and needs to know whether the company on the invoice is the supplier you already pay. Point a client at this server and it can resolve that company, compare two records, walk the identifier graph out from an entity, read the receipt for any decision it made, check what it has spent and work its own review queue. Two of the eleven tools cost credits. Nine are free.

When the evidence will not carry a match the tool says so, ranks what it was choosing between, names the column that would settle it, and charges nothing. Same pipeline as the REST API: same cascade, same 0.90 confidence floor, same credit rule, same receipt.

- Tools: 11, two of them billable
- Transports: stdio and streamable HTTP
- Credential: The rk_ key you already hold
- Sessions: Off. Stateless by design
- An abstention costs: 0 credits

What happens next: Tell us what your agent resolves. The first slice we ask for is your agent's own traffic rather than ours. [Start free →](https://app.entitybind.com/app?signup=1)

Self-service. Create your workspace online and verify your email to start matching company records. Free starts with 300 credits and no card. Paid monthly plans are available in workspace Billing; VAT is added where applicable. Usage is tracked without automatic overage charges. [Trust and security](https://entitybind.com/trust)

Read off the product repository’s own MCP document. Where the two disagree, the code wins and this page is the bug.

## The eleven tools

Two spend credits. The other nine are free and unmetered — every read, enrichment discovery, the review queue and [the receipt that redeems a decision](https://entitybind.com/glossary/decision-receipt) — so an agent can check its own work without paying to.

### `resolve_company`

Resolve one company record to an entity, or abstain with a ranked shortlist.

Billable · Scope: RESOLVE · Ledger: mcp.resolve_company

Arguments: name, website, domain, email, phone, registration_number, jurisdiction, lei, country — at least one identifying — plus depth (default research) and enrich (default true). resolve_company does not currently accept context or address; use REST /v1/resolve to evaluate those input shapes

Send one company record and get a decision back: entity_id, match_confidence, entity_created, replayed, a decision_id that reopens the evidence trail, and a usage block. Where the evidence will not carry a match, the same call returns the ranked shortlist instead and costs nothing. The whole cascade sits behind this one tool call, and the credential caps how deep it may run before the cascade starts, so a key pinned to the probabilistic stage cannot reach agentic research by choosing this surface — it abstains instead. Capping depth saves money without lowering precision, because the alternative to a deep answer is unresolved rather than a cheap guess. Accepted matches must clear the 0.90 floor. Read match_confidence_basis alongside the score. Confidence bounds depend on the recorded evidence population; there is no universal score for every match. Read match_confidence_basis and the evidence. A 0.99 filter can return some v8 adjudicated matches while excluding valid identifier matches, so validate its coverage cost on your own inputs.

### `match_records`

Compare two records and report whether they are the same company.

Billable · Scope: MATCH · Ledger: mcp.match_records

Arguments: left and right — record dicts, each also accepting address and extra; each side needs at least one identifying field

Hand it two records and it tells you whether they are the same company. The comparison is stateless, and both of this tool's properties follow from that. It takes no depth argument at all, so your plan's ceiling is the only cap in play. And it mints no receipt — no decision_id, no review_id, no audit row — so resolve rather than match when you need the evidence trail. A pair where either side carries nothing identifying is never scored: probability comes back null rather than a number. Scoring it would mean falling back on the no-evidence prior, the default the scorer holds before it has looked at anything, and returning that as a confident no_match about two records nobody actually compared.

No replay path: the comparison is stateless, so there is nothing to replay.

### `get_entity`

Read one entity's golden record — the merged view your tenant holds today.

Free, unmetered · Scope: READ

Arguments: entity_id (maximum 64 characters)

Read the entity as your tenant holds it now. Every fact arrives with the date it was first seen, the date it was last checked and the number of times a source has confirmed it, rather than as a single undated value. An id belonging to another tenant reports not found, never forbidden — a distinguishable “not yours” would confirm the id exists, and an agent could then walk the id space to learn what other tenants hold. That is an enumeration oracle, and one uniform reply is what prevents it.

### `get_crosswalk`

Walk the identifier graph out from one entity.

Free, unmetered · Scope: READ

Arguments: entity_id (maximum 64 characters), max_hops (default 1, range 1–10), as_of (source-effective date)

Start at one entity and collect every identifier it is known by: LEI, company number, CIK, ticker and digital identifiers, each edge carrying the source it was cited from. Keep two answers apart. An identifier we looked for and did not find is not the same as one we never had a source configured for, and the payload distinguishes them rather than returning a shared null. as_of reads the graph as the sources themselves said it stood on that date, rather than as it stood when we ingested them.

### `get_enrichment_fields`

Discover field availability, suppliers and caller resale rights.

Free, unmetered · Scope: READ

Arguments: None

Find out what a field is worth asking for before you ask for it. This is the same field catalogue as GET /v1/reference/enrichment-fields, and it distinguishes available, not_configured, unavailable, pass_through_only and not_supported fields. A configured field means a source is wired, not that a value exists: it guarantees neither a value for any particular company nor continuous refresh. Optional company-data exports bundle no supplier or data rights.

### `search_entities`

Look up known entities by name or identifier, without resolving anything.

Free, unmetered · Scope: READ

Arguments: name, domain, registration_number, lei, country — at least one retrievable — plus limit (default 10, range 1–100)

Look a company up without committing to anything: this tool writes nothing, spends nothing and binds nothing. The result worth writing into an agent's prompt is the empty one. An empty search_entities response is not a claim that the company does not exist. It says only that no known entity shares an identifier or a name key with the input, which is a statement about this store and not about the world.

### `get_decision`

Reopen the receipt a resolve returned.

Free, unmetered · Scope: READ

Arguments: decision_id (maximum 64 characters)

Open a decision back up and see what it rested on: the features that were compared, the thresholds in force at the time, the evidence each side carried, and enough to reproduce the outcome offline. This is how an agent turns “I matched it” into something a person can check a year later. It is free, so reading the receipt on every decision costs an agent nothing but context.

### `get_usage`

Read what this tenant has spent over a date window.

Free, unmetered · Scope: READ

Arguments: since and until — UTC dates. The window is half-open: it ends at the start of the day after until, so both dates you name are counted in full

Check what this tenant has already spent before deciding how much more to spend: credits and volume over a date window you choose. One field is routinely misread. automation_rate is the share of calls answered automatically, not an accuracy rate. 742 of 1,176 pairs were decided automatically in the September 5 saved gate report. This measures automation, separately from accepted-merge precision. The current pairwise automation rate is 63.10%, measured on the continuous-integration fixture rather than on your traffic. Precision cannot be read from usage at all: review items exist only where the pipeline abstained, so the automatically merged population carries no labels to score.

### `review_queue_list`

List the abstentions waiting for a human verdict.

Free, unmetered · Scope: REVIEW

Arguments: limit (default 20, range 1–100), offset (default 0), status = open | resolved | all, stage, record_id, min_confidence (0–1), sort = oldest_first | newest_first | confidence_desc | confidence_asc

Page through the queue an abstention lands in, filtered by stage, record, status or confidence. Sort by confidence descending and the near-misses come first, which is the order a reviewer working through a backlog actually wants: the rows where one more column would have decided it, ahead of the rows where nothing would have.

### `review_queue_get`

Read one review item, with the shortlist that produced it.

Free, unmetered · Scope: REVIEW

Arguments: review_id (maximum 64 characters)

Read one item and see everything the pipeline saw before it stopped: the candidates, and for each one a calibrated probability — how often decisions scoring like this turned out right in recorded trials. Where the adjudicator, the model asked to settle an escalated pair, was consulted, you also get its verdict and the confidence it reported about itself. That self-report is uncalibrated: it has not been checked against recorded outcomes the way the probabilities beside it have. Both adjudicator fields are null when the model never saw that candidate, which is the usual case, because only the escalated pair is adjudicated — so a null there means not asked rather than not sure.

### `review_submit`

Settle one review item with a human verdict.

Free, unmetered · Scope: REVIEW

Arguments: review_id, decision = match | no_match, reviewer_id (maximum 255 characters, an optional equality assertion), reason (maximum 2,000 characters)

Close one open item with a person's verdict and a reason. This is the only tool on the surface that changes what the store says, and it needs a review credential a resolve key does not have. A merge a human reviewer approves is recorded against the reviewer, excluded from the automated population every accuracy figure is measured over, and withheld on replay. That exclusion is why a human verdict cannot inflate a published rate: the automated population every accuracy figure is measured over never contains one.

### Ledger rows, and what a misspelt argument does

An unknown field raises an error before the tool body runs.

Only resolve_company and match_records write a ledger row, as mcp.resolve_company and mcp.match_records, so a usage export can separate what the agent surface cost from which tool agents are actually paying for. The other nine write no usage-ledger row; review_submit can still apply a verdict and change entity membership.

Argument caps come off the tool signature before the body runs, and every generated argument model refuses unknown fields. A model that writes registered_number for registration_number gets an error. A silently dropped value would resolve a record stripped of its strongest identifier and return a confident answer to the wrong question.

resolve_company is one tool call over a seven-stage cascade that stops at the cheapest stage able to decide. [See the cascade →](https://entitybind.com/how-it-works)

## What it looks like when the tool does not know

An accounts payable agent has an invoice in front of it and a supplier master behind it. Before it raises a purchase order it has to know whether the company on the invoice is the supplier already being paid.

### The exchange in full

Two resolve calls and one receipt read. The abstention is the turn that mattered.

**Operator**

Before we raise the PO, check that Ardent Systems on this invoice is the supplier we already pay.

**call · resolve_company**

```json
{
  "name": "Ardent Systems",
  "country": "GB",
  "depth": "research"
}
```

**result · resolve_company**

```json
{
  "decision": "unresolved",
  "match_confidence": 0.0,
  "more_input_needed": true,
  "depth_capped": false,
  "escalation_unavailable": false,
  "no_identifying_signal": false,
  "candidates": [
    {"entity_id": "ent_4c81...", "legal_name": "ARDENT SYSTEMS LTD",
     "domain": "ardentsystems.co.uk", "probability": 0.61,
     "adjudicator_decision": null, "adjudicator_confidence": null},
    {"entity_id": null, "external": true, "legal_name": "ARDENT SYSTEMS (UK) LIMITED",
     "domain": null, "probability": 0.58,
     "adjudicator_decision": null, "adjudicator_confidence": null},
    {"entity_id": "ent_9a20...", "legal_name": "ARDENT SYSTEMS GROUP LTD",
     "domain": "ardent-group.com", "probability": 0.54,
     "adjudicator_decision": null, "adjudicator_confidence": null}
  ],
  "review_id": "rev_2f7b...",
  "decision_id": "dec_5e13...",
  "usage": {"credits_charged": 0, "credits_remaining": 4871,
            "deep_attempts_remaining": 2988, "plan": "growth", "enforced": false}
}
```

**Agent reasoning**

Three live candidates, none above the floor, and more_input_needed is true — so the paid stages already ran and could not separate them. Retrying deeper on the same three fields will return this same answer and spend credits doing it. The second row carries entity_id null and external true, which means it is a register company nobody has stored yet, so I must not hand that id to anything. The abstention cost 0 credits. What I need is one column the invoice has and I did not send.

**Agent to operator**

I cannot bind this safely. Three live GB companies are registered under that name, and nothing on the record separates them — the closest is ARDENT SYSTEMS LTD at 0.61, which is well below the 0.90 floor. The one column that would settle it is the company registration number, usually in the invoice footer next to the VAT number. An LEI or the supplier's own domain would do it too. Finding that out cost nothing.

**Operator**

Footer says company number 09884412.

**call · resolve_company**

```json
{
  "name": "Ardent Systems",
  "registration_number": "09884412",
  "jurisdiction": "GB",
  "country": "GB"
}
```

**result · resolve_company**

```json
{
  "decision": "match",
  "entity_id": "ent_9a20...",
  "match_confidence": 0.94,
  "match_confidence_basis": "identifier_rule",
  "stage": "deterministic",
  "entity_created": false,
  "replayed": false,
  "decision_id": "dec_7c02...",
  "usage": {"credits_charged": 1, "credits_remaining": 4870,
            "deep_attempts_remaining": 2988, "plan": "growth", "enforced": false}
}
```

**call · get_decision**

```json
{"decision_id": "dec_7c02..."}
```

**Agent to operator**

Resolved, and it is not the supplier you think. The registration number binds to ent_9a20, which is ARDENT SYSTEMS GROUP LTD — an identifier agreement at the deterministic stage, 0.94 confidence, 1 credit. Your supplier master has this vendor pointed at ent_4c81, ARDENT SYSTEMS LTD, a different legal entity with a similar trading name and a different bank record. Receipt dec_7c02 has the comparison. I have not changed anything; the PO is still yours to raise.

**What that cost**

Two resolve calls and one receipt read. The abstention was free, the identifier match was one credit, and get_decision is unmetered. The turn that changed the outcome was the one where the tool said it did not know.

Composed from the documented response shapes. No live log was replayed to produce it, and identifiers are illustrative and truncated. Accepted matches must clear the 0.90 floor; confidence values depend on their evidence population. Confidence bounds depend on the recorded evidence population; there is no universal score for every match. Read match_confidence_basis and the evidence. A 0.99 filter can return some v8 adjudicated matches while excluding valid identifier matches, so validate its coverage cost on your own inputs.

### What an agent builder takes from it

Four readings, including the two ways an agent can get an abstention wrong.

- The abstention is the useful turn, not the failed one. It carried a typed reason, a ranked shortlist and the name of the column that would decide it — which is what let the agent ask a specific question instead of apologising or guessing.
- An agent that treats unresolved as no_match gets this wrong twice: it concludes the supplier is new, and it never asks the question that would have caught the mismatch.
- Every candidate row that came from a register rather than the store carries entity_id null and external true. An agent that reads ids off a shortlist without checking that would have handed a downstream call an id that does not resolve.
- The receipt is free and the abstention is free. The only credit spent on this whole exchange was the one that bought a decision.

> unresolved is the product working. It is not an error, not a null, and not a no_match.
>
> docs/MCP.md, the product repository

## Four flags, and what each one tells an agent to do

Two of the four are worth retrying and two are not. An agent that cannot tell them apart will spend credits to receive the same honest answer.

### `more_input_needed` · retrying will not help

The paid stages could not separate the shortlist: nothing discriminating on the record, the name alone agreeing in the closest comparison, and a tie at the top of the ranking.

Do next. Ask for one more column — a domain, a VAT number, a registration number or an LEI — and resubmit. It is the flag that lets an agent behave like a good colleague rather than a stubborn one: one specific question, rather than the same call sent again and deeper.

### `depth_capped` · retrying may help

The cascade reached the ceiling your depth argument or your plan set, and stopped there rather than spending past it.

Do next. Raising depth, or a plan permitting more, may resolve it on the same input.

### `escalation_unavailable` · retrying may help

A dependency the deep escalation needed was absent or failed — the adjudicator, or a research or discrimination pass. The receipt's research_unavailable feature says which.

Do next. Retry it. This is a transient failure rather than a settled cannot-tell, so the same input may resolve on a second call.

### `no_identifying_signal` · retrying will not help

The fields you sent normalised to nothing. No comparison happened, no entity was created, no review item exists and review_id is null.

Do next. Send field values that survive normalisation rather than raising depth: this is the only abstention an agent can fix by itself, and the only one where retrying deeper is guaranteed to be wasted spend. This surface produces it most, because here a model is writing the field values.

On [an abstention](https://entitybind.com/glossary/abstention) the top-level match_confidence is 0.0. That is a statement about binding: nothing was bound. How close each candidate came sits on the shortlist rows, as a calibrated probability per candidate, plus the adjudicator’s verdict and its uncalibrated self-report where the model was asked about that row at all.

The rows your agent gets wrong today are worth putting through these same tools, refusals included. Create your Free workspace and API key to test shallow matching; choose a paid plan in Billing for greater depth. [Start free →](https://app.entitybind.com/app?signup=1)

## Zero wrong merges, on every shape of input we tested

[The abstention is what buys this number](https://entitybind.com/guides/precision-recall-and-abstention). An answer we would not stand behind never becomes a match, so it never becomes a merge either.

0 wrong bindings, across the whole fixture.

On 317 live GB companies recorded 2026-08-29, across 7 shapes of input from a company number down to a name cut off by a field limit. Where the evidence would not carry a match, the tool returned unresolved and charged nothing for it.

Zero wrong bindings were observed across the historical six-shape recording and the separate domain fixture. Observed correctness was 1.0000 on accepted outcomes; this does not guarantee an error-free new workload. Historical results on an internal UK fixture with a preloaded catalogue. The September 5 replay has no full-prompt-verified calls and cannot establish current calibration. No independent customer holdout or global coverage claim.

[Every figure, its population and what it does not show →](https://entitybind.com/accuracy)

### Confidence floor 0.90

A match below 0.90 confidence is unrepresentable. The record comes back as an abstention with a ranked shortlist instead.

It is an acceptance contract, not calibration evidence: it says what we refuse to return, not how often an accepted match is right. Read it with the coverage it costs.

Internal fixtures only. The historical 2026-08-29 per-tier recording covers 317 GB companies with identifier-rich arrivals seeding the catalogue first; the context tier includes country and registered-town prose. Its legacy replay has no full-prompt-verified calls and cannot certify current calibration. The September 5 CI reports cover 1,176 labelled pairs and 705 records over 250 companies using offline stubs. These are not independent customer holdouts or live production validation; the 99% minimum-input product target remains unmet. Scope is company and organisation identity only — not people, vessels, bank-account ownership, sanctions or emissions. Results may not be used for eligibility decisions about individuals, including employment, credit and tenancy.

### Why it refuses, at length

- [Entity binding failures in AI agents](https://entitybind.com/guides/entity-binding-failures-in-ai-agents) (Guide): An entity binding failure is an agent calling the right tool on the wrong real-world entity. What a 2026 study of 1,800 runs found, and four safeguards.
- [Entity resolution for AI agents](https://entitybind.com/guides/entity-resolution-for-ai-agents) (Guide): An agent about to pay or contract a counterparty needs a decision with a receipt, not a similarity score. Why abstention is the safe default for an autonomous caller, and what the MCP surface exposes.

Every rate this product publishes carries its fraction, its interval and the fixture it was measured on — including the input shape that comes off worst. [Read the evidence →](https://entitybind.com/accuracy)

## Static bearer works. You do not need OAuth to deploy this

The key you create in your workspace authenticates the remote transport unchanged, so five steps take you from that key to a client calling every tool a resolve key admits; settling a review takes a separate review credential. An OAuth token goes through the same check when a consumer-style install wants one.

### 01 Create a key in your workspace

Create your Entity Bind workspace online and verify your email, and you create a resolve-scoped key there; the raw key is shown once. It looks like rk_ followed by a secret, and it is the same credential the HTTP API takes.

### 02 Point your client at the MCP URL

The path is /mcp on the hostname that comes with your workspace. Transport is streamable HTTP, so there is nothing to install and no process to run locally.

### 03 Present the key as a static bearer

Authorization: Bearer rk_... on every request. Clients with a credential vault call this option static_bearer and key it by server URL, in which case the URL must match the server entry byte for byte.

### 04 Prove reachability before you write a prompt

Curl the health probe, then post a tools/list. A green probe is not evidence that tool calls are admitted: the probes sit outside the host check, so they answer whatever Host header they are sent.

### 05 Read the catalogue your client received

tools/list is the reference surface. Argument schemas and descriptions arrive before any call and without spending one, which is why there is no reference-data tool set to add to a prompt.

### Config, per client shape

#### Remote, static bearer

A client that lets you set request headers. The common case, and the one to try first.

Try this first.

Client config:

```json
{
  "mcpServers": {
    "entitybind": {
      "type": "http",
      "url": "https://host.example.com/mcp",
      "headers": {
        "Authorization": "Bearer rk_YOUR_KEY_HERE"
      }
    }
  }
}
```

Prove reachability from a shell before wiring a client:

```bash
curl -s https://host.example.com/healthz

curl -s https://host.example.com/mcp \
  -H "Authorization: Bearer $ENTITYBIND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

- The rk_ key you create in your Entity Bind workspace is the credential. There is nothing further to mint, register or exchange, and no separate MCP credential exists.
- The hostname comes with your workspace; host.example.com is a placeholder rather than a live endpoint. The path is /mcp, the header is Authorization and the scheme is Bearer on every remote shape.
- The key under mcpServers is the name your client displays. Ours is spelled entitybind here; nothing on either side of the wire reads it.

#### Remote, credential vault

A client that keeps secrets out of the server config and holds them in a vault keyed by server URL.

Client config:

```json
{
  "mcpServers": {
    "entitybind": { "type": "http", "url": "https://host.example.com/mcp" }
  },
  "mcpCredentials": {
    "https://host.example.com/mcp": {
      "static_bearer": "rk_YOUR_KEY_HERE"
    }
  }
}
```

- static_bearer is the credential type. That is the vocabulary vaults use for exactly this: a token presented unchanged on every request.
- The URL in the credential entry must be byte-identical to the one in the server entry, trailing slash included. If it is not, the vault finds no credential, the client presents none, and what arrives here is unauthorized rather than a configuration error the client can describe.
- The key names around the credential differ between clients. What is fixed on our side is the URL, the type and the token.

#### Local stdio

A developer evaluating on a machine whose operator already holds the database credentials.

Client config:

```json
{
  "mcpServers": {
    "entitybind": {
      "command": "uv",
      "args": ["run", "python", "-m", "resolvd.mcpserver"],
      "env": {
        "RESOLVD_DATABASE_URL": "postgresql+asyncpg://user@localhost/dbname",
        "RESOLVD_MCP_TENANT_ID": "ten_acme",
        "RESOLVD_ANTHROPIC_API_KEY": "sk-ant-..."
      }
    }
  }
}
```

The same process, started by hand:

```bash
uv run python -m resolvd.mcpserver
```

- The module path and the environment variable names are the engine package's own, and they are verbatim because the process reads exactly those strings. They do not track the product name.
- This shape holds database credentials directly, which is why it is correct only where the operator already holds them. It serves one configured tenant and nothing else, at the SCALE plan.
- Every call is metered against key_id mcp_stdio, so a laptop evaluation never looks like customer traffic in a usage export.

#### Remote, OAuth connector

A consumer-style install, where somebody pastes a URL into a client and will not paste an API key into a dialog.

Shell:

```bash
# 1. We register your client, once. There is no dynamic registration endpoint.
#    -> {"client_id":"ocl_...","redirect_uris":[...],"scopes":["resolve"]}

# 2. A tenant administrator mints a single-use install ticket (900s TTL):
curl -sX POST "$BASE/v1/admin/oauth/install-tickets" \
  -H "Authorization: Bearer $ADMIN_KEY" -H 'Content-Type: application/json' \
  -d '{"tenant_id":"ten_acme"}'

# 3. The person pastes the server URL into the client. The consent page asks
#    for that ticket, and the grant is written against the tenant it names.

GET {base}/v1/oauth/authorize
  ?response_type=code
  &client_id=ocl_...
  &redirect_uri=<exact string match, a trailing slash is a different URI>
  &scope=resolve
  &state=<8-255 chars from [A-Za-z0-9-._~]>
  &code_challenge=<base64url(sha256(verifier)), 43 chars, unpadded>
  &code_challenge_method=S256

POST {base}/v1/oauth/token       (application/x-www-form-urlencoded)
  grant_type=authorization_code&code=...&redirect_uri=...&client_id=ocl_...&code_verifier=...
  -> {"access_token":"oat_...","token_type":"Bearer","expires_in":3600,
      "refresh_token":"...","scope":"resolve"}
```

- The access token is then presented exactly as an rk_ key is: Authorization: Bearer oat_... . It is verified by the same code path, which is why there is one implementation of “is this credential valid” rather than two.
- The client has to prove it started the flow it is finishing, by sending a secret it hashed at the beginning — PKCE, with the S256 hash method only. It is required, and plain is refused permanently. state is required too, and must be 8–255 characters from the unreserved URI alphabet, which is stricter than RFC 6749. Every client here is public — none of them holds a secret — so there is no client_secret and no field for one.
- Read the discovery gap below before you plan an install around this. A client that starts its OAuth flow from a 401 challenge will never start it.

Working in a named client? The setup each one takes, with its own config block and its own limits: [Claude Code](https://entitybind.com/integrations/claude-code), [Claude Desktop](https://entitybind.com/integrations/claude-desktop) and [Cursor](https://entitybind.com/integrations/cursor). Dated changes to the API, to access and to the published figures are in the [changelog](https://entitybind.com/changelog), which has an [Atom feed](https://entitybind.com/changelog/feed.xml).

### Three settings a remote deployment cannot be wrong about

Host name, MCP path, shared rate-limit store. The names are the engine package's own, verbatim, because the server reads exactly those strings.

- `RESOLVD_PUBLIC_BASE_URL`: Must name the hostname clients actually put in the Host header. A request arriving under any other name is refused with 421 Invalid Host header before authentication, before routing and before any tool body. A proxy that rewrites Host to an internal service name or a second vanity domain is refused, and there is no setting to add it today.
- `RESOLVD_MCP_PATH`: Defaults to /mcp, which is the conventional path, so a client that guesses guesses right.
- `RESOLVD_REDIS_URL`: Set it before running the API and the MCP server as separate deployments. Without it each process holds its own rate-limit buckets and the published limit is enforced once per process rather than once per tenant.

Create your workspace online and verify your email, then create a resolve-scoped API key and configure the static-bearer path above. Free needs no card and is capped at shallow matching. Contact us to register an OAuth client if your install requires one. [Start free →](https://app.entitybind.com/app?signup=1)

## One credential, and the tenant comes out of it

The rk_ key that authenticates the HTTP API authenticates the remote transport unchanged, and an OAuth token is checked by the same code path. There is no second credential to mint and no tenant field to pass: the tenant is derived per call from the credential itself. Local stdio is for a laptop; streamable HTTP is what other people’s agents connect to.

### Tenancy: Derived per call, from the credential

Every repository method takes the tenant id as a required argument, so a tool that never obtained one cannot compile a scoped read at all. A read for another tenant’s id reports not found. A distinguishable “not yours” would confirm the id exists. Two callers under different credentials go through one process at the same instant in the test suite.

### Sessions: Off, and that is the security property

A session id honoured on later requests is a second credential — one the credential system never issued and cannot revoke. So every request carries its own bearer token and nothing is carried between them, which also means any replica can serve any request, as the published rate limits assume.

### The two transports, side by side

What each one is, who the tenant is, which credential it wants and what to use it for.

| Transport | What it is | Who the tenant is | Credential | Use it for |
| --- | --- | --- | --- | --- |
| stdio | One process on one machine. No listener, no port, no proxy. | Fixed at startup from RESOLVD_MCP_TENANT_ID. | None. There is no second party to authenticate. | A laptop evaluation, or a developer's own agent on a machine whose operator already holds the database credentials. |
| streamable-http | A URL other people's agents connect to. | Derived from this call's credential. Never a request parameter. | Required on every request. | Deployment: a product surface, where the caller is somebody else's agent. |

### The key you create, and what an OAuth token adds

The key shown once, the token a consumer-style install uses instead, and why neither is signed.

A key at creation, shown once:

```
{"key_id":"key_...","api_key":"rk_...","tenant_id":"ten_acme","scope":"resolve"}

Authorization: Bearer rk_...
```

The raw key is shown once, when you create it in your Entity Bind workspace, and only its digest is stored. A lost key is revoked and replaced there; it cannot be recovered. One implementation of “is this credential valid” serves both surfaces, which is why revocation, the rotation grace window and the plan lookup cannot drift between them.

OAuth, through the same seam:

```
Authorization: Bearer oat_...

access token   opaque, digest-stored, 3,600s
refresh token  rotating, 30 days
scope          resolve — the only issuable scope
PKCE           required, S256 only
client auth    none. Every client is public
registration   no endpoint, deliberately
```

Tokens are opaque and checked against the database on every call. A revoked grant stops working on the next call, and the plan is read off the tenant’s row at verification time. A signed token would be validated offline, which is the property that makes revocation wait for the next expiry.

### Four key families: three this surface admits, one it refuses

A read key cannot resolve. A resolve key cannot settle a review. A review key cannot resolve. An admin key is refused before a caller object exists.

#### Read key

Admits. The free reads — entities, crosswalks, enrichment fields, search, decision receipts and usage — plus read-only access to its own tenant's review queue.

Refuses. It cannot resolve, match or settle a review, so neither billable tool admits it.

#### Resolve key

Admits. The seven data-plane tools, plus read-only access to its own tenant's review queue.

Refuses. It cannot settle a review. A resolve credential cannot approve the match it was withheld from making.

#### Review key

Admits. The three review tools: list the queue, read one item, settle it with a reason.

Refuses. It cannot invoke resolution. A review credential cannot create the decisions it exists to adjudicate.

#### Admin key

Admits. Nothing. It is refused with key_scope_forbidden before a caller object is constructed at all.

Refuses. Tenant creation, key minting and grant management are HTTP-surface operations for a person. No model reaches them by choosing this transport.

### Planning an OAuth install? Read this first

There is no discovery document and no 401 challenge, which breaks one common client.

The listener serves three paths. Protected-resource metadata is not one of them, and an unauthenticated call returns an HTTP 200 tool error, so a client that begins its flow from a 401 challenge never begins it. The metadata document is owned, unbuilt work. [The boundary in full](https://entitybind.com/mcp#boundary-7)

Isolation, retention, sub-processors and what happens to a record after a resolve are answered in one place. [Read the trust page →](https://entitybind.com/trust)

## What a call costs, and how often you may make one

The same credit rule as the HTTP API, computed by the module that writes the ledger row and serves the public pricing endpoint — so the number here, the number in your usage block and the number in your ledger cannot drift apart.

The usage block, on both billable tools:

```json
"usage": {
  "credits_charged": 0,
  "credits_remaining": 4871,
  "deep_attempts_remaining": 2988,
  "plan": "growth",
  "enforced": false
}
```

A tool call has no response headers, so this block is the only place an agent can read what a call cost. Both billable tools return it, which makes charge-on-success checkable. enforced: false means usage is metered without enforcing the usage quota. Paid subscriptions are purchased separately through Stripe in workspace Billing; usage creates no automatic overage charge.

### The credit rule, in five lines

- 1 credit(s): A decision reached by the deterministic or probabilistic stage. CPU only, no egress.
- 5 credit(s): A decision that needed LLM adjudication or agentic research.
- 0 credit(s): An abstention, at any stage, including the ones that spent the most getting there.
- 0 credit(s): A byte-identical replay. Per tenant, per exact content, and with no expiry.
- 0 credit(s): An input that normalised to no identifying signal. There was nothing to answer.

### Rate limits, per minute, shared with the HTTP API

| Scope | Tools | Per minute |
| --- | --- | --- |
| READ | get_entity, get_crosswalk, search_entities, get_decision, get_usage | 1,000 |
| MATCH | match_records | 300 |
| RESOLVE | resolve_company | 300 |
| REVIEW | review_queue_list, review_queue_get, review_submit | 120 |

#### How the buckets work, and what a shed call looks like

Multiplied by plan, bucketed per tenant, shared with the HTTP API.

Multiplied by plan — free ×0.2, starter and growth ×1.0, scale ×3.0 — and bucketed per tenant rather than per key, because a limit a customer can multiply by minting a second key is not a limit. The same buckets serve the HTTP API: a tenant's per-minute limit is what they hold, not what they hold twice for using both surfaces. A shed call raises rate_limited with the retry window in the message text, because a tool call has no response headers an agent could read it from.

Plans differ in credits, deep attempts, rate multiplier and how deep the cascade may go. The free plan is a hard no-egress cap on the pipeline, not a time-limited trial. [See the plans →](https://entitybind.com/pricing)

## What this surface deliberately will not do

Some of these capabilities live on the HTTP API. The rest are things nothing should hand a model, and the code has no path to them at all.

### 01 No bulk anything

No batch tool, no catalogue enumeration, no NDJSON export, no bulk reconcile. A tool returning five hundred outcomes returns them into a context window. The header that makes a repeated call safe to send twice — the idempotency key — is an HTTP one, and a tool call has nowhere to carry it. And exporting a catalogue is a decision with a person at the keyboard.

Where it does live: The HTTP API. The boundary is written down per capability in the parity test rather than left to drift.

### 02 Nothing here destroys data

There is no erasure tool, for entities or for records. An erasure obligation is discharged by a person who can be asked why; it is not a step in an agent's plan.

Where it does live: The HTTP API, under a credential this surface does not admit.

### 03 Nothing here provisions

No tenant creation, no key minting, no OAuth grant management. This is structural rather than a policy: the surface admits only tenant-scoped read, resolve and review credentials, and an admin key is refused before a caller object exists. The credential that can mint credentials is unreachable by a model.

### 04 A resolve credential cannot settle a review, and a review credential cannot resolve

Each tool declares the scope it needs and the credential is checked against it per call. So the key running your ingestion cannot approve the match it was withheld from making, and the key your reviewers hold cannot create the decisions they adjudicate.

### 05 match_records mints no receipt

Stateless by design: no decision_id, no review_id, no audit row. If the answer has to be defensible later, resolve rather than match.

### 06 There is no reference-data tool set

The tool registry is the reference surface. Schemas and descriptions arrive at tools/list, before any call and without spending one, so ten more tools answering questions the model already holds the answer to would cost context and buy nothing.

### 07 No RFC 9728 metadata, and no 401 challenge

We do not serve the metadata document RFC 9728 defines — the file a client fetches to learn where it should go and authenticate — and an unauthenticated tool call is answered with an HTTP 200 tool error rather than a 401, because authentication happens per tool rather than in transport middleware. So a client that waits for a 401 before starting its OAuth flow will never start it: it will call the tool, read a tool error, and report a broken server. OAuth works where the client can simply be told the authorization server, and for discovery-driven clients only where the MCP path and the API share an origin.

### 08 An install ticket authorises a tenant, not a person

The OAuth flow carries no end-user identity — no users, no passwords, no sessions and no upstream identity provider behind the consent page — so there is nobody for the authorization server to authenticate at the consent step. Anyone holding a ticket authorises as that tenant, and the audit trail records which admin key minted it rather than who used it.

### 09 The tool catalogue is readable without a credential

initialize and tools/list carry no credential check, so anyone who can reach the port can read tool names, argument schemas and descriptions. No customer data is reachable that way — every tool body derives a tenant first and there is no path that skips it — but the port should not be public without a reason.

### The error vocabulary, shared with the HTTP API

`unauthorized` · `key_scope_forbidden` · `rate_limited` · `invalid_record` · `entity_not_found` · `review_item_not_found` · `decision_not_found` · `review_already_resolved` · `internal_error` · `storage_error`

#### What a tool error looks like, and why two of them say so little

Every failure arrives as a tool error with the text `<code>: <message> (correlation_id=req_...)`. The vocabulary is the HTTP surface's, so one failure is described the same way whichever surface produced it and can be quoted into a support ticket unchanged. internal_error and storage_error are reported opaquely — the server could not complete this call, quote the correlation id — which is stricter than the HTTP surface for a specific reason: a tool result lands in a model's context window, and an escaped driver error would put raw SQL, table names and bound parameters in front of a model that may repeat them. On this surface those bound parameters are the caller's own company records.

This is the boundary the parity test between the two surfaces asserts. If the capability you need is on the other side of it, it exists — on [the HTTP API](https://entitybind.com/api), under a credential a model does not hold.

For a service integration the same pipeline is one HTTP call, and the receipt is the same receipt. [Read the API docs →](https://entitybind.com/api)

## Built for one job, wrong for several others

If you already run a licensed company-data feed or a master data management platform, run Entity Bind alongside it. It is the independent decision and evidence layer: it tests, repairs and governs the matches those systems propose, and it will contradict them with a citation when they are wrong.

### Built for

Agents that touch a company master somebody has to defend — a supplier file read before a payment run, an MDM or stewardship queue, a CRM after a migration or a merger, counterparty onboarding that has to survive an audit. The receipt is what makes an agent’s merge reviewable after the fact.

### Wrong for

Buying breadth as the product: we hold a fraction of an incumbent’s records and are not building a global company graph. Entity Bind resolves companies and organisations only. It does not resolve people, vessels, bank-account ownership, sanctions exposure, emissions or geolocation, and its results may not be used for eligibility decisions about individuals, including employment, credit and tenancy.

The retention, erasure and tenancy detail a security review asks for is on the trust page, with where assurance stands: no SOC 2, no external validation of the accuracy figures, no customer holdout. [Trust and security →](https://entitybind.com/trust)

## Questions an agent builder asks

The five MCP questions, and the six adjacent ones that come up in the same sitting.

### What is the MCP server?

The same pipeline exposed to agents as eleven tools over the Model Context Protocol, on local stdio or a remote URL. Two of the eleven are billable — resolving a company and comparing two records — and nine are free: read an entity, read a crosswalk, discover enrichment fields, search, redeem a decision receipt, read usage, and the three review-queue tools. It delegates to the same application services as the HTTP API, so a resolve over MCP is the same resolve, at the same price, against the same rate-limit bucket. Limits are shared rather than doubled. Deliberately absent: batch, catalogue export, erasure and provisioning, all of which stay on the HTTP surface.

### Do I need OAuth to connect an agent?

No, and static bearer is the path to reach for first. The `rk_` API key you create in your Entity Bind workspace authenticates the remote MCP transport unchanged as an `Authorization: Bearer` header — the option credential vaults call `static_bearer`. OAuth exists, with PKCE S256 required, one issuable scope and operator-registered clients, but there is an integration gap to know about before you plan around it: the listener publishes no RFC 9728 protected-resource metadata and answers an unauthenticated tool call with a tool error rather than a 401 challenge. A client that starts its OAuth flow from a 401 will never start it. Discovery-driven clients work only behind a shared origin.

### Can an agent spend money without us noticing?

Every billable tool returns a `usage` block — credits charged, credits remaining, deep attempts remaining, plan, and whether billing is enforced — and that block is the only place an agent can learn what a call cost, because a tool call has no response headers to read. So an agent can audit its own spend rather than be told it is cheap. The credential also caps depth before the cascade runs, so a key pinned to `probabilistic` cannot reach agentic research by choosing this surface. And the abstention rule holds here too: Free-tier or not, an `unresolved` costs zero credits, so an agent asking a question nobody can answer is not a bill.

### Can an agent delete data or create credentials?

No, structurally rather than by policy. There is no erasure tool and no provisioning tool on the MCP surface, and an admin-scoped key is refused outright before a caller object is even constructed — so the credential that can mint credentials is unreachable by a model. Tenancy is derived per call from the credential and is never a request parameter; every repository method requires a tenant id as a keyword argument with no default; and a cross-tenant read reports 404 rather than 403. An erasure obligation is discharged by a person who can be asked why, which is a support conversation rather than a step in an agent's plan.

### What does an agent get back when the answer is ambiguous?

An `unresolved`, with everything it needs to decide what to do next: the ranked shortlist with calibrated per-candidate probabilities, a review id, a decision id, and four flags that carry different instructions. `depth_capped` means you stopped this and a deeper call may resolve it. `escalation_unavailable` means a dependency failed and a retry is worth trying. `more_input_needed` means the paid stages could not separate the shortlist and retrying deeper will not change it — send a domain, a registration number or an LEI instead. `no_identifying_signal` means the fields normalised to nothing, and it is the only one the agent can fix by itself.

### How do I get an API key?

You create it yourself, in the Entity Bind workspace you create online. Create your workspace online and verify your email; it starts on the Free plan with no card. Create your workspace online at https://app.entitybind.com/app?signup=1 and verify your email. Once you are signed in, you create, list and revoke your own keys, each scoped to your tenant with read or resolve permission, and each raw key is shown once. Paid plans are available through Stripe in workspace Billing; registry checks follow your active plan. The evaluation opens at a separately agreed activation date. The planned 1,000-credit evaluation runs at full research depth and is separate from the shallow Free plan. Keys are stored as SHA-256 digests. A read key reads what your tenant already holds and cannot resolve; a resolve key runs the data plane; review and admin credentials carry separate scopes. In your workspace, rotating a key is create, switch, revoke; the admin API's rotate endpoint adds a grace window, and an exposed key can be revoked.

### What are the rate limits?

Per endpoint class, and bucketed per tenant rather than per key, so a customer with three keys has bought one allowance. The published defaults are 1,000 reads a minute, 300 resolves, 300 matches, 30 batches and 120 review calls, scaled by a plan multiplier. `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` come back on success as well as on a 429, and a `Retry-After` is added when a request is refused. Two caveats worth reading before you size anything: the limits are deployment settings you should read from `GET /v1/reference/limits` rather than hard-code, and without a shared limiter store they are enforced once per replica.

### What does the error model look like?

One envelope on every failure — `{"error": {"code", "message", "correlation_id"}}` — with a stable machine code from a published vocabulary you can fetch at `GET /v1/reference/error-codes`. Internal detail never reaches a caller: any 5xx message is replaced with canned text, so a driver error cannot leak SQL or bound parameters. Two choices are worth knowing. A wrong-tenant id returns 404 rather than 403, because a 403 confirms the id exists and turns every read into an enumeration oracle. And `deadline_exceeded` is a 504 rather than a 503, because nothing upstream is down — our budget for that request ran out, and the fix is a lower depth or a longer allowance.

### How is our data isolated from another customer's?

By application-enforced tenant scoping, and the exact shape of that matters. Every table carries a tenant id; every persistence method requires it as a keyword argument with no default, so a forgotten argument raises rather than silently reading a shared tenant; reads scope in SQL before retrieval rather than filtering after it; and the replay cache is keyed on a composite primary key, so byte-identical content from two customers resolves to two separate entities. A wrong-tenant read is a 404, never a 403. What it is not is database-enforced: one database, one schema, one runtime role, no row-level security, so a SQL-injection defect would cross every tenant at once. The internal workbench sits outside that guarantee.

### Does our data go to an LLM provider?

Yes, on the records that reach adjudication or research, and it is worth being exact about what travels. The adjudication prompt renders the candidate record's populated fields — including the submitted address — plus the free-text context and the entries of the `extra` map, so a CRM column mapped into `extra` reaches the model verbatim. The research agent uses the provider's own server-side web tools, so search queries and fetched URLs reach them too; if "which companies is this customer looking at" is sensitive, that signal reaches them. Two things do not exist and should not be assumed: a zero-data-retention addendum, and any code-level enforcement of the provider's not-used-for-training term, which is contractual. Setting `depth=probabilistic` sends nothing at all.

### Why is an abstention free?

An unresolved identity costs zero credits even when adjudication or research did the work. Deep attempts are tracked separately, because those calls still carry a provider cost. Existing monthly plans can reduce depth when their attempt quota runs out. The planned 1,000-credit evaluation instead requires an explicit limit response or a disclosed pause when research cannot continue, so a budget limit cannot be mistaken for an evidence-based abstention. That change is not live yet.

## Start with your own company records.

Create your Free workspace and API key, then test these same tools on your agent’s company records, including the rows we cannot decide. Free supports shallow matching; choose a paid plan in Billing for greater depth. We write nothing to your systems.

[Start free](https://app.entitybind.com/app?signup=1) · [Plans and prices](https://entitybind.com/pricing)

300 credits. No card.

For a guided review of your file, explore [the Merge Safety Sprint](https://entitybind.com/audit-pack).

---

This is the Markdown copy of https://entitybind.com/mcp, assembled from the same data the page renders and carrying every sentence, table row, figure and question the page's main content carries. The HTML page is canonical; it adds the layout, the navigation and the interactive parts (the ask panel, the forms), which a copy can name but not run. Every page on the site, with a line on each: https://entitybind.com/llms.txt.
