ODATANO-MCP

@odatano/core-mcp is a Model Context Protocol (MCP) server that exposes ODATANO, the Cardano OData bridge, to AI agents. An agent connected to it can read chain, address, UTxO, asset, staking and governance data, query the indexer with standard OData $filter, build transactions, hand them to a human or wallet for signing, verify signatures and submit signed CBOR — through plain MCP tool calls, without knowing OData, CBOR or Cardano’s UTxO model.

It is the Cardano sibling of NIGHTGATE-MCP, and it makes the same promise: the agent gets a notary, not a wallet. Nothing in this server can sign. HSM signing (SignWithHsm*) and every admin operation (crawler / worker pause and resume) are deliberately not exposed; wallet-worker jobs, where a server-held key signs, are opt-in.

Why it is safe to let an agent build transactions

ODATANO’s write path is Build → Sign (external) → Submit, and the server never holds the sender’s key in the default flow. That maps cleanly onto what an agent may and may not do:

SurfaceExposed?Reason
Reads (20 entities, 19 actions)yespure reads, cached by the indexer
Build* actionsyesreturn unsigned CBOR + fee; nothing moves without a signature the agent cannot produce
Signing handoff + verificationyesproduces instructions for a human / CIP-30 wallet; verifies, never signs
Submityesthe agent can only submit CBOR a wallet already signed
HSM signingnevera server-held key is exactly the wallet the agent must not have
Wallet worker jobs (2.0)opt-inserver wallets sign — off unless ODATANO_MCP_ENABLE_WALLET_JOBS=1
Admin (pause / resume crawler, worker)neveroperator actions

Requirements

RequirementVersion
Node.js≥ 20
ODATANO host@odatano/core ≥ 1.11.0; ≥ 2.0.0-rc.3 recommended

Earlier hosts have two keyed-read defects: rc.1 drops $expand / $select on keyed reads (the get_transaction tool works around it automatically), and everything before rc.3 can answer a keyed read with a row the query excludes — that one has no workaround.

The crawler and wallet-worker tools appear automatically when the host serves those services: at startup the server probes cardano-worker/$metadata and cardano-indexer/$metadata, so a 1.x host gets a clean 36-tool catalogue and a 2.0 host 40.

Getting an ODATANO instance

# Docker (published from the ODATANO repo on every release)
docker run -d --name odatano -p 4004:4004 \
  -e NETWORK=preview \
  -e BACKENDS=blockfrost,koios \
  -e BLOCKFROST_API_KEY=preview_xxx \
  ghcr.io/odatano/odatano:latest

Or any CAP app using the plugin — npm i @odatano/core @cap-js/sqlite, add cds.requires.odatano-core, cds watch. See Get Started.

Configuration

VariableDefaultPurpose
ODATANO_BASE_URLhttp://localhost:4004ODATANO host app
ODATANO_USERNAME / ODATANO_PASSWORDunsetBasic auth (CAP mocked / dev auth, e.g. alice)
ODATANO_TOKENunsetBearer JWT (XSUAA / auth: jwt); an odat_… value is reserved for the planned agent-grant token
ODATANO_SERVICE_PREFIX/odata/v4Prefix before the five service paths
ODATANO_TIMEOUT_MS30000Per-request timeout
ODATANO_MCP_MAX_ROWS50Default $top for query_entity, cap on arrays in tool output
ODATANO_MCP_ENABLE_WALLET_JOBSfalseRegister the wallet-worker write tools

Use with Claude Code

claude mcp add odatano \
  --env ODATANO_BASE_URL=http://localhost:4004 \
  --env ODATANO_USERNAME=alice \
  -- npx -y @odatano/core-mcp

Or in a project .mcp.json:

{
  "mcpServers": {
    "odatano": {
      "command": "npx",
      "args": ["-y", "@odatano/core-mcp"],
      "env": {
        "ODATANO_BASE_URL": "http://localhost:4004",
        "ODATANO_USERNAME": "alice"
      }
    }
  }
}

Tools

Read

ToolWhat it does
get_network_infoHost network (mainnet / preview / preprod), tip, backend health — call this first
get_latest_block · get_blockLatest block, or block by hash
get_epochEpoch by number, or the current one
get_protocol_parametersFees, min-UTxO, cost models, collateral percentage
get_transactionTransaction by hash, optionally with resolved inputs and outputs
get_transaction_metadataMetadata entries (CIP-20, label-1447, CIP-25, …)
parse_transaction_cborDecode signed or unsigned CBOR — pure, no network call
get_address · get_utxos · get_address_assets · get_address_transactionsBalance, UTxOs (by address or payment credential), assets, recent txs
get_asset_info · get_asset_historySupply + CIP-25/26 metadata, mint / burn history
get_account · get_pool · get_drepStake account, stake pool, governance DRep

Lookups return { found: false } as a clean negative rather than an error.

Query

query_entity runs a standard OData V4 query ($filter, $select, $expand, $orderby, $top/$skip, $count) over the indexer entity sets — everything the get_* tools fetched before, plus, on hosts running the 2.0 crawler, contiguously pre-synced blocks and transactions. This is what turns the pre-sync into something an agent can actually reason over: questions no fixed action covers (“transactions above 500 000 lovelace in the last 1 000 blocks”) become one call.

Build — unsigned

ToolWhat it does
build_ada_transferADA transfer; optional assets, inline datum, lock at script address, CIP-33 reference script
build_metadata_transactionTransfer carrying transaction metadata (anchoring)
build_multi_asset_transferNative assets + ADA to one recipient
build_mint_transactionMint / burn under a Plutus V3 policy — parameters, required signers, reference inputs, metadata, __INPUT_IDX__ placeholders
build_plutus_spendSpend a script UTxO: validator + redeemer, continuing datum, extra outputs, combined mint
set_collateralEnsure an ADA-only collateral UTxO exists
get_build_details · list_builds_by_addressRe-fetch a build, build audit trail
derive_script_address · extract_payment_key_hashPure utilities

Every build returns { id, unsignedTxCbor, fee, … }nothing is signed or submitted.

Sign handoff, verify, submit

ToolWhat it does
create_signing_requestBuild → signing request with instructions and a ready-to-run cardano-cli command for the human / wallet
get_signing_request · list_signing_requestsState: pending → verified → submitted
verify_signatureCheck a signed CBOR against its request (required signers, fee payer) — no submit
verify_data_signatureCIP-30 signData (COSE_Sign1) verification for wallet login / consent
submit_signed_transactionSubmit already-signed CBOR; a duplicate returns alreadySubmitted: true instead of an error
submit_verified_transactionVerify + submit for a signing request (deferSubmit supported)
get_submission_statusSubmission state, optionally re-checking the chain

ODATANO 2.0 subsystems

get_sync_status and get_reorg_log (crawler / pre-sync), get_worker_status and get_wallet_job_status (wallet worker). The write tools submit_wallet_job / cancel_wallet_job are registered only with ODATANO_MCP_ENABLE_WALLET_JOBS=1 — those jobs are signed by a server-managed wallet and move funds.

Typical agent flow

1. get_network_info            → which network am I on?
2. get_utxos(address)          → what can be spent?
3. build_ada_transfer(...)     → { buildId, unsignedTxCbor, fee }   (nothing moved)
4. parse_transaction_cbor      → show the human what they are about to sign
5. create_signing_request      → instructions + cardano-cli command
   … human / CIP-30 wallet signs …
6. verify_signature            → witnesses valid for fee payer + required signers
7. submit_signed_transaction   → { txHash, status }
8. get_submission_status       → confirmed

Errors

Upstream OData errors surface as tool errors with { httpStatus, code, message }ODATANO_INVALID_INPUT, ODATANO_NOT_FOUND, ODATANO_INSUFFICIENT_FUNDS, ODATANO_TX_VALIDATION_FAILED, ODATANO_PROVIDER_UNAVAILABLE, ODATANO_PROVIDER_RATE_LIMITED — so the agent can react (401 → credentials, 429 → back off, 400 → fix arguments) instead of failing blind. Long arrays are capped at ODATANO_MCP_MAX_ROWS, and large CBOR blobs nested in results are elided (tools whose job is returning CBOR keep it).

Stack

LayerTech
ProtocolModel Context Protocol (@modelcontextprotocol/sdk), stdio transport
UpstreamODATANO OData V4 — five services under /odata/v4
ChainCardano mainnet / preview / preprod (whatever the host targets)
RuntimeNode.js ≥ 20, TypeScript, zod schemas
AuthBasic auth or bearer JWT
TestsUnit tests plus an in-memory MCP client integration check, with an optional live round-trip

Sourcecode

github.com/ODATANO/ODATANO-CORE-MCP · npm: @odatano/core-mcp