Skip to main content
Glama

xrpl-did-mcp

An MCP server for did:xrpl — W3C-compatible decentralized identifiers for AI agents on the XRP Ledger.

Point any MCP client at it and your agent can derive, resolve, and authorize identities natively.

# not on PyPI yet -- install from GitHub, pinned to the release tag
pip install "xrpl-did-mcp @ git+https://github.com/DuNorth595/xrpl_did_mcp.git@v0.6.0"

That one command pulls in xrpl-agent-id and xrpl-py as well. Both are pinned to release tags, so an install cannot silently change underneath you.


What it gives an MCP client

Tool

What it does

Writes to the ledger?

derive_did

Turn an XRP address into a did:xrpl

No

resolve_did

Read a DID Document for a did:xrpl

No

check_authorization

Decide whether an agent may act, with reasons

No

lookup_account

Read an account's balance and on-ledger state

No

lookup_transaction

Read a transaction's result and inclusion status

No

get_credential

Read a credential's state — exists, accepted, URI, expiry

No

list_credentials

List the credentials on an account, held and issued

No

issue_credential

Create a credential for a subject

Yes — spends XRP

accept_credential

Accept a credential issued to the signing account

Yes — spends XRP

publish_did_set

Publish an agent's DID Document

Yes — spends XRP

revoke_did

Delete an account's DID Document

Yes — spends XRP

revoke_credential

Withdraw one credential

Yes — spends XRP

Seven of the twelve tools are read-only. The five that write:

  • Refuse by default. Each returns WRITES_DISABLED unless the server was started with --allow-writes.

  • Take no seed argument. The signing seed comes from the OS keychain, which the server reads itself, so the seed never passes through the agent's context.

Revocation

revoke_did submits a DIDDelete, so the DID no longer resolves. DIDDelete always targets the signer's own DID, so the tool refuses with ADDRESS_MISMATCH if the address is not the one the seed controls — before anything is submitted.

revoke_credential submits a CredentialDelete, withdrawing one credential. A credential is identified by the triple (issuer, subject, credential_type); the subject may be an address or a did:xrpl.

Deleting a DID does not delete the account's credentials. A DID and a credential are separate ledger objects — a DID is keyed by its owner account, a credential by (issuer, subject, credential_type). rippled's DIDDelete erases only the DID. To withdraw an authorization, revoke the credential too.

The credential lifecycle

A credential is identified by the triple (issuer, subject, credential_type), and it takes two signatures before it is valid for the subject:

Step

Tool

Who signs

1. Create

issue_credential

the issuer

2. Accept

accept_credential

the subject

3. Read one

get_credential

nobody — it is a read

4. List all

list_credentials

nobody — it is a read

5. Withdraw

revoke_credential

the issuer

issue_credential submits a CredentialCreate. The credential exists on the ledger at that point, but XLS-70 only sets the accepted flag when the subject signs — so until accept_credential runs, the credential does not count as held. get_credential reports the two states separately, because existence is not acceptance.

The subject and issuer may each be an address or a did:xrpl; a did:xrpl is reduced to its address before the transaction is built.

Finding credentials without knowing which

get_credential needs all three identifiers: issuer, subject, and credential type. When you know the account but not the credential, list_credentials enumerates instead:

{
  "account": "rSubjectAddress",
  "held_count": 2,
  "issued_count": 0,
  "held": [
    {
      "issuer": "did:xrpl:2:rIssuer",
      "subject": "did:xrpl:2:rSubject",
      "credential_type": "KYC",
      "accepted": false,
      "uri": null,
      "expiration": null,
      "role": "subject"
    }
  ]
}

held is what the account holds as a subject; issued is what it has issued to others. Each entry carries the issuer, the subject, the credential type, the URI, the expiry, and whether the subject has accepted it — the same fields get_credential returns, so the two tools answer the same shape of question at different scopes.

Pass include_issued=False to skip the issued side when you only care about what the account holds.


Related MCP server: xrpl-identity-mcp

Connect it

Claude Desktop / Claude Code

{
  "mcpServers": {
    "xrpl-did": {
      "command": "xrpl-did-mcp",
      "env": { "XRPL_MCP_NETWORK": "testnet" }
    }
  }
}

Any other MCP client (stdio)

xrpl-did-mcp                       # stdio, testnet, read-only
xrpl-did-mcp --network mainnet     # stdio, mainnet, read-only
xrpl-did-mcp --allow-writes        # enable the write tools
xrpl-did-mcp --http 8080           # streamable-http

Enabling the write tools

The write tools — publish_did_set, revoke_did, revoke_credential, issue_credential and accept_credential — need two things: the server started with --allow-writes, and a signing seed in the OS keychain.

# Store a seed (macOS). The server reads it; the agent never sees it.
security add-generic-password -a "$USER" -s xrpl-did-mcp -w '<your seed>'

The keychain service defaults to xrpl-did-mcp; set XRPL_MCP_KEYCHAIN_SERVICE to change it.

If the keychain has no entry, the tool returns NO_SEED with the setup instructions. It never falls back to something weaker.

For throwaway testnet use where a seed in the conversation is harmless, --allow-inline-seed permits passing one directly as the tool's seed argument. It is off by default, because a seed in the agent's context is a seed the agent can read.

Without that flag the seed argument is refused: the tool returns INLINE_SEED_REFUSED and falls back to the keychain. The keychain is always tried first, even when an inline seed is supplied.


Operator trust policy

The caller of a tool is not necessarily the operator of the server. An agent that chooses its own trust policy can always choose a permissive one, so the server keeps its own policy on disk and applies it on top of whatever the caller passes.

Put a trust_policy.json in the server's working directory, or point XRPL_MCP_POLICY at one:

{
  "require": {
    "issuers": ["rTrustedIssuer..."],
    "credential_type": "agent-authorization"
  },
  "deny": {
    "issuers": ["rRevokedIssuer..."],
    "credential_type": "agent-authorization"
  },
  "ban": {
    "agents": ["did:xrpl:1:rBanned...", "rBannedPlain..."]
  }
}

Every section is optional.

  • require — the agent must hold this credential.

  • deny — holding this credential is grounds for refusal, whatever else it holds.

  • ban — the agent is refused outright.

The operator's rules cannot be waived by the caller. A missing policy file means the operator has not expressed an opinion — it does not mean allow everything; it means no operator rules are added to the caller's.

A policy file that exists but cannot be parsed is surfaced as POLICY_INVALID rather than ignored, because silently dropping the operator's deny rules would quietly remove the property the file exists to provide.


What it looks like in a conversation

Agent: Give me a DID for rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh.

Tool derive_did: did:xrpl:1:rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh — derived from the address. No registration, no fee, no database row.

Agent: Resolve it.

Tool resolve_did: reads the DIDSet from the ledger and returns the DID Document. Anyone can do this — there is no server of ours in the path.

Agent: Can this agent act?

Tool check_authorization: ALLOW — or DENY with the list of missing credentials and the deny rules that fired.


Why a DID and not just an address

An XRP address says who holds a key. It does not say what that key is allowed to do, who vouches for it, or whether it was revoked.

did:xrpl adds a resolvable DID Document, so an agent's identity can carry credentials — and the authorization decision becomes a ledger read rather than a trust-me.

Built on xrpl_agent_id, which implements the XLS-40d DID model and XLS-70d credentials.


Networks

Testnet by default. Mainnet is the same code with network="mainnet".

xrpl_agent_id has been validated on XRPL mainnet — all six authorization roles, verified against the public ledger. See the mainnet decision record.


Development

git clone https://github.com/DuNorth595/xrpl_did_mcp
cd xrpl_did_mcp
pip install -e ".[dev]"
pytest

The default suite is fully offline. Two ledger-backed tests are skipped unless XRPL_MCP_LIVE=1 is set.

Documentation

Document

What it covers

SECURITY.md

Reporting a vulnerability, and the security-relevant design decisions

CONTRIBUTING.md

How to contribute, and how to add a tool

docs/CHANGELOG.md

Release history

License

MIT — see LICENSE.

Available Tools

12 tools
accept_credentialAccept a credential issued to this account (writes to the ledger)A

Accept a credential that was issued to this account, making it valid for the subject. THIS WRITES TO THE LEDGER and spends XRP.

A credential created with issue_credential is not valid for the subject until the subject signs it. This tool is the subject's side of that: it submits the CredentialAccept.

The signing seed comes from the OS keychain, which the server reads itself, so the agent never sees the seed. This tool is only available if the server was started with --allow-writes.

An optional seed argument exists for throwaway testnet use. It is refused unless the server was started with --allow-inline-seed.

To read a credential's state without changing anything, use get_credential. To withdraw a credential, use revoke_credential.

Inputs: issuer (required), credential_type (required), seed, network. Returns: object

check_authorizationDecide whether an agent is authorized to actA

Evaluate an agent against a trust policy and return an allow/deny decision with the reasons. The result reports which required credentials are missing and which deny rules matched, so the decision is auditable rather than a bare boolean.

The caller's policy is combined with the server operator's policy file, if one is configured. The operator's deny and ban rules are applied on top of the caller's and cannot be waived by them.

Inputs: agent_did (required), credential_type (required), trusted_issuers (required), network, +2 more. Returns: object

derive_didDerive a did:xrpl from an XRP addressA

Derive the W3C-compatible decentralized identifier for an account on the XRP Ledger. Format: did:xrpl::. This is a pure derivation -- no transaction, no registration, no fee.

Inputs: address (required), network. Returns: object

get_credentialRead a credential's state on the ledgerA

Read one credential's state from the XRP Ledger: whether it exists, whether the subject has accepted it, its URI, and its expiration.

This is a pure read. It submits no transaction, spends nothing, and does not need --allow-writes.

Existence is not acceptance: a credential issued with issue_credential does not become valid for the subject until the subject accepts it. This tool reports which of those is true.

For an account's balance and other state, use lookup_account. To accept a credential, use accept_credential.

Inputs: issuer (required), subject (required), credential_type (required), network. Returns: object

issue_credentialIssue a credential to a subject (writes to the ledger)A

Create a credential on the XRP Ledger, so a subject can be authorised to act. THIS WRITES TO THE LEDGER and spends XRP.

The signing seed comes from the OS keychain, which the server reads itself, so the agent never sees the seed. This tool is only available if the server was started with --allow-writes.

An optional seed argument exists for throwaway testnet use. It is refused unless the server was started with --allow-inline-seed, because a seed in the agent's context is a seed the agent can read.

The credential is created but not yet valid for the subject until the subject accepts it with accept_credential. To withdraw a credential, use revoke_credential instead. To read a credential's state without changing anything, use get_credential.

Inputs: subject (required), credential_type (required), uri, seed, +2 more. Returns: object

list_credentialsList the credentials on an accountA

List every credential on the XRP Ledger that involves an account -- the ones it holds as a subject, and the ones it issued to others.

This is a pure read. It submits no transaction, spends nothing, and does not need --allow-writes.

Use this when you know WHO but not WHICH credential: it answers 'what credentials does this account have?' without needing the issuer, subject and type up front. For one known credential's state, use get_credential instead.

Results carry the issuer, the subject, the credential type, and whether the subject has accepted each one. Credentials that exist but were never accepted are marked accepted=false, because XLS-70 only sets that flag when the subject signs.

Inputs: account (required), network, include_issued. Returns: object

lookup_accountLook up an account's balance and state on the ledgerA

Read an XRP Ledger account's on-ledger state: its balance in drops, the number of objects it owns, and its next transaction sequence.

For an account's DID Document, use resolve_did instead -- that tool returns the document itself, not the account's other state.

Inputs: address (required), network. Returns: object

lookup_transactionLook up a transaction's result on the ledgerA

Read a transaction's outcome from the XRP Ledger by its hash: the result code (for example tesSUCCESS for a successful transaction, or tecUNFUNDED_PAYMENT for one that failed), the ledger index it was included in, and whether it has been validated.

For the account that sent or received it, use lookup_account.

Inputs: tx_hash (required), network. Returns: object

publish_did_setPublish an agent's DID Document (writes to the ledger)A

Submit a DIDSet transaction so the agent's DID resolves to a DID Document. THIS WRITES TO THE LEDGER and spends XRP.

The signing seed comes from the OS keychain, which the server reads itself, so the agent never sees the seed. This tool is only available if the server was started with --allow-writes.

An optional seed argument exists for throwaway testnet use. It is refused unless the server was started with --allow-inline-seed, because a seed in the agent's context is a seed the agent can read.

Defaults to testnet; pass network='mainnet' explicitly to use mainnet.

Inputs: address (required), seed, network, document_uri. Returns: object

resolve_didResolve a did:xrpl to its DID DocumentA

Resolve a did:xrpl into its DID Document by reading the XRP Ledger. The document is fetched from the ledger itself, so the result is verifiable by anyone and does not depend on this server.

This is the only tool that returns a DID Document. For an account's balance and other on-ledger state, use lookup_account.

Inputs: did (required), network. Returns: object

revoke_credentialRevoke a credential (writes to the ledger)A

Submit a CredentialDelete transaction, withdrawing one credential so it no longer counts as held. THIS WRITES TO THE LEDGER and spends XRP.

A credential is identified by the triple (issuer, subject, credential_type). The issuer or the subject may sign the delete.

This tool is only available if the server was started with --allow-writes. The signing seed comes from the OS keychain, which the server reads itself, so the seed never passes through the agent's context.

Inputs: subject (required), credential_type (required), seed, network. Returns: object

revoke_didDelete an account's DID Document (writes to the ledger)A

Submit a DIDDelete transaction, removing the account's DID Document from the ledger so the DID no longer resolves. THIS WRITES TO THE LEDGER and spends XRP.

DIDDelete takes only common fields -- it always deletes the signer's own DID, so the address must match the account the seed controls.

This does NOT delete the account's credentials. A DID and a credential are separate ledger objects, and rippled's DIDDelete erases only the DID. To withdraw an authorization, revoke the credential too.

This tool is only available if the server was started with --allow-writes. The signing seed comes from the OS keychain, which the server reads itself, so the seed never passes through the agent's context.

Inputs: address (required), seed, network. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 12 tools

    Disambiguation5/5

    Each tool targets a distinct ledger action or read: derive/resolve/publish/revoke for DIDs, issue/accept/revoke/get/list for credentials, plus account and transaction reads. Descriptions explicitly disambiguate the closest pairs (derive_did vs resolve_did, get_credential vs list_credentials, lookup_account vs lookup_transaction) with explicit 'use X instead' pointers.

    Naming Consistency4/5

    Nearly all names follow verb_noun (derive_did, issue_credential, revoke_credential, lookup_account). The exceptions are publish_did_set, which is named after the ledger transaction type rather than a verb_noun, and the split between get_/list_/lookup_/resolve_ for read operations, which is readable but not perfectly uniform.

    Tool Count5/5

    Twelve tools is well within a comfortable range and each one maps to a concrete step in the DID or credential lifecycle. Nothing appears redundant or padded; the read-only/write split also justifies separate tools.

    Completeness4/5

    Coverage is strong: DIDs have derive, publish (also usable to update the document), resolve, and revoke; credentials have issue, accept, read, list, and revoke; policy evaluation and account/transaction reads round it out. The main gap is transaction discovery — lookup_transaction requires a known hash and there is no way to list an account's transactions, so agents must obtain hashes elsewhere.

    Maintenance

    ActivityNo data
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      A
      quality
      B
      maintenance
      Mock identity-management MCP server for decentralized identity that allows minting DIDs, issuing signed Verifiable Credentials, and verifying them.
      8
      -
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables MCP hosts to verify agent spending mandates and receipts, providing stateless tools for authorization, chain verification, credential verification, and DID resolution.
      Apache 2.0
    • A
      license
      A
      quality
      B
      maintenance
      Enables AI agents to manage identity, permissions, secrets, and verifiable proofs through a hosted MCP server, letting clients sign in and check what an agent may do before it acts.
      15
      286 npm
      Apache 2.0