MCP server for dynamically generating tools from contract ABIs. Automatically creates tools for each function in a smart contract ABI, supporting both read and write operations.
This server has critical structural and documentation gaps. The tool is dynamically generated from contract ABIs at runtime, which means tool definitions are inferred rather than explicitly registered in source code. The single tool pattern '{contractname}_{functionname}' is generated by generateToolsFromAbi() in src/tools/generateTool.ts. While the code shows reasonable parameter schema (Zod object with 'args' array) and basic error handling, there are severe deficiencies: (1) The tool naming convention lacks action verbs and violates the verb_noun pattern, '{contractname}_{functionname}' is structural, not semantic (e.g., 'usdc_transfer' instead of 'transfer_tokens' or 'call_contract_function'). (2) The parameter 'args' accepts an untyped array with minimal guidance, the description says 'Function arguments as an array. Example: ["0x123...", 100, true]' but provides no specification of how many args, what types each should be, or how to map them to function parameters. This violates the constrained-input pattern and forces the LLM to guess argument positions and types. (3) Error messages are user-friendly but lack actionable recovery guidance per the recovery-guide pattern. (4) No parameter descriptions for the 'args' field explaining format or constraints beyond a generic example. (5) Output schema is documented in comments (read vs write responses) but not formalized in the Tool definition, responses are free-form strings rather than structured JSON. (6) No input validation of argument types, counts, or ranges, validation only happens at contract call time, risking late failures. (7) Tool descriptions are auto-generated and generic, not LLM-optimized, they list function metadata (inputs/outputs) but do not guide when/why to call the tool or what the consequences are.
Dynamically generated tool for each function in the loaded contract ABI. Read functions become query tools; write functions become execution tools. Tool name pattern is {contractname}_{functionname} where contract name is lowercased.
Tool naming lacks action verbs and does not follow verb_noun convention. Pattern '{contractname}_{functionname}' (e.g., 'usdc_transfer') is structural, not semantic. LLMs cannot infer intent from the name alone. Should follow patterns like 'call_contract_read_function', 'call_contract_write_function', or expose function names as separate discovery tool.
Parameter 'args' accepts an untyped array with no formal schema constraints. Description provides only a generic example '["0x123...", 100, true]' without specifying: required argument count, expected types per position, how arguments map to function inputs, or validation rules. This violates constrained-input and tool-description patterns.
Output is returned as free-form markdown strings rather than structured JSON objects. Response format is undocumented in the Tool schema. LLMs cannot reliably parse unstructured strings to extract transaction hashes, gas values, or block numbers for downstream calls.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | F | 26 | 2026-07-28+ | v2 |
Tool definitions are dynamically generated at runtime from contract ABIs. Actual tool names, descriptions, and schemas are not visible in source code, they are inferred. This prevents static validation and integration testing.
Error handling returns user-friendly messages but lacks recovery guidance. Errors are returned as success responses (free-form text) rather than structured error objects with error codes, retryability classification, or next steps. Violates recovery-guide and error-classification patterns.
No input validation before contract calls. Argument count validation only checks if args.length matches func.inputs.length, but does not validate argument types, ranges, or formats. Late failures (at contract execution) lack context for the LLM to self-correct.
Tool descriptions are auto-generated from function metadata and do not answer: What does this tool do? When should I call it vs. other tools? What are the side effects? Descriptions list inputs/outputs but lack LLM-optimized guidance per the tool-description pattern.
No distinction between read (safe, repeatable) and write (state-changing, irreversible) operations in the tool name or description. While code internally checks func.isReadFunction, this is not exposed as tool metadata. LLMs cannot infer destructiveness from the name alone.