Skip to main content
Glama
mcp-server-hyperledger-fabric

mcp-server-hyperledger-fabric

by rene-dekart

Features

  • πŸ” Query chaincode β€” evaluate read-only functions, private data supported

  • ✍️ Invoke chaincode β€” submit transactions, returns txid + commit status

  • πŸ§ͺ Endorse chaincode β€” dry-run a write without committing

  • πŸ“¦ Read the ledger β€” blocks, transactions, chaincode events

  • 🧾 Structured output β€” every tool declares an outputSchema and returns structuredContent, so clients validate results instead of parsing text

  • πŸ₯ Check peer health β€” height, last block, latency (no REST required)

  • πŸ“š Chaincode API discovery β€” mcp-server-hyperledger-fabric scan generates descriptions served as MCP resources

  • 🐳 Docker image β€” isolated server with per-call stderr logging

Related MCP server: Hyperledger Fabric MCP Server

Installation

npm install -g mcp-server-hyperledger-fabric

Requires Node.js β‰₯ 20 and a Fabric 2.5+ network with the Gateway service enabled.

Quickstart

1. Configure the network

Copy .env.example to .env in the project root and fill in your network values. The server reads .env from its own directory automatically β€” no env vars to pass from the MCP client:

cp .env.example .env   # fill in your network values

Claude Code config stays minimal:

{
  "mcpServers": {
    "hyperledger": {
      "command": "mcp-server-hyperledger-fabric"
    }
  }
}

Existing environment variables take precedence over .env (standard dotenv behavior), so you can still override per-launch if needed.

To work with several networks at once, give each one its own MCP server entry with its own env block β€” those values override .env. Copy .mcp.example.json to .mcp.json (git-ignored) and fill in the paths.

The model needs to know your chaincode's contract and method names. Generate descriptions once (see Chaincode API scan):

mcp-server-hyperledger-fabric scan ./chaincode-typescript --name asset-transfer
# β†’ chaincode-api/asset-transfer.json

The server picks up ./chaincode-api by default β€” no extra config needed.

3. Verify

Ask the model to read something, e.g. "read asset asset1". If it finds and calls query_chaincode, the server is working.

Configuration

All configuration comes from environment variables (see .env.example).

Variable

Required

Default

Description

FABRIC_PEER_ENDPOINT

βœ…

β€”

gRPC endpoint of the gateway peer, host:port

FABRIC_CHANNEL

β€”

β€”

Default channel named in the server instructions sent to the client

FABRIC_TLS

β€”

true

Use TLS. false is dev-only (test-network)

FABRIC_PEER_TLS_CERT

with TLS

β€”

Path to the peer's TLS CA cert (PEM)

FABRIC_PEER_TLS_SERVER_NAME

β€”

β€”

Override TLS server name (SSH tunnel)

FABRIC_MSP_ID

βœ…

β€”

MSP ID of the identity

FABRIC_CERT

βœ…

β€”

Path to the enrollment signcert (PEM)

FABRIC_KEY

βœ…

β€”

Path to the private key (PEM)

FABRIC_REST_URL

β€”

β€”

Gateway REST API base URL (for get_blockchain_info)

FABRIC_CHAINCODE_API

β€”

./chaincode-api

Chaincode API descriptions (file or dir) from mcp-server-hyperledger-fabric scan

FABRIC_READ_ONLY

β€”

false

Register only read tools; invoke_chaincode and endorse_chaincode are not exposed

FABRIC_LOG_TRANSIENT

β€”

false

Log transientData values in full. Default: keys, byte sizes and a short hash only

REQUEST_TIMEOUT_MS

β€”

30000

Deadline for each network call

FABRIC_DEDUP_WINDOW_MS

β€”

0

Dedup window for invoke_chaincode, ms. 0 = off. When > 0, an identical write (same chaincode, method, args) within the window is not re-sent β€” the previous txid is returned instead, guarding against double-writes from retried invokes. In-memory, exact-args match only

Tools

Tool

Description

Read-only

query_chaincode

Evaluate a chaincode read function (supports private data)

βœ…

invoke_chaincode

Submit a transaction; returns txid + commit status (supports private data)

❌ (destructive)

endorse_chaincode

Dry-run a write: endorse without committing

βœ…

get_block

Block summary by number (hashes, txids)

βœ…

get_block_by_hash

Find a block by its data hash (block scan)

βœ…

get_transaction

Find a transaction by ID (block scan)

βœ…

get_blockchain_info

Ledger height via Fabric Gateway REST API

βœ…

get_chaincode_events

Recent chaincode events, filterable by eventName and startBlock

βœ…

check_network_status

Peer health: height, last block, response latency

βœ…

Chaincode API scan

The model needs to know a chaincode's contract and method names to call it. Instead of guessing, generate a machine-readable description from the chaincode source once:

mcp-server-hyperledger-fabric scan ./chaincode-typescript --name asset-transfer
# β†’ chaincode-api/asset-transfer.json

Several chaincodes? Scan each into the same directory β€” the server serves them all:

mcp-server-hyperledger-fabric scan ./cc-one --name cc-one
mcp-server-hyperledger-fabric scan ./cc-two --name cc-two
# β†’ chaincode-api/cc-one.json, chaincode-api/cc-two.json

--name is the chaincode name in Fabric; if omitted, the directory name is used. Re-run the scan whenever the chaincode changes β€” the server re-reads the files on every access, no restart needed.

The scanner reads fabric-contract-api decorators from TypeScript source:

Decorator

Meaning

@Default()

Marks the default contract

@Transaction()

Submit (write) method

@Transaction(false)

Evaluate (read) method

@Returns('type')

Return type

Only @Transaction-marked methods are exposed by the chaincode runtime, so helpers are skipped.

The generated chaincode-api/*.json files are served to the model as MCP resources (fabric://chaincode/<name>), so it can look up contracts and methods itself instead of guessing. The server picks up the directory from ./chaincode-api (or FABRIC_CHAINCODE_API); files are re-read on every access, so re-running the scan applies without a restart.

Scan output goes to chaincode-api/ by default β€” a directory that is git-ignored in this repo, so generated descriptions never get committed. Use -o <file.json> for a single file or -o <dir> for a custom directory.

Docker (local, for logs)

The container isolates the server and prints every tool call to stderr β€” you see what the agent invokes and with which parameters, without reading the MCP protocol:

[mcp-server-hyperledger-fabric] query_chaincode <- {"channel":"mychannel","contract":"AssetContract","method":"GetAsset","args":["1"]}
[mcp-server-hyperledger-fabric] query_chaincode OK 12ms β†’ {"assetId":"1","owner":"alice","value":100}
[mcp-server-hyperledger-fabric] invoke_chaincode ERROR 900ms β†’ Fabric error: endorsement failed: chaincode error

Build and verify:

docker compose build                        # build the image
docker compose run -T --rm mcp-server-hyperledger-fabric  # one-off, MCP server over stdio
node scripts/docker-smoke.mjs               # smoke test against a real network

Paths are not hardcoded: WALLET_DIR (where the certs live) is set in your local .env (git-ignored), defaulting to ./wallets in the project folder.

Claude Code connects to the image via docker compose run β€” the compose file carries the env file and mounts, so the client config stays path-free:

claude mcp add hyperledger -- docker compose run -T --rm mcp-server-hyperledger-fabric

Logs: while a Claude Code session is open, the container is alive in Docker Desktop β€” Containers tab β†’ View β†’ Logs (or docker logs <name>). The container is --rm, so it is removed when the session closes, logs included.

What gets mounted (not baked into the image β€” no rebuild needed on edits):

Container path

From

What

/wallets

WALLET_DIR from .env (default ./wallets)

certs and key (ro)

/app/chaincode-api

CHAINCODE_API_DIR (default ./chaincode-api)

chaincode API descriptions (ro)

Secrets live only in .env / .env.docker (both git-ignored) and never enter the image. docker compose build is only needed when the server code changes; a fresh chaincode scan applies without a rebuild.

Testing against the Fabric test-network

# start the test network with the asset-transfer sample
./network.sh up createChannel -c mychannel -ca
./network.sh deployCC -ccn asset-transfer -ccp ../asset-transfer-basic/chaincode-go

# export the generated identity and TLS cert
export FABRIC_PEER_ENDPOINT=localhost:7051
export FABRIC_TLS=true
export FABRIC_PEER_TLS_CERT=$PWD/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export FABRIC_MSP_ID=Org1MSP
export FABRIC_CERT=$PWD/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/cert.pem
export FABRIC_KEY=$PWD/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/priv_sk

npm run dev

Development

npm install
npm run build    # tsc β†’ dist/
npm run lint     # Biome: lint + format + import order
npm run format   # apply Biome fixes
npm test         # node:test unit tests
npm run dev      # tsx watch

Security

This server holds a real Fabric identity (private key). Read SECURITY.md before deploying. Key points:

  • Secrets only via environment variables β€” never commit .env or certs.

  • TLS on by default; FABRIC_TLS=false is for local test-network only.

  • All tool inputs validated with strict zod schemas.

  • invoke_chaincode is marked destructive so clients confirm before writes.

  • Business errors return { isError: true } β€” no raw stack traces.

License

MIT

Related MCP Connectors

Related MCP Servers