Model Context Protocol (MCP) server that lets an AI agent read Starknet: balances, transactions, blocks, storage, and arbitrary read-only contract calls.
The Starknet MCP server defines 11 read-only tools with explicit input schemas and descriptions. All tools follow a clear verb_noun naming convention (get_*, list_*) appropriate for their actions. Descriptions are present and moderately detailed (avg ~120 chars), explaining what each tool does and when to use it. However, there are systematic gaps: (1) most parameter descriptions lack format constraints or range guidance (e.g., 'address' says '0x...' but doesn't validate format or explain what constitutes a valid Starknet address); (2) output schemas are not documented, callers must infer structure from code or trial; (3) error handling provides no recovery guidance, failures return raw error strings without actionable next steps; (4) no tool annotations (readOnlyHint, idempotentHint) are declared despite all tools being read-only and idempotent; (5) parameter naming occasionally conflates types (e.g., 'block_id' can be 'latest'|'pending'|number|hash, but this is only hinted in description, not enforced via schema). These gaps are typical for community servers (C-grade territory) but prevent production-grade LLM interaction. The good: schemas are properly typed JSON Schema, all 11 tools are registered explicitly, naming is clear and consistent.
Make a read-only (view) call to any Starknet contract and return the raw felt result.
List non-zero balances of well-known tokens (ETH, STRK, USDC, USDT, DAI, WBTC, ...) for a Starknet address.
Get a block with its transaction hashes. Accepts 'latest' (default), 'pending', a block number, or a 0x block hash.
Get the configured network, RPC URL, chain id, and latest block number.
Get the class hash deployed at an address (errors if nothing is deployed there).
Get the nonce of a Starknet account / address.
Read a raw storage slot value from a contract.
Output schemas not documented. Tool responses return structured JSON (via toText() and ok() helpers), but callers cannot discover the shape, they must read implementation code or make test calls. LLMs cannot plan downstream tool usage without knowing what fields to extract.
Parameter descriptions lack format constraints and validation guidance. 'address' parameter says 'Starknet address (0x...)' but doesn't specify length, checksum validation, or what makes an address invalid. LLMs will pass malformed inputs without guidance on valid format. Compare to baseline: 'Starknet address (0x-prefixed, 66 hex chars, e.g. 0x1234...abcd)' with validation rules.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 56 | 2026-07-28+ | v2 |
Read the balance of a specific ERC-20 token for a Starknet address.
Get a transaction's details by hash.
Get a transaction's finality and execution status by hash.
List the built-in known token symbols and contract addresses (Starknet mainnet).
Error handling provides no recovery guidance. Errors are returned as raw text via fail() handler (e.g., 'Error: <raw message>'). No indication whether to retry, ask the user, or switch tools. Pattern requires: 'User address not found. Try calling get_balances first to verify the address exists.' Blank errors leave agents stuck.
No tool annotations declaring read-only or idempotent semantics. All 11 tools are read-only (no state mutation) and idempotent (same input → same output). Missing readOnlyHint and idempotentHint annotations prevent agents from optimizing caching, parallelization, and retry strategies. Per spec 2026-07-28, these should be declared in tool definitions.
Ambiguous parameter type acceptance without schema enforcement. 'get_block' accepts block_id as 'latest'|'pending'|number|0x hash, but schema only declares type:string with no enum or pattern. LLMs may pass invalid values (e.g., 'newest', 'block123'). Schema should use oneOf or enum to constrain valid options.
Optional parameters not marked as such in schema. 'calldata' in call_contract and 'block_id' in get_block are described as optional but schema doesn't declare them outside required[] array, good. However, 'block_id' is not in required[] yet schema doesn't explicitly mark it as optional via default or examples. This is correct but could be clearer.