@dizzlkheinz/ynab-mcpb 0.16.1 → 0.17.1
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 +33 -33
- package/.github/workflows/ci-tests.yml +45 -45
- package/.github/workflows/claude-code-review.yml +57 -57
- package/.github/workflows/claude.yml +50 -50
- package/.github/workflows/full-integration.yml +22 -22
- package/.github/workflows/publish.yml +11 -2
- package/CLAUDE.md +33 -47
- package/README.md +8 -10
- package/dist/bundle/index.cjs +54 -54
- package/dist/server/YNABMCPServer.d.ts +120 -54
- package/dist/server/YNABMCPServer.js +28 -381
- package/dist/server/config.d.ts +2 -0
- package/dist/server/config.js +1 -0
- package/dist/server/securityMiddleware.d.ts +37 -8
- package/dist/tools/accountTools.d.ts +2 -0
- package/dist/tools/accountTools.js +45 -0
- package/dist/tools/adapters.d.ts +12 -0
- package/dist/tools/adapters.js +25 -0
- package/dist/tools/budgetTools.d.ts +2 -0
- package/dist/tools/budgetTools.js +30 -0
- package/dist/tools/categoryTools.d.ts +2 -0
- package/dist/tools/categoryTools.js +45 -0
- package/dist/tools/monthTools.d.ts +2 -0
- package/dist/tools/monthTools.js +32 -0
- package/dist/tools/payeeTools.d.ts +2 -0
- package/dist/tools/payeeTools.js +32 -0
- package/dist/tools/reconciliation/index.d.ts +2 -0
- package/dist/tools/reconciliation/index.js +33 -0
- package/dist/tools/schemas/common.d.ts +3 -0
- package/dist/tools/schemas/common.js +3 -0
- package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/index.d.ts +2 -2
- package/dist/tools/schemas/outputs/index.js +2 -2
- package/dist/tools/schemas/outputs/utilityOutputs.d.ts +0 -15
- package/dist/tools/schemas/outputs/utilityOutputs.js +0 -9
- package/dist/tools/transactionTools.d.ts +2 -0
- package/dist/tools/transactionTools.js +124 -0
- package/dist/tools/utilityTools.d.ts +2 -7
- package/dist/tools/utilityTools.js +19 -38
- package/dist/types/index.d.ts +1 -0
- package/dist/types/toolRegistration.d.ts +27 -0
- package/dist/types/toolRegistration.js +1 -0
- package/docs/maintainers/npm-publishing.md +27 -0
- package/docs/reference/API.md +15 -70
- package/docs/technical/reconciliation-system-architecture.md +2251 -2251
- package/package.json +6 -6
- package/scripts/analyze-bundle.mjs +41 -41
- package/scripts/generate-mcpb.ps1 +95 -95
- package/scripts/run-domain-integration-tests.js +4 -1
- package/scripts/watch-and-restart.ps1 +49 -49
- package/src/__tests__/comprehensive.integration.test.ts +0 -28
- package/src/__tests__/performance.test.ts +4 -12
- package/src/__tests__/setup.ts +45 -14
- package/src/__tests__/workflows.e2e.test.ts +1 -51
- package/src/server/YNABMCPServer.ts +33 -519
- package/src/server/__tests__/YNABMCPServer.test.ts +0 -1
- package/src/server/__tests__/toolRegistration.test.ts +236 -0
- package/src/server/config.ts +1 -0
- package/src/tools/__tests__/adapters.test.ts +113 -0
- package/src/tools/__tests__/transactionTools.integration.test.ts +63 -3
- package/src/tools/__tests__/utilityTools.integration.test.ts +1 -85
- package/src/tools/__tests__/utilityTools.test.ts +1 -123
- package/src/tools/accountTools.ts +53 -0
- package/src/tools/adapters.ts +74 -0
- package/src/tools/budgetTools.ts +37 -0
- package/src/tools/categoryTools.ts +53 -0
- package/src/tools/monthTools.ts +39 -0
- package/src/tools/payeeTools.ts +39 -0
- package/src/tools/reconciliation/index.ts +45 -0
- package/src/tools/schemas/common.ts +18 -0
- package/src/tools/schemas/outputs/index.ts +0 -3
- package/src/tools/schemas/outputs/utilityOutputs.ts +2 -43
- package/src/tools/toolCategories.ts +0 -1
- package/src/tools/transactionTools.ts +140 -0
- package/src/tools/utilityTools.ts +24 -55
- package/src/types/index.ts +3 -0
- package/src/types/toolRegistration.ts +88 -0
- package/vitest.config.ts +2 -1
- package/.chunkhound.json +0 -11
- package/.code/agents/01a13ef4-3f23-4f52-b33b-3585b73cfa60/error.txt +0 -3
- package/.code/agents/084fd32f-e298-4728-9103-a78d7dc39613/error.txt +0 -3
- package/.code/agents/0fed51e1-a943-4b97-a2a8-a6f0f27c844d/status.txt +0 -1
- package/.code/agents/1059b6bd-5ccd-4d83-a12c-7c9d89137399/error.txt +0 -5
- package/.code/agents/110/exec-call_F9BDNG7JfxKkq7Vc8ESAvdft.txt +0 -1569
- package/.code/agents/11ebcef3-b13f-4e44-ad80-d94a866804b7/error.txt +0 -3
- package/.code/agents/1398/exec-call_CjItcWMU1G6JoPshX62QvpaR.txt +0 -2832
- package/.code/agents/1398/exec-call_SUVq2ivmONQ5LMCmd7ngmOqr.txt +0 -2709
- package/.code/agents/1398/exec-call_SdNY4NOffdcC5pRYjVXHjPCK.txt +0 -2832
- package/.code/agents/1398/exec-call_qblJo9et1gsFFB63TtLOiji2.txt +0 -2832
- package/.code/agents/1398/exec-call_zaRrzlGz7GJcNzVfkAmML7Zg.txt +0 -2709
- package/.code/agents/171834fd-5905-42fc-bbcc-2c755145b0fc/status.txt +0 -1
- package/.code/agents/1724/exec-call_HvHQe0w5CCG3T7Q3ULT6MO3g.txt +0 -5217
- package/.code/agents/1724/exec-call_QwUNESVzfxxk78K1frh1Vahb.txt +0 -2594
- package/.code/agents/1724/exec-call_aJ1Xwz71XmIpD4SBxSHERzLe.txt +0 -2594
- package/.code/agents/1d7d7ab7-7473-4b69-8b97-6e914f56056a/result.txt +0 -231
- package/.code/agents/210/exec-call_0tQCsKNJ1WTuIchb8wlcFJpW.txt +0 -2590
- package/.code/agents/210/exec-call_8ZlY9cUc8Ft1twi4ch8UJ6IN.txt +0 -5195
- package/.code/agents/2188/exec-call_5HqayBxIteJtoI8oPTiLWgvJ.txt +0 -286
- package/.code/agents/2188/exec-call_XRbBKBq3adZe6dcppAvQtM7G.txt +0 -218
- package/.code/agents/2188/exec-call_ehA0SjpYtrUi6GJXmibLjp4i.txt +0 -180
- package/.code/agents/21902821-ecaf-4759-bb9d-222b90921af5/error.txt +0 -3
- package/.code/agents/232073be-aa0e-46da-b478-5b64dbf03cf5/status.txt +0 -1
- package/.code/agents/234ff534-2336-4771-a8d9-aa04421a63be/result.txt +0 -747
- package/.code/agents/253e2695-dc36-4022-b436-27655e0fc6c7/status.txt +0 -1
- package/.code/agents/2583/exec-call_M59I4eDjpjlBIWBiSxyS0YlJ.txt +0 -2594
- package/.code/agents/2583/exec-call_usLRGh7OhVHtsRBL4iUwRhjq.txt +0 -2594
- package/.code/agents/292aa3ff-dbab-470f-97c9-e7e8fd65e0db/result.txt +0 -144
- package/.code/agents/3134/exec-call_IgCAMGx19lWfuo8zfYIt5FFC.txt +0 -416
- package/.code/agents/3134/exec-call_IxvLR2Oo7kba2QTsI1gHVko8.txt +0 -2590
- package/.code/agents/3134/exec-call_jYvc8hksZChSiysbzKjl2ZbB.txt +0 -2590
- package/.code/agents/329/exec-call_4QdP3SfSO7HGPCwVcqZIth6s.txt +0 -2590
- package/.code/agents/472/exec-call_4AxzEEcWwkKhpqRB3bE8Ha4L.txt +0 -790
- package/.code/agents/472/exec-call_CB3LPYQA8QIZRi8I6kj4J17A.txt +0 -766
- package/.code/agents/472/exec-call_YeoUWvaFoktay2nqVUsa9KKX.txt +0 -790
- package/.code/agents/472/exec-call_jPWgKVquBBXTg0T3Lks5ZfkK.txt +0 -2594
- package/.code/agents/472/exec-call_qBkvunpGBDEHph2jPmTwtcsb.txt +0 -1000
- package/.code/agents/472/exec-call_v0ffRV1p0kTckBmJPzzHAEy0.txt +0 -3489
- package/.code/agents/472/exec-call_xAX5FXqWIlk02d9WubHbHWh8.txt +0 -766
- package/.code/agents/5346/exec-call_9q0muXUuLaucwEqI51Pt7idT.txt +0 -2594
- package/.code/agents/5346/exec-call_B2el3B79rVkq9LhWTI2VYlz7.txt +0 -2456
- package/.code/agents/5346/exec-call_BfX08f02qkZI9uJD5dvCvuoj.txt +0 -2594
- package/.code/agents/543328d0-61d6-4fd1-a723-bb168656e2e2/error.txt +0 -18
- package/.code/agents/5580c02c-1383-4d18-9cbd-cc8a06e3408d/result.txt +0 -48
- package/.code/agents/60ce1a22-5126-44b2-b977-1d5b56142a7b/status.txt +0 -1
- package/.code/agents/6215d9db-7fa9-4429-aeec-3835c3212291/error.txt +0 -1
- package/.code/agents/6743db55-30e5-4b4e-9366-a8214fc7f714/error.txt +0 -1
- package/.code/agents/6bf9591b-b9c9-422c-b0a5-e968c7d8422a/status.txt +0 -1
- package/.code/agents/7/exec-call_eww3GfdEiJZx61sJEQ9wNmt3.txt +0 -1271
- package/.code/agents/70/exec-call_owUtDMYiVgqDf8vsz1i32PFf.txt +0 -1570
- package/.code/agents/8/exec-call_UtrjAcLbhYLatxR4O97fZgnm.txt +0 -2590
- package/.code/agents/82490bc9-f34e-4b1b-8a8e-bccc2e6254f5/error.txt +0 -3
- package/.code/agents/841/exec-call_7nTNhSBCNjTDUIJv7py6CepO.txt +0 -3299
- package/.code/agents/841/exec-call_TLI0yUdUijuUAvI4o3DXEvHO.txt +0 -3299
- package/.code/agents/9/exec-call_XaABQT1hIlRpnKZ2uyBMWsTC.txt +0 -1882
- package/.code/agents/941/exec-call_GuGHRx7NNXWIDAnxUG2NEWPa.txt +0 -2594
- package/.code/agents/95d9fbab-19a2-48af-83f9-c792566a347f/error.txt +0 -1
- package/.code/agents/b0098cb8-cb32-4ada-9bc4-37c587518896/result.txt +0 -170
- package/.code/agents/b4fe59a4-81df-42e2-a112-0153e504faca/error.txt +0 -1
- package/.code/agents/bf4ce152-f623-49d7-aa52-c18631625c3c/error.txt +0 -3
- package/.code/agents/d7d1db75-d7eb-468e-adea-4ef4d916d187/status.txt +0 -1
- package/.code/agents/e2baa9c8-bac3-49e3-a39d-024333e6a990/status.txt +0 -1
- package/.code/agents/e350b8c3-8483-408c-b2bb-94515f492a11/error.txt +0 -3
- package/.code/agents/e63f9919-719f-4ad0-bccf-01b1a596e1e9/status.txt +0 -1
- package/.code/agents/e71695a8-3044-478d-8f12-ed13d02884c7/status.txt +0 -1
- package/.code/agents/f95b7464-3e25-4897-b153-c8dfd63fd605/error.txt +0 -5
- package/.code/agents/fa3c5ddf-cdf7-47a2-930a-b806c6363689/status.txt +0 -1
- package/.github/workflows/pr-description-check.yml +0 -88
- package/AGENTS.md +0 -36
- package/NUL +0 -1
- package/docs/README.md +0 -72
- package/docs/getting-started/CONFIGURATION.md +0 -175
- package/docs/getting-started/INSTALLATION.md +0 -333
- package/docs/getting-started/QUICKSTART.md +0 -282
- package/docs/guides/ARCHITECTURE.md +0 -533
- package/docs/guides/DEPLOYMENT.md +0 -189
- package/docs/guides/INTEGRATION_TESTING.md +0 -730
- package/docs/guides/TESTING.md +0 -591
- package/docs/reconciliation-flow.md +0 -83
- package/docs/reference/EXAMPLES.md +0 -946
- package/docs/reference/TOOLS.md +0 -348
- package/docs/reference/TROUBLESHOOTING.md +0 -481
- package/package.json.tmp +0 -105
- package/temp-recon.ts +0 -126
- package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_09-04-53.json +0 -23
- package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_10-37-42.json +0 -23
- package/test-exports/ynab_account_e9ddc2a6_minimal_4items_2025-11-19_09-02-09.json +0 -44
- package/test-exports/ynab_account_e9ddc2a6_minimal_6items_2025-11-19_10-37-52.json +0 -58
- package/test-exports/ynab_since_2025-10-16_account_53298e13_238items_2025-11-28_13-46-20.json +0 -3662
- package/test-exports/ynab_since_2025-11-01_account_4c18e9f0_minimal_14items_2025-11-16_10-07-10.json +0 -115
|
@@ -1,533 +0,0 @@
|
|
|
1
|
-
# YNAB MCP Server Architecture
|
|
2
|
-
|
|
3
|
-
This guide explains the modular architecture, core components, and architectural patterns.
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [Modular Architecture](#modular-architecture)
|
|
8
|
-
- [Core Components](#core-components)
|
|
9
|
-
- [Dependency Injection Pattern](#dependency-injection-pattern)
|
|
10
|
-
- [Developing Tools](#developing-tools)
|
|
11
|
-
- [Cache Management](#cache-management)
|
|
12
|
-
- [Service Module Patterns](#service-module-patterns)
|
|
13
|
-
|
|
14
|
-
## Modular Architecture
|
|
15
|
-
|
|
16
|
-
The server uses a modular architecture that improves maintainability, testability, and performance.
|
|
17
|
-
|
|
18
|
-
### Architecture Overview
|
|
19
|
-
|
|
20
|
-
The architecture consists of several key components working together:
|
|
21
|
-
|
|
22
|
-
```
|
|
23
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
24
|
-
│ YNABMCPServer │
|
|
25
|
-
│ (Main Orchestrator) │
|
|
26
|
-
├─────────────────────────────────────────────────────────────┤
|
|
27
|
-
│ ┌──────────────┐ ┌───────────────┐ ┌─────────────────┐ │
|
|
28
|
-
│ │ Tool Registry│ │ Cache Manager │ │ Budget Resolver │ │
|
|
29
|
-
│ │ │ │ │ │ │ │
|
|
30
|
-
│ └──────────────┘ └───────────────┘ └─────────────────┘ │
|
|
31
|
-
│ ┌──────────────┐ ┌───────────────┐ ┌─────────────────┐ │
|
|
32
|
-
│ │Config Module │ │Resource Mgr │ │ Prompt Manager │ │
|
|
33
|
-
│ │ │ │ │ │ │ │
|
|
34
|
-
│ └──────────────┘ └───────────────┘ └─────────────────┘ │
|
|
35
|
-
│ ┌──────────────┐ ┌───────────────┐ ┌─────────────────┐ │
|
|
36
|
-
│ │Error Handler │ │Security Mdlwr │ │Diagnostic Mgr │ │
|
|
37
|
-
│ │ │ │ │ │ │ │
|
|
38
|
-
│ └──────────────┘ └───────────────┘ └─────────────────┘ │
|
|
39
|
-
└─────────────────────────────────────────────────────────────┘
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Core Components
|
|
43
|
-
|
|
44
|
-
### Tool Registry
|
|
45
|
-
Centralized management of all MCP tools with consistent validation, security, and error handling.
|
|
46
|
-
|
|
47
|
-
**Key Benefits:**
|
|
48
|
-
- Uniform tool registration and validation
|
|
49
|
-
- Consistent error messages across all tools
|
|
50
|
-
- Centralized security checks
|
|
51
|
-
- Automatic JSON schema generation
|
|
52
|
-
|
|
53
|
-
**Example Tool Definition:**
|
|
54
|
-
```typescript
|
|
55
|
-
registry.register({
|
|
56
|
-
name: 'my_custom_tool',
|
|
57
|
-
description: 'A custom tool for specific operations',
|
|
58
|
-
inputSchema: MyToolSchema,
|
|
59
|
-
handler: adapt(handleMyTool),
|
|
60
|
-
defaultArgumentResolver: resolveBudgetId(),
|
|
61
|
-
security: { requiresValidation: true }
|
|
62
|
-
});
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Enhanced Cache Manager
|
|
66
|
-
Advanced caching system with observability, LRU eviction, and performance optimization.
|
|
67
|
-
|
|
68
|
-
**Key Features:**
|
|
69
|
-
- Hit/miss tracking with detailed metrics
|
|
70
|
-
- LRU eviction with configurable limits
|
|
71
|
-
- Stale-while-revalidate for improved performance
|
|
72
|
-
- Concurrent fetch deduplication
|
|
73
|
-
- Cache warming for faster initial loads
|
|
74
|
-
|
|
75
|
-
### Budget Resolver
|
|
76
|
-
Standardized budget ID resolution with consistent error handling across all tools.
|
|
77
|
-
|
|
78
|
-
**Benefits:**
|
|
79
|
-
- Uniform budget validation
|
|
80
|
-
- Clear, actionable error messages
|
|
81
|
-
- Automatic default budget injection
|
|
82
|
-
- Consistent user experience
|
|
83
|
-
|
|
84
|
-
### Service Modules
|
|
85
|
-
Focused modules handling specific server concerns:
|
|
86
|
-
|
|
87
|
-
- **Config Module**: Environment validation and configuration management
|
|
88
|
-
- **Resource Manager**: MCP resource definitions and handlers
|
|
89
|
-
- **Prompt Manager**: MCP prompt definitions and handlers
|
|
90
|
-
- **Diagnostic Manager**: System diagnostics and health monitoring
|
|
91
|
-
|
|
92
|
-
## Dependency Injection Pattern
|
|
93
|
-
|
|
94
|
-
The architecture uses explicit dependency injection for better testability and maintainability:
|
|
95
|
-
|
|
96
|
-
```typescript
|
|
97
|
-
// Explicit dependency injection pattern
|
|
98
|
-
class MyService {
|
|
99
|
-
constructor(
|
|
100
|
-
private cacheManager: CacheManager,
|
|
101
|
-
private errorHandler: ErrorHandler,
|
|
102
|
-
private budgetResolver: BudgetResolver
|
|
103
|
-
) {}
|
|
104
|
-
|
|
105
|
-
async performOperation(budgetId: string) {
|
|
106
|
-
const resolved = this.budgetResolver.resolveBudgetId(budgetId, defaultBudgetId);
|
|
107
|
-
if (typeof resolved !== 'string') {
|
|
108
|
-
return resolved; // Error response
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return this.cacheManager.wrap(`operation_${resolved}`, {
|
|
112
|
-
ttl: CACHE_TTLS.MEDIUM,
|
|
113
|
-
loader: () => this.executeOperation(resolved)
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Service instantiation with dependencies
|
|
119
|
-
const myService = new MyService(cacheManager, errorHandler, budgetResolver);
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Developing Tools
|
|
123
|
-
|
|
124
|
-
### Tool Development Patterns
|
|
125
|
-
|
|
126
|
-
Creating new tools follows the Tool Registry pattern for consistency and maintainability.
|
|
127
|
-
|
|
128
|
-
#### 1. Define Tool Schema
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
import { z } from 'zod';
|
|
132
|
-
|
|
133
|
-
export const MyToolSchema = z.object({
|
|
134
|
-
budget_id: z.string().optional(),
|
|
135
|
-
custom_parameter: z.string(),
|
|
136
|
-
optional_parameter: z.number().optional().default(100)
|
|
137
|
-
}).describe('Schema for my custom tool');
|
|
138
|
-
|
|
139
|
-
export type MyToolRequest = z.infer<typeof MyToolSchema>;
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
#### 2. Implement Tool Handler
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
import { adapt } from '../server/toolRegistry.js';
|
|
146
|
-
import { BudgetResolver } from '../server/budgetResolver.js';
|
|
147
|
-
import { cacheManager, CACHE_TTLS } from '../server/cacheManager.js';
|
|
148
|
-
|
|
149
|
-
export async function handleMyTool(
|
|
150
|
-
params: MyToolRequest
|
|
151
|
-
): Promise<any> {
|
|
152
|
-
// Budget resolution is handled automatically by defaultArgumentResolver
|
|
153
|
-
const { budget_id, custom_parameter, optional_parameter } = params;
|
|
154
|
-
|
|
155
|
-
// Use enhanced caching
|
|
156
|
-
return cacheManager.wrap(`my_tool_${budget_id}_${custom_parameter}`, {
|
|
157
|
-
ttl: CACHE_TTLS.SHORT,
|
|
158
|
-
staleWhileRevalidate: 60000,
|
|
159
|
-
loader: async () => {
|
|
160
|
-
// Implement your tool logic here
|
|
161
|
-
const result = await performMyOperation(budget_id, custom_parameter);
|
|
162
|
-
return {
|
|
163
|
-
success: true,
|
|
164
|
-
data: {
|
|
165
|
-
custom_result: result,
|
|
166
|
-
parameter_used: custom_parameter,
|
|
167
|
-
optional_value: optional_parameter
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
#### 3. Register Tool with Registry
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
// In YNABMCPServer.ts or a tool registration module
|
|
179
|
-
import { resolveBudgetId } from './budgetResolver.js';
|
|
180
|
-
|
|
181
|
-
registry.register({
|
|
182
|
-
name: 'my_custom_tool',
|
|
183
|
-
description: 'Performs custom operation with enhanced caching and error handling',
|
|
184
|
-
inputSchema: MyToolSchema,
|
|
185
|
-
handler: adapt(handleMyTool),
|
|
186
|
-
defaultArgumentResolver: resolveBudgetId(),
|
|
187
|
-
cacheConfig: {
|
|
188
|
-
enabled: true,
|
|
189
|
-
ttl: CACHE_TTLS.SHORT
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Using Default Argument Resolution
|
|
195
|
-
|
|
196
|
-
The Tool Registry provides automatic budget ID resolution:
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
// Budget ID is automatically resolved when not provided
|
|
200
|
-
export const resolveBudgetId = (): DefaultArgumentResolver =>
|
|
201
|
-
async (args, context) => {
|
|
202
|
-
if (!args.budget_id) {
|
|
203
|
-
const defaultBudget = context.getDefaultBudget();
|
|
204
|
-
if (!defaultBudget) {
|
|
205
|
-
return {
|
|
206
|
-
isError: true,
|
|
207
|
-
content: [{
|
|
208
|
-
type: 'text',
|
|
209
|
-
text: JSON.stringify({
|
|
210
|
-
success: false,
|
|
211
|
-
error: {
|
|
212
|
-
code: 'VALIDATION_ERROR',
|
|
213
|
-
message: 'No default budget set. Use set_default_budget first or provide budget_id parameter.'
|
|
214
|
-
}
|
|
215
|
-
})
|
|
216
|
-
}]
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
args.budget_id = defaultBudget;
|
|
220
|
-
}
|
|
221
|
-
return null; // No error, continue with resolved args
|
|
222
|
-
};
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Error Handling Best Practices
|
|
226
|
-
|
|
227
|
-
Use the centralized error handling system for consistent responses:
|
|
228
|
-
|
|
229
|
-
```typescript
|
|
230
|
-
import { ErrorHandler } from '../server/errorHandler.js';
|
|
231
|
-
|
|
232
|
-
export async function handleMyTool(params: MyToolRequest): Promise<any> {
|
|
233
|
-
try {
|
|
234
|
-
// Tool implementation
|
|
235
|
-
const result = await performOperation(params);
|
|
236
|
-
return result;
|
|
237
|
-
} catch (error) {
|
|
238
|
-
// Use centralized error handling
|
|
239
|
-
return ErrorHandler.createErrorResponse(
|
|
240
|
-
'OPERATION_FAILED',
|
|
241
|
-
`Custom tool operation failed: ${error.message}`,
|
|
242
|
-
{ operation: 'my_custom_tool', params }
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
## Cache Management
|
|
249
|
-
|
|
250
|
-
### Understanding the Enhanced Cache System
|
|
251
|
-
|
|
252
|
-
The server includes a sophisticated caching system designed for performance and observability.
|
|
253
|
-
|
|
254
|
-
#### Cache Configuration
|
|
255
|
-
|
|
256
|
-
```typescript
|
|
257
|
-
// Environment variables for cache tuning
|
|
258
|
-
YNAB_MCP_CACHE_MAX_ENTRIES=1000 // Maximum cache entries
|
|
259
|
-
YNAB_MCP_CACHE_DEFAULT_TTL_MS=1800000 // Default TTL (30 minutes)
|
|
260
|
-
YNAB_MCP_CACHE_STALE_MS=120000 // Stale-while-revalidate window
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
#### Using Cache.wrap() Method
|
|
264
|
-
|
|
265
|
-
The primary interface for caching is the `wrap()` method:
|
|
266
|
-
|
|
267
|
-
```typescript
|
|
268
|
-
import { cacheManager, CACHE_TTLS } from '../server/cacheManager.js';
|
|
269
|
-
|
|
270
|
-
// Basic usage
|
|
271
|
-
const result = await cacheManager.wrap('my_cache_key', {
|
|
272
|
-
ttl: CACHE_TTLS.ACCOUNTS,
|
|
273
|
-
loader: async () => {
|
|
274
|
-
// Expensive operation (API call, computation, etc.)
|
|
275
|
-
return await ynabAPI.accounts.getAccounts(budgetId);
|
|
276
|
-
}
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
// Advanced usage with stale-while-revalidate
|
|
280
|
-
const result = await cacheManager.wrap('complex_operation', {
|
|
281
|
-
ttl: CACHE_TTLS.LONG,
|
|
282
|
-
staleWhileRevalidate: 300000, // 5 minutes
|
|
283
|
-
loader: async () => {
|
|
284
|
-
return await performComplexAnalysis(budgetId);
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
#### Cache Strategy Guidelines
|
|
290
|
-
|
|
291
|
-
**Long TTL (1 hour+):** Budget data, categories, accounts
|
|
292
|
-
```typescript
|
|
293
|
-
// Budget data changes infrequently
|
|
294
|
-
const budgets = await cacheManager.wrap(`budgets_${userId}`, {
|
|
295
|
-
ttl: CACHE_TTLS.BUDGETS, // 1 hour
|
|
296
|
-
loader: () => ynabAPI.budgets.getBudgets()
|
|
297
|
-
});
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
**Medium TTL (30 minutes):** Account balances, category balances
|
|
301
|
-
```typescript
|
|
302
|
-
// Account data changes moderately
|
|
303
|
-
const accounts = await cacheManager.wrap(`accounts_${budgetId}`, {
|
|
304
|
-
ttl: CACHE_TTLS.ACCOUNTS, // 30 minutes
|
|
305
|
-
staleWhileRevalidate: 120000, // 2 minutes
|
|
306
|
-
loader: () => ynabAPI.accounts.getAccounts(budgetId)
|
|
307
|
-
});
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
**Short TTL (5-15 minutes):** Recent transactions, monthly data
|
|
311
|
-
```typescript
|
|
312
|
-
// Recent transactions change frequently
|
|
313
|
-
const recentTransactions = await cacheManager.wrap(`recent_txns_${budgetId}`, {
|
|
314
|
-
ttl: CACHE_TTLS.SHORT, // 5 minutes
|
|
315
|
-
loader: () => ynabAPI.transactions.getTransactions(budgetId, { since_date })
|
|
316
|
-
});
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
**No Caching:** User-specific filtered transactions, write operations
|
|
320
|
-
```typescript
|
|
321
|
-
// Don't cache filtered or user-specific data
|
|
322
|
-
const filteredTransactions = await ynabAPI.transactions.getTransactions(budgetId, {
|
|
323
|
-
account_id: accountId,
|
|
324
|
-
category_id: categoryId,
|
|
325
|
-
since_date: userSpecificDate
|
|
326
|
-
});
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
#### Cache Invalidation Patterns
|
|
330
|
-
|
|
331
|
-
```typescript
|
|
332
|
-
// Invalidate related caches after write operations
|
|
333
|
-
export async function handleCreateAccount(params: CreateAccountRequest) {
|
|
334
|
-
const result = await ynabAPI.accounts.createAccount(params);
|
|
335
|
-
|
|
336
|
-
// Invalidate related caches
|
|
337
|
-
cacheManager.delete(`accounts_${params.budget_id}`);
|
|
338
|
-
cacheManager.delete(`budget_${params.budget_id}`);
|
|
339
|
-
|
|
340
|
-
return result;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Pattern-based invalidation
|
|
344
|
-
export function invalidateAccountCaches(budgetId: string) {
|
|
345
|
-
const keysToInvalidate = [
|
|
346
|
-
`accounts_${budgetId}`,
|
|
347
|
-
`budget_${budgetId}`
|
|
348
|
-
];
|
|
349
|
-
|
|
350
|
-
keysToInvalidate.forEach(key => cacheManager.delete(key));
|
|
351
|
-
}
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
#### Cache Observability
|
|
355
|
-
|
|
356
|
-
Monitor cache performance with built-in metrics:
|
|
357
|
-
|
|
358
|
-
```typescript
|
|
359
|
-
// Get cache statistics
|
|
360
|
-
const stats = cacheManager.getStats();
|
|
361
|
-
console.log('Cache Performance:', {
|
|
362
|
-
hitRate: stats.hit_rate,
|
|
363
|
-
totalHits: stats.total_hits,
|
|
364
|
-
totalMisses: stats.total_misses,
|
|
365
|
-
totalEntries: stats.total_entries,
|
|
366
|
-
evictions: stats.evictions
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
// Example output:
|
|
370
|
-
// Cache Performance: {
|
|
371
|
-
// hitRate: 0.75, // 75% hit rate
|
|
372
|
-
// totalHits: 150,
|
|
373
|
-
// totalMisses: 50,
|
|
374
|
-
// totalEntries: 45,
|
|
375
|
-
// evictions: 5
|
|
376
|
-
// }
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
### Cache Warming Strategies
|
|
380
|
-
|
|
381
|
-
Implement proactive cache warming for better user experience:
|
|
382
|
-
|
|
383
|
-
```typescript
|
|
384
|
-
// Cache warming after budget selection
|
|
385
|
-
export async function warmBudgetCache(budgetId: string) {
|
|
386
|
-
// Fire and forget - don't block user operations
|
|
387
|
-
const warmingPromises = [
|
|
388
|
-
cacheManager.wrap(`accounts_${budgetId}`, {
|
|
389
|
-
ttl: CACHE_TTLS.ACCOUNTS,
|
|
390
|
-
loader: () => ynabAPI.accounts.getAccounts(budgetId)
|
|
391
|
-
}),
|
|
392
|
-
cacheManager.wrap(`categories_${budgetId}`, {
|
|
393
|
-
ttl: CACHE_TTLS.CATEGORIES,
|
|
394
|
-
loader: () => ynabAPI.categories.getCategories(budgetId)
|
|
395
|
-
}),
|
|
396
|
-
cacheManager.wrap(`payees_${budgetId}`, {
|
|
397
|
-
ttl: CACHE_TTLS.PAYEES,
|
|
398
|
-
loader: () => ynabAPI.payees.getPayees(budgetId)
|
|
399
|
-
})
|
|
400
|
-
];
|
|
401
|
-
|
|
402
|
-
// Don't await - let these run in background
|
|
403
|
-
Promise.all(warmingPromises).catch(error => {
|
|
404
|
-
console.warn('Cache warming failed:', error.message);
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Trigger cache warming
|
|
409
|
-
export async function handleSetDefaultBudget(params: SetDefaultBudgetRequest) {
|
|
410
|
-
const result = await setDefaultBudget(params.budget_id);
|
|
411
|
-
|
|
412
|
-
// Warm cache for better subsequent performance
|
|
413
|
-
if (result.success) {
|
|
414
|
-
warmBudgetCache(params.budget_id);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
return result;
|
|
418
|
-
}
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
## Service Module Patterns
|
|
422
|
-
|
|
423
|
-
### Working with Service Modules
|
|
424
|
-
|
|
425
|
-
The server decomposes functionality into focused service modules.
|
|
426
|
-
|
|
427
|
-
#### Resource Manager
|
|
428
|
-
|
|
429
|
-
Handle MCP resources consistently:
|
|
430
|
-
|
|
431
|
-
```typescript
|
|
432
|
-
// Custom resource definition
|
|
433
|
-
class MyResourceManager extends ResourceManager {
|
|
434
|
-
getResources() {
|
|
435
|
-
return [
|
|
436
|
-
...super.getResources(),
|
|
437
|
-
{
|
|
438
|
-
uri: 'my-app://custom-resource',
|
|
439
|
-
name: 'Custom Resource',
|
|
440
|
-
description: 'Application-specific resource',
|
|
441
|
-
mimeType: 'application/json'
|
|
442
|
-
}
|
|
443
|
-
];
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
async readResource(uri: string) {
|
|
447
|
-
if (uri === 'my-app://custom-resource') {
|
|
448
|
-
return {
|
|
449
|
-
contents: [{
|
|
450
|
-
type: 'text',
|
|
451
|
-
text: JSON.stringify({
|
|
452
|
-
custom_data: 'value',
|
|
453
|
-
timestamp: new Date().toISOString()
|
|
454
|
-
})
|
|
455
|
-
}]
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
return super.readResource(uri);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
#### Prompt Manager
|
|
464
|
-
|
|
465
|
-
Create dynamic prompts with context:
|
|
466
|
-
|
|
467
|
-
```typescript
|
|
468
|
-
// Custom prompt with dynamic context
|
|
469
|
-
class MyPromptManager extends PromptManager {
|
|
470
|
-
getPrompts() {
|
|
471
|
-
return [
|
|
472
|
-
...super.getPrompts(),
|
|
473
|
-
{
|
|
474
|
-
name: 'analyze_spending',
|
|
475
|
-
description: 'Analyze spending patterns with budget context'
|
|
476
|
-
}
|
|
477
|
-
];
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
async getPrompt(name: string, args: any) {
|
|
481
|
-
if (name === 'analyze_spending') {
|
|
482
|
-
const budgetContext = await this.getBudgetContext(args.budget_id);
|
|
483
|
-
return {
|
|
484
|
-
messages: [{
|
|
485
|
-
role: 'user',
|
|
486
|
-
content: {
|
|
487
|
-
type: 'text',
|
|
488
|
-
text: `Analyze spending patterns for budget: ${budgetContext.name}.
|
|
489
|
-
Current month: ${budgetContext.current_month}.
|
|
490
|
-
Focus on categories with significant changes.`
|
|
491
|
-
}
|
|
492
|
-
}]
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
return super.getPrompt(name, args);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
#### Diagnostic Manager
|
|
501
|
-
|
|
502
|
-
Extend diagnostics for custom monitoring:
|
|
503
|
-
|
|
504
|
-
```typescript
|
|
505
|
-
class MyDiagnosticManager extends DiagnosticManager {
|
|
506
|
-
async getSystemDiagnostics() {
|
|
507
|
-
const baseDiagnostics = await super.getSystemDiagnostics();
|
|
508
|
-
|
|
509
|
-
return {
|
|
510
|
-
...baseDiagnostics,
|
|
511
|
-
custom_metrics: {
|
|
512
|
-
active_integrations: this.getActiveIntegrations(),
|
|
513
|
-
last_sync_time: this.getLastSyncTime(),
|
|
514
|
-
error_rate: this.calculateErrorRate()
|
|
515
|
-
}
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
private getActiveIntegrations() {
|
|
520
|
-
// Custom integration monitoring
|
|
521
|
-
return {
|
|
522
|
-
external_apis: ['ynab', 'my_custom_api'],
|
|
523
|
-
webhooks: this.activeWebhooks.length,
|
|
524
|
-
background_jobs: this.backgroundJobs.size
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
---
|
|
531
|
-
|
|
532
|
-
For practical development patterns and examples, see [`DEVELOPMENT.md`](DEVELOPMENT.md).
|
|
533
|
-
For troubleshooting guidance, see [`../reference/TROUBLESHOOTING.md`](../reference/TROUBLESHOOTING.md).
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
# YNAB MCP Server Deployment Guide
|
|
2
|
-
|
|
3
|
-
This guide provides instructions for deploying the YNAB MCP Server with security best practices.
|
|
4
|
-
|
|
5
|
-
## Prerequisites
|
|
6
|
-
|
|
7
|
-
### System Requirements
|
|
8
|
-
|
|
9
|
-
- **Node.js**: Version 18.0.0 or higher
|
|
10
|
-
- **npm**: Version 8.0.0 or higher
|
|
11
|
-
- **Operating System**: Linux, macOS, or Windows
|
|
12
|
-
- **Memory**: Minimum 512MB RAM
|
|
13
|
-
- **Storage**: Minimum 100MB free space
|
|
14
|
-
|
|
15
|
-
### YNAB Requirements
|
|
16
|
-
|
|
17
|
-
- Active YNAB subscription
|
|
18
|
-
- YNAB Personal Access Token
|
|
19
|
-
|
|
20
|
-
## Environment Setup
|
|
21
|
-
|
|
22
|
-
### 1. YNAB Personal Access Token
|
|
23
|
-
|
|
24
|
-
1. Log in to your YNAB account at [app.youneedabudget.com](https://app.youneedabudget.com)
|
|
25
|
-
2. Go to Account Settings → Developer Settings
|
|
26
|
-
3. Click "New Token"
|
|
27
|
-
4. Enter a descriptive name (e.g., "MCP Server")
|
|
28
|
-
5. Copy the generated token immediately (it won't be shown again)
|
|
29
|
-
|
|
30
|
-
### 2. Environment Variables
|
|
31
|
-
|
|
32
|
-
Create a `.env` file in your project root:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
# Required
|
|
36
|
-
YNAB_ACCESS_TOKEN=your_personal_access_token_here
|
|
37
|
-
|
|
38
|
-
# Optional
|
|
39
|
-
NODE_ENV=production
|
|
40
|
-
LOG_LEVEL=info
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Build Process
|
|
44
|
-
|
|
45
|
-
### Production Build
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
# Install dependencies
|
|
49
|
-
npm install
|
|
50
|
-
|
|
51
|
-
# Validate environment
|
|
52
|
-
npm run validate-env
|
|
53
|
-
|
|
54
|
-
# Run tests
|
|
55
|
-
npm run test:all
|
|
56
|
-
|
|
57
|
-
# Build for production
|
|
58
|
-
npm run build:prod
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Deployment Options
|
|
62
|
-
|
|
63
|
-
### Option 1: Local Development
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
# Start the server
|
|
67
|
-
npm start
|
|
68
|
-
|
|
69
|
-
# Or with environment variables inline
|
|
70
|
-
YNAB_ACCESS_TOKEN=your_token npm start
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Option 2: Docker Deployment
|
|
74
|
-
|
|
75
|
-
1. Create `Dockerfile`:
|
|
76
|
-
```dockerfile
|
|
77
|
-
FROM node:18-alpine
|
|
78
|
-
|
|
79
|
-
WORKDIR /usr/src/app
|
|
80
|
-
|
|
81
|
-
COPY package*.json ./
|
|
82
|
-
RUN npm ci --only=production
|
|
83
|
-
|
|
84
|
-
COPY dist/ ./dist/
|
|
85
|
-
|
|
86
|
-
RUN addgroup -g 1001 -S nodejs
|
|
87
|
-
RUN adduser -S nodejs -u 1001
|
|
88
|
-
RUN chown -R nodejs:nodejs /usr/src/app
|
|
89
|
-
USER nodejs
|
|
90
|
-
|
|
91
|
-
CMD ["node", "dist/index.js"]
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
2. Build and run:
|
|
95
|
-
```bash
|
|
96
|
-
docker build -t ynab-mcp-server .
|
|
97
|
-
docker run -d \
|
|
98
|
-
--name ynab-mcp-server \
|
|
99
|
-
-e YNAB_ACCESS_TOKEN=your_token \
|
|
100
|
-
-e NODE_ENV=production \
|
|
101
|
-
--restart unless-stopped \
|
|
102
|
-
ynab-mcp-server
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Option 3: Claude Desktop Integration
|
|
106
|
-
|
|
107
|
-
1. Build the .mcpb package:
|
|
108
|
-
```bash
|
|
109
|
-
npm run package:mcpb
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
2. Extract and configure:
|
|
113
|
-
```bash
|
|
114
|
-
# Extract the .mcpb file
|
|
115
|
-
Expand-Archive ynab-mcp-server-1.0.0.mcpb
|
|
116
|
-
|
|
117
|
-
# Add to Claude Desktop MCP configuration
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Security Best Practices
|
|
121
|
-
|
|
122
|
-
### 1. Token Security
|
|
123
|
-
|
|
124
|
-
- **Never commit tokens to version control**
|
|
125
|
-
- Store tokens in environment variables only
|
|
126
|
-
- Use different tokens for different environments
|
|
127
|
-
- Rotate tokens regularly (every 90 days recommended)
|
|
128
|
-
|
|
129
|
-
### 2. File Permissions
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
# Set restrictive permissions on sensitive files
|
|
133
|
-
chmod 600 .env
|
|
134
|
-
chmod 700 scripts/
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### 3. Process Security
|
|
138
|
-
|
|
139
|
-
- Run the server as a non-root user
|
|
140
|
-
- Use process managers with automatic restart capabilities
|
|
141
|
-
- Implement proper logging without exposing sensitive data
|
|
142
|
-
|
|
143
|
-
## Monitoring and Maintenance
|
|
144
|
-
|
|
145
|
-
### Health Checks
|
|
146
|
-
|
|
147
|
-
Monitor server health and performance:
|
|
148
|
-
- Monitor server response times
|
|
149
|
-
- Track API rate limiting and usage patterns
|
|
150
|
-
- Monitor for unusual access patterns
|
|
151
|
-
|
|
152
|
-
### Updates and Maintenance
|
|
153
|
-
|
|
154
|
-
1. **Regular Updates**:
|
|
155
|
-
```bash
|
|
156
|
-
# Update dependencies
|
|
157
|
-
npm audit
|
|
158
|
-
npm update
|
|
159
|
-
|
|
160
|
-
# Rebuild and test
|
|
161
|
-
npm run build:prod
|
|
162
|
-
npm run test:all
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
2. **Token Rotation**:
|
|
166
|
-
- Generate new token in YNAB
|
|
167
|
-
- Update environment variables
|
|
168
|
-
- Restart the server
|
|
169
|
-
- Revoke old token
|
|
170
|
-
|
|
171
|
-
## Troubleshooting
|
|
172
|
-
|
|
173
|
-
### Common Issues
|
|
174
|
-
|
|
175
|
-
#### Authentication Errors
|
|
176
|
-
**Symptoms**: 401 Unauthorized errors
|
|
177
|
-
**Solutions**:
|
|
178
|
-
- Verify `YNAB_ACCESS_TOKEN` is set correctly
|
|
179
|
-
- Check token hasn't expired in YNAB settings
|
|
180
|
-
- Ensure token has necessary permissions
|
|
181
|
-
|
|
182
|
-
#### Rate Limiting
|
|
183
|
-
**Symptoms**: 429 Too Many Requests errors
|
|
184
|
-
**Solutions**:
|
|
185
|
-
- Implement request throttling
|
|
186
|
-
- Add retry logic with exponential backoff
|
|
187
|
-
- Monitor API usage patterns
|
|
188
|
-
|
|
189
|
-
For more deployment information, see the [Environment Guide](ENVIRONMENT.md).
|