bkper 3.3.3 → 3.5.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## 2025
4
+
5
+ ### **July 2025**
6
+
7
+ **MCP Server**
8
+ - Added support for AI assistants to analyze your books with monthly and year-to-date balances
9
+ - Improved date filtering with more intuitive `before:` operator
10
+ - Added setup instructions for Claude Desktop and other AI tools
11
+
12
+ ### **June 2025**
13
+
14
+ **bkper-node CLI**
15
+ - Introduced MCP server - connect AI assistants to your Bkper books with `bkper mcp start`
16
+ - Added book name filtering to quickly find specific books
package/README.md CHANGED
@@ -34,6 +34,7 @@ yarn global add bkper
34
34
  - ```logout``` - Logs out the user by deleting client credentials.
35
35
  - ```app -c``` - Create a new App based on ```./bkperapp.yaml``` file.
36
36
  - ```app -u``` - Update an existing App based on ```./bkperapp.yaml``` file.
37
+ - ```mcp start``` - Start the Bkper MCP (Model Context Protocol) server.
37
38
 
38
39
  ### Examples
39
40
  ```
@@ -76,9 +77,13 @@ id: my-custom-app
76
77
  # The readable name of the App or Bot.
77
78
  name: My Custom App
78
79
 
79
- # Set your logo url from public host. Best fit 200x200 px. Use https://
80
+ # The logo url from public host. Best fit 200x200 px. Use https://
80
81
  logoUrl: https://static.thenounproject.com/png/2318500-200.png
81
82
 
83
+ # The logo url to be used when in dark mode
84
+ logoUrlDark: https://static.thenounproject.com/png/2318500-200.png
85
+
86
+
82
87
 
83
88
  # CONTEXT MENU CONFIGURATION
84
89
 
@@ -95,15 +100,17 @@ menuPopupWidth: 500 # width in pixels. Default to 80% of screen width.
95
100
  menuPopupHeight: 300 # height in pixels. Default to 90% of screen height.
96
101
 
97
102
 
98
- # BOT EVENTS CONFIGURATION
103
+ #ASSISTANT CONFIGURATION
104
+
105
+ # The conversation url to be called by Bkper when a new conversation message is added
106
+ conversationUrl: https://us-central1-bkper-tax-trigger.cloudfunctions.net/chat
99
107
 
100
- # The production webhook url to be called by Bkper when an event occurs.
101
- webhookUrl: https://us-central1-bkper-tax-trigger.cloudfunctions.net/prod
102
108
 
103
- # The development webhook url to be called while developing.
104
- # It will be prioritized over webhookUrl when the bot is running by the app owner or developer.
105
- # Unset it when done developing.
106
- webhookUrlDev: https://9850-2804-1b2-1003-88ab-bca4-9ed8-d199-bb7f.ngrok-free.app
109
+
110
+ # BOT EVENTS CONFIGURATION
111
+
112
+ # The webhook url to be called by Bkper when an event occurs.
113
+ webhookUrl: https://us-central1-bkper-tax-trigger.cloudfunctions.net/events
107
114
 
108
115
  # The events the Bot is capable of processing by the webhook.
109
116
  # This is optional and, if not specified, no events will be processed.
@@ -195,6 +202,72 @@ Bkper.setConfig({
195
202
  })
196
203
  ```
197
204
 
205
+ ### MCP (Model Context Protocol) Server
206
+
207
+ Bkper includes an MCP server that allows AI assistants and other tools to interact with your Bkper books through the [Model Context Protocol](https://modelcontextprotocol.io).
208
+
209
+ #### Starting the MCP Server
210
+
211
+ ```bash
212
+ bkper mcp start
213
+ ```
214
+
215
+ The server runs on stdio and provides the following tools:
216
+
217
+ - **list_books** - List all books accessible by the authenticated user
218
+ - **get_book** - Get details of a specific book by ID
219
+ - **get_balances** - Get account balances for a specific date or period
220
+ - **list_transactions** - List transactions with filtering options
221
+
222
+ #### Prerequisites
223
+
224
+ Before using the MCP server:
225
+ 1. Login using `bkper login` to set up authentication
226
+ 2. Ensure the `BKPER_API_KEY` environment variable is set
227
+
228
+ The MCP server uses the same authentication as the CLI, reading credentials from `~/.bkper-credentials.json`.
229
+
230
+ #### Integration Examples
231
+
232
+ ##### Claude Desktop
233
+
234
+ Add to your configuration file:
235
+ - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
236
+ - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
237
+
238
+ ```json
239
+ {
240
+ "mcpServers": {
241
+ "bkper": {
242
+ "command": "npx",
243
+ "args": ["bkper", "mcp", "start"],
244
+ "env": {
245
+ "BKPER_API_KEY": "your-api-key-here"
246
+ }
247
+ }
248
+ }
249
+ }
250
+ ```
251
+
252
+ ##### Other MCP Clients
253
+
254
+ For other MCP-compatible clients, configure them to run:
255
+ ```bash
256
+ BKPER_API_KEY=your-api-key npx bkper mcp start
257
+ ```
258
+
259
+ The server communicates via stdio, so any MCP client that supports stdio transport can connect to it.
260
+
261
+ #### Available MCP Tools
262
+
263
+ Once connected, the MCP client can:
264
+ - List your Bkper books
265
+ - Get account balances for any date or period
266
+ - Search and filter transactions
267
+ - Analyze your financial data
268
+
269
+ For more information about the Model Context Protocol, visit [modelcontextprotocol.io](https://modelcontextprotocol.io).
270
+
198
271
 
199
272
 
200
273
  ## Documentation
package/lib/cli.js CHANGED
@@ -8,12 +8,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- import { App, Bkper } from 'bkper-js';
11
+ import { App } from 'bkper-js';
12
12
  import program from 'commander';
13
13
  import fs from 'fs';
14
14
  import * as YAML from 'yaml';
15
- import { getOAuthToken, login, logout } from './auth/local-auth-service.js';
15
+ import { login, logout } from './auth/local-auth-service.js';
16
16
  import dotenv from 'dotenv';
17
+ import { setupBkper } from './mcp/bkper-factory.js';
17
18
  dotenv.config();
18
19
  program
19
20
  .command('login')
@@ -34,10 +35,7 @@ program
34
35
  .option('-c, --create', 'Create a new App')
35
36
  .action((options) => __awaiter(void 0, void 0, void 0, function* () {
36
37
  try {
37
- Bkper.setConfig({
38
- apiKeyProvider: () => __awaiter(void 0, void 0, void 0, function* () { return process.env.BKPER_API_KEY || ''; }),
39
- oauthTokenProvider: () => getOAuthToken()
40
- });
38
+ setupBkper();
41
39
  let json;
42
40
  if (fs.existsSync('./bkperapp.json')) {
43
41
  json = JSON.parse(fs.readFileSync('./bkperapp.json', 'utf8'));
@@ -66,5 +64,21 @@ program
66
64
  console.log(err);
67
65
  }
68
66
  }));
67
+ const mcpCommand = program.command('mcp').description('Bkper MCP server commands');
68
+ mcpCommand
69
+ .command('start')
70
+ .description('Start Bkper MCP server')
71
+ .action(() => __awaiter(void 0, void 0, void 0, function* () {
72
+ try {
73
+ // Import and start the MCP server directly
74
+ const { BkperMcpServer } = yield import('./mcp/server.js');
75
+ const server = new BkperMcpServer();
76
+ yield server.run();
77
+ }
78
+ catch (err) {
79
+ console.error('Error starting MCP server:', err);
80
+ process.exit(1);
81
+ }
82
+ }));
69
83
  program.parse(process.argv);
70
84
  //# sourceMappingURL=cli.js.map
package/lib/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { Bkper } from 'bkper-js';
2
+ /**
3
+ * Get a configured Bkper instance with authentication setup.
4
+ * Uses singleton pattern to avoid repeated configuration.
5
+ *
6
+ * @returns Configured Bkper instance
7
+ */
8
+ export declare function getBkperInstance(): Bkper;
9
+ export declare function setupBkper(): void;
10
+ //# sourceMappingURL=bkper-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bkper-factory.d.ts","sourceRoot":"","sources":["../../src/mcp/bkper-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAKjC;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,KAAK,CAiBxC;AAED,wBAAgB,UAAU,SAKzB"}
@@ -0,0 +1,40 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Bkper } from 'bkper-js';
11
+ import { getOAuthToken } from '../auth/local-auth-service.js';
12
+ let configuredBkperInstance = undefined;
13
+ /**
14
+ * Get a configured Bkper instance with authentication setup.
15
+ * Uses singleton pattern to avoid repeated configuration.
16
+ *
17
+ * @returns Configured Bkper instance
18
+ */
19
+ export function getBkperInstance() {
20
+ // Return mock instance if in test environment
21
+ if (process.env.NODE_ENV === 'test' || globalThis.__mockBkper) {
22
+ return globalThis.__mockBkper || Bkper;
23
+ }
24
+ // Return cached instance if already configured
25
+ if (configuredBkperInstance) {
26
+ return configuredBkperInstance;
27
+ }
28
+ // Configure Bkper with authentication
29
+ setupBkper();
30
+ // Cache the configured instance
31
+ configuredBkperInstance = new Bkper();
32
+ return configuredBkperInstance;
33
+ }
34
+ export function setupBkper() {
35
+ Bkper.setConfig({
36
+ apiKeyProvider: () => __awaiter(this, void 0, void 0, function* () { return process.env.BKPER_API_KEY || ''; }),
37
+ oauthTokenProvider: () => getOAuthToken()
38
+ });
39
+ }
40
+ //# sourceMappingURL=bkper-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bkper-factory.js","sourceRoot":"","sources":["../../src/mcp/bkper-factory.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,IAAI,uBAAuB,GAAsB,SAAS,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,8CAA8C;IAC9C,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,IAAK,UAAkB,CAAC,WAAW,EAAE,CAAC;QACvE,OAAQ,UAAkB,CAAC,WAAW,IAAI,KAAK,CAAC;IAClD,CAAC;IAED,+CAA+C;IAC/C,IAAI,uBAAuB,EAAE,CAAC;QAC5B,OAAO,uBAAuB,CAAC;IACjC,CAAC;IAED,sCAAsC;IACtC,UAAU,EAAE,CAAC;IACb,gCAAgC;IAChC,uBAAuB,GAAG,IAAI,KAAK,EAAE,CAAC;IAEtC,OAAO,uBAAuB,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,KAAK,CAAC,SAAS,CAAC;QACd,cAAc,EAAE,GAAS,EAAE,gDAAC,OAAA,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAA,GAAA;QAC3D,kBAAkB,EAAE,GAAG,EAAE,CAAC,aAAa,EAAE;KAC1C,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import { CallToolResult, ListToolsResult } from '@modelcontextprotocol/sdk/types.js';
3
+ declare class BkperMcpServer {
4
+ private server;
5
+ constructor();
6
+ private setupToolHandlers;
7
+ private setupErrorHandling;
8
+ run(): Promise<void>;
9
+ testListTools(): Promise<ListToolsResult>;
10
+ testCallTool(name: string, args?: Record<string, unknown>): Promise<CallToolResult>;
11
+ }
12
+ export { BkperMcpServer };
13
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":";AAOA,OAAO,EAEL,cAAc,EAGd,eAAe,EAEhB,MAAM,oCAAoC,CAAC;AAO5C,cAAM,cAAc;IAClB,OAAO,CAAC,MAAM,CAAS;;IAmBvB,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,kBAAkB;IAWpB,GAAG;IAOH,aAAa,IAAI,OAAO,CAAC,eAAe,CAAC;IAczC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAc9F;AAGD,OAAO,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ import dotenv from 'dotenv';
12
+ dotenv.config();
13
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
14
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
15
+ import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
16
+ import { handleGetBook, getBookToolDefinition } from './tools/get_book.js';
17
+ import { handleGetBalances, getBalancesToolDefinition } from './tools/get_balances.js';
18
+ import { handleListTransactions, listTransactionsToolDefinition } from './tools/list_transactions.js';
19
+ import { handleListBooks, listBooksToolDefinition } from './tools/list_books.js';
20
+ class BkperMcpServer {
21
+ constructor() {
22
+ this.server = new Server({
23
+ name: 'bkper-mcp-server',
24
+ version: '1.0.0',
25
+ }, {
26
+ capabilities: {
27
+ tools: {}
28
+ }
29
+ });
30
+ this.setupToolHandlers();
31
+ this.setupErrorHandling();
32
+ }
33
+ setupToolHandlers() {
34
+ this.server.setRequestHandler(ListToolsRequestSchema, () => __awaiter(this, void 0, void 0, function* () {
35
+ return {
36
+ tools: [
37
+ listBooksToolDefinition,
38
+ getBookToolDefinition,
39
+ getBalancesToolDefinition,
40
+ listTransactionsToolDefinition,
41
+ ],
42
+ };
43
+ }));
44
+ this.server.setRequestHandler(CallToolRequestSchema, (request) => __awaiter(this, void 0, void 0, function* () {
45
+ switch (request.params.name) {
46
+ case 'list_books':
47
+ return yield handleListBooks(request.params.arguments);
48
+ case 'get_book':
49
+ return yield handleGetBook(request.params.arguments);
50
+ case 'get_balances':
51
+ return yield handleGetBalances(request.params.arguments);
52
+ case 'list_transactions':
53
+ return yield handleListTransactions(request.params.arguments);
54
+ default:
55
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
56
+ }
57
+ }));
58
+ }
59
+ setupErrorHandling() {
60
+ this.server.onerror = (error) => {
61
+ console.error('[MCP Error]', error);
62
+ };
63
+ process.on('SIGINT', () => __awaiter(this, void 0, void 0, function* () {
64
+ yield this.server.close();
65
+ process.exit(0);
66
+ }));
67
+ }
68
+ run() {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ const transport = new StdioServerTransport();
71
+ yield this.server.connect(transport);
72
+ console.error('Bkper MCP server running on stdio');
73
+ });
74
+ }
75
+ // Test helper methods for accessing MCP handlers directly
76
+ testListTools() {
77
+ return __awaiter(this, void 0, void 0, function* () {
78
+ // Call the list tools handler directly for testing
79
+ const requestHandlers = this.server._requestHandlers;
80
+ const handler = requestHandlers.get('tools/list');
81
+ if (!handler)
82
+ throw new Error('ListTools handler not found');
83
+ // Create proper MCP request format
84
+ const request = {
85
+ method: 'tools/list',
86
+ params: {}
87
+ };
88
+ return yield handler(request);
89
+ });
90
+ }
91
+ testCallTool(name_1) {
92
+ return __awaiter(this, arguments, void 0, function* (name, args = {}) {
93
+ // Call the call tool handler directly for testing
94
+ const requestHandlers = this.server._requestHandlers;
95
+ const handler = requestHandlers.get('tools/call');
96
+ if (!handler)
97
+ throw new Error('CallTool handler not found');
98
+ // Create proper MCP request format
99
+ const request = {
100
+ method: 'tools/call',
101
+ params: { name, arguments: args }
102
+ };
103
+ return yield handler(request);
104
+ });
105
+ }
106
+ }
107
+ // Export the class for testing
108
+ export { BkperMcpServer };
109
+ // Only run the server if this file is executed directly
110
+ if (import.meta.url === `file://${process.argv[1]}`) {
111
+ const server = new BkperMcpServer();
112
+ server.run().catch(console.error);
113
+ }
114
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,192 @@
1
+ # Bkper MCP Usage Guide
2
+
3
+ ## Essential Tools
4
+
5
+ ### Discovery
6
+ - `list_books()` - Find available books
7
+ - `get_book({ bookId })` - Book details, structure, and group hierarchies
8
+
9
+ ### Analysis
10
+ - `get_balances({ bookId, query })` - **THE** tool for all balance analysis
11
+ - `list_transactions({ bookId, query })` - Transaction inspection only (never for balance calculations)
12
+
13
+ ## Core Rules
14
+
15
+ ### 1. Root Group Discovery
16
+ **Always discover and use root groups dynamically based on account group types:**
17
+ 1. Call `get_book({ bookId })` to see the hierarchy
18
+ 2. Look for the top-level group that contains:
19
+ - **For Balance Sheet**: Both ASSET and LIABILITY account types
20
+ - **For P&L**: Both INCOMING and OUTGOING account types
21
+ 3. Use that group name (not its children) in your queries
22
+
23
+ **Example**: If you see this structure:
24
+ ```
25
+ "Total Equity" (root)
26
+ ├── "Assets" (type: ASSET)
27
+ ├── "Liabilities" (type: LIABILITY)
28
+ ```
29
+ Then use `group:'Total Equity'` - NOT `group:'Assets'` or `group:'Liabilities'`
30
+
31
+ ### 2. Date Filter Matching
32
+
33
+ - **Permanent accounts** (ASSET, LIABILITY): Use `before:` dates for point-in-time
34
+ - **Non-permanent accounts** (INCOMING, OUTGOING): Use `after:` and `before:` for periods
35
+
36
+ - **IMPORTANT:** `after:` is inclusive and `before:` is exclusive, so, for example, to filter the whole 2024 year, the range might be `after:2024-01-01 before:2025-01-01`
37
+
38
+ ### 3. Correct Tool Selection
39
+ - **Balance analysis**: Always use `get_balances`
40
+ - **Transaction inspection**: Use `list_transactions` (never for calculations)
41
+
42
+ ### 4. Balance Analysis Query Requirements (MANDATORY)
43
+ - **MUST include either `group:` or `account:` operator in EVERY query**
44
+ - Never query subgroups directly (Assets, Liabilities, Revenue, Expenses) - use root groups
45
+ - When using `account:`, be specific about individual accounts
46
+ - Always include appropriate date filters
47
+ - Balance analysis queries without group/account operators will be rejected with an error
48
+ - Transaction queries can be without group/account operators
49
+
50
+ ## Examples
51
+
52
+ ### Balance Sheet Analysis
53
+ ```javascript
54
+ // After discovering root group for ASSET + LIABILITY accounts
55
+ get_balances({
56
+ bookId: "book-123",
57
+ query: "group:'Total Equity' before:$m"
58
+ })
59
+ ```
60
+
61
+ ### P&L Analysis
62
+ ```javascript
63
+ // After discovering root group for INCOMING + OUTGOING accounts
64
+ get_balances({
65
+ bookId: "book-123",
66
+ query: "group:'Profit & Loss' after:$m-12 before:$m"
67
+ })
68
+ ```
69
+
70
+ ### Complete Financial Statements
71
+ ```javascript
72
+ // After discovering both root groups
73
+ const balanceSheet = await get_balances({
74
+ bookId: "book-123",
75
+ query: "group:'Total Equity' before:$m+1"
76
+ });
77
+
78
+ const pnl = await get_balances({
79
+ bookId: "book-123",
80
+ query: "group:'Profit & Loss' after:$m-1 before:$m"
81
+ });
82
+ ```
83
+
84
+ ### Transaction Inspection
85
+ ```javascript
86
+ // Recent transactions (inspection only)
87
+ list_transactions({
88
+ bookId: "book-123",
89
+ query: "after:$d-30"
90
+ })
91
+
92
+ // Large transactions
93
+ list_transactions({
94
+ bookId: "book-123",
95
+ query: "amount>1000 after:$m-1 before:$m"
96
+ })
97
+ ```
98
+
99
+ ## Account Structure Reference
100
+
101
+ ### Account Types
102
+ | Type | Nature | Examples |
103
+ |------|--------|----------|
104
+ | **ASSET** | Permanent | Cash, Accounts Receivable |
105
+ | **LIABILITY** | Permanent | Accounts Payable, Loans |
106
+ | **INCOMING** | Non-permanent | Sales, Interest Income |
107
+ | **OUTGOING** | Non-permanent | Rent, Utilities |
108
+
109
+ ### Example Group Hierarchies
110
+
111
+ **Example 1: Permanent Accounts (Balance Sheet Analysis)**
112
+ ```
113
+ "Total Equity"
114
+ ├── Assets
115
+ │ ├── Current Assets
116
+ │ └── Fixed Assets
117
+ └── Liabilities
118
+ ├── Current Liabilities
119
+ └── Long-term Liabilities
120
+ ```
121
+
122
+ **Example 2: Non-Permanent Accounts (Profit & Loss Analysis)**
123
+ ```
124
+ "Profit & Loss"
125
+ ├── Revenue
126
+ │ ├── Product Sales
127
+ │ └── Service Revenue
128
+ └── Expenses
129
+ ├── Operating Expenses
130
+ └── Administrative Expenses
131
+ ```
132
+
133
+ **Note**: Root group names vary by book. Always discover the actual names using `get_book()` which includes group hierarchies.
134
+
135
+ ## Quick Reference
136
+
137
+ ### Date Variables
138
+ - `$d` - Today
139
+ - `$m` - Current month end
140
+ - `$y` - Current year end
141
+ - `$d-N` - N days ago
142
+ - `$m-N` - N months ago
143
+
144
+ ### Query Patterns
145
+ ```
146
+ group:'[ROOT_GROUP_NAME]' before:$m // Balance sheet (permanent accounts)
147
+ group:'[ROOT_GROUP_NAME]' after:$m-1 before:$m // P&L (non-permanent accounts)
148
+ after:2024-01-01 before:2025-01-01 // Date range
149
+ amount>1000 // Amount filter (transactions only)
150
+ ```
151
+
152
+ ### Analysis Workflow
153
+ 1. **Discover**: `list_books()` → `get_book()` (includes groups)
154
+ 2. **Identify Root Groups**: Find groups containing the group types you need
155
+ 3. **Analyze**: `get_balances()` with discovered root groups
156
+ 4. **Inspect**: `list_transactions()` for transaction details
157
+
158
+ ## Reports and Insights Formatting
159
+
160
+ ### Markdown Output Preference
161
+ When generating financial insights, reports, or analysis, **strongly prefer well-structured Markdown** for better readability across platforms (cloud, desktop, mobile). Use tables for financial data, clear headers for sections, bold formatting for totals and key metrics, and icons to enhance visual appeal.
162
+
163
+
164
+ ## Common Mistakes to Avoid
165
+
166
+ ❌ **Wrong**: `get_balances({ query: "before:$m" })` - Missing group/account operator!
167
+ ✅ **Right**: `get_balances({ query: "group:'[ROOT_GROUP]' before:$m" })`
168
+
169
+ ❌ **Wrong**: `get_balances({ query: "after:2024-01-01" })` - Missing group/account operator!
170
+ ✅ **Right**: `get_balances({ query: "group:'[ROOT_GROUP]' after:2024-01-01 before:2025-01-01" })`
171
+
172
+ ❌ **Wrong**: `get_balances({ query: "group:'Assets'" })` - Using subgroup instead of root group
173
+ ✅ **Right**: `get_balances({ query: "group:'[ROOT_GROUP_WITH_ASSETS]' before:$m" })`
174
+
175
+ ❌ **Wrong**: Query without any filtering
176
+ ✅ **Right**: Always include `group:` or `account:` operator for focused analysis
177
+
178
+ ❌ **Wrong**: Using `list_transactions` for balance calculations
179
+ ✅ **Right**: Use `get_balances` for all balance analysis
180
+
181
+ ❌ **Wrong**: Using `before:` without `after:` dates with non-permanent accounts
182
+ ✅ **Right**: Use `after:` and `before:` with INCOMING/OUTGOING accounts
183
+
184
+ ❌ **Wrong**: Using different date variables on same query. E.g.`after:$y-1` and `before:$d`
185
+ ✅ **Right**: Use same date variables on same query. E.g. `after:$y-1` and `before:$y`
186
+
187
+ ❌ **Wrong**: Assuming fixed root group names
188
+ ✅ **Right**: Always discover root groups using `get_book()` then navigate its `groups` property
189
+
190
+ ## CRITICAL REQUIREMENT
191
+
192
+ **⚠️ ALL get_balances queries MUST include either `group:` or `account:` operator.**
@@ -0,0 +1,26 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ interface GetBalancesParams {
3
+ bookId: string;
4
+ query: string;
5
+ }
6
+ export declare function handleGetBalances(params: GetBalancesParams): Promise<CallToolResult>;
7
+ export declare const getBalancesToolDefinition: {
8
+ name: string;
9
+ description: string;
10
+ inputSchema: {
11
+ type: string;
12
+ properties: {
13
+ bookId: {
14
+ type: string;
15
+ description: string;
16
+ };
17
+ query: {
18
+ type: string;
19
+ description: string;
20
+ };
21
+ };
22
+ required: string[];
23
+ };
24
+ };
25
+ export {};
26
+ //# sourceMappingURL=get_balances.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_balances.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/get_balances.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAuB,MAAM,oCAAoC,CAAC;AAIzF,UAAU,iBAAiB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AASD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAoG1F;AAED,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;CAiBrC,CAAC"}
@@ -0,0 +1,106 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
11
+ import { getBkperInstance } from '../bkper-factory.js';
12
+ import { BalanceType } from 'bkper-js';
13
+ export function handleGetBalances(params) {
14
+ return __awaiter(this, void 0, void 0, function* () {
15
+ try {
16
+ // Validate required parameters
17
+ if (!params.bookId) {
18
+ throw new McpError(ErrorCode.InvalidParams, 'Missing required parameter: bookId');
19
+ }
20
+ if (!params.query) {
21
+ throw new McpError(ErrorCode.InvalidParams, 'Missing required parameter: query');
22
+ }
23
+ // Validate query contains either group: or account: operator
24
+ if (!params.query.includes('group:') && !params.query.includes('account:')) {
25
+ throw new McpError(ErrorCode.InvalidParams, 'Query must include either \'group:\' or \'account:\' operator for proper balance filtering. Example: "group:\'Assets\' before:$m" or "account:\'Cash\' before:$m"');
26
+ }
27
+ // Get configured Bkper instance
28
+ const bkper = getBkperInstance();
29
+ // Get the book first
30
+ const book = yield bkper.getBook(params.bookId, true);
31
+ if (!book) {
32
+ throw new McpError(ErrorCode.InvalidParams, `Book not found: ${params.bookId}`);
33
+ }
34
+ // Get balances from the book with query (required by bkper-js)
35
+ let actualQuery = params.query;
36
+ // Enforce monthly periodicity by modifying query
37
+ if (actualQuery.includes('by:d')) {
38
+ // Replace daily periodicity with monthly
39
+ actualQuery = actualQuery.replace(/by:d/g, 'by:m');
40
+ }
41
+ else if (actualQuery.includes('by:y')) {
42
+ // Replace yearly periodicity with monthly
43
+ actualQuery = actualQuery.replace(/by:y/g, 'by:m');
44
+ }
45
+ else if (!actualQuery.includes('by:')) {
46
+ // Append monthly periodicity if no by: operator present
47
+ actualQuery = actualQuery + ' by:m';
48
+ }
49
+ // If by:m is already present, keep it unchanged
50
+ // Get the first container to access createDataTable
51
+ const balancesReport = yield book.getBalancesReport(actualQuery);
52
+ // Determine balance type based on presence of after: operator
53
+ const type = actualQuery.includes('after:') ? BalanceType.PERIOD : BalanceType.CUMULATIVE;
54
+ // Use BalancesDataTableBuilder to generate matrix
55
+ let matrix;
56
+ // Get the first container to access createDataTable
57
+ const dataTableBuilder = balancesReport.createDataTable()
58
+ .formatValues(false) // Raw numbers for LLMs
59
+ .formatDates(true) // YYYY-MM-DD dates
60
+ .raw(true) // Raw balances
61
+ .expanded(4)
62
+ .type(type); //
63
+ matrix = dataTableBuilder.build();
64
+ // Build response with matrix format
65
+ const response = {
66
+ matrix,
67
+ query: actualQuery
68
+ };
69
+ return {
70
+ content: [
71
+ {
72
+ type: 'text',
73
+ text: JSON.stringify(response, null, 2),
74
+ },
75
+ ],
76
+ };
77
+ }
78
+ catch (error) {
79
+ // Re-throw MCP errors as-is
80
+ if (error instanceof McpError) {
81
+ throw error;
82
+ }
83
+ // Handle other errors
84
+ throw new McpError(ErrorCode.InternalError, `Failed to get balances: ${error instanceof Error ? error.message : String(error)}`);
85
+ }
86
+ });
87
+ }
88
+ export const getBalancesToolDefinition = {
89
+ name: 'get_balances',
90
+ description: 'Get account balances with query filtering. Use get_book first to understand group hierarchy and usage rules.',
91
+ inputSchema: {
92
+ type: 'object',
93
+ properties: {
94
+ bookId: {
95
+ type: 'string',
96
+ description: 'The unique identifier of the book'
97
+ },
98
+ query: {
99
+ type: 'string',
100
+ description: 'Required query to filter balances (e.g., "account:\'Cash\'", "group:\'Assets\'", "before:2024-01-31")'
101
+ }
102
+ },
103
+ required: ['bookId', 'query']
104
+ }
105
+ };
106
+ //# sourceMappingURL=get_balances.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_balances.js","sourceRoot":"","sources":["../../../src/mcp/tools/get_balances.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ interface GetBookParams {
3
+ bookId: string;
4
+ }
5
+ export declare function handleGetBook(params: GetBookParams): Promise<CallToolResult>;
6
+ export declare const getBookToolDefinition: {
7
+ name: string;
8
+ description: string;
9
+ inputSchema: {
10
+ type: string;
11
+ properties: {
12
+ bookId: {
13
+ type: string;
14
+ description: string;
15
+ };
16
+ };
17
+ required: string[];
18
+ };
19
+ };
20
+ export {};
21
+ //# sourceMappingURL=get_book.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_book.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/get_book.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAuB,MAAM,oCAAoC,CAAC;AAOzF,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAkED,wBAAsB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAqElF;AAED,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;CAajC,CAAC"}
@@ -0,0 +1,126 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
11
+ import { getBkperInstance } from '../bkper-factory.js';
12
+ import { readFileSync } from 'fs';
13
+ import { join, dirname } from 'path';
14
+ import { fileURLToPath } from 'url';
15
+ function buildHierarchicalStructure(groups) {
16
+ const groupMap = new Map();
17
+ const rootGroups = [];
18
+ // First pass: create all group nodes
19
+ groups.forEach(group => {
20
+ const node = {
21
+ id: group.getId() || '',
22
+ name: group.getName() || '',
23
+ type: group.getType() || '',
24
+ hidden: group.isHidden() || false,
25
+ permanent: group.isPermanent() || false,
26
+ properties: group.getProperties() || {},
27
+ children: []
28
+ };
29
+ groupMap.set(node.id, node);
30
+ });
31
+ // Second pass: build hierarchy
32
+ groups.forEach(group => {
33
+ const node = groupMap.get(group.getId() || '');
34
+ if (!node)
35
+ return;
36
+ const parent = group.getParent();
37
+ if (parent) {
38
+ const parentNode = groupMap.get(parent.getId() || '');
39
+ if (parentNode) {
40
+ parentNode.children.push(node);
41
+ }
42
+ }
43
+ else {
44
+ // No parent = root group
45
+ rootGroups.push(node);
46
+ }
47
+ });
48
+ return rootGroups;
49
+ }
50
+ function loadSystemPrompt() {
51
+ try {
52
+ // Get the directory where this file is located and navigate to the parent mcp directory
53
+ const __filename = fileURLToPath(import.meta.url);
54
+ const __dirname = dirname(__filename);
55
+ const mcpPath = join(__dirname, '..');
56
+ const systemPrompt = readFileSync(join(mcpPath, 'system-prompt.md'), 'utf-8');
57
+ return systemPrompt;
58
+ }
59
+ catch (error) {
60
+ return `# Bkper MCP System Prompt\n\nDocumentation not available.\nError: ${error instanceof Error ? error.message : String(error)}\nPath attempted: ${join(dirname(fileURLToPath(import.meta.url)), '..', 'system-prompt.md')}`;
61
+ }
62
+ }
63
+ export function handleGetBook(params) {
64
+ return __awaiter(this, void 0, void 0, function* () {
65
+ try {
66
+ // Validate required parameters
67
+ if (!params.bookId) {
68
+ throw new McpError(ErrorCode.InvalidParams, 'Missing required parameter: bookId');
69
+ }
70
+ // Get configured Bkper instance
71
+ const bkperInstance = getBkperInstance();
72
+ // Get the specific book
73
+ const book = yield bkperInstance.getBook(params.bookId, false, true);
74
+ if (!book) {
75
+ throw new McpError(ErrorCode.InvalidParams, `Book not found: ${params.bookId}`);
76
+ }
77
+ // Get book JSON data
78
+ const bookJson = book.json();
79
+ // Get groups from the book
80
+ const groups = yield book.getGroups();
81
+ // Build hierarchical structure
82
+ const hierarchicalGroups = buildHierarchicalStructure(groups || []);
83
+ bookJson.groups = hierarchicalGroups;
84
+ // Build response with book data and groups
85
+ const response = {
86
+ book: bookJson,
87
+ readme: loadSystemPrompt()
88
+ };
89
+ return {
90
+ content: [
91
+ {
92
+ type: 'text',
93
+ text: JSON.stringify(response, null, 2),
94
+ },
95
+ ],
96
+ };
97
+ }
98
+ catch (error) {
99
+ // Handle specific book not found errors
100
+ if (error instanceof Error && error.message.includes('not found')) {
101
+ throw new McpError(ErrorCode.InvalidParams, `Book not found: ${params.bookId}`);
102
+ }
103
+ // Re-throw MCP errors as-is
104
+ if (error instanceof McpError) {
105
+ throw error;
106
+ }
107
+ // Handle other errors
108
+ throw new McpError(ErrorCode.InternalError, `Failed to get book: ${error instanceof Error ? error.message : String(error)}`);
109
+ }
110
+ });
111
+ }
112
+ export const getBookToolDefinition = {
113
+ name: 'get_book',
114
+ description: 'Retrieve detailed information about a specific book including its group hierarchy and system prompt',
115
+ inputSchema: {
116
+ type: 'object',
117
+ properties: {
118
+ bookId: {
119
+ type: 'string',
120
+ description: 'The unique identifier of the book to retrieve'
121
+ }
122
+ },
123
+ required: ['bookId']
124
+ }
125
+ };
126
+ //# sourceMappingURL=get_book.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_book.js","sourceRoot":"","sources":["../../../src/mcp/tools/get_book.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ interface ListBooksParams {
3
+ filter: string;
4
+ }
5
+ export declare function handleListBooks(params: ListBooksParams): Promise<CallToolResult>;
6
+ export declare const listBooksToolDefinition: {
7
+ name: string;
8
+ description: string;
9
+ inputSchema: {
10
+ type: string;
11
+ properties: {
12
+ filter: {
13
+ type: string;
14
+ description: string;
15
+ };
16
+ };
17
+ required: string[];
18
+ };
19
+ };
20
+ export {};
21
+ //# sourceMappingURL=list_books.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list_books.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/list_books.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAuB,MAAM,oCAAoC,CAAC;AAGzF,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAsBD,wBAAsB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CA+BtF;AAED,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;CAanC,CAAC"}
@@ -0,0 +1,67 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
11
+ import { getBkperInstance } from '../bkper-factory.js';
12
+ function fetchBooks(filter) {
13
+ return __awaiter(this, void 0, void 0, function* () {
14
+ // Get configured Bkper instance
15
+ const bkperInstance = getBkperInstance();
16
+ // Get books using high-level wrapper with required filter
17
+ const bkperBooks = yield bkperInstance.getBooks(filter);
18
+ // Return full book JSON data - no filtering, no transforming
19
+ const books = bkperBooks.map((book) => book.json());
20
+ const total = books.length;
21
+ return { books, total };
22
+ });
23
+ }
24
+ export function handleListBooks(params) {
25
+ return __awaiter(this, void 0, void 0, function* () {
26
+ try {
27
+ // Fetch all books with required filter
28
+ const { books, total } = yield fetchBooks(params.filter);
29
+ // Build response
30
+ const response = {
31
+ total,
32
+ books
33
+ };
34
+ return {
35
+ content: [
36
+ {
37
+ type: 'text',
38
+ text: JSON.stringify(response, null, 2),
39
+ },
40
+ ],
41
+ };
42
+ }
43
+ catch (error) {
44
+ // Re-throw MCP errors as-is
45
+ if (error instanceof McpError) {
46
+ throw error;
47
+ }
48
+ // Handle other errors
49
+ throw new McpError(ErrorCode.InternalError, `Failed to list books: ${error instanceof Error ? error.message : String(error)}`);
50
+ }
51
+ });
52
+ }
53
+ export const listBooksToolDefinition = {
54
+ name: 'list_books',
55
+ description: 'List books with mandatory filtering by name or property',
56
+ inputSchema: {
57
+ type: 'object',
58
+ properties: {
59
+ filter: {
60
+ type: 'string',
61
+ description: 'Required filter to search books by name or property (case-insensitive substring match)'
62
+ }
63
+ },
64
+ required: ['filter']
65
+ }
66
+ };
67
+ //# sourceMappingURL=list_books.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list_books.js","sourceRoot":"","sources":["../../../src/mcp/tools/list_books.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAkB,SAAS,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAWvD,SAAe,UAAU,CAAC,MAAc;;QACtC,gCAAgC;QAChC,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;QAEzC,0DAA0D;QAC1D,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAExD,6DAA6D;QAC7D,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAEzD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;QAE3B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC1B,CAAC;CAAA;AAED,MAAM,UAAgB,eAAe,CAAC,MAAuB;;QAC3D,IAAI,CAAC;YACH,uCAAuC;YACvC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAEzD,iBAAiB;YACjB,MAAM,QAAQ,GAAkB;gBAC9B,KAAK;gBACL,KAAK;aACN,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;qBACxC;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,4BAA4B;YAC5B,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;gBAC9B,MAAM,KAAK,CAAC;YACd,CAAC;YAED,sBAAsB;YACtB,MAAM,IAAI,QAAQ,CAChB,SAAS,CAAC,aAAa,EACvB,yBAAyB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAClF,CAAC;QACJ,CAAC;IACH,CAAC;CAAA;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,IAAI,EAAE,YAAY;IAClB,WAAW,EAAE,yDAAyD;IACtE,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,wFAAwF;aACtG;SACF;QACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;KACrB;CACF,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ interface ListTransactionsParams {
3
+ bookId: string;
4
+ cursor?: string;
5
+ query: string;
6
+ limit?: number;
7
+ }
8
+ export declare function handleListTransactions(params: ListTransactionsParams): Promise<CallToolResult>;
9
+ export declare const listTransactionsToolDefinition: {
10
+ name: string;
11
+ description: string;
12
+ inputSchema: {
13
+ type: string;
14
+ properties: {
15
+ bookId: {
16
+ type: string;
17
+ description: string;
18
+ };
19
+ cursor: {
20
+ type: string;
21
+ description: string;
22
+ };
23
+ query: {
24
+ type: string;
25
+ description: string;
26
+ };
27
+ limit: {
28
+ type: string;
29
+ description: string;
30
+ minimum: number;
31
+ maximum: number;
32
+ };
33
+ };
34
+ required: string[];
35
+ };
36
+ };
37
+ export {};
38
+ //# sourceMappingURL=list_transactions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list_transactions.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/list_transactions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAuB,MAAM,oCAAoC,CAAC;AAGzF,UAAU,sBAAsB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAUD,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,cAAc,CAAC,CA0EpG;AAED,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2B1C,CAAC"}
@@ -0,0 +1,95 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
11
+ import { getBkperInstance } from '../bkper-factory.js';
12
+ export function handleListTransactions(params) {
13
+ return __awaiter(this, void 0, void 0, function* () {
14
+ try {
15
+ // Validate required parameters
16
+ if (!params.bookId) {
17
+ throw new McpError(ErrorCode.InvalidParams, 'Missing required parameter: bookId');
18
+ }
19
+ if (!params.query) {
20
+ throw new McpError(ErrorCode.InvalidParams, 'Missing required parameter: query');
21
+ }
22
+ // Get configured Bkper instance
23
+ const bkperInstance = getBkperInstance();
24
+ // Get the book first
25
+ const book = yield bkperInstance.getBook(params.bookId);
26
+ if (!book) {
27
+ throw new McpError(ErrorCode.InvalidParams, `Book not found: ${params.bookId}`);
28
+ }
29
+ // Set up pagination parameters
30
+ const limit = Math.min(params.limit || 25, 100); // Default 25, max 100
31
+ // Get transactions using native API pagination
32
+ const transactionList = yield book.listTransactions(params.query, limit, params.cursor);
33
+ // Get transactions from list
34
+ const transactionItems = transactionList.getItems();
35
+ const transactions = transactionItems.map((transaction) => transaction.json());
36
+ // Get pagination info from list
37
+ const hasMore = transactionItems.length > 0;
38
+ const nextCursor = transactionList.getCursor() || null;
39
+ // Build response
40
+ const response = {
41
+ transactions,
42
+ hasMore,
43
+ cursor: nextCursor,
44
+ limit
45
+ };
46
+ // Include query in response (now always provided)
47
+ response.query = params.query;
48
+ return {
49
+ content: [
50
+ {
51
+ type: 'text',
52
+ text: JSON.stringify(response, null, 2),
53
+ },
54
+ ],
55
+ };
56
+ }
57
+ catch (error) {
58
+ // Re-throw MCP errors as-is
59
+ if (error instanceof McpError) {
60
+ throw error;
61
+ }
62
+ // Handle other errors
63
+ throw new McpError(ErrorCode.InternalError, `Failed to list transactions: ${error instanceof Error ? error.message : String(error)}`);
64
+ }
65
+ });
66
+ }
67
+ export const listTransactionsToolDefinition = {
68
+ name: 'list_transactions',
69
+ description: 'List transactions with native API cursor-based pagination and query filtering. Use get_book first to understand query syntax and usage rules.',
70
+ inputSchema: {
71
+ type: 'object',
72
+ properties: {
73
+ bookId: {
74
+ type: 'string',
75
+ description: 'The unique identifier of the book'
76
+ },
77
+ cursor: {
78
+ type: 'string',
79
+ description: 'Pagination cursor for next page (provided by previous response)'
80
+ },
81
+ query: {
82
+ type: 'string',
83
+ description: 'Required query string to filter transactions using comprehensive syntax (account:, from:, to:, group:, on:, after:, before:, amount:, text search, logical operators, etc.)'
84
+ },
85
+ limit: {
86
+ type: 'number',
87
+ description: 'Number of transactions per page (default: 25, maximum: 100)',
88
+ minimum: 1,
89
+ maximum: 100
90
+ }
91
+ },
92
+ required: ['bookId', 'query']
93
+ }
94
+ };
95
+ //# sourceMappingURL=list_transactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list_transactions.js","sourceRoot":"","sources":["../../../src/mcp/tools/list_transactions.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bkper",
3
- "version": "3.3.3",
3
+ "version": "3.5.0",
4
4
  "description": "Node.js command line client for Bkper",
5
5
  "bin": {
6
6
  "bkper": "./lib/cli.js"
@@ -17,14 +17,32 @@
17
17
  "files": [
18
18
  "lib/**/*"
19
19
  ],
20
+ "mcp": {
21
+ "server": {
22
+ "command": "node",
23
+ "args": [
24
+ "lib/cli.js",
25
+ "mcp",
26
+ "start"
27
+ ],
28
+ "env": {}
29
+ }
30
+ },
20
31
  "scripts": {
21
32
  "clean": "rm -rf ./lib & rm -rf ./node_modules & wait",
22
33
  "prebuild": "bun install",
23
34
  "build": "run-s build:*",
24
- "build:clean": "gts clean",
35
+ "build:clean": "gts clean && rimraf temp",
25
36
  "build:compile": "tsc",
37
+ "build:copy-docs": "mkdir -p lib/docs && cp -f docs/*.md lib/docs/ 2>/dev/null || true",
38
+ "build:copy-mcp-assets": "mkdir -p lib/mcp && cp -f src/mcp/*.md lib/mcp/ 2>/dev/null || true",
26
39
  "predev": "bun run build",
27
- "dev": "tsc -w",
40
+ "dev": "bun run build && npx concurrently \"tsc -w\" \"./scripts/dev-mcp.sh\"",
41
+ "mcp": "bun run build && node lib/cli.js mcp start",
42
+ "test": "TS_NODE_PROJECT=tsconfig.test.json mocha",
43
+ "test:unit": "TS_NODE_PROJECT=tsconfig.test.json mocha --config .mocharc.json",
44
+ "test:integration": "TS_NODE_PROJECT=tsconfig.test.json mocha --config .mocharc.integration.json",
45
+ "test:all": "bun run test:unit && bun run test:integration",
28
46
  "upgrade:api": "bun update @bkper/bkper-api-types --latest && bun update bkper-js --latest",
29
47
  "patch": "yarn version --patch",
30
48
  "minor": "yarn version --minor",
@@ -34,19 +52,28 @@
34
52
  },
35
53
  "dependencies": {
36
54
  "@google-cloud/local-auth": "^3.0.1",
37
- "bkper-js": "^1.30.0",
55
+ "@modelcontextprotocol/sdk": "^1.13.1",
56
+ "bkper-js": "^2.4.0",
38
57
  "commander": "^6.2.1",
39
58
  "dotenv": "^8.2.0",
40
59
  "google-auth-library": "^9.14.0",
41
60
  "yaml": "^2.5.1"
42
61
  },
43
62
  "devDependencies": {
44
- "@bkper/bkper-api-types": "^5.15.1",
63
+ "@bkper/bkper-api-types": "^5.23.0",
64
+ "@types/chai": "^4.3.0",
45
65
  "@types/commander": "^2.12.2",
66
+ "@types/mocha": "^10.0.0",
46
67
  "@types/node": "^22.5.1",
68
+ "chai": "^4.3.0",
69
+ "concurrently": "^8.2.2",
47
70
  "gts": "^3.0.3",
71
+ "mocha": "^10.0.0",
72
+ "nodemon": "^3.0.1",
48
73
  "npm-run-all": "^4.1.5",
49
74
  "rimraf": "^3.0.2",
75
+ "ts-node": "^10.9.2",
76
+ "tsconfig-paths": "^4.2.0",
50
77
  "typescript": "^5.5.4"
51
78
  }
52
79
  }