@ktmcp-cli/nordigen 1.0.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/.env.example ADDED
@@ -0,0 +1,11 @@
1
+ # Nordigen API Credentials
2
+ # Get these from https://nordigen.com dashboard
3
+
4
+ NORDIGEN_SECRET_ID=your_secret_id_here
5
+ NORDIGEN_SECRET_KEY=your_secret_key_here
6
+
7
+ # Optional: Default country for commands
8
+ NORDIGEN_DEFAULT_COUNTRY=GB
9
+
10
+ # Optional: Enable debug mode
11
+ # DEBUG=1
package/.eslintrc.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "env": {
3
+ "es2021": true,
4
+ "node": true
5
+ },
6
+ "extends": "eslint:recommended",
7
+ "parserOptions": {
8
+ "ecmaVersion": "latest",
9
+ "sourceType": "module"
10
+ },
11
+ "rules": {
12
+ "indent": ["error", 2],
13
+ "linebreak-style": ["error", "unix"],
14
+ "quotes": ["error", "single"],
15
+ "semi": ["error", "always"]
16
+ }
17
+ }
package/AGENT.md ADDED
@@ -0,0 +1,480 @@
1
+ # Nordigen CLI - AI Agent Integration Guide
2
+
3
+ This guide shows how AI agents can effectively use the Nordigen CLI to access open banking data.
4
+
5
+ ## Agent Capabilities
6
+
7
+ The Nordigen CLI enables AI agents to:
8
+
9
+ 1. Browse and search financial institutions across Europe
10
+ 2. Create and manage end user agreements
11
+ 3. Initiate bank account connections
12
+ 4. Retrieve account balances and details
13
+ 5. Fetch and analyze transaction history
14
+ 6. Process payment information
15
+
16
+ ## Basic Agent Patterns
17
+
18
+ ### Pattern 1: Institution Discovery
19
+
20
+ ```javascript
21
+ // Agent discovers banks in a specific country
22
+ async function findInstitutions(country, query = null) {
23
+ if (query) {
24
+ // Search for specific institution
25
+ const result = await exec(`nordigen institutions search "${query}" --country ${country} --json`);
26
+ return JSON.parse(result.stdout);
27
+ } else {
28
+ // List all institutions
29
+ const result = await exec(`nordigen institutions list --country ${country} --json`);
30
+ return JSON.parse(result.stdout);
31
+ }
32
+ }
33
+
34
+ // Usage
35
+ const banks = await findInstitutions('GB', 'Barclays');
36
+ ```
37
+
38
+ ### Pattern 2: Account Connection Flow
39
+
40
+ ```javascript
41
+ // Agent guides user through bank connection
42
+ async function connectBankAccount(institutionId, redirectUrl) {
43
+ // 1. Create end user agreement
44
+ const agreementCmd = `nordigen agreements create --institution-id ${institutionId} --max-days 90 --json`;
45
+ const agreement = JSON.parse((await exec(agreementCmd)).stdout);
46
+
47
+ // 2. Create requisition
48
+ const reqCmd = `nordigen requisitions create --institution-id ${institutionId} --redirect ${redirectUrl} --agreement ${agreement.id} --json`;
49
+ const requisition = JSON.parse((await exec(reqCmd)).stdout);
50
+
51
+ // 3. Return authentication link for user
52
+ return {
53
+ requisitionId: requisition.id,
54
+ authLink: requisition.link,
55
+ status: requisition.status
56
+ };
57
+ }
58
+ ```
59
+
60
+ ### Pattern 3: Transaction Analysis
61
+
62
+ ```javascript
63
+ // Agent retrieves and analyzes transactions
64
+ async function analyzeTransactions(accountId, dateFrom, dateTo) {
65
+ const cmd = `nordigen accounts transactions ${accountId} --from ${dateFrom} --to ${dateTo} --json`;
66
+ const result = await exec(cmd);
67
+ const data = JSON.parse(result.stdout);
68
+
69
+ const transactions = data.transactions.booked || [];
70
+
71
+ // Analyze spending patterns
72
+ const analysis = {
73
+ totalSpent: 0,
74
+ totalIncome: 0,
75
+ categories: {},
76
+ transactionCount: transactions.length
77
+ };
78
+
79
+ transactions.forEach(tx => {
80
+ const amount = parseFloat(tx.transactionAmount.amount);
81
+ if (amount < 0) {
82
+ analysis.totalSpent += Math.abs(amount);
83
+ } else {
84
+ analysis.totalIncome += amount;
85
+ }
86
+ });
87
+
88
+ return analysis;
89
+ }
90
+ ```
91
+
92
+ ### Pattern 4: Multi-Account Balance Check
93
+
94
+ ```javascript
95
+ // Agent checks balances across multiple accounts
96
+ async function checkAllBalances(accountIds) {
97
+ const balances = await Promise.all(
98
+ accountIds.map(async (accountId) => {
99
+ try {
100
+ const cmd = `nordigen accounts balances ${accountId} --json`;
101
+ const result = await exec(cmd);
102
+ const data = JSON.parse(result.stdout);
103
+
104
+ return {
105
+ accountId,
106
+ balances: data.balances,
107
+ success: true
108
+ };
109
+ } catch (error) {
110
+ return {
111
+ accountId,
112
+ error: error.message,
113
+ success: false
114
+ };
115
+ }
116
+ })
117
+ );
118
+
119
+ return balances;
120
+ }
121
+ ```
122
+
123
+ ## Advanced Agent Workflows
124
+
125
+ ### Workflow 1: Automated Financial Health Check
126
+
127
+ ```javascript
128
+ async function financialHealthCheck(userId) {
129
+ // 1. Get all user's requisitions
130
+ const reqList = await exec('nordigen requisitions list --json');
131
+ const requisitions = JSON.parse(reqList.stdout).results;
132
+
133
+ // 2. Extract all account IDs
134
+ const accountIds = requisitions.flatMap(req => req.accounts);
135
+
136
+ // 3. Check balances
137
+ const balances = await checkAllBalances(accountIds);
138
+
139
+ // 4. Get recent transactions (last 30 days)
140
+ const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
141
+ .toISOString().split('T')[0];
142
+ const today = new Date().toISOString().split('T')[0];
143
+
144
+ const transactionAnalysis = await Promise.all(
145
+ accountIds.map(id => analyzeTransactions(id, thirtyDaysAgo, today))
146
+ );
147
+
148
+ // 5. Generate report
149
+ return {
150
+ totalAccounts: accountIds.length,
151
+ totalBalance: balances.reduce((sum, b) => {
152
+ const balance = b.balances?.find(bal => bal.balanceType === 'expected')?.balanceAmount?.amount || 0;
153
+ return sum + parseFloat(balance);
154
+ }, 0),
155
+ monthlySpending: transactionAnalysis.reduce((sum, a) => sum + a.totalSpent, 0),
156
+ monthlyIncome: transactionAnalysis.reduce((sum, a) => sum + a.totalIncome, 0)
157
+ };
158
+ }
159
+ ```
160
+
161
+ ### Workflow 2: Smart Institution Recommendation
162
+
163
+ ```javascript
164
+ async function recommendInstitution(country, requirements) {
165
+ // Get all institutions
166
+ const cmd = `nordigen institutions list --country ${country} --json`;
167
+ const institutions = JSON.parse((await exec(cmd)).stdout);
168
+
169
+ // Score institutions based on requirements
170
+ const scored = institutions.map(inst => {
171
+ let score = 0;
172
+
173
+ // Prefer institutions with longer transaction history
174
+ if (inst.transaction_total_days >= 730) score += 3;
175
+ else if (inst.transaction_total_days >= 365) score += 2;
176
+ else if (inst.transaction_total_days >= 90) score += 1;
177
+
178
+ // Check payment support if required
179
+ if (requirements.payments && inst.payments_enabled) score += 2;
180
+
181
+ // Check account selection if required
182
+ if (requirements.accountSelection && inst.account_selection_supported) score += 1;
183
+
184
+ return { ...inst, score };
185
+ });
186
+
187
+ // Sort by score and return top recommendations
188
+ return scored
189
+ .sort((a, b) => b.score - a.score)
190
+ .slice(0, 5);
191
+ }
192
+ ```
193
+
194
+ ### Workflow 3: Transaction Categorization
195
+
196
+ ```javascript
197
+ async function categorizeTransactions(accountId, dateFrom, dateTo) {
198
+ const cmd = `nordigen accounts transactions ${accountId} --from ${dateFrom} --to ${dateTo} --json`;
199
+ const result = await exec(cmd);
200
+ const data = JSON.parse(result.stdout);
201
+
202
+ const transactions = data.transactions.booked || [];
203
+
204
+ // Simple rule-based categorization
205
+ const categories = {
206
+ groceries: /tesco|sainsbury|asda|morrisons|aldi|lidl/i,
207
+ transport: /uber|lyft|tfl|national rail|trainline/i,
208
+ utilities: /electricity|gas|water|internet|broadband/i,
209
+ entertainment: /netflix|spotify|amazon prime|cinema/i,
210
+ restaurants: /restaurant|cafe|pizza|burger|mcdonald/i
211
+ };
212
+
213
+ const categorized = transactions.map(tx => {
214
+ const description = tx.remittanceInformationUnstructured || '';
215
+ let category = 'other';
216
+
217
+ for (const [cat, pattern] of Object.entries(categories)) {
218
+ if (pattern.test(description)) {
219
+ category = cat;
220
+ break;
221
+ }
222
+ }
223
+
224
+ return {
225
+ ...tx,
226
+ category,
227
+ amount: parseFloat(tx.transactionAmount.amount)
228
+ };
229
+ });
230
+
231
+ // Summarize by category
232
+ const summary = {};
233
+ categorized.forEach(tx => {
234
+ if (!summary[tx.category]) {
235
+ summary[tx.category] = { count: 0, total: 0 };
236
+ }
237
+ summary[tx.category].count++;
238
+ summary[tx.category].total += Math.abs(tx.amount);
239
+ });
240
+
241
+ return { transactions: categorized, summary };
242
+ }
243
+ ```
244
+
245
+ ## Natural Language Interface
246
+
247
+ ### Example: Agent Conversation Flow
248
+
249
+ ```
250
+ User: "Connect my Barclays account"
251
+
252
+ Agent Analysis:
253
+ 1. User wants to connect a bank account
254
+ 2. Bank name: "Barclays"
255
+ 3. Need to determine country (ask or infer from context)
256
+
257
+ Agent Response:
258
+ "I'll help you connect your Barclays account. Which country is your Barclays account in? (UK, Germany, etc.)"
259
+
260
+ User: "UK"
261
+
262
+ Agent Actions:
263
+ 1. nordigen institutions search "Barclays" --country GB --json
264
+ 2. Select appropriate institution ID
265
+ 3. nordigen agreements create --institution-id BARCLAYS_BARCGB22 --max-days 90 --json
266
+ 4. nordigen requisitions create --institution-id BARCLAYS_BARCGB22 --redirect https://app.example.com/callback --agreement <ID> --json
267
+
268
+ Agent Response:
269
+ "I've created a secure connection request. Please visit this link to authorize access to your Barclays account: [AUTH_LINK]"
270
+ ```
271
+
272
+ ### Example: Transaction Query
273
+
274
+ ```
275
+ User: "How much did I spend on groceries last month?"
276
+
277
+ Agent Actions:
278
+ 1. Calculate date range (last month)
279
+ 2. Get user's account IDs from stored requisitions
280
+ 3. For each account:
281
+ - nordigen accounts transactions <ID> --from 2024-01-01 --to 2024-01-31 --json
282
+ 4. Categorize transactions
283
+ 5. Sum amounts in "groceries" category
284
+
285
+ Agent Response:
286
+ "You spent £342.67 on groceries last month across 23 transactions. The main stores were Tesco (£178.42), Sainsbury's (£121.30), and Aldi (£42.95)."
287
+ ```
288
+
289
+ ## Error Handling for Agents
290
+
291
+ ```javascript
292
+ async function safeExecute(command) {
293
+ try {
294
+ const result = await exec(command);
295
+ return { success: true, data: JSON.parse(result.stdout) };
296
+ } catch (error) {
297
+ // Parse error message
298
+ const errorData = {
299
+ success: false,
300
+ command: command,
301
+ exitCode: error.code
302
+ };
303
+
304
+ // Check for common errors
305
+ if (error.stderr.includes('Not authenticated')) {
306
+ errorData.type = 'AUTH_REQUIRED';
307
+ errorData.message = 'User needs to authenticate first';
308
+ errorData.action = 'Please run: nordigen auth login --secret-id <id> --secret-key <key>';
309
+ } else if (error.stderr.includes('Token is invalid or expired')) {
310
+ errorData.type = 'TOKEN_EXPIRED';
311
+ errorData.message = 'Authentication token expired';
312
+ errorData.action = 'Re-authenticating automatically...';
313
+ } else if (error.stderr.includes('Rate limit')) {
314
+ errorData.type = 'RATE_LIMIT';
315
+ errorData.message = 'API rate limit exceeded';
316
+ errorData.action = 'Waiting before retry...';
317
+ } else {
318
+ errorData.type = 'UNKNOWN';
319
+ errorData.message = error.stderr;
320
+ }
321
+
322
+ return errorData;
323
+ }
324
+ }
325
+ ```
326
+
327
+ ## Security Best Practices
328
+
329
+ 1. **Never log sensitive data**: Avoid logging full API responses that may contain IBANs or account details
330
+ 2. **Use environment variables**: Store credentials in env vars, not code
331
+ 3. **Implement access controls**: Ensure agents can only access data for authorized users
332
+ 4. **Audit agent actions**: Log all CLI commands executed by agents
333
+ 5. **Time-limited access**: Regularly refresh agreements with minimal access duration
334
+
335
+ ## Agent State Management
336
+
337
+ ```javascript
338
+ class NordigenAgent {
339
+ constructor(userId) {
340
+ this.userId = userId;
341
+ this.state = {
342
+ authenticated: false,
343
+ activeRequisitions: [],
344
+ accountIds: [],
345
+ lastSync: null
346
+ };
347
+ }
348
+
349
+ async initialize() {
350
+ // Check authentication
351
+ const authStatus = await exec('nordigen auth status --json');
352
+ this.state.authenticated = JSON.parse(authStatus.stdout).authenticated;
353
+
354
+ if (this.state.authenticated) {
355
+ await this.syncRequisitions();
356
+ }
357
+ }
358
+
359
+ async syncRequisitions() {
360
+ const result = await exec('nordigen requisitions list --json');
361
+ const data = JSON.parse(result.stdout);
362
+ this.state.activeRequisitions = data.results;
363
+ this.state.accountIds = data.results.flatMap(r => r.accounts);
364
+ this.state.lastSync = new Date().toISOString();
365
+ }
366
+
367
+ async handleQuery(userInput) {
368
+ // Parse user intent
369
+ const intent = this.parseIntent(userInput);
370
+
371
+ switch (intent.type) {
372
+ case 'GET_BALANCE':
373
+ return await this.getBalances();
374
+ case 'GET_TRANSACTIONS':
375
+ return await this.getTransactions(intent.params);
376
+ case 'CONNECT_ACCOUNT':
377
+ return await this.connectAccount(intent.params);
378
+ default:
379
+ return { error: 'Unknown intent' };
380
+ }
381
+ }
382
+ }
383
+ ```
384
+
385
+ ## Performance Optimization
386
+
387
+ 1. **Cache institution lists**: Store institution data locally, refresh periodically
388
+ 2. **Batch operations**: Use Promise.all() for parallel account queries
389
+ 3. **Pagination**: Use --limit and --offset for large result sets
390
+ 4. **Conditional fetching**: Only fetch data when needed, not proactively
391
+
392
+ ## Testing Agent Integration
393
+
394
+ ```javascript
395
+ // Mock CLI responses for testing
396
+ const mockCLI = {
397
+ 'nordigen auth status': { authenticated: true },
398
+ 'nordigen institutions list --country GB --json': [
399
+ { id: 'BARCLAYS_BARCGB22', name: 'Barclays' }
400
+ ]
401
+ };
402
+
403
+ async function testAgentFlow() {
404
+ // Test institution discovery
405
+ const banks = await findInstitutions('GB', 'Barclays');
406
+ assert(banks.length > 0);
407
+
408
+ // Test error handling
409
+ const result = await safeExecute('nordigen accounts get invalid-id --json');
410
+ assert(result.success === false);
411
+
412
+ // Test transaction analysis
413
+ const analysis = await analyzeTransactions('test-account', '2024-01-01', '2024-01-31');
414
+ assert(analysis.transactionCount >= 0);
415
+ }
416
+ ```
417
+
418
+ ## Example: Claude/GPT Integration
419
+
420
+ ```python
421
+ import subprocess
422
+ import json
423
+
424
+ def run_nordigen_command(command):
425
+ """Execute Nordigen CLI command and return JSON output."""
426
+ result = subprocess.run(
427
+ command,
428
+ shell=True,
429
+ capture_output=True,
430
+ text=True
431
+ )
432
+
433
+ if result.returncode != 0:
434
+ raise Exception(f"Command failed: {result.stderr}")
435
+
436
+ return json.loads(result.stdout)
437
+
438
+ # Agent function
439
+ def get_account_summary(account_id):
440
+ """Get comprehensive account summary."""
441
+
442
+ # Get balances
443
+ balances = run_nordigen_command(
444
+ f"nordigen accounts balances {account_id} --json"
445
+ )
446
+
447
+ # Get transactions (last 30 days)
448
+ from datetime import datetime, timedelta
449
+ end_date = datetime.now().strftime('%Y-%m-%d')
450
+ start_date = (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')
451
+
452
+ transactions = run_nordigen_command(
453
+ f"nordigen accounts transactions {account_id} --from {start_date} --to {end_date} --json"
454
+ )
455
+
456
+ # Format for LLM context
457
+ return {
458
+ "account_id": account_id,
459
+ "balances": balances["balances"],
460
+ "recent_transactions": len(transactions["transactions"]["booked"]),
461
+ "transaction_data": transactions
462
+ }
463
+
464
+ # Use in prompt
465
+ account_summary = get_account_summary("abc-123")
466
+ prompt = f"""
467
+ Analyze this bank account and provide insights:
468
+
469
+ {json.dumps(account_summary, indent=2)}
470
+
471
+ Please provide:
472
+ 1. Current financial position
473
+ 2. Spending patterns
474
+ 3. Recommendations
475
+ """
476
+ ```
477
+
478
+ ## Conclusion
479
+
480
+ The Nordigen CLI provides a powerful, simple interface for AI agents to interact with open banking APIs. By following these patterns and best practices, agents can provide sophisticated financial analysis and account management capabilities.
package/CHANGELOG.md ADDED
@@ -0,0 +1,69 @@
1
+ # Changelog
2
+
3
+ All notable changes to the Nordigen CLI will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2024-02-16
9
+
10
+ ### Added
11
+ - Initial release of Nordigen CLI
12
+ - Complete coverage of Nordigen API v2 endpoints
13
+ - Authentication commands (login, logout, status)
14
+ - Institution commands (list, search, get)
15
+ - Account commands (get, balances, details, transactions)
16
+ - Agreement commands (list, create, get, delete, accept)
17
+ - Requisition commands (list, create, get, delete)
18
+ - Payment commands (list, get, delete, creditors, fields)
19
+ - Configuration management commands
20
+ - JWT token authentication with automatic refresh
21
+ - Secure credential storage using Conf
22
+ - Rich terminal output with colors and formatting
23
+ - JSON output mode for all commands
24
+ - Comprehensive error handling with detailed messages
25
+ - Support for all European banking institutions
26
+ - Premium transaction endpoint support
27
+ - Date range filtering for transactions
28
+ - Pagination support for list commands
29
+ - Environment variable support for credentials
30
+ - Debug mode with detailed stack traces
31
+ - Alias support for common commands (acc, inst, eua, req, pay)
32
+
33
+ ### Documentation
34
+ - Complete README with installation and usage
35
+ - AGENT.md with AI agent integration patterns
36
+ - OPENCLAW.md with OpenClaw integration guide
37
+ - EXAMPLES.md with practical usage examples
38
+ - CONTRIBUTING.md with development guidelines
39
+ - Comprehensive JSDoc comments throughout codebase
40
+
41
+ ### Testing
42
+ - Unit tests for API client
43
+ - Test infrastructure with Node.js test runner
44
+ - ESLint configuration for code quality
45
+
46
+ ### Security
47
+ - Secure config file with 0600 permissions
48
+ - Credential redaction in output
49
+ - Token expiry handling
50
+ - Automatic token refresh
51
+
52
+ ## [Unreleased]
53
+
54
+ ### Planned Features
55
+ - Transaction export to multiple formats (CSV, Excel, QIF)
56
+ - Account balance tracking over time
57
+ - Transaction categorization with custom rules
58
+ - Multi-account aggregation commands
59
+ - Budget tracking and alerts
60
+ - Recurring transaction detection
61
+ - Data visualization commands
62
+ - Shell completion (bash, zsh, fish)
63
+ - Interactive mode
64
+ - Configuration profiles for multiple users
65
+ - Webhook support for real-time updates
66
+ - Integration with accounting software
67
+ - OpenClaw server mode
68
+ - Docker container support
69
+ - Binary releases for all platforms