Skip to main content
Glama
README.md
# AssetMCP

stdio [Model Context Protocol](https://modelcontextprotocol.io/) server for **UluOS**: normalized **ASA**, **ARC-200**, and **ARC-72** reads plus **unsigned** transaction payloads on **Algorand mainnet** and **Voi mainnet**.

Signing and broadcasting are intentionally out of scope—use **UluWalletMCP** and **UluBroadcastMCP** (or your wallet) after fetching base64 txn groups from this service.

## Service summary (for UluOS docs)

| Field | Value |
|--------|--------|
| **Name** | `asset-mcp` |
| **Transport** | stdio |
| **Version** | 0.1.0 |
| **Networks** | `algorand-mainnet`, `voi-mainnet` |
| **Stack** | `@modelcontextprotocol/sdk`, `algosdk` (ASA), `ulujs` → `arccjs` (ARC-200 / ARC-72 simulation) |
| **Output** | JSON text content; BigInts as strings; binary as base64; txn builders return ordered `transactions: string[]` (base64 unsigned bytes) |

## Setup

```bash
npm install
npm start
# or: node /absolute/path/to/AssetMCP/index.js
```

**Node:** >= 20.9 (required by `ulujs`).

## Configuration

Defaults use [Nodely free-tier](https://nodely.io/docs/free/start) Algod + Indexer endpoints (Algorand mainnet: `*.4160.nodely.dev`; Voi mainnet: `*.voi.nodely.dev`). No API key required. Override with environment variables:

Pattern: `ASSET_MCP_<NETWORK>_<SUFFIX>` where `<NETWORK>` is `ALGORAND_MAINNET` or `VOI_MAINNET`, and `<SUFFIX>` is one of:

- `ALGOD_URL`
- `ALGOD_TOKEN`
- `INDEXER_URL`
- `INDEXER_TOKEN`

Example:

```bash
export ASSET_MCP_ALGORAND_MAINNET_ALGOD_URL="https://..."
export ASSET_MCP_ALGORAND_MAINNET_ALGOD_TOKEN="..."
```

## Response shape

Successful tool calls return JSON:

```json
{
  "ok": true,
  "network": "algorand-mainnet",
  "standard": "asa",
  "method": "asa_get_asset",
  "assetId": 31566704,
  "data": { }
}
```

Transaction builders add `txCount` and put base64 unsigned transactions under `data.transactions` (array order is the signing order).

Errors:

```json
{
  "ok": false,
  "error": {
    "code": "malformed_address",
    "message": "...",
    "details": null
  }
}
```

## Tools (by namespace)

### `asa_*` (algosdk / AVM)

| Tool | Purpose |
|------|---------|
| `asa_get_asset` | ASA params by ID |
| `asa_get_holding` | One account’s balance / frozen flag for an ASA |
| `asa_search_holdings` | Indexer: paginated holdings; optional `assetId` filter |
| `asa_transfer_txn` | Unsigned `axfer` |
| `asa_optin_txn` | Unsigned opt-in (0-amount self transfer) |
| `asa_closeout_txn` | Unsigned transfer + `closeRemainderTo` (supply full `amount`) |

### `arc200_*` (`ulujs` ARC-200)

| Tool | Purpose |
|------|---------|
| `arc200_get_metadata` | `name`, `symbol`, `decimals`, `totalSupply` |
| `arc200_balance_of` | Balance (base units, string in JSON) |
| `arc200_allowance` | Allowance |
| `arc200_has_balance` | `hasBalance` (ulujs dual-ABI helper) |
| `arc200_has_allowance` | `hasAllowance` (ulujs dual-ABI helper) |
| `arc200_get_events` | `getEvents` → Transfer + Approval streams |
| `arc200_transfer_txn` | Simulated transfer → unsigned txn group |
| `arc200_transfer_from_txn` | Simulated `transferFrom` |
| `arc200_approve_txn` | Simulated `approve` |

### `arc72_*` (`ulujs` ARC-72)

| Tool | Purpose |
|------|---------|
| `arc72_get_metadata` | `totalSupply`; with `tokenId`: `owner`, `tokenURI`, `approved`; optional `supportsInterface` via `interfaceSelectorHex` |
| `arc72_owner_of` | Owner |
| `arc72_balance_of` | NFT count |
| `arc72_get_approved` | Approved address for token |
| `arc72_is_approved_for_all` | Operator flag |
| `arc72_token_uri` | Token URI |
| `arc72_total_supply` | Total supply |
| `arc72_get_events` | Transfer / Approval / ApprovalForAll |
| `arc72_transfer_txn` | `transferFrom(from, to, tokenId)` (signer must be authorized) |
| `arc72_approve_txn` | Approve spender for `tokenId` |
| `arc72_set_approval_for_all_txn` | `setApprovalForAll` via **arccjs** (ulujs wrapper is broken upstream) |

## Cursor MCP config

This repo includes [`.cursor/mcp.json`](.cursor/mcp.json) so **asset-mcp** is available when you open the repo as the workspace folder. Reload the window or restart Cursor after changing it.

To add manually or merge into another project:

```json
{
  "mcpServers": {
    "asset-mcp": {
      "command": "node",
      "args": ["${workspaceFolder}/index.js"]
    }
  }
}
```

## Machine-readable capabilities

See [`examples/capabilities.json`](examples/capabilities.json).

**Live demo:** from the repo root, `npm run demo` (or `node examples/demo.mjs`) spawns the MCP server over stdio and calls `asa_get_asset` (Algorand USDC), `arc200_get_metadata` (Voi wVOI), and `arc72_total_supply` (sample ARC-72 collection).

## Implementation notes

- **stdout hygiene:** `ulujs` / `arccjs` call `console.log` during simulation. Unless `ASSET_MCP_DEBUG` is set, `console.log` is redirected to **stderr** so MCP JSON-RPC on stdout stays valid.
- **ARC txn bytes:** Built by simulating with `simulate: true` and returning the unsigned group from `arccjs` (`txns` array). Extra group transactions (e.g. box / resource-sharing) may appear—sign the full ordered group.
- **ARC-200 / ARC-72 reads:** Use ulujs `Contract` with the public simulation sender (`oneAddress` from ulujs) for readonly ABI calls.
- **Event filters:** `address` is forwarded to the indexer as **`sender`** (arccjs API).
- **ARC-72 `setApprovalForAll`:** Implemented against `contractInstance.arc72_setApprovalForAll` because `ulujs` `safe_arc72_setApprovalForAll` references undefined variables (upstream bug).

## License

MIT (see [LICENSE](LICENSE)).

TDQS

B3.2/5.0

Scored across 29 tools

Disambiguation5/5

Each tool is unambiguously prefixed by its protocol (asa_, arc200_, nt200_, arc72_) and named after a specific action or query, so no two tools appear to target the same operation. Even similar functions like `balance_of` and `has_balance` have distinct meanings and clear descriptions. The grouping by asset type eliminates cross-protocol confusion.

Naming Consistency5/5

All tool names follow a strict lowercase snake_case pattern with a protocol prefix and a verb_noun or verb_txn suffix. Transaction builders consistently end in `_txn`, while queries use `get_`, `search_`, `has_`, etc., making the naming perfectly predictable across all 29 tools.

Tool Count2/5

With 29 tools, the server significantly exceeds the recommended 3-15 range for well-scoped MCP servers, and is above the 25+ threshold explicitly labeled as 'too many' in the rubric. While each tool serves a distinct purpose and covers multiple token standards, the sheer number is likely to overwhelm agents and increase selection complexity.

Completeness5/5

The tool set provides comprehensive coverage for each supported asset type: ASA queries, holdings, transfers, opt-in, and close-out; ARC-200 includes metadata, balances, allowances, approvals, transfers, and events; ARC-72 covers ownership, approvals, token URI, supply, transfers, and events. NT200 covers deposit, withdrawal, and balance box creation. Missing operations like asset creation are outside the apparent scope, so there are no critical gaps.

Maintenance

ActivityInactive
ResponsivenessNo issues