A banking application MCP server that manages bank accounts and transactions using SQLite database
The Banking MCP Server defines 6 tools with consistent naming (verb_noun pattern: create_account, deposit, withdraw, get_balance, get_transaction_history, list_accounts) and complete input schemas. However, descriptions are generic and lack specificity about error recovery, constraints, and when each tool should be used. Tool outputs return free-text strings rather than structured objects, forcing LLMs to parse unstructured responses. Parameter descriptions are present but minimal (e.g., 'Amount to deposit (must be positive)' lacks range bounds or format clarity). Error handling exists but provides no recovery guidance. The server lacks pagination support for list_accounts and get_transaction_history despite potential large result sets. No tool annotations (readOnlyHint/destructiveHint/idempotentHint) are present. Security concerns exist: no validation of initial_deposit/amount ranges before database operations, no rate limiting, and no audit logging.
Create a new bank account with an optional initial deposit.
Deposit money into an existing account.
Get the current balance of an account.
Get transaction history for an account.
List all bank accounts in the system.
Withdraw money from an existing account.
All tools return unstructured free-text strings instead of structured JSON objects. LLMs must parse natural language ('Account created successfully!\nAccount ID: 1\nAccount Name: Checking\nInitial Balance: $100.00') rather than accessing typed fields. This wastes tokens, introduces parsing errors, and breaks downstream tool chaining.
Parameter descriptions lack numeric bounds, precision, and format constraints. 'Amount to deposit (must be positive)' does not specify: minimum (0.01?), maximum (1000000?), decimal places (2?), or currency. This invites LLMs to pass invalid values (e.g., -50, 1.123456789) that may cause database errors.
Error handling provides no recovery guidance. 'Error: Account {account_id} not found' does not suggest alternatives (e.g., 'Try list_accounts() to find valid account IDs' or 'Did you mean account 42?'). This forces extra LLM planning steps.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 56 | 2026-07-28+ | v2 |
| 2026-03-09 | D | 56 | - | v1 |
No pagination for list_accounts or get_transaction_history. A banking system could have hundreds or thousands of accounts/transactions. Returning all results unfiltered blows the context window and violates the pattern: 'cap results at 20 - 50 and offer pagination.' get_transaction_history defaults limit=10 (reasonable) but has no maximum, unbounded limit parameter risks runaway requests.
Tool descriptions are generic and do not explain when to call each tool or what distinguishes them. 'Get the current balance of an account' (get_balance) vs. 'Get transaction history for an account' (get_transaction_history), when would an LLM choose one over the other? Missing context on purpose, dependencies, and data freshness.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). Create_account, deposit, and withdraw are clearly destructive (modify state), but LLMs have no explicit signal. Missing destructiveHint means LLMs may not recognize these as irreversible and could retry inappropriately, causing duplicate charges or accounts.
No input validation or sanitization. The code checks initial_deposit < 0 and amount <= 0, but does not validate: account_id is a positive integer, account_name is non-empty, amount fits float precision, or description is reasonable length. Attackable via prompt injection (e.g., description=''; DROP TABLE accounts; --).
No audit logging or permission checks. Banking operations must be traceable for compliance. Who initiated create_account? When? With what result? No logging means no audit trail. No permission gating means any agent can create accounts, deposit, withdraw without authorization.
No rate limiting. An agent in a retry loop or intentionally malicious could generate thousands of create_account or deposit calls per minute without guards, potentially DoSing the service or creating runaway charges.
Insufficient funds error returns a readable message but provides no recovery guidance or alternative actions. Should suggest: 'Insufficient funds (have $45.23, need $50). Try: reduce withdrawal amount to $45, or deposit more funds first.' This guides the LLM toward solutions.