ExpenseBot
REMOTE · MCP.EXPENSEBOT.AI · 2 COMPONENTS · SCANNED SEP 20
Capture receipts, scan Gmail, analyze spending, and create reviewed reports from AI assistants.
Available components
How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. How we score → Why this is hard to score →
Endpoint Security63
- The endpoint's TLS certificate is valid, in date, and uses a strong key. View diagnostics → Pass
- Authorisation check failed: no authorisation is required to call this server, and it exposes a tool marked destructive (share_report). See how to fix → View diagnostics → Fail
- HTTPS is enforced; there's no plaintext access path. View diagnostics → Pass
- The HSTS (Strict-Transport-Security) header is present. View diagnostics → Pass
- DNSSEC check failed: this domain isn't protected by DNSSEC. See how to fix → View diagnostics → Fail
Transport & Reachability100
- Verified streamable-http transport via a live MCP handshake. View diagnostics → Pass
Schema Quality & AI Usability64
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 14731 tokens (~249/item across 59 items; 59 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management96
- Stability check failed: schema churn in the 30 days we've observed: 0 tool removals, 5 breaking changes, 0 auth/transport breaks, 0 additions. See how to fix → Fail
Tool Coverage99
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 97% of tool parameters carry a description.Partial
- Structured output schemas are declared (100% of tools); any adoption earns full credit.Pass
Tool Safety100
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- All 1 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.Pass
- An AI judge read all 60 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
- Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
How do I install the ExpenseBot MCP server?
ExpenseBot is a hosted endpoint at https://mcp.expensebot.ai/mcp, so there is nothing to install locally. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
remote · mcp.expensebot.ai
claude mcp add --transport http totesmagotes-mcp-server-auth 'https://mcp.expensebot.ai/mcp'
{
"mcpServers": {
"totesmagotes-mcp-server-auth": {
"url": "https://mcp.expensebot.ai/mcp"
}
}
} {
"servers": {
"totesmagotes-mcp-server-auth": {
"type": "http",
"url": "https://mcp.expensebot.ai/mcp"
}
}
} [mcp_servers.totesmagotes-mcp-server-auth] url = "https://mcp.expensebot.ai/mcp"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"totesmagotes-mcp-server-auth": {
"type": "remote",
"url": "https://mcp.expensebot.ai/mcp",
"enabled": true
}
}
} openclaw mcp add totesmagotes-mcp-server-auth --url 'https://mcp.expensebot.ai/mcp' --transport streamable-http
mcp_servers:
totesmagotes-mcp-server-auth:
url: "https://mcp.expensebot.ai/mcp" {
"McpServers": {
"totesmagotes-mcp-server-auth": {
"Transport": "http",
"Url": "https://mcp.expensebot.ai/mcp"
}
}
} assistant mcp add totesmagotes-mcp-server-auth -t streamable-http -u 'https://mcp.expensebot.ai/mcp'
{
"mcpServers": {
"totesmagotes-mcp-server-auth": {
"type": "http",
"url": "https://mcp.expensebot.ai/mcp"
}
}
} The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.
Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 10 Sept 26 +2
- A breaking change shipped without a version bump: still 0.3.1-m3.6 ▼ security
- Tool “update_expense” rewrote its description, which is the text the model reads security
- Tool “fix_compliance” rewrote its description, which is the text the model reads security
- Tool “add_income” rewrote its description, which is the text the model reads security
- “fix_compliance” added a required parameter “operationId”, so existing callers break ▼ functional
- “fix_compliance” dropped the required parameter “value” ▼ functional
- “fix_compliance” dropped the required parameter “reportId” ▼ functional
- “fix_compliance” dropped the required parameter “action” ▼ functional
- Tool coverage: 61% → 100% ▲ functional
- Tool “trace_document” now declares an output schema ▲ functional
- Tool “submit_receipt” now declares an output schema ▲ functional
- Tool “update_expense” now declares an output schema ▲ functional
- Tool “fix_compliance” now declares an output schema ▲ functional
- Tool “export_report” now declares an output schema ▲ functional
- Tool “create_report” now declares an output schema ▲ functional
- Tool “create_client_invoice” now declares an output schema ▲ functional
- Tool “correct_expenses” now declares an output schema ▲ functional
- Tool “add_mileage_entry” now declares an output schema ▲ functional
- Tool “add_income_from_file” now declares an output schema ▲ functional
- Tool “add_income_from_csv” now declares an output schema ▲ functional
- Tool “add_income” now declares an output schema ▲ functional
- Tool “add_cash_expense” now declares an output schema ▲ functional
- Tool “group_expenses” now declares an output schema ▲ functional
- Tool “mark_client_invoice_paid” now declares an output schema ▲ functional
- Tool “parse_expense” now declares an output schema ▲ functional
- Tool “prepare_client_invoice” now declares an output schema ▲ functional
- Tool “process_gmail_receipts” now declares an output schema ▲ functional
- Tool “request_accounting_integration” now declares an output schema ▲ functional
- Tool “scan_gmail” now declares an output schema ▲ functional
- Tool “scan_gmail_years” now declares an output schema ▲ functional
- Tool “send_report_to_accounting” now declares an output schema ▲ functional
- Tool “share_report” now declares an output schema ▲ functional
- “fix_compliance” added an optional parameter “selection” cosmetic
- “fix_compliance” added an optional parameter “confirm” cosmetic
- “fix_compliance” added an optional parameter “change” cosmetic
- “fix_compliance” added an optional parameter “status” cosmetic
- “fix_compliance” added an optional parameter “undo” cosmetic
- 9 Sept 26 +4
- Tool coverage: 10% → 61% ▲ functional
- Tool “get_expense_splits” now declares an output schema ▲ functional
- Tool “get_expense_by_id” now declares an output schema ▲ functional
- Tool “get_deep_analytics” now declares an output schema ▲ functional
- Tool “get_credits_refunds” now declares an output schema ▲ functional
- Tool “get_client_invoice” now declares an output schema ▲ functional
- Tool “get_client_advance_balances” now declares an output schema ▲ functional
- Tool “get_accounting_push_status” now declares an output schema ▲ functional
- Tool “get_accounting_integration_status” now declares an output schema ▲ functional
- Tool “check_tax_deductibility” now declares an output schema ▲ functional
- Tool “check_feature” now declares an output schema ▲ functional
- Tool “check_compliance” now declares an output schema ▲ functional
- Tool “get_subscription_audit” now declares an output schema ▲ functional
- Tool “get_spending_summary” now declares an output schema ▲ functional
- Tool “list_categories” now declares an output schema ▲ functional
- Tool “list_client_invoices” now declares an output schema ▲ functional
- Tool “list_income_categories” now declares an output schema ▲ functional
- Tool “list_reports” now declares an output schema ▲ functional
- Tool “list_tags” now declares an output schema ▲ functional
- Tool “whatif_afford” now declares an output schema ▲ functional
- Tool “whatif_client” now declares an output schema ▲ functional
- Tool “whatif_tax_setaside” now declares an output schema ▲ functional
- Tool “get_trip_suggestions” now declares an output schema ▲ functional
- Tool “get_scan_status” now declares an output schema ▲ functional
- Tool “get_report_details” now declares an output schema ▲ functional
- Tool “get_recent_activity” now declares an output schema ▲ functional
- Tool “get_pnl” now declares an output schema ▲ functional
- Tool “get_per_tag_pnl” now declares an output schema ▲ functional
- Tool “get_monthly_books_review” now declares an output schema ▲ functional
- Tool “get_mileage_summary” now declares an output schema ▲ functional
- Tool “get_income_summary” now declares an output schema ▲ functional
- 8 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 46 to 49.
- 6 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 39 to 43.
- 4 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 33 to 36.
- 2 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 26 to 29.
- 31 Aug 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 19 to 23.
- 29 Aug 26 +1
- Tool “share_report” rewrote its description, which is the text the model reads security
- “create_report” added an optional parameter “recipientRole” cosmetic
- “share_report” added an optional parameter “recipientRole” cosmetic
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 20 Sept 2026 · Probed https://mcp.expensebot.ai/mcp
TLS valid
Negotiated TLS 1.3 with TLS_AES_128_GCM_SHA256 .
| Subject | Issuer | Valid from | Valid until | Key | Signature | Serial |
|---|---|---|---|---|---|---|
| CN=mcp.expensebot.ai | CN=YR2,O=Let's Encrypt,C=US | 8 Sept 2026 | 7 Dec 2026 | RSA 2048 | SHA256-RSA | 69f8cf9d11963ed221401116573c17f0a84 |
| SANs: mcp.expensebot.ai | ||||||
| CN=YR2,O=Let's Encrypt,C=US (CA) | CN=Root YR,O=ISRG,C=US | 3 Sept 2025 | 2 Sept 2028 | RSA 2048 | SHA256-RSA | 4ebd24947e24d394802d84a52fd5b319 |
| CN=Root YR,O=ISRG,C=US (CA) | CN=ISRG Root X1,O=Internet Security Research Group,C=US | 13 May 2026 | 2 Sept 2032 | RSA 4096 | SHA256-RSA | f24b6d17f9d9ad7cb1c9fea78782699f |
Background: What to check on a remote MCP endpoint →
DNSSEC insecure
Validation of mcp.expensebot.ai. — Not signed
| Zone | DS | Keys | Algorithms | Outcome |
|---|---|---|---|---|
| . | trust_anchor | 20326, 38696 | 8, 8 | Verified |
| ai. | present | 3799 | 8 | Verified |
| expensebot.ai. | absent | Unsigned (proven) parent-signed NSEC/NSEC3 proves an unsigned delegation |
Authentication No authorisation required
The endpoint answered without asking for a token. Anyone who knows the URL can reach it.
| Result | No authorisation required |
|---|---|
| HTTP status | 200 |
| Header | Value |
|---|---|
| strict-transport-security | max-age=63072000 |
Background: How OAuth 2.1 works in the 2026 MCP spec →
Transports 2 probes
| Transport | URL | Outcome | Status | Location |
|---|---|---|---|---|
| streamable-http | https://mcp.expensebot.ai/mcp | Verified | 200 | |
| http (plaintext) | http://mcp.expensebot.ai/mcp | HTTPS enforced | 308 | https://mcp.expensebot.ai/mcp |
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
group_expenses ~638
Group an exact set of recorded expenses for a client, project, trip, job, or other user-named purpose. Examples: 'group my Mexico meals for client Rob', 'put these Vegas expenses under the Vegas project', or 'group these for client Rob and create a report'. The user does not need to know about tags: ExpenseBot resolves the requested name against existing groups and proposes creating one only when needed. Use exact expenseId values returned by search_expenses. A grounded preview is automatic for every bulk request; the user does not need to ask for one. First call with confirm omitted/false, show the returned exact rows, count, totals, proposed group, exclusions, and conflicts, then ask for approval. Only after explicit user approval, repeat the same operationId and selection with confirm:true. A premature confirm:true is converted to preview. When the request includes a report, set createReport:true on that confirmed group_expenses call and use its report result; do not run a separate broader create_report query. For 'without personal expenses', set excludePersonal:true; ExpenseBot removes Personal-tagged and Personal-category rows before preview so their Personal marker is never overwritten. The confirmed operation returns exact report and Bill Client links. Expenses already assigned to another ordinary report are excluded; if none remain, no duplicate report is created and the result links to Reports instead. Keep the user-facing response concise and do not add unsolicited tax or substantiation advice. Do not call check_compliance, get_report_details, or tax/deductibility tools before or after this workflow unless the user explicitly asks for that separate analysis. In user-facing prose call the destination a group, not a tag. Treat preview totals as provisional; after confirmation use only the terminal result's exact count, total, and currency without reconciling it against the preview. A returned Bill Client URL opens the reviewed billing handoff and does not m…
| Name | Type | Req | Description |
|---|---|---|---|
| approveNewGroup | boolean | – | True only when the preview says a new group is required. |
| confirm | boolean | – | Omit/false for preview; true only after explicit approval. |
| createReport | boolean | – | Create a report from the exact eligible grouped expenses. |
| excludePersonal | boolean | – | Exclude Personal-tagged and Personal-category expenses from both grouping and the exact report. |
| excludedCategories | array | – | Expense categories to exclude before preview and grouping. |
| excludedTags | array | – | Existing groups to exclude before preview and grouping. |
| group | object | – | The user-named destination group to apply to every eligible selected expense. |
| operationId | string | yes | Stable idempotency key generated once for the preview and reused unchanged. |
| reportTitle | string | – | Optional title when createReport is true; otherwise ExpenseBot derives one from the group. |
| selection | object | – | Exact expenses returned by search_expenses and approved for this bounded operation. |
| status | boolean | – | Read operation status using operationId. |
| undo | boolean | – | Undo a completed grouping within its undo window. |
| Name | Type | Req | Description |
|---|---|---|---|
| candidates | array | – | – |
| confirmation | object | – | – |
| creationStatus | string | – | – |
| exclusions | object | – | – |
| finalAnswer | string | – | – |
| group | object | – | – |
| manifest | object | – | – |
| message | string | – | – |
| operationId | string | – | – |
| preview | object | – | – |
| report | object | – | – |
| reportsUrl | string | – | – |
| requiresConfirmation | boolean | – | – |
| requiresNewGroupApproval | boolean | – | – |
| responseGuidance | string | – | – |
| reviewExpensesUrl | string | – | – |
| status | string | yes | – |
| success | boolean | yes | – |
| suggestedTag | string | – | – |
| undo | object | – | – |
No examples provided.
list_categories ~61
List the user's configured expense categories and billing accounts. Use this BEFORE calling tools that need a category (add_cash_expense, add_mileage_entry, create_report) so you pass the user's actual category names, not guesses. Returns the live list from their spreadsheet.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| billingAccounts | array | – | Billing/card accounts, when configured. |
| categories | array | yes | Expense categories for the user. |
| success | boolean | yes | – |
No examples provided.
list_client_invoices ~199
List the user's issued client invoices (accounts receivable) — who owes them money, how much, and when it is due. Examples: 'which invoices are outstanding', 'what does Acme still owe me', 'any overdue invoices', 'how much am I waiting to get paid'. status accepts 'active' (default), 'open', 'overdue', 'needs_review', 'paid', 'void', 'superseded', or 'all'. Returns invoice numbers, status, totals by currency, delivery state, and private document links. Read-only.
| Name | Type | Req | Description |
|---|---|---|---|
| clientEmail | string | – | Client account email. Accountants may use this only for an accepted ExpenseBot client. |
| clientName | string | – | Optional: exact client name (case-insensitive). |
| limit | number | – | Max invoices to return (default 25, max 100). |
| status | string | – | Filter by invoice status (default active/outstanding). |
| Name | Type | Req | Description |
|---|---|---|---|
| invoices | array | yes | – |
| success | boolean | yes | – |
No examples provided.
list_income_categories ~46
List ExpenseBot's fixed income tax categories. Unlike Expense Accounts, these are not user-configured. Use this BEFORE calling add_income so you pass an exact canonical category instead of guessing.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| categories | array | yes | – |
| legacy | array | – | Deprecated income categories still accepted on read. |
| notes | string | – | – |
| success | boolean | yes | – |
No examples provided.
list_reports ~143
List the user's expense reports with pagination. Filter by status (All, Draft, Submitted, Shared). Each exact report includes its app, Bill Client, and accounting handoff URLs. Use the matching accounting URL only when the user explicitly asks to send or export that report; the app keeps organization, mapping, preview, and final confirmation.
| Name | Type | Req | Description |
|---|---|---|---|
| clientEmail | string | – | Client account email. Accountants may use this only for an accepted ExpenseBot client. |
| filter | string | – | Optional report-status filter; All returns every authorized status. |
| limit | integer | – | Maximum reports to return on this page. |
| page | integer | – | One-based results page to return. |
| Name | Type | Req | Description |
|---|---|---|---|
| creationStatus | string | yes | Provenance of the listing, e.g. "existing_only". |
| filter | string | – | – |
| limit | integer | – | – |
| page | integer | – | – |
| reports | array | – | Present when the user has reports. |
| responseGuidance | string | – | – |
| success | boolean | yes | – |
No examples provided.
list_tags ~83
List the user's configured groups. ExpenseBot stores clients, projects, project codes, properties, and trips as tags (for example 'Client: Acme', 'Vegas Trip', or 'Property: 123 Main'). Use this when the user asks 'what groups/projects/clients do I have?' and before filtering, grouping, or reporting when the intended existing name is unclear.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| success | boolean | yes | – |
| tags | array | yes | Client / project / trip groups (Column K values). |
No examples provided.
mark_client_invoice_paid ~130
Mark an issued client invoice as paid after explicit confirmation. Requires a strict calendar-valid paidDate in YYYY-MM-DD format. This updates the accounts-receivable invoice record only; it never creates or moves an Income row. Record the actual payment separately or link an existing Income row.
| Name | Type | Req | Description |
|---|---|---|---|
| invoiceId | string | yes | Stable invoice ID from list_client_invoices. |
| paidAmount | number | – | Optional amount received. |
| paidDate | string | yes | Calendar-valid payment date in YYYY-MM-DD format. |
| paidVia | string | – | Optional payment method or source, such as bank transfer or check. |
| Name | Type | Req | Description |
|---|---|---|---|
| incomeRowCreated | boolean | yes | – |
| invoiceId | string | yes | – |
| invoiceNumber | string|null | – | – |
| message | string | yes | – |
| paidAmount | number|null | yes | – |
| paidDate | string | yes | – |
| paidVia | string | yes | – |
| status | string | yes | – |
| success | boolean | yes | – |
No examples provided.
parse_expense ~86
Parse a natural language expense description into structured fields. Does NOT add the expense — just returns the parsed fields for review. Example: "Lunch at Chipotle $15.50 today" → {merchant: "Chipotle", total: 15.50, ...}
| Name | Type | Req | Description |
|---|---|---|---|
| text | string | yes | Natural language expense (e.g., "Coffee at Starbucks $6.50 yesterday") |
| Name | Type | Req | Description |
|---|---|---|---|
| categoryNames | array | yes | – |
| expenses | array | yes | – |
| success | boolean | yes | – |
| validationErrors | array | yes | – |
No examples provided.
prepare_client_invoice ~370
Prepare a client invoice from a saved ExpenseBot report without creating it. Reads canonical report rows, allocates a unique invoice number, verifies the client ledger identity and advance, and returns exact totals plus a short-lived preparationId. Call this first, show the preview to the user, then call create_client_invoice after explicit confirmation. It reserves the invoice number and preview for 30 minutes but creates no invoice, document, Income row, email, or payment.
| Name | Type | Req | Description |
|---|---|---|---|
| advanceApplied | number | – | Verified client advance to apply against this invoice. |
| billToEmail | string | – | Optional client email printed on the invoice. This does not send email. |
| clientAddress | string | – | Optional billing address printed in the invoice's Bill To section. |
| clientName | string | yes | Full client or business name shown on the invoice. |
| customLineItems | array | – | Optional fee/service lines. They are taxed but never marked up. |
| invoiceNumber | string | – | Optional custom invoice number. ExpenseBot allocates one when omitted. |
| issueDate | string | yes | Calendar-valid invoice date in YYYY-MM-DD format. |
| markupPct | number | – | Markup applied only to rebilled expense rows. |
| mentionSupportingReceipts | boolean | – | When true, note on the invoice that supporting receipts are available. |
| paymentTerms | string | – | Invoice due terms. Defaults to the client's billing profile or net30. |
| receiptIds | array | – | Optional exact report receipt IDs. Defaults to every current report expense. |
| reportId | string | yes | Saved ExpenseBot report ID to invoice. |
| taxLabel | string | – | Tax label such as HST, GST, or VAT. |
| taxPct | number | – | Tax percentage applied after markup and custom line items; use 0 for no tax. |
| Name | Type | Req | Description |
|---|---|---|---|
| expiresAt | string | yes | – |
| preparationId | string | yes | – |
| preview | object | yes | – |
| success | boolean | yes | – |
No examples provided.
process_gmail_receipts ~95
Process specific Gmail emails as receipts. Pass Gmail message IDs and they'll be converted to PDF, extracted by AI, and added to the user's expense spreadsheet. Max 25 emails per request. Requires Gmail to be connected in ExpenseBot settings.
| Name | Type | Req | Description |
|---|---|---|---|
| accountEmail | string | – | Optional: which Gmail account to use (for users with multiple linked accounts) |
| emailIds | array | yes | Gmail message IDs to process as receipts |
| Name | Type | Req | Description |
|---|---|---|---|
| data | object | – | – |
| emailCount | integer | – | – |
| error | string | – | – |
| message | string | yes | – |
| processInfo | object | – | – |
| processedItems | array | – | – |
| reviewExpensesUrl | string | – | – |
| spreadsheetUrl | string|null | – | – |
| submissionId | string | – | – |
| success | boolean | yes | – |
No examples provided.
request_accounting_integration ~144
Request support for an accounting package only after checking ExpenseBot's reviewed direct and import-file destinations and finding no matching package. Do not use this for a listed or Beta destination, a general compatibility question, or without the exact package name. This is a confirmation-gated write: after the user approves, it creates one deduplicated request bound to the authenticated ExpenseBot account and emails ExpenseBot's internal team. It does not create an integration or make the requested package immediately available. On accepted or previously recorded requests, tell the user ExpenseBot will email them within one week with an update.
| Name | Type | Req | Description |
|---|---|---|---|
| packageName | string | yes | Exact accounting package requested by the user. |
| Name | Type | Req | Description |
|---|---|---|---|
| duplicate | boolean | yes | – |
| message | string | yes | – |
| packageName | string | yes | – |
| success | boolean | yes | – |
No examples provided.
scan_gmail ~175
Trigger a background Gmail scan to discover and process receipt emails from the last ~60 days (default). Runs asynchronously — returns immediately, user gets an email summary when done. Like clicking "Find Receipts in Gmail" in the UI. For whole PAST YEARS (e.g. 2023, or 2020-2022) use scan_gmail_years instead; to check a scan's progress use get_scan_status.
| Name | Type | Req | Description |
|---|---|---|---|
| accountEmail | string | – | Optional: which Gmail account to scan |
| endDate | string | – | Optional: explicit end date (YYYY-MM-DD) |
| lookbackDays | integer | – | How many days back to scan (default 60, max depends on subscription) |
| startDate | string | – | Optional: explicit start date (YYYY-MM-DD) instead of lookbackDays |
| Name | Type | Req | Description |
|---|---|---|---|
| _metadata | object | – | – |
| data | object | – | – |
| message | string | – | – |
| orchestrated | boolean | – | – |
| reviewExpensesUrl | string|null | – | – |
| scanInProgress | boolean | – | – |
| sessionId | string | – | – |
| spreadsheetUrl | string|null | – | – |
| status | string | – | – |
| submissionId | string | – | – |
| success | boolean | yes | – |
No examples provided.
scan_gmail_years ~231
Scan one or more COMPLETED PRIOR years of the user's Gmail for receipts (e.g. years:[2023] or years:[2020,2021,2022]). Long-running background job: the first eligible year starts immediately and the rest queue, running one at a time (each full year typically takes a couple of hours; the user can close the app). ALWAYS call first WITHOUT confirmStart to preview which years are eligible, then ask the user to confirm, then call again with confirmStart:true. For receipts from the last ~60 days use scan_gmail instead. To check how a scan is going, use get_scan_status. The current in-progress year cannot be year-scanned.
| Name | Type | Req | Description |
|---|---|---|---|
| accountEmail | string | – | Optional: which connected Gmail inbox to scan (multi-inbox users). Defaults to the primary. |
| confirmStart | boolean | – | Omit or false = preview only (no scan starts). Must be exactly true to start scanning. |
| years | array | yes | Completed prior years to scan, e.g. [2020, 2021]. Max 10. |
| Name | Type | Req | Description |
|---|---|---|---|
| account | string | – | – |
| alreadyQueued | array | – | – |
| alreadyScanned | array | – | – |
| archiveThroughYear | integer | – | – |
| eligible | array | – | – |
| gmailUrl | string | – | – |
| message | string | yes | – |
| notEligible | array | – | – |
| preview | boolean | – | – |
| queuedYears | array | – | – |
| scanning | array | – | – |
| startedYear | integer|null | – | – |
| success | boolean | yes | – |
No examples provided.
search ~87
Search the user's ExpenseBot data — expenses, reports, and knowledge base. Returns matching items with id, title, and url. Use the returned id with the `fetch` tool to get full details. Powers Deep Research and Company Knowledge integrations.
| Name | Type | Req | Description |
|---|---|---|---|
| query | string | yes | Natural language search (e.g., 'Uber rides in March', 'Q1 client report', 'mileage Acme') |
| Name | Type | Req | Description |
|---|---|---|---|
| results | array | yes | – |
No examples provided.
search_expenses ~472
Search and filter the user's expenses. Returns matching expense rows from their spreadsheet. Filter by category, merchant, date range, amount, or tags. Results are paginated: when hasMore is true, call again with nextCursor and the same filters. Do not split a date range into repeated overlapping searches. Use the optional query parameter for deterministic natural-language recall over merchant, city/location, Notes (including receipt items, delivery source, payer, and Business purpose), Tag, and category. Each matching result includes matchedFields and a short matchReason so you can explain why it was selected. When several rows plausibly match, a disambiguation list is returned; each option carries the exact expenseId. Structured filters (categories, merchants, dateRange, tags, minAmount, maxAmount) combine with the query using AND semantics. Each result includes expenseId, the exact durable Receipt ID required by update_expense.
| Name | Type | Req | Description |
|---|---|---|---|
| categories | array | – | Filter by expense categories (e.g., ['Travel', 'Meals']) |
| clientEmail | string | – | Client account email. Accountants may use this only for an accepted ExpenseBot client. |
| cursor | string | – | Opaque nextCursor returned by the previous search_expenses page. Reuse the same filters; never construct or edit this value. |
| dateRange | – | – | Time period filter. Use exactly one variant — pick the shape that matches the user's phrasing. |
| hasReceipt | boolean | – | When true, return only expenses with a receipt link. When false, return only expenses without one. |
| limit | integer | – | Results per page (default 20, maximum 50) |
| maxAmount | number | – | Maximum expense amount |
| merchants | array | – | Filter by merchant names (e.g., ['Uber', 'Starbucks']) |
| minAmount | number | – | Minimum expense amount |
| query | string | – | Optional natural-language recall over the user's existing expense fields (merchant, city/location, Notes including receipt items, delivery source, payer, and Business purpose, Tag, category). Determi… |
| tags | array | – | Filter by tags |
| Name | Type | Req | Description |
|---|---|---|---|
| count | integer | yes | Rows matched by the filters. |
| expenses | array | yes | Matching expense rows, newest first, paginated. |
| hasMore | boolean | – | – |
| message | string | – | – |
| nextCursor | string|null | – | Opaque signed continuation; reuse with identical filters. |
| pageInfo | object | – | – |
| queryApplied | string | – | – |
| total | number | yes | Sum of the matched set in home currency. |
| totalMatched | integer | – | Present for free-text searches. |
No examples provided.
search_knowledge ~174
Search ExpenseBot's complete product and help knowledge base. Use this before guessing when the user asks how a feature works, what ExpenseBot supports, or needs setup, billing, Gmail, scan, report, workflow, or troubleshooting instructions. Set fullAnswer:true on the first call when the user asks for complete, detailed, or step-by-step instructions. Use the default excerpt mode for discovery and quick answers; if an excerpt is truncated, call again with fullAnswer:true before presenting complete instructions. Each result has a stable id.
| Name | Type | Req | Description |
|---|---|---|---|
| fullAnswer | boolean | – | Set true on the first call for complete, detailed, or step-by-step instructions. Leave false for discovery or quick-answer excerpts. |
| limit | integer | – | Number of ranked matching articles to return (1-10). |
| query | string | yes | Search terms |
| Name | Type | Req | Description |
|---|---|---|---|
| results | array | yes | – |
| success | boolean | yes | – |
No examples provided.
send_report_to_accounting ~433
Review and then post an ExpenseBot report to the owner's accounting destination. STEP 1: call with provider, reportId, and optional mode/mappings, without confirm. The server reads the live report and destination, applies Omit/Personal/split/date/currency rules, checks Zoho bank-feed matches, and returns the complete proposal plus proposalId. NOTHING is posted in step 1. Show the complete proposal and ask for approval. STEP 2: call with only provider, proposalId, and confirm:true. The server posts only the frozen, account-bound proposal, revalidates live state, and rejects changed reports or mappings. Never add mapping fields to the confirmation call. Owner accounts only; acting for a client is not supported. Agent-driven posting is currently available only for Zoho Books (zoho_books); QuickBooks Online, Xero, Wave, and FreeAgent are intentionally not accepted by this tool yet because their previews cannot freeze/revalidate the canonical provider plan that shareReport executes. Use those providers' web flows until they satisfy the full push contract.
| Name | Type | Req | Description |
|---|---|---|---|
| accountMappings | object | – | Step 1 only: ExpenseBot category to live destination account ID overrides. |
| confirm | boolean | – | Step 2 only: true posts the exact staged proposal. No other options may change. |
| forceRepush | boolean | – | Step 1 only: explicitly include disclosed uncertain prior outcomes in the frozen proposal. |
| mode | string | – | Step 1 only (Zoho Books): automatic document treatment, paid expenses, or bills. |
| proposalId | string | – | Step 2 only: proposalId returned by the read-only preview. |
| provider | string | yes | Accounting destination provider. Agent-driven posting is currently available only for Zoho Books. |
| reportId | string | – | Step 1 only: ExpenseBot report ID owned by the signed-in account. |
| reportingTagMappings | object | – | Step 1 only: ExpenseBot tag to live destination reporting-tag option. |
| taxMappings | object | – | Step 1 only: observed tax-rate string to live destination tax ID overrides. |
| Name | Type | Req | Description |
|---|---|---|---|
| confirmationContract | string | – | – |
| confirmationQuestion | string | – | – |
| confirmationRequired | boolean | – | – |
| counts | object | – | – |
| created | integer | – | – |
| expiresAt | string | – | – |
| finalAnswer | string | – | – |
| idempotentReplay | boolean | – | – |
| matchedExisting | integer | – | – |
| omittedExpenses | integer | – | – |
| organization | object | – | – |
| phase | string | yes | – |
| proposalId | string | yes | – |
| provider | string | yes | – |
| reconciliationRequired | boolean | – | – |
| reportId | string | – | – |
| responseGuidance | string | – | – |
| skippedAlreadyPushed | integer | – | – |
| success | boolean | yes | – |
| units | array | – | – |
No examples provided.
share_report ~116
Share one existing expense report after confirmation. Default to reviewer: they can open and edit that report's Google Sheet, inspect receipts, comment, approve, or request changes, but cannot act for the owner or use accounting integrations. Use accountant only when the user explicitly asks for ongoing accounting access; it creates the established accountant relationship and broader report-management workflow.
| Name | Type | Req | Description |
|---|---|---|---|
| recipientRole | string | – | Use reviewer for report-only approval. Use accountant only for explicit ongoing accountant access. |
| recipients | array | yes | – |
| reportId | string | yes | – |
| Name | Type | Req | Description |
|---|---|---|---|
| finalAnswer | string | – | – |
| message | string | yes | – |
| recipientRole | string | yes | – |
| responseGuidance | string | – | – |
| sharedWith | array | yes | – |
| success | boolean | yes | – |
No examples provided.
submit_receipt ~639
Submit a photo or PDF of a receipt for processing. Covers requests phrased as 'log this', 'log this receipt', 'save this receipt', 'expense this', or 'add this to my expenses', including when the user simply shares a photo of a receipt or invoice. The receipt image is validated, uploaded to cloud storage, and processed by AI to extract vendor, amount, date, tax, and category. The expense appears in the user's spreadsheet in about 1-3 minutes, and longer for PDFs or large batches. Handles images and PDFs, mixed together in one batch. TO SEND FILES (preferred, and required for PDFs): call this tool with filesToUpload listing every file the user gave you. It returns one signed upload URL per file. Upload them ONE AT A TIME with an HTTP PUT, telling the user which file you just finished and how many remain, then call this tool ONCE with uploadRefs for all of them — that processes the whole set as a single batch, like the ExpenseBot web app. Do not call this tool once per file. Use the photo parameter for one image or PDF attached in ChatGPT. MCP clients that cannot supply file references may use photoBase64 for one small image; use the upload flow for large files or batches. Optional note and tag values use the same receipt metadata path as ExpenseBot's camera, file uploader, and forwarded-email intake. The note is stored in the Notes column (L); the tag is stored in the Tag column (K). Batch defaults apply to every file, and each uploadRefs item may override either value for that file.
| Name | Type | Req | Description |
|---|---|---|---|
| filename | string | – | Optional filename (e.g., 'lunch_receipt.jpg') |
| filesToUpload | array | – | Request upload URLs for one or more files. Include EVERY file the user provided in a single call. Returns one signedUrl + uploadRef per file; upload each, then call this tool again with uploadRefs. |
| mimeType | string | – | MIME type of the file (default: image/jpeg) |
| note | string | – | Batch-level context note applied to every file in the upload. Individual files may override this with their own note. Stored through the existing receipt-note pipeline in the Notes column (L). |
| photo | object | – | Single attached receipt image or PDF supplied by ChatGPT. For multiple files or large files, use filesToUpload and uploadRefs instead. |
| photoBase64 | string | – | Legacy fallback for MCP clients that send one small image as complete base64 data. ChatGPT should use photo or uploadRefs instead. |
| tag | string | – | Batch-level project or client tag applied to every expense row. Individual files may override it. Stored in the Tag column (K) through the same path as the web receipt uploader. |
| uploadRef | string | – | Single-file shorthand for uploadRefs. Use uploadRefs when there is more than one file. |
| uploadRefs | array | – | Submit previously uploaded files as ONE batch. Include every uploadRef from the filesToUpload step. If any file fails validation the whole batch is rejected and nothing is processed. |
| Name | Type | Req | Description |
|---|---|---|---|
| data | object | – | – |
| fileCount | integer | – | – |
| files | array | – | – |
| message | string | – | – |
| reviewExpensesUrl | string | – | – |
| spreadsheetUrl | string|null | – | – |
| status | string | – | – |
| statusCode | integer | – | – |
| submissionId | string | – | – |
| success | boolean | yes | – |
No examples provided.
trace_document ~208
Trace what happened to a specific receipt, invoice, forwarded email, Gmail receipt, or PDF in ExpenseBot processing. Use when the user asks where a document went, why it did not show up, whether it was skipped, routed to income, deduped, or removed as a duplicate. Returns a read-only timeline from the user's lineage audit trail.
| Name | Type | Req | Description |
|---|---|---|---|
| days | integer | – | How many days back to search. |
| limit | integer | – | Maximum lineage events to return. |
| messageId | string | – | Gmail message ID if known. |
| outcome | string | – | Optional processing outcome filter. |
| query | string | – | Merchant, sender, subject, invoice number, or phrase to search for, e.g. 'Blue Rocket', 'Driversnote', 'Uber receipt'. |
| receiptId | string | – | ExpenseBot receipt/invoice ID if known, e.g. IR-123, RO-abc, RR-456. |
| threadId | string | – | Gmail thread ID if known. |
| Name | Type | Req | Description |
|---|---|---|---|
| data | object | yes | – |
| message | string | yes | – |
| success | boolean | yes | – |
| type | string | yes | – |
No examples provided.
update_expense ~290
Update a single existing expense row's category, tag, merchant, or business purpose (e.g. 'recategorize expense X to Meals' or 'tag it Client – Acme'). Identify it only by the exact expenseId returned by search_expenses (the full Receipt ID from Column Q). Never use a date, displayed number, or sheet row number as expenseId. Two-phase operation: calls with confirm omitted or false return a preview of the current→proposed change and make no change. After user confirmation, a call with confirm:true applies the proposed change. If the expense is not found, search once for a current exact expenseId; never retry the same stale ID. If a business-purpose update exceeds the Notes capacity, stop and send the user to https://www.expensebot.ai/review-expenses?source=mcp instead of retrying. Only category/tag/merchant/businessPurpose are editable — amounts, dates, and notes are not editable via the assistant. Does not create or delete rows.
| Name | Type | Req | Description |
|---|---|---|---|
| confirm | boolean | – | Omit or false = preview only (no write). Must be exactly true to apply the change. |
| expenseId | string | yes | Exact full Receipt ID from Column Q, returned as expenseId by search_expenses. |
| fields | object | yes | Partial update object. Include one or more of these optional keys; omit every field that should remain unchanged. |
| Name | Type | Req | Description |
|---|---|---|---|
| data | object | yes | – |
| finalAnswer | string | – | – |
| message | string | yes | – |
| responseGuidance | string | – | – |
| success | boolean | yes | – |
No examples provided.
whatif_afford ~123
Can I afford $X/month? Recomputes the user's Safe Monthly Draw (how much they can safely pay themselves) with an added recurring monthly cost, and returns a yes/tight/no verdict plus the before/after numbers. Use for questions like 'can I afford a $500/mo hire' or 'what if I add a $200/mo software subscription'. Requires at least 3 months of income history — otherwise returns insufficient_data rather than a guess.
| Name | Type | Req | Description |
|---|---|---|---|
| deltaMonthly | number | yes | The new recurring monthly cost being considered, in the user's home currency |
| Name | Type | Req | Description |
|---|---|---|---|
| data | object | – | The full what-if scenario: assumptions, evidence, and computed outcome. |
| message | string | – | Scenario narrative; may be absent if the scenario carried no template or evidence. |
| success | boolean | yes | – |
No examples provided.
whatif_client ~167
What if a client pays late or leaves? mode='late30' shifts that client's OPEN invoice amounts out of the near-term expectation (they still owe it, it's just not landing this month). mode='gone' removes that client's trailing monthly income contribution and recomputes Safe Draw against the reduced baseline. Use for questions like 'what if Acme Corp pays 30 days late' or 'what happens if I lose my biggest client'. Client identity is matched against the Income tab's tag/source/description fields — best effort, not a guaranteed match.
| Name | Type | Req | Description |
|---|---|---|---|
| clientName | string | yes | The client's name as it appears on invoices or income rows |
| mode | string | yes | 'late30' = shift open invoices 30 days late; 'gone' = client stops paying entirely |
| Name | Type | Req | Description |
|---|---|---|---|
| data | object | – | The full what-if scenario: assumptions, evidence, and computed outcome. |
| message | string | – | Scenario narrative; may be absent if the scenario carried no template or evidence. |
| success | boolean | yes | – |
No examples provided.
whatif_tax_setaside ~73
What should I set aside for taxes? Surfaces the same monthly tax set-aside estimate already computed for Safe Monthly Draw — wiring, not new math. Flat-rate estimate (default 30%) against trailing income minus recurring + variable spend. Use for 'how much should I set aside for taxes this month'.
Input schema present but exposes no named parameters.
| Name | Type | Req | Description |
|---|---|---|---|
| data | object | – | The full what-if scenario: assumptions, evidence, and computed outcome. |
| message | string | – | Scenario narrative; may be absent if the scenario carried no template or evidence. |
| success | boolean | yes | – |
No examples provided.
What is the ExpenseBot MCP server?
ExpenseBot is an MCP server listed in the public MCP registry as io.github.TotesMagotes/mcp-server-auth. Capture receipts, scan Gmail, analyze spending, and create reviewed reports from AI assistants. This page covers its hosted endpoint (https://mcp.expensebot.ai/mcp).
Is the ExpenseBot MCP server safe to use?
ExpenseBot scores 78 out of 100 on VerifyMCP. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the ExpenseBot MCP server expose?
ExpenseBot exposes 59 tools: search_expenses, get_spending_summary, get_deep_analytics, create_report, list_reports, and 54 more. Their descriptions and schemas cost roughly 13,521 tokens of context every time the server is loaded.
Does the ExpenseBot MCP server require authentication?
No. We connected to ExpenseBot without credentials and it answered, so anything it exposes is reachable by anyone who knows the address.
Is the ExpenseBot MCP server still maintained?
ExpenseBot is still listed as active in the MCP registry. We last reached this channel on 20 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.