@dizzlkheinz/ynab-mcpb 0.17.0 → 0.18.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 +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 +12 -3
- package/.github/workflows/release.yml +2 -2
- package/CHANGELOG.md +10 -1
- package/CLAUDE.md +16 -12
- package/README.md +6 -1
- package/dist/bundle/index.cjs +49 -49
- package/dist/server/YNABMCPServer.d.ts +125 -54
- package/dist/server/YNABMCPServer.js +42 -11
- package/dist/server/cacheManager.js +6 -5
- package/dist/server/completions.d.ts +25 -0
- package/dist/server/completions.js +160 -0
- package/dist/server/config.d.ts +2 -2
- package/dist/server/errorHandler.js +1 -0
- package/dist/server/rateLimiter.js +3 -1
- package/dist/server/resources.d.ts +1 -0
- package/dist/server/resources.js +33 -16
- package/dist/server/securityMiddleware.d.ts +38 -8
- package/dist/server/securityMiddleware.js +1 -0
- package/dist/server/toolRegistry.d.ts +9 -0
- package/dist/server/toolRegistry.js +11 -0
- package/dist/tools/adapters.d.ts +3 -1
- package/dist/tools/adapters.js +1 -0
- package/dist/tools/reconciliation/executor.d.ts +2 -0
- package/dist/tools/reconciliation/executor.js +26 -1
- package/dist/tools/reconciliation/index.d.ts +3 -2
- package/dist/tools/reconciliation/index.js +4 -3
- 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/utilityTools.d.ts +0 -7
- package/dist/tools/utilityTools.js +1 -50
- package/docs/maintainers/npm-publishing.md +27 -0
- package/docs/reference/API.md +83 -97
- 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/watch-and-restart.ps1 +49 -49
- package/src/__tests__/comprehensive.integration.test.ts +4 -32
- package/src/__tests__/performance.test.ts +5 -14
- package/src/__tests__/setup.ts +45 -14
- package/src/__tests__/smoke.e2e.test.ts +70 -0
- package/src/__tests__/testUtils.ts +2 -113
- package/src/server/YNABMCPServer.ts +64 -10
- package/src/server/__tests__/YNABMCPServer.test.ts +0 -1
- package/src/server/__tests__/completions.integration.test.ts +117 -0
- package/src/server/__tests__/completions.test.ts +319 -0
- package/src/server/__tests__/resources.template.test.ts +3 -3
- package/src/server/__tests__/resources.test.ts +3 -3
- package/src/server/__tests__/toolRegistration.test.ts +3 -3
- package/src/server/cacheManager.ts +7 -6
- package/src/server/completions.ts +279 -0
- package/src/server/errorHandler.ts +1 -0
- package/src/server/rateLimiter.ts +4 -1
- package/src/server/resources.ts +49 -13
- package/src/server/securityMiddleware.ts +1 -0
- package/src/server/toolRegistry.ts +42 -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/adapters.ts +22 -1
- package/src/tools/reconciliation/__tests__/executor.progress.test.ts +462 -0
- package/src/tools/reconciliation/executor.ts +55 -1
- package/src/tools/reconciliation/index.ts +7 -3
- 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/utilityTools.ts +5 -76
- package/vitest.config.ts +4 -1
- package/.chunkhound.json +0 -11
- package/.code/agents/0098661e-0fa3-4990-beb9-c0cbf3f123aa/status.txt +0 -1
- 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/1324/exec-call_tIpx9uV1TpARbAMZonRQm8AO.txt +0 -757
- 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/1572/exec-call_GjVFBFOWcY7lE0idc5nWlLNh.txt +0 -781
- 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/1846/exec-call_1YNAVD18RjrMN7JnfkkQhUP3.txt +0 -766
- package/.code/agents/1846/exec-call_lh3lDzE4WJAh1lFiomiiZ73D.txt +0 -766
- package/.code/agents/1d7d7ab7-7473-4b69-8b97-6e914f56056a/result.txt +0 -231
- package/.code/agents/2038/exec-call_DYwOukaYsL8VCONWmV2rUW5u.txt +0 -766
- package/.code/agents/2038/exec-call_c7fOQ7UrpVcTtvdfGBRM146V.txt +0 -652
- package/.code/agents/2038/exec-call_ySNyq9Mm55jWE480s54r5QcA.txt +0 -766
- 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/2256/exec-call_AtPcRWPmFPMcmX6qOFm1fCEY.txt +0 -766
- 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/2454/exec-call_aFJpupwjfZeOBm7ixI5Vc8z2.txt +0 -766
- package/.code/agents/2454/exec-call_wogZ4HfXTodTEXvdgXlVUBpv.txt +0 -766
- 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/2e905864-aa07-4314-bcf9-c5b32277e4ac/result.txt +0 -36
- package/.code/agents/3073/exec-call_Peeagc9DxGYLgE6pNdMZhqIE.txt +0 -766
- package/.code/agents/3073/exec-call_d2YSE3hXF08KRSoUM3qd8Z3x.txt +0 -766
- 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/335aa031-466d-4fb7-925f-3cd864e264d0/result.txt +0 -191
- package/.code/agents/3364/exec-call_NbhIrsM5HhyDZDmJZG5CuCYL.txt +0 -766
- package/.code/agents/3364/exec-call_cKtJg0NrXiwXEFwlsE3uPZRA.txt +0 -766
- package/.code/agents/36d98414-5cde-4d9d-9a67-a240a18c1f07/result.txt +0 -189
- package/.code/agents/4604e866-b7b8-44f5-992f-2f683b0a523b/status.txt +0 -1
- 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/5f8dc01c-47b3-4163-b0b3-aa31be89fcdc/status.txt +0 -1
- 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_HltHpkDox0Zm1vGEjdksUgpE.txt +0 -1120
- package/.code/agents/7/exec-call_LCATrOPPAgbxW9Q1z0XaVi2E.txt +0 -2646
- package/.code/agents/7/exec-call_W8DeRfNG9hvbgVFvf0clBf6R.txt +0 -2646
- 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/94a0ddf3-a304-4ec3-913e-3cceef509948/error.txt +0 -1
- 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/e2c752b7-711d-423a-af57-f53c809deb84/result.txt +0 -160
- 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/e6601719-c31f-4a0e-8c71-d70787d0ab71/status.txt +0 -1
- package/.code/agents/e71695a8-3044-478d-8f12-ed13d02884c7/status.txt +0 -1
- package/.code/agents/f250b7ed-5bd5-4036-aa8c-ce63caee7d61/result.txt +0 -20
- 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/AGENTS.md +0 -1
- package/NUL +0 -0
- package/package.json.tmp +0 -105
- package/src/__tests__/delta.performance.test.ts +0 -80
- package/src/__tests__/workflows.e2e.test.ts +0 -1702
- 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
|
@@ -16,6 +16,7 @@ export declare class YNABMCPServer {
|
|
|
16
16
|
private deltaFetcher;
|
|
17
17
|
private diagnosticManager;
|
|
18
18
|
private errorHandler;
|
|
19
|
+
private completionsManager;
|
|
19
20
|
constructor(exitOnError?: boolean);
|
|
20
21
|
validateToken(): Promise<boolean>;
|
|
21
22
|
private isMalformedTokenResponse;
|
|
@@ -33,39 +34,44 @@ export declare class YNABMCPServer {
|
|
|
33
34
|
private warmCacheForBudget;
|
|
34
35
|
handleListTools(): Promise<{
|
|
35
36
|
tools: {
|
|
36
|
-
name: string;
|
|
37
37
|
inputSchema: {
|
|
38
|
+
[x: string]: unknown;
|
|
38
39
|
type: "object";
|
|
39
|
-
required?: string[] | undefined;
|
|
40
40
|
properties?: {
|
|
41
41
|
[x: string]: object;
|
|
42
42
|
} | undefined;
|
|
43
|
+
required?: string[] | undefined;
|
|
43
44
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
} | undefined;
|
|
47
|
-
icons?: {
|
|
48
|
-
src: string;
|
|
49
|
-
mimeType?: string | undefined | undefined;
|
|
50
|
-
sizes?: string[] | undefined;
|
|
51
|
-
}[] | undefined;
|
|
52
|
-
title?: string | undefined | undefined;
|
|
53
|
-
description?: string | undefined | undefined;
|
|
45
|
+
name: string;
|
|
46
|
+
description?: string | undefined;
|
|
54
47
|
outputSchema?: {
|
|
48
|
+
[x: string]: unknown;
|
|
55
49
|
type: "object";
|
|
56
|
-
required?: string[] | undefined;
|
|
57
50
|
properties?: {
|
|
58
51
|
[x: string]: object;
|
|
59
52
|
} | undefined;
|
|
60
|
-
|
|
53
|
+
required?: string[] | undefined;
|
|
61
54
|
} | undefined;
|
|
62
55
|
annotations?: {
|
|
63
|
-
title?: string | undefined
|
|
64
|
-
readOnlyHint?: boolean | undefined
|
|
65
|
-
destructiveHint?: boolean | undefined
|
|
66
|
-
idempotentHint?: boolean | undefined
|
|
67
|
-
openWorldHint?: boolean | undefined
|
|
56
|
+
title?: string | undefined;
|
|
57
|
+
readOnlyHint?: boolean | undefined;
|
|
58
|
+
destructiveHint?: boolean | undefined;
|
|
59
|
+
idempotentHint?: boolean | undefined;
|
|
60
|
+
openWorldHint?: boolean | undefined;
|
|
61
|
+
} | undefined;
|
|
62
|
+
execution?: {
|
|
63
|
+
taskSupport?: "optional" | "forbidden" | "required" | undefined;
|
|
64
|
+
} | undefined;
|
|
65
|
+
_meta?: {
|
|
66
|
+
[x: string]: unknown;
|
|
68
67
|
} | undefined;
|
|
68
|
+
icons?: {
|
|
69
|
+
src: string;
|
|
70
|
+
mimeType?: string | undefined;
|
|
71
|
+
sizes?: string[] | undefined;
|
|
72
|
+
theme?: "light" | "dark" | undefined;
|
|
73
|
+
}[] | undefined;
|
|
74
|
+
title?: string | undefined;
|
|
69
75
|
}[];
|
|
70
76
|
}>;
|
|
71
77
|
handleListResources(): Promise<{
|
|
@@ -78,6 +84,11 @@ export declare class YNABMCPServer {
|
|
|
78
84
|
content: ({
|
|
79
85
|
type: "text";
|
|
80
86
|
text: string;
|
|
87
|
+
annotations?: {
|
|
88
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
89
|
+
priority?: number | undefined;
|
|
90
|
+
lastModified?: string | undefined;
|
|
91
|
+
} | undefined;
|
|
81
92
|
_meta?: {
|
|
82
93
|
[x: string]: unknown;
|
|
83
94
|
} | undefined;
|
|
@@ -85,6 +96,11 @@ export declare class YNABMCPServer {
|
|
|
85
96
|
type: "image";
|
|
86
97
|
data: string;
|
|
87
98
|
mimeType: string;
|
|
99
|
+
annotations?: {
|
|
100
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
101
|
+
priority?: number | undefined;
|
|
102
|
+
lastModified?: string | undefined;
|
|
103
|
+
} | undefined;
|
|
88
104
|
_meta?: {
|
|
89
105
|
[x: string]: unknown;
|
|
90
106
|
} | undefined;
|
|
@@ -92,47 +108,67 @@ export declare class YNABMCPServer {
|
|
|
92
108
|
type: "audio";
|
|
93
109
|
data: string;
|
|
94
110
|
mimeType: string;
|
|
111
|
+
annotations?: {
|
|
112
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
113
|
+
priority?: number | undefined;
|
|
114
|
+
lastModified?: string | undefined;
|
|
115
|
+
} | undefined;
|
|
95
116
|
_meta?: {
|
|
96
117
|
[x: string]: unknown;
|
|
97
118
|
} | undefined;
|
|
98
119
|
} | {
|
|
99
|
-
type: "resource_link";
|
|
100
|
-
name: string;
|
|
101
120
|
uri: string;
|
|
121
|
+
name: string;
|
|
122
|
+
type: "resource_link";
|
|
123
|
+
description?: string | undefined;
|
|
124
|
+
mimeType?: string | undefined;
|
|
125
|
+
annotations?: {
|
|
126
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
127
|
+
priority?: number | undefined;
|
|
128
|
+
lastModified?: string | undefined;
|
|
129
|
+
} | undefined;
|
|
102
130
|
_meta?: {
|
|
103
131
|
[x: string]: unknown;
|
|
104
132
|
} | undefined;
|
|
105
|
-
mimeType?: string | undefined | undefined;
|
|
106
133
|
icons?: {
|
|
107
134
|
src: string;
|
|
108
|
-
mimeType?: string | undefined
|
|
135
|
+
mimeType?: string | undefined;
|
|
109
136
|
sizes?: string[] | undefined;
|
|
137
|
+
theme?: "light" | "dark" | undefined;
|
|
110
138
|
}[] | undefined;
|
|
111
|
-
title?: string | undefined
|
|
112
|
-
description?: string | undefined | undefined;
|
|
139
|
+
title?: string | undefined;
|
|
113
140
|
} | {
|
|
114
141
|
type: "resource";
|
|
115
142
|
resource: {
|
|
116
143
|
uri: string;
|
|
117
144
|
text: string;
|
|
145
|
+
mimeType?: string | undefined;
|
|
118
146
|
_meta?: {
|
|
119
147
|
[x: string]: unknown;
|
|
120
148
|
} | undefined;
|
|
121
|
-
mimeType?: string | undefined | undefined;
|
|
122
149
|
} | {
|
|
123
150
|
uri: string;
|
|
124
151
|
blob: string;
|
|
152
|
+
mimeType?: string | undefined;
|
|
125
153
|
_meta?: {
|
|
126
154
|
[x: string]: unknown;
|
|
127
155
|
} | undefined;
|
|
128
|
-
mimeType?: string | undefined | undefined;
|
|
129
156
|
};
|
|
157
|
+
annotations?: {
|
|
158
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
159
|
+
priority?: number | undefined;
|
|
160
|
+
lastModified?: string | undefined;
|
|
161
|
+
} | undefined;
|
|
130
162
|
_meta?: {
|
|
131
163
|
[x: string]: unknown;
|
|
132
164
|
} | undefined;
|
|
133
165
|
})[];
|
|
134
166
|
_meta?: {
|
|
135
167
|
[x: string]: unknown;
|
|
168
|
+
progressToken?: string | number | undefined;
|
|
169
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
170
|
+
taskId: string;
|
|
171
|
+
} | undefined;
|
|
136
172
|
} | undefined;
|
|
137
173
|
structuredContent?: {
|
|
138
174
|
[x: string]: unknown;
|
|
@@ -152,6 +188,11 @@ export declare class YNABMCPServer {
|
|
|
152
188
|
content: ({
|
|
153
189
|
type: "text";
|
|
154
190
|
text: string;
|
|
191
|
+
annotations?: {
|
|
192
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
193
|
+
priority?: number | undefined;
|
|
194
|
+
lastModified?: string | undefined;
|
|
195
|
+
} | undefined;
|
|
155
196
|
_meta?: {
|
|
156
197
|
[x: string]: unknown;
|
|
157
198
|
} | undefined;
|
|
@@ -159,6 +200,11 @@ export declare class YNABMCPServer {
|
|
|
159
200
|
type: "image";
|
|
160
201
|
data: string;
|
|
161
202
|
mimeType: string;
|
|
203
|
+
annotations?: {
|
|
204
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
205
|
+
priority?: number | undefined;
|
|
206
|
+
lastModified?: string | undefined;
|
|
207
|
+
} | undefined;
|
|
162
208
|
_meta?: {
|
|
163
209
|
[x: string]: unknown;
|
|
164
210
|
} | undefined;
|
|
@@ -166,47 +212,67 @@ export declare class YNABMCPServer {
|
|
|
166
212
|
type: "audio";
|
|
167
213
|
data: string;
|
|
168
214
|
mimeType: string;
|
|
215
|
+
annotations?: {
|
|
216
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
217
|
+
priority?: number | undefined;
|
|
218
|
+
lastModified?: string | undefined;
|
|
219
|
+
} | undefined;
|
|
169
220
|
_meta?: {
|
|
170
221
|
[x: string]: unknown;
|
|
171
222
|
} | undefined;
|
|
172
223
|
} | {
|
|
173
|
-
type: "resource_link";
|
|
174
|
-
name: string;
|
|
175
224
|
uri: string;
|
|
225
|
+
name: string;
|
|
226
|
+
type: "resource_link";
|
|
227
|
+
description?: string | undefined;
|
|
228
|
+
mimeType?: string | undefined;
|
|
229
|
+
annotations?: {
|
|
230
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
231
|
+
priority?: number | undefined;
|
|
232
|
+
lastModified?: string | undefined;
|
|
233
|
+
} | undefined;
|
|
176
234
|
_meta?: {
|
|
177
235
|
[x: string]: unknown;
|
|
178
236
|
} | undefined;
|
|
179
|
-
mimeType?: string | undefined | undefined;
|
|
180
237
|
icons?: {
|
|
181
238
|
src: string;
|
|
182
|
-
mimeType?: string | undefined
|
|
239
|
+
mimeType?: string | undefined;
|
|
183
240
|
sizes?: string[] | undefined;
|
|
241
|
+
theme?: "light" | "dark" | undefined;
|
|
184
242
|
}[] | undefined;
|
|
185
|
-
title?: string | undefined
|
|
186
|
-
description?: string | undefined | undefined;
|
|
243
|
+
title?: string | undefined;
|
|
187
244
|
} | {
|
|
188
245
|
type: "resource";
|
|
189
246
|
resource: {
|
|
190
247
|
uri: string;
|
|
191
248
|
text: string;
|
|
249
|
+
mimeType?: string | undefined;
|
|
192
250
|
_meta?: {
|
|
193
251
|
[x: string]: unknown;
|
|
194
252
|
} | undefined;
|
|
195
|
-
mimeType?: string | undefined | undefined;
|
|
196
253
|
} | {
|
|
197
254
|
uri: string;
|
|
198
255
|
blob: string;
|
|
256
|
+
mimeType?: string | undefined;
|
|
199
257
|
_meta?: {
|
|
200
258
|
[x: string]: unknown;
|
|
201
259
|
} | undefined;
|
|
202
|
-
mimeType?: string | undefined | undefined;
|
|
203
260
|
};
|
|
261
|
+
annotations?: {
|
|
262
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
263
|
+
priority?: number | undefined;
|
|
264
|
+
lastModified?: string | undefined;
|
|
265
|
+
} | undefined;
|
|
204
266
|
_meta?: {
|
|
205
267
|
[x: string]: unknown;
|
|
206
268
|
} | undefined;
|
|
207
269
|
})[];
|
|
208
270
|
_meta?: {
|
|
209
271
|
[x: string]: unknown;
|
|
272
|
+
progressToken?: string | number | undefined;
|
|
273
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
274
|
+
taskId: string;
|
|
275
|
+
} | undefined;
|
|
210
276
|
} | undefined;
|
|
211
277
|
structuredContent?: {
|
|
212
278
|
[x: string]: unknown;
|
|
@@ -214,39 +280,44 @@ export declare class YNABMCPServer {
|
|
|
214
280
|
isError?: boolean | undefined;
|
|
215
281
|
} | import("./prompts.js").PromptResponse | {
|
|
216
282
|
tools: {
|
|
217
|
-
name: string;
|
|
218
283
|
inputSchema: {
|
|
284
|
+
[x: string]: unknown;
|
|
219
285
|
type: "object";
|
|
220
|
-
required?: string[] | undefined;
|
|
221
286
|
properties?: {
|
|
222
287
|
[x: string]: object;
|
|
223
288
|
} | undefined;
|
|
289
|
+
required?: string[] | undefined;
|
|
224
290
|
};
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
} | undefined;
|
|
228
|
-
icons?: {
|
|
229
|
-
src: string;
|
|
230
|
-
mimeType?: string | undefined | undefined;
|
|
231
|
-
sizes?: string[] | undefined;
|
|
232
|
-
}[] | undefined;
|
|
233
|
-
title?: string | undefined | undefined;
|
|
234
|
-
description?: string | undefined | undefined;
|
|
291
|
+
name: string;
|
|
292
|
+
description?: string | undefined;
|
|
235
293
|
outputSchema?: {
|
|
294
|
+
[x: string]: unknown;
|
|
236
295
|
type: "object";
|
|
237
|
-
required?: string[] | undefined;
|
|
238
296
|
properties?: {
|
|
239
297
|
[x: string]: object;
|
|
240
298
|
} | undefined;
|
|
241
|
-
|
|
299
|
+
required?: string[] | undefined;
|
|
242
300
|
} | undefined;
|
|
243
301
|
annotations?: {
|
|
244
|
-
title?: string | undefined
|
|
245
|
-
readOnlyHint?: boolean | undefined
|
|
246
|
-
destructiveHint?: boolean | undefined
|
|
247
|
-
idempotentHint?: boolean | undefined
|
|
248
|
-
openWorldHint?: boolean | undefined
|
|
302
|
+
title?: string | undefined;
|
|
303
|
+
readOnlyHint?: boolean | undefined;
|
|
304
|
+
destructiveHint?: boolean | undefined;
|
|
305
|
+
idempotentHint?: boolean | undefined;
|
|
306
|
+
openWorldHint?: boolean | undefined;
|
|
307
|
+
} | undefined;
|
|
308
|
+
execution?: {
|
|
309
|
+
taskSupport?: "optional" | "forbidden" | "required" | undefined;
|
|
310
|
+
} | undefined;
|
|
311
|
+
_meta?: {
|
|
312
|
+
[x: string]: unknown;
|
|
249
313
|
} | undefined;
|
|
314
|
+
icons?: {
|
|
315
|
+
src: string;
|
|
316
|
+
mimeType?: string | undefined;
|
|
317
|
+
sizes?: string[] | undefined;
|
|
318
|
+
theme?: "light" | "dark" | undefined;
|
|
319
|
+
}[] | undefined;
|
|
320
|
+
title?: string | undefined;
|
|
250
321
|
}[];
|
|
251
322
|
description: string;
|
|
252
323
|
messages: {
|
|
@@ -3,7 +3,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListPromptsRequestSchema, ReadResourceRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListPromptsRequestSchema, ReadResourceRequestSchema, GetPromptRequestSchema, CompleteRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
|
|
7
7
|
import * as ynab from 'ynab';
|
|
8
8
|
import { AuthenticationError, ConfigurationError, ValidationError as ConfigValidationError, } from '../utils/errors.js';
|
|
9
9
|
import { ValidationError } from '../types/index.js';
|
|
@@ -30,6 +30,7 @@ import { ServerKnowledgeStore } from './serverKnowledgeStore.js';
|
|
|
30
30
|
import { DeltaCache } from './deltaCache.js';
|
|
31
31
|
import { DeltaFetcher } from '../tools/deltaFetcher.js';
|
|
32
32
|
import { ToolAnnotationPresets } from '../tools/toolCategories.js';
|
|
33
|
+
import { CompletionsManager } from './completions.js';
|
|
33
34
|
export class YNABMCPServer {
|
|
34
35
|
constructor(exitOnError = true) {
|
|
35
36
|
this.exitOnError = exitOnError;
|
|
@@ -42,9 +43,13 @@ export class YNABMCPServer {
|
|
|
42
43
|
version: this.serverVersion,
|
|
43
44
|
}, {
|
|
44
45
|
capabilities: {
|
|
45
|
-
tools: { listChanged:
|
|
46
|
-
resources: {
|
|
47
|
-
|
|
46
|
+
tools: { listChanged: false },
|
|
47
|
+
resources: {
|
|
48
|
+
subscribe: false,
|
|
49
|
+
listChanged: false,
|
|
50
|
+
},
|
|
51
|
+
prompts: { listChanged: false },
|
|
52
|
+
completions: {},
|
|
48
53
|
},
|
|
49
54
|
});
|
|
50
55
|
this.errorHandler = createErrorHandler(responseFormatter);
|
|
@@ -111,6 +116,7 @@ export class YNABMCPServer {
|
|
|
111
116
|
serverKnowledgeStore: this.serverKnowledgeStore,
|
|
112
117
|
deltaCache: this.deltaCache,
|
|
113
118
|
});
|
|
119
|
+
this.completionsManager = new CompletionsManager(this.ynabAPI, cacheManager, () => this.defaultBudgetId);
|
|
114
120
|
this.setupToolRegistry();
|
|
115
121
|
this.setupHandlers();
|
|
116
122
|
}
|
|
@@ -162,12 +168,7 @@ export class YNABMCPServer {
|
|
|
162
168
|
});
|
|
163
169
|
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
164
170
|
const { uri } = request.params;
|
|
165
|
-
|
|
166
|
-
return await this.resourceManager.readResource(uri);
|
|
167
|
-
}
|
|
168
|
-
catch (error) {
|
|
169
|
-
return this.errorHandler.handleError(error, `reading resource: ${uri}`);
|
|
170
|
-
}
|
|
171
|
+
return await this.resourceManager.readResource(uri);
|
|
171
172
|
});
|
|
172
173
|
this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
173
174
|
return this.promptManager.listPrompts();
|
|
@@ -182,7 +183,10 @@ export class YNABMCPServer {
|
|
|
182
183
|
tools: this.toolRegistry.listTools(),
|
|
183
184
|
};
|
|
184
185
|
});
|
|
185
|
-
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
186
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
187
|
+
if (!this.toolRegistry.hasTool(request.params.name)) {
|
|
188
|
+
throw new McpError(ErrorCode.InvalidParams, `Unknown tool: ${request.params.name}`);
|
|
189
|
+
}
|
|
186
190
|
const rawArgs = (request.params.arguments ?? undefined);
|
|
187
191
|
const minifyOverride = this.extractMinifyOverride(rawArgs);
|
|
188
192
|
const sanitizedArgs = rawArgs
|
|
@@ -202,8 +206,35 @@ export class YNABMCPServer {
|
|
|
202
206
|
if (minifyOverride !== undefined) {
|
|
203
207
|
executionOptions.minifyOverride = minifyOverride;
|
|
204
208
|
}
|
|
209
|
+
const progressToken = request.params
|
|
210
|
+
._meta?.progressToken;
|
|
211
|
+
if (progressToken !== undefined && extra.sendNotification) {
|
|
212
|
+
executionOptions.sendProgress = async (params) => {
|
|
213
|
+
try {
|
|
214
|
+
await extra.sendNotification({
|
|
215
|
+
method: 'notifications/progress',
|
|
216
|
+
params: {
|
|
217
|
+
progressToken,
|
|
218
|
+
progress: params.progress,
|
|
219
|
+
...(params.total !== undefined && { total: params.total }),
|
|
220
|
+
...(params.message !== undefined && { message: params.message }),
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
205
228
|
return await this.toolRegistry.executeTool(executionOptions);
|
|
206
229
|
});
|
|
230
|
+
this.server.setRequestHandler(CompleteRequestSchema, async (request) => {
|
|
231
|
+
const { argument, context } = request.params;
|
|
232
|
+
const completionContext = context?.arguments ? { arguments: context.arguments } : undefined;
|
|
233
|
+
const result = await this.completionsManager.getCompletions(argument.name, argument.value, completionContext);
|
|
234
|
+
return {
|
|
235
|
+
completion: result.completion,
|
|
236
|
+
};
|
|
237
|
+
});
|
|
207
238
|
}
|
|
208
239
|
setupToolRegistry() {
|
|
209
240
|
const register = (definition) => {
|
|
@@ -79,14 +79,15 @@ export class CacheManager {
|
|
|
79
79
|
else {
|
|
80
80
|
const providedTtl = ttlOrOptions?.ttl;
|
|
81
81
|
ttl = providedTtl !== undefined ? providedTtl : this.defaultTTL;
|
|
82
|
-
|
|
82
|
+
const hasStaleWhileRevalidate = ttlOrOptions !== undefined && 'staleWhileRevalidate' in ttlOrOptions;
|
|
83
|
+
if (hasStaleWhileRevalidate) {
|
|
83
84
|
staleWhileRevalidate = ttlOrOptions.staleWhileRevalidate;
|
|
85
|
+
if (staleWhileRevalidate === undefined && this.defaultStaleWindow > 0) {
|
|
86
|
+
staleWhileRevalidate = this.defaultStaleWindow;
|
|
87
|
+
}
|
|
84
88
|
}
|
|
85
89
|
else {
|
|
86
|
-
staleWhileRevalidate =
|
|
87
|
-
}
|
|
88
|
-
if (staleWhileRevalidate === undefined && this.defaultStaleWindow > 0) {
|
|
89
|
-
staleWhileRevalidate = this.defaultStaleWindow;
|
|
90
|
+
staleWhileRevalidate = undefined;
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
const entry = {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type * as ynab from 'ynab';
|
|
2
|
+
import type { CacheManager } from './cacheManager.js';
|
|
3
|
+
export interface CompletionResult {
|
|
4
|
+
completion: {
|
|
5
|
+
values: string[];
|
|
6
|
+
total?: number;
|
|
7
|
+
hasMore?: boolean;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
interface CompletionContext {
|
|
11
|
+
arguments?: Record<string, string> | undefined;
|
|
12
|
+
}
|
|
13
|
+
export declare class CompletionsManager {
|
|
14
|
+
private readonly ynabAPI;
|
|
15
|
+
private readonly cacheManager;
|
|
16
|
+
private readonly getDefaultBudgetId;
|
|
17
|
+
constructor(ynabAPI: ynab.API, cacheManager: CacheManager, getDefaultBudgetId: () => string | undefined);
|
|
18
|
+
getCompletions(argumentName: string, value: string, context?: CompletionContext): Promise<CompletionResult>;
|
|
19
|
+
private completeBudgets;
|
|
20
|
+
private completeAccounts;
|
|
21
|
+
private completeCategories;
|
|
22
|
+
private completePayees;
|
|
23
|
+
private filterAndFormat;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { CACHE_TTLS } from './cacheManager.js';
|
|
2
|
+
const MAX_COMPLETIONS = 100;
|
|
3
|
+
export class CompletionsManager {
|
|
4
|
+
constructor(ynabAPI, cacheManager, getDefaultBudgetId) {
|
|
5
|
+
this.ynabAPI = ynabAPI;
|
|
6
|
+
this.cacheManager = cacheManager;
|
|
7
|
+
this.getDefaultBudgetId = getDefaultBudgetId;
|
|
8
|
+
}
|
|
9
|
+
async getCompletions(argumentName, value, context) {
|
|
10
|
+
const normalizedName = argumentName.toLowerCase();
|
|
11
|
+
switch (normalizedName) {
|
|
12
|
+
case 'budget_id':
|
|
13
|
+
return this.completeBudgets(value);
|
|
14
|
+
case 'account_id':
|
|
15
|
+
case 'account_name':
|
|
16
|
+
return this.completeAccounts(value, context);
|
|
17
|
+
case 'category':
|
|
18
|
+
case 'category_id':
|
|
19
|
+
return this.completeCategories(value, context);
|
|
20
|
+
case 'payee':
|
|
21
|
+
case 'payee_id':
|
|
22
|
+
return this.completePayees(value, context);
|
|
23
|
+
default:
|
|
24
|
+
return { completion: { values: [], total: 0, hasMore: false } };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async completeBudgets(value) {
|
|
28
|
+
const budgets = await this.cacheManager.wrap('completions:budgets', {
|
|
29
|
+
ttl: CACHE_TTLS.BUDGETS,
|
|
30
|
+
loader: async () => {
|
|
31
|
+
const response = await this.ynabAPI.budgets.getBudgets();
|
|
32
|
+
return response.data.budgets.map((b) => ({
|
|
33
|
+
id: b.id,
|
|
34
|
+
name: b.name,
|
|
35
|
+
}));
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
return this.filterAndFormat(budgets, value, (b) => [b.name, b.id]);
|
|
39
|
+
}
|
|
40
|
+
async completeAccounts(value, context) {
|
|
41
|
+
const budgetId = context?.arguments?.['budget_id'] ?? this.getDefaultBudgetId();
|
|
42
|
+
if (!budgetId) {
|
|
43
|
+
return { completion: { values: [], total: 0, hasMore: false } };
|
|
44
|
+
}
|
|
45
|
+
const accounts = await this.cacheManager.wrap(`completions:accounts:${budgetId}`, {
|
|
46
|
+
ttl: CACHE_TTLS.ACCOUNTS,
|
|
47
|
+
loader: async () => {
|
|
48
|
+
const response = await this.ynabAPI.accounts.getAccounts(budgetId);
|
|
49
|
+
return response.data.accounts
|
|
50
|
+
.filter((a) => !a.deleted && !a.closed)
|
|
51
|
+
.map((a) => ({
|
|
52
|
+
id: a.id,
|
|
53
|
+
name: a.name,
|
|
54
|
+
}));
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
return this.filterAndFormat(accounts, value, (a) => [a.name, a.id]);
|
|
58
|
+
}
|
|
59
|
+
async completeCategories(value, context) {
|
|
60
|
+
const budgetId = context?.arguments?.['budget_id'] ?? this.getDefaultBudgetId();
|
|
61
|
+
if (!budgetId) {
|
|
62
|
+
return { completion: { values: [], total: 0, hasMore: false } };
|
|
63
|
+
}
|
|
64
|
+
const categories = await this.cacheManager.wrap(`completions:categories:${budgetId}`, {
|
|
65
|
+
ttl: CACHE_TTLS.CATEGORIES,
|
|
66
|
+
loader: async () => {
|
|
67
|
+
const response = await this.ynabAPI.categories.getCategories(budgetId);
|
|
68
|
+
const result = [];
|
|
69
|
+
for (const group of response.data.category_groups) {
|
|
70
|
+
if (group.hidden || group.deleted)
|
|
71
|
+
continue;
|
|
72
|
+
for (const cat of group.categories) {
|
|
73
|
+
if (cat.hidden || cat.deleted)
|
|
74
|
+
continue;
|
|
75
|
+
result.push({
|
|
76
|
+
id: cat.id,
|
|
77
|
+
name: cat.name,
|
|
78
|
+
group: group.name,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
return this.filterAndFormat(categories, value, (c) => [c.name, `${c.group}: ${c.name}`, c.id]);
|
|
86
|
+
}
|
|
87
|
+
async completePayees(value, context) {
|
|
88
|
+
const budgetId = context?.arguments?.['budget_id'] ?? this.getDefaultBudgetId();
|
|
89
|
+
if (!budgetId) {
|
|
90
|
+
return { completion: { values: [], total: 0, hasMore: false } };
|
|
91
|
+
}
|
|
92
|
+
const payees = await this.cacheManager.wrap(`completions:payees:${budgetId}`, {
|
|
93
|
+
ttl: CACHE_TTLS.PAYEES,
|
|
94
|
+
loader: async () => {
|
|
95
|
+
const response = await this.ynabAPI.payees.getPayees(budgetId);
|
|
96
|
+
return response.data.payees
|
|
97
|
+
.filter((p) => !p.deleted)
|
|
98
|
+
.map((p) => ({
|
|
99
|
+
id: p.id,
|
|
100
|
+
name: p.name,
|
|
101
|
+
}));
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
return this.filterAndFormat(payees, value, (p) => [p.name, p.id]);
|
|
105
|
+
}
|
|
106
|
+
filterAndFormat(items, value, getSearchableValues) {
|
|
107
|
+
const lowerValue = value.toLowerCase();
|
|
108
|
+
const itemCache = new Map();
|
|
109
|
+
const getCachedValues = (item) => {
|
|
110
|
+
let cached = itemCache.get(item);
|
|
111
|
+
if (!cached) {
|
|
112
|
+
const values = getSearchableValues(item);
|
|
113
|
+
cached = { values, lowerValues: values.map((v) => v.toLowerCase()) };
|
|
114
|
+
itemCache.set(item, cached);
|
|
115
|
+
}
|
|
116
|
+
return cached;
|
|
117
|
+
};
|
|
118
|
+
const matches = items.filter((item) => {
|
|
119
|
+
const { lowerValues } = getCachedValues(item);
|
|
120
|
+
return lowerValues.some((v) => v.includes(lowerValue));
|
|
121
|
+
});
|
|
122
|
+
matches.sort((a, b) => {
|
|
123
|
+
const aCache = getCachedValues(a);
|
|
124
|
+
const bCache = getCachedValues(b);
|
|
125
|
+
const aStartsWith = aCache.lowerValues.some((v) => v.startsWith(lowerValue));
|
|
126
|
+
const bStartsWith = bCache.lowerValues.some((v) => v.startsWith(lowerValue));
|
|
127
|
+
if (aStartsWith && !bStartsWith)
|
|
128
|
+
return -1;
|
|
129
|
+
if (!aStartsWith && bStartsWith)
|
|
130
|
+
return 1;
|
|
131
|
+
return (aCache.values[0] ?? '').localeCompare(bCache.values[0] ?? '');
|
|
132
|
+
});
|
|
133
|
+
const uniqueValues = new Set();
|
|
134
|
+
for (const item of matches) {
|
|
135
|
+
const { values, lowerValues } = getCachedValues(item);
|
|
136
|
+
let selectedValue;
|
|
137
|
+
for (let i = 0; i < values.length; i++) {
|
|
138
|
+
if (lowerValues[i]?.includes(lowerValue)) {
|
|
139
|
+
if (selectedValue === undefined || i < values.indexOf(selectedValue)) {
|
|
140
|
+
selectedValue = values[i];
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (selectedValue) {
|
|
146
|
+
uniqueValues.add(selectedValue);
|
|
147
|
+
}
|
|
148
|
+
if (uniqueValues.size >= MAX_COMPLETIONS)
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
const resultValues = Array.from(uniqueValues).slice(0, MAX_COMPLETIONS);
|
|
152
|
+
return {
|
|
153
|
+
completion: {
|
|
154
|
+
values: resultValues,
|
|
155
|
+
total: matches.length,
|
|
156
|
+
hasMore: matches.length > MAX_COMPLETIONS,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
package/dist/server/config.d.ts
CHANGED
|
@@ -5,10 +5,10 @@ declare const envSchema: z.ZodObject<{
|
|
|
5
5
|
YNAB_DEFAULT_BUDGET_ID: z.ZodOptional<z.ZodString>;
|
|
6
6
|
MCP_PORT: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
7
7
|
LOG_LEVEL: z.ZodDefault<z.ZodEnum<{
|
|
8
|
+
debug: "debug";
|
|
8
9
|
error: "error";
|
|
9
10
|
warn: "warn";
|
|
10
11
|
info: "info";
|
|
11
|
-
debug: "debug";
|
|
12
12
|
trace: "trace";
|
|
13
13
|
fatal: "fatal";
|
|
14
14
|
}>>;
|
|
@@ -17,7 +17,7 @@ export type AppConfig = z.infer<typeof envSchema>;
|
|
|
17
17
|
export declare function loadConfig(env?: NodeJS.ProcessEnv): AppConfig;
|
|
18
18
|
export declare const config: {
|
|
19
19
|
YNAB_ACCESS_TOKEN: string;
|
|
20
|
-
LOG_LEVEL: "
|
|
20
|
+
LOG_LEVEL: "debug" | "error" | "warn" | "info" | "trace" | "fatal";
|
|
21
21
|
YNAB_DEFAULT_BUDGET_ID?: string | undefined;
|
|
22
22
|
MCP_PORT?: number | undefined;
|
|
23
23
|
};
|
|
@@ -78,5 +78,7 @@ export class RateLimitError extends Error {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
export const globalRateLimiter = new RateLimiter({
|
|
81
|
-
enableLogging: process.env['
|
|
81
|
+
enableLogging: process.env['RATE_LIMIT_LOGGING'] === 'true' ||
|
|
82
|
+
process.env['LOG_LEVEL'] === 'debug' ||
|
|
83
|
+
process.env['VERBOSE_TESTS'] === 'true',
|
|
82
84
|
});
|