An MCP server for managing personal expenses with SQLite backend, supporting adding, listing, deleting, modifying expenses and generating expense summaries by category.
The ExpenseTrackerServer has well-formed tool definitions with clear naming, complete input schemas, and reasonable descriptions. However, it lacks critical production-grade features: no output schemas are documented, error messages are string-based without structured guidance, no pagination despite potential for large datasets, no input validation hints in descriptions, and missing features like idempotent hints or tool annotations. Tool names follow verb_noun convention (add_expense, list_expenses, etc.), which is good. Descriptions are concise (40-100 chars), meeting the 10-1024 character baseline. All five tools have explicit input schemas with types and descriptions. However, descriptions lack context about when to use each tool relative to others, recovery guidance for errors, or operational implications. The modify_expense tool accepts optional parameters with null defaults, which is safe, but the server returns unstructured string responses rather than typed objects, forcing LLMs to parse natural language output. No pagination limits on list_expenses or expense_summary despite SQLite returning arbitrarily large datasets.
Add a new expense to the database with item name, amount, optional category and date
Delete an expense by its ID from the database
Generate a summary of total expenses and breakdown by category in a formatted table
List all recorded expenses in a formatted table with ID, item, amount, category, and date
Update an existing expense by ID with optional new item name, amount, category, and/or date
No output schema documentation. All tools return unstructured string responses (e.g., tabulate HTML tables, free-form success/error messages). LLMs cannot reliably extract structured data, amounts, or categories from the responses for downstream reasoning or chaining. Expected: document return type as object with fields like {items: [{id, item, amount, category, date}], total: number} for list_expenses.
No pagination on list_expenses or expense_summary. If the expenses table grows to thousands of rows, these tools return all data, exhausting token budgets and degrading LLM reasoning. Expected: add limit and offset/cursor parameters, cap results at 50, and return total_count or next_cursor.
Error handling is basic and returns bare error strings. No structured error categorization (retryable vs user-fixable vs fatal). No recovery hints. E.g., 'Error: Expense with ID 999 not found.' does not suggest calling list_expenses() to discover valid IDs. Expected: return structured errors with actionable next steps.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | C | 60 | 2026-07-28+ | v2 |
| 2026-03-09 | D | 50 | - | v1 |
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). The server does not signal which tools are safe to retry, which mutate state, or which are read-only. Expected: annotate delete_expense as destructive, list_expenses and expense_summary as readOnly, add_expense and modify_expense as idempotent (if re-called with same params, should not create duplicates).
Description for list_expenses is vague: 'List all recorded expenses in a formatted table with ID, item, amount, category, and date.' Does not explain when to call this vs expense_summary. Expected: 'Returns all individual expenses (one row per transaction). Use this to find a specific expense ID to modify or delete. For category breakdown, use expense_summary instead.'
Parameter descriptions lack constraint hints. E.g., date is described as 'Date of the expense in YYYY-MM-DD format' but does not specify: optional, what happens if omitted, whether future dates are allowed, timezone handling. Expected: 'Date in YYYY-MM-DD format (required). Must be a valid date in the past or today. Example: 2024-01-15. If omitted, today's date is used.'
modify_expense parameters (item, amount, category, date) use null defaults and optional=true. This is safe, but the description does not clearly state 'Only provided fields are updated; omitted fields retain their current value.' Expected: 'To update only the category, pass category='New Category' and omit item, amount, and date.'