MCP server for the Luno cryptocurrency exchange API, providing tools for account management, market data, trading, and transaction history
The server defines 9 tools with complete tool registration, descriptions, and parameter schemas. However, there are systematic gaps in description quality, parameter documentation, and schema depth that prevent a higher score. Tool names follow verb_noun convention well (GetBalances, GetTicker, PlaceMarketOrder, etc.), which is positive. Descriptions are present for all tools and range from 150-280 characters, meeting the minimum threshold. However, several descriptions rely on examples rather than formal constraints, and critical details about error handling and recovery paths are missing. Parameter schemas are present and include type declarations and basic descriptions, but lack crucial constraint information (enums, ranges, formats). Error handling is mentioned in descriptions but not structured into the actual tool design with actionable recovery guidance. The 'confirm' parameter pattern for destructive operations (PlaceMarketOrder, CreateAccount, UpdateAccountName) shows awareness of confirmation-request patterns, but the implementation is simplistic, a preliminary dry-run response followed by conditional execution rather than a proper MRTR (Multi Round-Trip Request) pattern.
Create account (requires confirmation). Equivalent to Luno POST /api/1/accounts. Parameters: 'currency' (string, required, e.g. 'ZAR'), 'name' (string, required, account label), 'confirm' (bool, required). Returns new account details. Typical errors: unsupported currency, missing parameters.
Get account balances. Equivalent to Luno GET /api/1/balance. No parameters. Returns all wallet balances for the authenticated user, including available, reserved, and total per currency. Typical errors: authentication failure.
Get full order book. Equivalent to Luno GET /api/1/orderbook?pair={pair}. Parameter: 'pair' (string, required). Returns all bids and asks for the specified market. Typical errors: invalid pair code.
Get top order book. Equivalent to Luno GET /api/1/orderbook_top?pair={pair}. Parameter: 'pair' (string, required). Returns best bid and ask for the specified market. Typical errors: invalid pair code.
Get ticker for a currency pair. Equivalent to Luno GET /api/1/ticker?pair={pair}. Parameter: 'pair' (string, required, e.g. 'XBTZAR'). Returns latest price, bid, ask, and volume for the specified currency pair. Typical errors: invalid pair code.
Parameter descriptions lack constraint details. 'pair' parameter in GetTicker is described as 'currency pair code (e.g. XBTZAR)', the example is included in description rather than as an enum or pattern constraint. LLMs may interpret the example literally or fail with invalid pair codes. Should specify valid format (uppercase, length, character set) or enumerate supported pairs.
Confirmation pattern is not implemented as Multi Round-Trip Request (MRTR). Tools like PlaceMarketOrder, CreateAccount, and UpdateAccountName return a string message requiring re-invocation with confirm=true, rather than using proper MRTR with result='input_required' to trigger user confirmation. This is a deprecated stateful pattern.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | F | 43 | <=2025-11-25 | v2 |
| 2026-03-09 | F | 16 | - | v1 |
List pending transactions. Equivalent to Luno GET /api/1/accounts/{accountId}/pending. Parameter: 'accountId' (string, required). Returns all unconfirmed transactions for the specified account. Typical errors: account not found.
List account transactions. Equivalent to Luno GET /api/1/accounts/{accountId}/transactions. Parameters: 'accountId' (string, required), 'minRow' (int, required), 'maxRow' (int, required). Returns transaction history for the account. Typical errors: account not found, invalid row range.
Place a market order (requires confirmation). Equivalent to Luno POST /api/1/marketorder. Parameters: 'pair' (string, required, market code), 'type' (string, required, BUY or SELL), 'volume' (string, required, amount to buy/sell), 'confirm' (bool, required). Returns order result or error. Typical errors: insufficient funds, invalid parameters.
Update account name (requires confirmation). Equivalent to Luno PUT /api/1/accounts/{accountId}/name. Parameters: 'accountId' (string, required), 'name' (string, required, new label), 'confirm' (bool, required). Typical errors: account not found, invalid name.
Error handling is passive. Descriptions mention 'Typical errors' (e.g., 'invalid pair code', 'authentication failure') but the implementation does not provide structured error responses with actionable recovery guidance. EnsureSuccessStatusCode() throws exceptions with no custom messages. LLMs receive HTTP exceptions instead of guidance on what to do next (e.g., 'Try search_pairs() to find valid pair codes').
Output schemas are not documented. Tools return JSON strings directly from the Luno API (e.g., 'return json;') without specifying what fields to expect. The LLM cannot infer response structure to plan downstream operations or extract values needed for chaining tool calls.
ListTransactions parameters minRow and maxRow lack range constraints and descriptions. Should specify minimum/maximum bounds (e.g., 'minimum row index, range 0 - 1000000') to prevent absurd values or out-of-bounds queries.
No pagination support documented or enforced. ListTransactions requires minRow/maxRow but does not document result limits, whether results are paginated, or what happens if the range spans millions of transactions. Should cap results and return cursor or next_token for large datasets.
Tool annotations (readOnlyHint, destructiveHint, idempotentHint) are absent. The source code uses [McpServerTool] attribute but does not include risk metadata. Destructive operations like PlaceMarketOrder, CreateAccount, and UpdateAccountName should be explicitly marked with destructiveHint to warn agents.
Parameters accept human-readable identifiers (pair codes, currency codes) but 'accountId' requires an opaque system ID with no fallback to account name or label. Users are unlikely to know account IDs; a lookup or name-based alternative would improve usability.
HTTP response bodies are returned as raw JSON strings without any structure validation or filtering. API responses may contain verbose metadata, audit fields, or internal IDs irrelevant to the agent. Should parse and reshape responses to include only fields the agent needs for decision-making or chaining.