> ## Documentation Index
> Fetch the complete documentation index at: https://docs.walletlink.social/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP server

> Eight tools that let an agent resolve wallets to social identities, over the same API and the same balance.

An agent can call walletlink.social directly, without a human first reading this
reference. The server is remote, and takes either an OAuth connection or the
same key the REST API uses.

```
https://walletlink.social/api/mcp
```

## Connect

### Sign in, from a client that supports OAuth

Add the URL above as a connector and the client does the rest. There is no key
to create, copy or paste: the first tool call opens a consent screen, you sign
in with your email, approve, and the call continues where it left off.

This is the shortest path in Claude, Claude Code and anything else that follows
the MCP authorization specification, and it is the one to prefer. A connection
carries no long-lived secret anywhere: access is a token that lasts an hour and
renews itself, and disconnecting is immediate.

The server registers clients two ways, so nothing needs arranging in advance:

* **Client ID metadata documents**, which is what Claude and Claude Code use
* **Dynamic client registration** ([RFC 7591](https://www.rfc-editor.org/rfc/rfc7591)),
  at `https://walletlink.social/api/oauth/register`, for clients that do not
  publish a metadata document

Both register as public clients, so PKCE with `S256` is required and no client
secret is issued. Discovery starts at
[`/.well-known/oauth-protected-resource`](https://walletlink.social/.well-known/oauth-protected-resource).

<Note>
  A connection can spend match credits, so it is worth knowing what it can
  reach. It can resolve wallets in both directions, run a background job, and
  read your credit balance. It cannot see your saved lookups, your billing
  details or your email address, and it cannot buy credits or change anything
  about your account.
</Note>

### Disconnect

**Account menu > API keys > Connected applications**, then Disconnect. It takes
effect on the next call, not at the end of the hour: the access token is
revoked along with the connection, and the refresh token that would have renewed
it stops working at the same moment.

### One click, from the app

When you create an API key, the modal offers **Add to Cursor** and **Copy
Claude Code command**, both already carrying that key. That is the shortest
path, and it is only offered there: a key is shown exactly once, so a link
published on this page could only carry a placeholder, and a placeholder
installs a server that fails on first use.

### Claude Code, one line

```bash theme={null}
claude mcp add --transport http walletlink https://walletlink.social/api/mcp \
  --header "Authorization: Bearer wts_live_YOUR_KEY"
```

That installs it for the current project. Add `-s user` to make it available in
every project instead.

### From Grok

The official walletlink plugin for Grok lives at
[github.com/starl3xx/walletlink-grok-plugin](https://github.com/starl3xx/walletlink-grok-plugin).
It points Grok Build at this server and ships a skill covering tool selection,
cost and result semantics. The plugin itself carries no credential.

**Use an API key here, not OAuth.** The chat host cannot open a consent screen
and reports `no_auth_link`, so an OAuth connection can never complete however
many times it is retried. Create a key from the account menu and paste it into
the connector's key field.

### By hand

Add the server to your client’s config. The key goes in an `Authorization`
header, exactly as it does for the REST API.

<CodeGroup>
  ```json Claude Code / Claude Desktop theme={null}
  {
    "mcpServers": {
      "walletlink": {
        "type": "http",
        "url": "https://walletlink.social/api/mcp",
        "headers": {
          "Authorization": "Bearer wts_live_YOUR_KEY"
        }
      }
    }
  }
  ```

  ```json Cursor theme={null}
  {
    "mcpServers": {
      "walletlink": {
        "url": "https://walletlink.social/api/mcp",
        "headers": {
          "Authorization": "Bearer wts_live_YOUR_KEY"
        }
      }
    }
  }
  ```
</CodeGroup>

Keys are self-serve from the account menu for any signed-in account, including one on the free allowance.

<Note>
  Discovery works without a credential. A client can connect and list the tools
  with no `Authorization` header at all, so you can see what is on offer before
  buying anything. Calling a tool needs one.
</Note>

### Which to use

|                     | OAuth                             | API key                              |
| ------------------- | --------------------------------- | ------------------------------------ |
| Set up              | Sign in when first asked          | Create a key, paste it into a config |
| Lives for           | An hour, renewed automatically    | Until you revoke it                  |
| Stored where        | In the client, refreshed          | Wherever you pasted it               |
| Ends it             | Disconnect, from your account     | Revoke, from your account            |
| Also works for REST | Yes, though there is no reason to | Yes                                  |

An OAuth connection is better for a person using a client. A key is better for
a server you run yourself, where there is no browser to sign in with.

<Note>
  **Some chat hosts cannot do OAuth at all, and a key is the only way in.** A
  connector running inside a chat app may have no way to open a consent screen;
  X and Grok report `no_auth_link` when asked to. The symptom is a connector
  that lists all eight tools happily, because discovery needs no credential, and
  then reports that it still needs to sign in however many times you authorize.
  If you see that, stop retrying the connection and paste a key instead.
</Note>

### The skill file

Some agents install a capability from a URL rather than a config block. Give
them this one:

```
https://walletlink.social/skill.md
```

It carries the whole contract: the tools, what each costs, how to authenticate,
and how to read a result. It contains no key, so it is safe to paste anywhere,
and it is generated from the same constants as this page, so the prices and
coverage figures in it are the ones actually charged and claimed.

## The eight tools

| Tool                                       | Answers                                      | Match credits                              |
| ------------------------------------------ | -------------------------------------------- | ------------------------------------------ |
| `walletlink_resolve_wallets`               | Who is behind these addresses?               | 1 per address that resolves                |
| `walletlink_estimate_list`                 | What would resolving this list cost?         | 0                                          |
| `walletlink_submit_job`                    | Run a big list, or re-check the misses, live | 1 per address that resolves, at completion |
| `walletlink_job_status`                    | How is that job doing, and what did it find? | 0                                          |
| `walletlink_wallets_by_x_handle`           | Which wallets belong to this X account?      | 1 per wallet returned, up to 100           |
| `walletlink_wallets_by_farcaster_username` | Which wallets belong to this Farcaster user? | 1 per wallet returned, up to 100           |
| `walletlink_index_coverage`                | How much of the index carries each identity? | 0                                          |
| `walletlink_account_balance`               | What is left on this key?                    | 0                                          |

Nine endpoints become eight tools because the single and batch lookups answer
the same question at two sizes and bill by the same rule.
`walletlink_resolve_wallets` takes 1 address up to your plan’s batch ceiling
(50 on the default plan; a live Scale or Index pack raises it, see
[plans](/api-reference/introduction#plans)) and picks the endpoint for you.

`walletlink_estimate_list` is the [dry run](/api-reference/estimate): counts
of what is in the index and the band a resolve would bill inside, free at any
balance, weighed against the rate window like the batch it previews. Have an
agent call it before spending on an unknown list.

`walletlink_submit_job` is the async surface: it submits a background
[job](/api-reference/jobs) that resolves wallets the index has not checked
against live sources, so it can find identities the resolve tool reports as
never seen. One job may be active per account at a time, a submission is capped
at 10 times the match balance, and the matches are billed when the job
completes, at the same price as resolving. `walletlink_job_status` polls it for
free on both meters and shows the first 100 result rows; the full set is one
free REST call away on the same key.

## It is the same meter

The tools carry your key into the same handlers the REST API uses, so nothing
about billing changes. Misses are free, a match means an X handle or a Farcaster
account, and an ENS name on its own is returned and not charged. See
[credits](/api-reference/introduction#credits).

Three consequences worth knowing before you point an agent at it:

**A reverse lookup can spend 100 credits in one call.** A page holds up to 100
wallets and each one is a match. The free allowance is 100 matches per rolling
30 days, so a single widely held handle can spend all of it at once. There is no
page-size parameter. Have the agent call `walletlink_account_balance` first,
which is free.

**A batch spends its size in rate limit.** A batch call spends one request-unit
per address of the per-minute window, and a full batch fills most of a minute
on every plan (60 units and 50-address batches on the default plan; a live
Scale or Index pack raises both), so a second full batch inside the same
minute is refused. Pace multi-batch runs a minute apart and read the reset
time from `quota`.

**Nothing is reserved.** The balance is checked, not held, so a call that
resolves more than you have left is allowed to finish and the next one fails
with no credits. That is the same behavior the REST API and the app have.

**A retry is a second purchase.** The debit is keyed on the call, so a tool
call resent by a retrying framework bills its matches again; the tools declare
`idempotentHint: false` for exactly this reason. A tool call cannot carry an
idempotency key; the REST [batch endpoint](/api-reference/batch) can, so a
pipeline that must retry safely should call REST with an `Idempotency-Key`
header instead. A job submission is half-covered on its own: a resend while
the job runs is refused with the active id, and only a resend after
completion runs and bills again. At zero balance the four free tools keep
answering, so a drained agent can always read its own meter and collect a
finished job.

## What the tools return

Not the raw API record. The 40-odd fields per wallet are trimmed to the identity,
whether the owner attested it, and whether the X handle still reaches anyone,
which is the field no competing source publishes. A Farcaster identity keeps its
`fid` through the trim, because Farcaster DMs address accounts by fid rather
than by username; a username can be renamed out from under a campaign, the fid
cannot. Resolved records carry a `stale` flag when the index has measured their
freshness, and a missed address reports `previously_checked`: a timestamp means
we looked and found nothing, `null` means we have never looked. Absent is not
false, at every layer.

```json theme={null}
{
  "requested": 2,
  "billed_matches": 1,
  "quota": {
    "matches_available_before_this_call": 87,
    "requests_remaining_this_window": 58,
    "window_resets_at": "2026-09-01T18:04:00.000Z"
  },
  "results": [
    {
      "address": "0x00000000000000000000000000000000000000a1",
      "ens": "example.eth",
      "x": {
        "handle": "example_user",
        "url": "https://x.com/example_user",
        "attested": true,
        "reaches_someone": true,
        "reachability": "live"
      },
      "evidence": ["onchain", "farcaster"],
      "stale": false
    },
    {
      "address": "0x0000000000000000000000000000000000000001",
      "found": false,
      "previously_checked": null
    }
  ]
}
```

`attested` answers whether the wallet owner published the link themselves. It
derives from the record’s evidence classes, the same array `evidence` shows: a
Farcaster verification, an onchain ENS record, an attested-social sign-in, or a
manually verified record makes it `true`; a record resting on correlation alone
is `false`; a record with no classified evidence reports `null`. Evidence
classes are recorded per wallet, so `attested` reflects the record’s
strongest evidence, not each identity separately. It is deliberately not the
REST API’s `verified` flag, which reports `false` on the majority
Farcaster-attested handles: see
[the verified flags](/concepts/data-quality#the-verified-flags).

`reaches_someone` and `reachability` are **omitted** when we have not resolved
the handle. Absent is not false: see
[when a handle stops being true](/concepts/data-quality#when-a-handle-stops-being-true).

Every metered tool result carries `quota`, so an agent learns what its call
left without a second call to ask. The balance is the one the call was admitted
with, before its own matches were debited: subtract `billed_matches` to know
what remains. The balance key is absent for the legacy unmetered accounts,
which have no balance to report; the request counters are always present.
`walletlink_account_balance` still exists for reading the balance without
resolving anything.

## The REST surface

Everything these tools do is also available over the REST API, described in
OpenAPI 3.1 at
[`docs.walletlink.social/openapi.yaml`](https://docs.walletlink.social/openapi.yaml)
if you would rather generate a client than install a server.

## Errors

A failed call comes back as a tool error with a readable message, never as a
transport failure, so the session survives and the agent can tell you what went
wrong. Out of credits reads as "No credits left. Buy a pack at
[https://walletlink.social/pricing](https://walletlink.social/pricing) to continue, or buy one with USDC, no account
needed, at POST [https://walletlink.social/api/x402/buy](https://walletlink.social/api/x402/buy)."

Authentication is the exception, and deliberately. A tool call arriving with no
credential, or with an OAuth access token that has expired or been revoked, is
refused with HTTP 401 and a `WWW-Authenticate` header rather than a tool error.
A client reads that as a signal to refresh its token or ask you to sign in, and
then retries the same call; a tool error would be handed to the model as text
and the turn would move on with nothing fixed.

A mistyped API key is not treated that way. It reaches the API and comes back as
a tool error saying the key is invalid, which is what somebody who has just
pasted one needs to read.

## Limits

Tool calls are limited per key, on the same three windows as the REST API. See
[rate limits](/api-reference/rate-limits).

Everything except a tool call is limited per IP address, at 120 requests an
hour. That covers `initialize`, `tools/list` and every other method, all of
which answer without reaching the API and so are not metered against any key.

Tool calls are not subject to it. They are metered against your key instead, on
the three windows above.
