financeops-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  3. package/.github/assets/banner.svg +104 -0
  4. package/.github/pull_request_template.md +25 -0
  5. package/CODE_OF_CONDUCT.md +40 -0
  6. package/CONTRIBUTING.md +71 -0
  7. package/LICENSE +21 -0
  8. package/README.md +390 -0
  9. package/SECURITY.md +30 -0
  10. package/dist/adapters/stripe.d.ts +15 -0
  11. package/dist/adapters/stripe.js +175 -0
  12. package/dist/adapters/types.d.ts +30 -0
  13. package/dist/adapters/types.js +2 -0
  14. package/dist/adapters/xero.d.ts +19 -0
  15. package/dist/adapters/xero.js +261 -0
  16. package/dist/analysis/anomaly.d.ts +12 -0
  17. package/dist/analysis/anomaly.js +103 -0
  18. package/dist/analysis/cashflow.d.ts +10 -0
  19. package/dist/analysis/cashflow.js +134 -0
  20. package/dist/analysis/pnl.d.ts +8 -0
  21. package/dist/analysis/pnl.js +56 -0
  22. package/dist/analysis/reconciliation.d.ts +14 -0
  23. package/dist/analysis/reconciliation.js +98 -0
  24. package/dist/analysis/tax.d.ts +11 -0
  25. package/dist/analysis/tax.js +81 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.js +565 -0
  28. package/dist/lib/audit.d.ts +17 -0
  29. package/dist/lib/audit.js +70 -0
  30. package/dist/lib/providers.d.ts +6 -0
  31. package/dist/lib/providers.js +25 -0
  32. package/dist/premium/gate.d.ts +12 -0
  33. package/dist/premium/gate.js +22 -0
  34. package/dist/types.d.ts +138 -0
  35. package/dist/types.js +7 -0
  36. package/mcpize.yaml +10 -0
  37. package/package.json +35 -0
  38. package/src/adapters/stripe.ts +190 -0
  39. package/src/adapters/types.ts +34 -0
  40. package/src/adapters/xero.ts +317 -0
  41. package/src/analysis/anomaly.ts +119 -0
  42. package/src/analysis/cashflow.ts +158 -0
  43. package/src/analysis/pnl.ts +80 -0
  44. package/src/analysis/reconciliation.ts +117 -0
  45. package/src/analysis/tax.ts +98 -0
  46. package/src/index.ts +649 -0
  47. package/src/lib/audit.ts +92 -0
  48. package/src/lib/providers.ts +29 -0
  49. package/src/premium/gate.ts +24 -0
  50. package/src/types.ts +153 -0
  51. package/tests/adapters/stripe.test.ts +150 -0
  52. package/tests/adapters/xero.test.ts +188 -0
  53. package/tests/analysis/anomaly.test.ts +137 -0
  54. package/tests/analysis/cashflow.test.ts +112 -0
  55. package/tests/analysis/pnl.test.ts +95 -0
  56. package/tests/analysis/reconciliation.test.ts +121 -0
  57. package/tests/analysis/tax.test.ts +163 -0
  58. package/tests/helpers/mock-data.ts +135 -0
  59. package/tests/lib/audit.test.ts +89 -0
  60. package/tests/lib/providers.test.ts +129 -0
  61. package/tests/premium/cash_flow_forecast.test.ts +157 -0
  62. package/tests/premium/detect_anomalies.test.ts +189 -0
  63. package/tests/premium/gate.test.ts +59 -0
  64. package/tests/premium/generate_pnl.test.ts +155 -0
  65. package/tests/premium/multi_currency_report.test.ts +141 -0
  66. package/tests/premium/reconcile.test.ts +174 -0
  67. package/tests/premium/tax_summary.test.ts +166 -0
  68. package/tests/tools/expense_tracker.test.ts +181 -0
  69. package/tests/tools/financial_health.test.ts +196 -0
  70. package/tests/tools/get_balances.test.ts +160 -0
  71. package/tests/tools/list_invoices.test.ts +191 -0
  72. package/tests/tools/list_transactions.test.ts +210 -0
  73. package/tests/tools/revenue_summary.test.ts +188 -0
  74. package/tsconfig.json +20 -0
  75. package/vitest.config.ts +9 -0
package/README.md ADDED
@@ -0,0 +1,390 @@
1
+ <div align="center">
2
+
3
+ <img src=".github/assets/banner.svg" alt="FinanceOps MCP" width="800">
4
+
5
+ <br/>
6
+ <br/>
7
+
8
+ [![npm version](https://img.shields.io/npm/v/financeops-mcp?style=flat-square&color=10b981&label=npm)](https://npmjs.com/package/financeops-mcp)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-10b981.svg?style=flat-square)](LICENSE)
10
+ [![Tests](https://img.shields.io/badge/tests-62%20passing-brightgreen?style=flat-square)]()
11
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.4+-3178c6?style=flat-square&logo=typescript&logoColor=white)]()
12
+ [![Node](https://img.shields.io/badge/Node.js-20+-339933?style=flat-square&logo=node.js&logoColor=white)]()
13
+ [![MCP](https://img.shields.io/badge/MCP-compatible-10b981?style=flat-square)]()
14
+
15
+ **Financial intelligence for AI coding agents.**
16
+ <br/>
17
+ Connect Stripe and Xero, then ask your AI agent anything about your business finances — health checks, P&L, cash flow forecasts, reconciliation, and anomaly detection.
18
+
19
+ [Quick Start](#-quick-start) · [Tools](#-tools) · [Pro Features](#-pro-features) · [Security](#-security) · [Architecture](#-architecture)
20
+
21
+ <br/>
22
+
23
+ </div>
24
+
25
+ ---
26
+
27
+ ## See it in action
28
+
29
+ ```
30
+ Agent "What's our financial health right now?"
31
+
32
+ FinanceOps financial_health({ providers: ["stripe", "xero"] })
33
+
34
+ Result:
35
+
36
+ Financial Health Report
37
+ ──────────────────────────────────────────────────
38
+ MRR $12,450.00 ▲ +8.3% vs last month
39
+ ARR $149,400.00
40
+ Burn Rate $4,200.00/mo
41
+ Runway 29.6 months
42
+ Churn Rate 2.1%
43
+ Active Customers 187
44
+
45
+ Stripe balance: $8,320.50 (available)
46
+ Xero balance: $41,200.00 (bank)
47
+
48
+ Status: HEALTHY ✓
49
+ Generated in 340ms
50
+ ```
51
+
52
+ ```
53
+ Agent "Generate a P&L for this quarter"
54
+
55
+ FinanceOps generate_pnl({
56
+ providers: ["stripe", "xero"],
57
+ period: "quarter",
58
+ date_from: "2026-01-01",
59
+ date_to: "2026-03-31"
60
+ })
61
+
62
+ Result:
63
+
64
+ Profit & Loss — Q1 2026
65
+ ──────────────────────────────────────────────────
66
+ Revenue
67
+ Stripe Payments $34,210.00
68
+ Xero Invoices $18,940.00
69
+ Total Revenue $53,150.00
70
+
71
+ Expenses
72
+ SaaS & Subscriptions $1,840.00
73
+ Payroll $9,000.00
74
+ Other $2,100.00
75
+ Total Expenses $12,940.00
76
+
77
+ Net Profit $40,210.00 (75.7% margin)
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Architecture
83
+
84
+ ```mermaid
85
+ graph LR
86
+ A["AI Agent<br/>(Claude, Cursor, Windsurf)"] -->|MCP Protocol| B["FinanceOps MCP"]
87
+ B --> C["Stripe API<br/>(Payments & Billing)"]
88
+ B --> D["Xero API<br/>(Accounting & Invoices)"]
89
+ B --> E["Analysis Engine<br/>(P&L · Cash Flow · Anomaly)"]
90
+ B --> F["Audit Trail<br/>(SQLite)"]
91
+
92
+ style A fill:#1f2937,stroke:#10b981,color:#f0f6fc
93
+ style B fill:#0d1117,stroke:#10b981,stroke-width:2px,color:#10b981
94
+ style C fill:#0a1f16,stroke:#635bff,color:#f0f6fc
95
+ style D fill:#0a1f16,stroke:#13b5ea,color:#f0f6fc
96
+ style E fill:#0a1f16,stroke:#10b981,color:#f0f6fc
97
+ style F fill:#161b22,stroke:#30363d,color:#8b949e
98
+ ```
99
+
100
+ ---
101
+
102
+ ## Quick Start
103
+
104
+ ```bash
105
+ npx financeops-mcp
106
+ ```
107
+
108
+ Set your provider credentials as environment variables:
109
+
110
+ ```bash
111
+ # Stripe
112
+ export STRIPE_SECRET_KEY=sk_live_...
113
+
114
+ # Xero (OAuth2)
115
+ export XERO_CLIENT_ID=...
116
+ export XERO_CLIENT_SECRET=...
117
+ export XERO_TENANT_ID=...
118
+ ```
119
+
120
+ <details>
121
+ <summary><strong>Claude Desktop</strong></summary>
122
+
123
+ Add to your `claude_desktop_config.json`:
124
+
125
+ - **macOS / Linux:** `~/.config/Claude/claude_desktop_config.json`
126
+ - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
127
+
128
+ ```json
129
+ {
130
+ "mcpServers": {
131
+ "financeops": {
132
+ "command": "npx",
133
+ "args": ["financeops-mcp"],
134
+ "env": {
135
+ "STRIPE_SECRET_KEY": "sk_live_...",
136
+ "XERO_CLIENT_ID": "...",
137
+ "XERO_CLIENT_SECRET": "...",
138
+ "XERO_TENANT_ID": "..."
139
+ }
140
+ }
141
+ }
142
+ }
143
+ ```
144
+
145
+ </details>
146
+
147
+ <details>
148
+ <summary><strong>Cursor</strong></summary>
149
+
150
+ Add to `.cursor/rules/mcp_servers.json` in your project root:
151
+
152
+ ```json
153
+ {
154
+ "mcpServers": {
155
+ "financeops": {
156
+ "command": "npx",
157
+ "args": ["financeops-mcp"],
158
+ "env": {
159
+ "STRIPE_SECRET_KEY": "sk_live_..."
160
+ }
161
+ }
162
+ }
163
+ }
164
+ ```
165
+
166
+ </details>
167
+
168
+ <details>
169
+ <summary><strong>Windsurf</strong></summary>
170
+
171
+ Open settings (`Cmd+Shift+P` / `Ctrl+Shift+P` then search "MCP") and add:
172
+
173
+ ```json
174
+ {
175
+ "mcpServers": {
176
+ "financeops": {
177
+ "command": "npx",
178
+ "args": ["financeops-mcp"],
179
+ "env": {
180
+ "STRIPE_SECRET_KEY": "sk_live_..."
181
+ }
182
+ }
183
+ }
184
+ }
185
+ ```
186
+
187
+ </details>
188
+
189
+ <details>
190
+ <summary><strong>With Pro License</strong></summary>
191
+
192
+ Set the `PRO_LICENSE` environment variable to unlock premium analysis tools:
193
+
194
+ ```json
195
+ {
196
+ "mcpServers": {
197
+ "financeops": {
198
+ "command": "npx",
199
+ "args": ["financeops-mcp"],
200
+ "env": {
201
+ "STRIPE_SECRET_KEY": "sk_live_...",
202
+ "PRO_LICENSE": "your-license-key"
203
+ }
204
+ }
205
+ }
206
+ }
207
+ ```
208
+
209
+ Or from the command line:
210
+
211
+ ```bash
212
+ PRO_LICENSE=your-license-key npx financeops-mcp
213
+ ```
214
+
215
+ </details>
216
+
217
+ ---
218
+
219
+ ## Tools
220
+
221
+ ### Free Tools (6)
222
+
223
+ | Tool | Description |
224
+ |:-----|:------------|
225
+ | **`list_transactions`** | Fetch paginated transactions from Stripe or Xero with date range and status filters |
226
+ | **`get_balances`** | Get current account balances — Stripe available/pending, Xero bank accounts |
227
+ | **`list_invoices`** | Fetch invoices with status filtering (paid, pending, overdue) and date range |
228
+ | **`revenue_summary`** | Calculate revenue metrics for a period — total, average transaction, top customers |
229
+ | **`expense_tracker`** | Categorize and summarize expenses by type with period filtering |
230
+ | **`financial_health`** | Quick health check across providers — MRR, ARR, burn rate, runway, churn rate |
231
+
232
+ ### Pro Features (6)
233
+
234
+ | Tool | Description |
235
+ |:-----|:------------|
236
+ | **`generate_pnl`** | Full Profit & Loss statement from Stripe + Xero data for any period (PRO) |
237
+ | **`cash_flow_forecast`** | 3/6/12-month cash flow forecast — conservative, realistic, and optimistic scenarios (PRO) |
238
+ | **`reconcile`** | Cross-reference Stripe payments with Xero records to find discrepancies (PRO) |
239
+ | **`detect_anomalies`** | Statistical anomaly detection — flag unusual transactions, spikes, and drops (PRO) |
240
+ | **`tax_summary`** | VAT/sales tax calculation and period summary by jurisdiction (PRO) |
241
+ | **`multi_currency_report`** | Consolidated reporting across EUR, USD, GBP with live rate conversion (PRO) |
242
+
243
+ ---
244
+
245
+ ## Feature Comparison
246
+
247
+ <table>
248
+ <tr>
249
+ <th>Capability</th>
250
+ <th align="center">Free</th>
251
+ <th align="center">Pro</th>
252
+ </tr>
253
+ <tr><td><strong>Transaction Listing</strong> (paginated, filtered)</td><td align="center">&#9989;</td><td align="center">&#9989;</td></tr>
254
+ <tr><td><strong>Account Balances</strong></td><td align="center">&#9989;</td><td align="center">&#9989;</td></tr>
255
+ <tr><td><strong>Invoice Management</strong></td><td align="center">&#9989;</td><td align="center">&#9989;</td></tr>
256
+ <tr><td><strong>Revenue Summary</strong></td><td align="center">&#9989;</td><td align="center">&#9989;</td></tr>
257
+ <tr><td><strong>Expense Tracking</strong></td><td align="center">&#9989;</td><td align="center">&#9989;</td></tr>
258
+ <tr><td><strong>Financial Health Check</strong></td><td align="center">&#9989;</td><td align="center">&#9989;</td></tr>
259
+ <tr><td colspan="3"></td></tr>
260
+ <tr><td><strong>P&amp;L Statement Generation</strong></td><td align="center">&#10060;</td><td align="center">&#9989;</td></tr>
261
+ <tr><td><strong>Cash Flow Forecasting</strong> (3 scenarios)</td><td align="center">&#10060;</td><td align="center">&#9989;</td></tr>
262
+ <tr><td><strong>Stripe ↔ Xero Reconciliation</strong></td><td align="center">&#10060;</td><td align="center">&#9989;</td></tr>
263
+ <tr><td><strong>Anomaly Detection</strong></td><td align="center">&#10060;</td><td align="center">&#9989;</td></tr>
264
+ <tr><td><strong>Tax Summary</strong> (VAT / sales tax)</td><td align="center">&#10060;</td><td align="center">&#9989;</td></tr>
265
+ <tr><td><strong>Multi-Currency Reporting</strong></td><td align="center">&#10060;</td><td align="center">&#9989;</td></tr>
266
+ </table>
267
+
268
+ ---
269
+
270
+ ## Analysis Algorithms
271
+
272
+ ### P&L Generation
273
+ Aggregates income transactions from Stripe (charges, refunds) and Xero (invoices, credit notes), subtracts categorized expenses, and produces a structured statement with gross profit, operating expenses, and net profit with margin percentages.
274
+
275
+ ### Cash Flow Forecasting
276
+ Uses a **weighted moving average** over the last 3 months of historical data (weights: 1, 2, 3 oldest to newest). Applies a linear trend multiplier derived from first-vs-last net cash flow change. Outputs three scenarios:
277
+
278
+ | Scenario | Income Adjustment | Use Case |
279
+ |:---------|:------------------|:---------|
280
+ | **Conservative** | −20% | Worst-case planning, fundraising |
281
+ | **Realistic** | Baseline trend | Default projection |
282
+ | **Optimistic** | +20% | Best-case, growth planning |
283
+
284
+ Minimum 3 months of historical data required.
285
+
286
+ ### Reconciliation
287
+ Cross-references Stripe `payment_intent` IDs against Xero invoice references using fuzzy amount matching (±1% tolerance for currency conversion). Flags unmatched, duplicate, and reversed transactions.
288
+
289
+ ### Anomaly Detection
290
+ Statistical Z-score analysis over rolling 30-day windows. Configurable sensitivity:
291
+ - **Low** — flags transactions >3σ from mean
292
+ - **Medium** — flags >2σ (default)
293
+ - **High** — flags >1.5σ
294
+
295
+ ---
296
+
297
+ ## Supported Providers
298
+
299
+ <table>
300
+ <tr>
301
+ <th>Feature</th>
302
+ <th align="center">Stripe</th>
303
+ <th align="center">Xero</th>
304
+ </tr>
305
+ <tr><td>Transactions</td><td align="center">&#9989; Balance transactions</td><td align="center">&#9989; Bank transactions</td></tr>
306
+ <tr><td>Invoices</td><td align="center">&#9989; Stripe Invoicing</td><td align="center">&#9989; Xero Invoices</td></tr>
307
+ <tr><td>Balances</td><td align="center">&#9989; Available + pending</td><td align="center">&#9989; Bank account balances</td></tr>
308
+ <tr><td>Expenses</td><td align="center">&#9989; Payouts + fees</td><td align="center">&#9989; Bills + spend</td></tr>
309
+ <tr><td>Multi-currency</td><td align="center">&#9989; Native</td><td align="center">&#9989; Native</td></tr>
310
+ <tr><td>Auth method</td><td align="center">Secret key</td><td align="center">OAuth2</td></tr>
311
+ </table>
312
+
313
+ ### Credentials
314
+
315
+ ```bash
316
+ # Stripe — secret key from dashboard.stripe.com/apikeys
317
+ STRIPE_SECRET_KEY=sk_live_...
318
+
319
+ # Xero — OAuth2 app from developer.xero.com
320
+ XERO_CLIENT_ID=...
321
+ XERO_CLIENT_SECRET=...
322
+ XERO_TENANT_ID=... # Organisation ID from Xero connections
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Security
328
+
329
+ <div align="center">
330
+
331
+ [![Read-Only Default](https://img.shields.io/badge/Default-Read%20Only-10b981?style=flat-square)]()
332
+ [![No Stored Credentials](https://img.shields.io/badge/Credentials-Not%20Stored-10b981?style=flat-square)]()
333
+ [![MIT License](https://img.shields.io/badge/License-MIT-10b981?style=flat-square)](LICENSE)
334
+ [![Audit Trail](https://img.shields.io/badge/All%20Calls-Audit%20Trail-10b981?style=flat-square)]()
335
+
336
+ </div>
337
+
338
+ **FinanceOps MCP is read-only by design.** It calls provider APIs with read scopes only — it cannot move money, create invoices, or modify accounting records.
339
+
340
+ | Principle | Details |
341
+ |:----------|:--------|
342
+ | **Read-only by default** | All tools only read data — no write operations, no fund transfers |
343
+ | **No stored credentials** | API keys are passed via environment variables and never written to disk |
344
+ | **No telemetry** | Zero analytics, zero phone-home. Your financial data stays local |
345
+ | **Audit trail** | Every tool call is logged locally in SQLite with timestamp, tool name, provider, and success/failure |
346
+ | **Least privilege** | Use restricted Stripe keys (read-only) and Xero connections with Reader scope |
347
+
348
+ ### Best Practices
349
+
350
+ ```bash
351
+ # Use restricted Stripe keys — create at dashboard.stripe.com/apikeys
352
+ # Toggle off all write permissions, keep only: Read balance, Read charges, Read invoices
353
+
354
+ # Use environment variables — never hardcode API keys
355
+ STRIPE_SECRET_KEY=sk_restricted_... npx financeops-mcp
356
+
357
+ # Rotate credentials regularly
358
+ # Xero OAuth tokens expire — implement refresh token rotation in production
359
+ ```
360
+
361
+ ---
362
+
363
+ ## Pro License
364
+
365
+ Unlock P&L generation, cash flow forecasting, reconciliation, anomaly detection, tax summaries, and multi-currency reporting.
366
+
367
+ **Get your license:**
368
+ - [Gumroad](https://gumroad.com) — Instant delivery
369
+ - [MCPize](https://mcpize.com/mcp/financeops-mcp) — MCP marketplace
370
+
371
+ ```bash
372
+ # Activate
373
+ export PRO_LICENSE=your-license-key
374
+ npx financeops-mcp
375
+
376
+ # Or inline
377
+ PRO_LICENSE=your-license-key npx financeops-mcp
378
+ ```
379
+
380
+ ---
381
+
382
+ <div align="center">
383
+
384
+ **Built by [Craftpipe](https://craftpipe.dev)** — AI-powered developer tools
385
+
386
+ [GitHub](https://github.com/AuditSite/financeops-mcp) · [npm](https://npmjs.com/package/financeops-mcp) · [Support](mailto:support@craftpipe.dev)
387
+
388
+ <sub>MIT License &copy; 2026 Craftpipe</sub>
389
+
390
+ </div>
package/SECURITY.md ADDED
@@ -0,0 +1,30 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ | Version | Supported |
6
+ |---------|-----------|
7
+ | Latest | Yes |
8
+
9
+ ## Reporting a Vulnerability
10
+
11
+ If you discover a security vulnerability in FinanceOps MCP — particularly anything related to credential handling, API key exposure, or data leakage — please report it responsibly:
12
+
13
+ 1. **Do NOT** open a public GitHub issue
14
+ 2. Email us at **security@heijnesdigital.com**
15
+ 3. Include a description of the vulnerability and steps to reproduce
16
+ 4. We will acknowledge receipt within 48 hours
17
+ 5. We will provide a fix or mitigation within 7 days
18
+
19
+ ## Scope
20
+
21
+ This policy applies to the latest published version. We take all security reports seriously, especially those related to:
22
+
23
+ - Exposure of Stripe secret keys or Xero OAuth tokens
24
+ - Audit log bypass or tampering
25
+ - Unintended write access to financial provider APIs
26
+ - Credential leakage via error messages or logs
27
+
28
+ ## Security Model
29
+
30
+ FinanceOps MCP is designed read-only. It does not store credentials, does not transmit data to third parties, and writes only to a local SQLite audit log. Provider credentials must be supplied via environment variables, not hardcoded in config files.
@@ -0,0 +1,15 @@
1
+ import type { FinanceProvider, TransactionQuery, InvoiceQuery, ExpenseQuery } from './types.js';
2
+ import type { Transaction, Invoice, Balance, Expense, Subscription, PaginatedResult } from '../types.js';
3
+ export declare class StripeAdapter implements FinanceProvider {
4
+ private client;
5
+ constructor();
6
+ getTransactions(opts: TransactionQuery): Promise<PaginatedResult<Transaction>>;
7
+ getBalances(): Promise<Balance[]>;
8
+ getInvoices(opts: InvoiceQuery): Promise<PaginatedResult<Invoice>>;
9
+ getExpenses(opts: ExpenseQuery): Promise<PaginatedResult<Expense>>;
10
+ getSubscriptions(): Promise<Subscription[]>;
11
+ private mapStripeType;
12
+ private mapStripeInvoiceStatus;
13
+ private mapStripeSubStatus;
14
+ }
15
+ //# sourceMappingURL=stripe.d.ts.map
@@ -0,0 +1,175 @@
1
+ import Stripe from 'stripe';
2
+ export class StripeAdapter {
3
+ client;
4
+ constructor() {
5
+ const key = process.env.STRIPE_SECRET_KEY;
6
+ if (!key) {
7
+ throw new Error('STRIPE_SECRET_KEY environment variable is required');
8
+ }
9
+ this.client = new Stripe(key, { apiVersion: '2023-10-16' });
10
+ }
11
+ async getTransactions(opts) {
12
+ const params = {
13
+ limit: Math.min(opts.limit ?? 100, 100),
14
+ };
15
+ if (opts.cursor)
16
+ params.starting_after = opts.cursor;
17
+ if (opts.date_from)
18
+ params.created = { gte: Math.floor(new Date(opts.date_from).getTime() / 1000) };
19
+ if (opts.date_to) {
20
+ params.created = {
21
+ ...(typeof params.created === 'object' && params.created !== null ? params.created : {}),
22
+ lte: Math.floor(new Date(opts.date_to).getTime() / 1000),
23
+ };
24
+ }
25
+ const result = await this.client.balanceTransactions.list(params);
26
+ const transactions = result.data.map((bt) => ({
27
+ id: bt.id,
28
+ date: new Date(bt.created * 1000).toISOString(),
29
+ amount: bt.amount, // Stripe already returns amounts in cents
30
+ currency: bt.currency,
31
+ description: bt.description ?? bt.type,
32
+ category: bt.type,
33
+ type: this.mapStripeType(bt.type),
34
+ provider: 'stripe',
35
+ metadata: undefined,
36
+ }));
37
+ return {
38
+ data: transactions,
39
+ has_more: result.has_more,
40
+ next_cursor: result.has_more ? result.data[result.data.length - 1]?.id : undefined,
41
+ };
42
+ }
43
+ async getBalances() {
44
+ const balance = await this.client.balance.retrieve();
45
+ const balances = [];
46
+ for (const available of balance.available) {
47
+ const pending = balance.pending.find((p) => p.currency === available.currency);
48
+ balances.push({
49
+ currency: available.currency,
50
+ available: available.amount, // Already in cents
51
+ pending: pending?.amount ?? 0,
52
+ provider: 'stripe',
53
+ });
54
+ }
55
+ return balances;
56
+ }
57
+ async getInvoices(opts) {
58
+ const params = {
59
+ limit: 100,
60
+ };
61
+ if (opts.cursor)
62
+ params.starting_after = opts.cursor;
63
+ if (opts.status) {
64
+ if (opts.status === 'paid')
65
+ params.status = 'paid';
66
+ else if (opts.status === 'pending')
67
+ params.status = 'open';
68
+ // 'overdue' = open past due_date; we post-filter below
69
+ }
70
+ const result = await this.client.invoices.list(params);
71
+ let invoices = result.data.map((inv) => ({
72
+ id: inv.id,
73
+ number: inv.number ?? '',
74
+ customer_name: typeof inv.customer_name === 'string' ? inv.customer_name : '',
75
+ customer_email: typeof inv.customer_email === 'string' ? inv.customer_email : undefined,
76
+ amount: inv.amount_due, // Already in cents
77
+ currency: inv.currency,
78
+ status: this.mapStripeInvoiceStatus(inv),
79
+ due_date: inv.due_date ? new Date(inv.due_date * 1000).toISOString() : '',
80
+ issued_date: new Date(inv.created * 1000).toISOString(),
81
+ paid_date: inv.status_transitions?.paid_at
82
+ ? new Date(inv.status_transitions.paid_at * 1000).toISOString()
83
+ : undefined,
84
+ provider: 'stripe',
85
+ }));
86
+ if (opts.date_from) {
87
+ const from = new Date(opts.date_from).getTime();
88
+ invoices = invoices.filter((inv) => new Date(inv.issued_date).getTime() >= from);
89
+ }
90
+ if (opts.date_to) {
91
+ const to = new Date(opts.date_to).getTime();
92
+ invoices = invoices.filter((inv) => new Date(inv.issued_date).getTime() <= to);
93
+ }
94
+ if (opts.status === 'overdue') {
95
+ const now = Date.now();
96
+ invoices = invoices.filter((inv) => inv.status !== 'paid' && inv.due_date && new Date(inv.due_date).getTime() < now);
97
+ }
98
+ return {
99
+ data: invoices,
100
+ has_more: result.has_more,
101
+ next_cursor: result.has_more ? result.data[result.data.length - 1]?.id : undefined,
102
+ };
103
+ }
104
+ async getExpenses(opts) {
105
+ // Stripe expenses = charges (fees), refunds, and payouts
106
+ const params = {
107
+ limit: 100,
108
+ type: 'payout',
109
+ };
110
+ if (opts.cursor)
111
+ params.starting_after = opts.cursor;
112
+ const result = await this.client.balanceTransactions.list(params);
113
+ const expenses = result.data.map((bt) => ({
114
+ id: bt.id,
115
+ date: new Date(bt.created * 1000).toISOString(),
116
+ amount: Math.abs(bt.amount), // Cents
117
+ currency: bt.currency,
118
+ description: bt.description ?? bt.type,
119
+ category: bt.type === 'payout' ? 'Payouts' : bt.type,
120
+ provider: 'stripe',
121
+ }));
122
+ return {
123
+ data: expenses,
124
+ has_more: result.has_more,
125
+ next_cursor: result.has_more ? result.data[result.data.length - 1]?.id : undefined,
126
+ };
127
+ }
128
+ async getSubscriptions() {
129
+ const result = await this.client.subscriptions.list({ limit: 100, status: 'all' });
130
+ return result.data.map((sub) => ({
131
+ id: sub.id,
132
+ customer_name: typeof sub.customer === 'string' ? sub.customer : sub.customer?.name ?? '',
133
+ amount: sub.items.data.reduce((sum, item) => sum + (item.price.unit_amount ?? 0), 0),
134
+ currency: sub.currency,
135
+ interval: (sub.items.data[0]?.price.recurring?.interval ?? 'month'),
136
+ status: this.mapStripeSubStatus(sub.status),
137
+ current_period_start: new Date(sub.current_period_start * 1000).toISOString(),
138
+ current_period_end: new Date(sub.current_period_end * 1000).toISOString(),
139
+ provider: 'stripe',
140
+ }));
141
+ }
142
+ mapStripeType(type) {
143
+ if (['charge', 'payment', 'adjustment'].includes(type))
144
+ return 'income';
145
+ if (['refund', 'payout', 'stripe_fee', 'application_fee'].includes(type))
146
+ return 'expense';
147
+ return 'transfer';
148
+ }
149
+ mapStripeInvoiceStatus(inv) {
150
+ if (inv.status === 'paid')
151
+ return 'paid';
152
+ if (inv.status === 'draft')
153
+ return 'draft';
154
+ if (inv.status === 'void')
155
+ return 'voided';
156
+ if (inv.status === 'open') {
157
+ if (inv.due_date && Date.now() > inv.due_date * 1000)
158
+ return 'overdue';
159
+ return 'pending';
160
+ }
161
+ return 'pending';
162
+ }
163
+ mapStripeSubStatus(status) {
164
+ if (status === 'active')
165
+ return 'active';
166
+ if (status === 'canceled')
167
+ return 'canceled';
168
+ if (status === 'past_due' || status === 'unpaid')
169
+ return 'past_due';
170
+ if (status === 'trialing')
171
+ return 'trialing';
172
+ return 'active';
173
+ }
174
+ }
175
+ //# sourceMappingURL=stripe.js.map
@@ -0,0 +1,30 @@
1
+ import type { Transaction, Invoice, Balance, Expense, Subscription, PaginatedResult } from '../types.js';
2
+ export type { Transaction, Invoice, Balance, Expense, Subscription, PaginatedResult };
3
+ export interface TransactionQuery {
4
+ date_from?: string;
5
+ date_to?: string;
6
+ limit?: number;
7
+ cursor?: string;
8
+ status?: string;
9
+ }
10
+ export interface InvoiceQuery {
11
+ status?: 'paid' | 'pending' | 'overdue';
12
+ date_from?: string;
13
+ date_to?: string;
14
+ cursor?: string;
15
+ }
16
+ export interface ExpenseQuery {
17
+ period?: string;
18
+ categories?: string[];
19
+ date_from?: string;
20
+ date_to?: string;
21
+ cursor?: string;
22
+ }
23
+ export interface FinanceProvider {
24
+ getTransactions(opts: TransactionQuery): Promise<PaginatedResult<Transaction>>;
25
+ getBalances(): Promise<Balance[]>;
26
+ getInvoices(opts: InvoiceQuery): Promise<PaginatedResult<Invoice>>;
27
+ getExpenses(opts: ExpenseQuery): Promise<PaginatedResult<Expense>>;
28
+ getSubscriptions?(): Promise<Subscription[]>;
29
+ }
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,19 @@
1
+ import type { FinanceProvider, TransactionQuery, InvoiceQuery, ExpenseQuery } from './types.js';
2
+ import type { Transaction, Invoice, Balance, Expense, PaginatedResult } from '../types.js';
3
+ export declare class XeroAdapter implements FinanceProvider {
4
+ private tokens;
5
+ private tenantId;
6
+ constructor();
7
+ private loadTokens;
8
+ private saveTokens;
9
+ private refreshAccessToken;
10
+ private ensureFreshToken;
11
+ private request;
12
+ getTransactions(opts: TransactionQuery): Promise<PaginatedResult<Transaction>>;
13
+ getBalances(): Promise<Balance[]>;
14
+ getInvoices(opts: InvoiceQuery): Promise<PaginatedResult<Invoice>>;
15
+ getExpenses(opts: ExpenseQuery): Promise<PaginatedResult<Expense>>;
16
+ private mapXeroInvoice;
17
+ private mapXeroInvoiceStatus;
18
+ }
19
+ //# sourceMappingURL=xero.d.ts.map