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

mcp-server-hyperledger-fabric

by rene-dekart
README.md
<p align="center">
  <h1 align="center">mcp-server-hyperledger-fabric</h1>
  <p align="center">
    MCP server for <a href="https://www.hyperledger.org/use/fabric">Hyperledger Fabric</a> โ€”
    let AI agents query and invoke chaincode, read blocks and events from a Fabric network.
  </p>
</p>

<p align="center">
  <a href="https://github.com/rene-dekart/mcp-server-hyperledger-fabric/actions/workflows/ci.yml"><img src="https://github.com/rene-dekart/mcp-server-hyperledger-fabric/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://www.npmjs.com/package/mcp-server-hyperledger-fabric"><img src="https://img.shields.io/npm/v/mcp-server-hyperledger-fabric.svg" alt="npm version"></a>
  <a href="https://www.npmjs.com/package/mcp-server-hyperledger-fabric"><img src="https://img.shields.io/npm/dw/mcp-server-hyperledger-fabric.svg" alt="npm downloads"></a>
  <a href="https://nodejs.org"><img src="https://img.shields.io/node/v/mcp-server-hyperledger-fabric.svg" alt="node compatibility"></a>
  <a href="https://www.npmjs.com/package/mcp-server-hyperledger-fabric"><img src="https://img.shields.io/npm/l/mcp-server-hyperledger-fabric.svg" alt="license"></a>
</p>

## 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

## Installation

```bash
npm install -g mcp-server-hyperledger-fabric
```

Requires Node.js โ‰ฅ 20 and a Fabric 2.5+ network with the
[Gateway service](https://hyperledger-fabric.readthedocs.io/en/latest/gateway.html) 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:

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

Claude Code config stays minimal:

```json
{
  "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.

### 2. Scan your chaincodes (recommended)

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

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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

```bash
# 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

```bash
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](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](LICENSE)