@warmio/mcp 4.2.2 → 4.3.2

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/dist/stdio.js ADDED
@@ -0,0 +1,200 @@
1
+ import process from 'node:process';
2
+ const CONTENT_LENGTH_HEADER = 'content-length:';
3
+ export class AdaptiveStdioTransport {
4
+ stdin;
5
+ stdout;
6
+ onclose;
7
+ onerror;
8
+ onmessage;
9
+ buffer = Buffer.alloc(0);
10
+ clientFraming = null;
11
+ closed = false;
12
+ started = false;
13
+ constructor(stdin = process.stdin, stdout = process.stdout) {
14
+ this.stdin = stdin;
15
+ this.stdout = stdout;
16
+ }
17
+ async start() {
18
+ if (this.started) {
19
+ throw new Error('AdaptiveStdioTransport already started');
20
+ }
21
+ this.started = true;
22
+ this.stdin.on('data', this.handleData);
23
+ this.stdin.on('error', this.handleError);
24
+ this.stdin.on('end', this.handleEnd);
25
+ this.stdin.resume();
26
+ }
27
+ async close() {
28
+ if (this.closed) {
29
+ return;
30
+ }
31
+ this.closed = true;
32
+ this.stdin.off('data', this.handleData);
33
+ this.stdin.off('error', this.handleError);
34
+ this.stdin.off('end', this.handleEnd);
35
+ this.stdin.pause();
36
+ this.buffer = Buffer.alloc(0);
37
+ this.onclose?.();
38
+ }
39
+ async send(message, _options) {
40
+ const json = JSON.stringify(message);
41
+ const framing = this.clientFraming ?? 'content-length';
42
+ const payload = framing === 'jsonl'
43
+ ? `${json}\n`
44
+ : `Content-Length: ${Buffer.byteLength(json, 'utf8')}\r\n\r\n${json}`;
45
+ await new Promise((resolve) => {
46
+ if (this.stdout.write(payload)) {
47
+ resolve();
48
+ return;
49
+ }
50
+ this.stdout.once('drain', resolve);
51
+ });
52
+ }
53
+ handleData = (chunk) => {
54
+ const incoming = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
55
+ this.buffer = this.buffer.length === 0 ? incoming : Buffer.concat([this.buffer, incoming]);
56
+ this.processBuffer();
57
+ };
58
+ handleError = (error) => {
59
+ this.onerror?.(error);
60
+ };
61
+ handleEnd = () => {
62
+ void this.close();
63
+ };
64
+ processBuffer() {
65
+ while (true) {
66
+ try {
67
+ this.trimLeadingNewlines();
68
+ const parsed = this.readMessage();
69
+ if (parsed === null) {
70
+ return;
71
+ }
72
+ this.clientFraming ??= parsed.framing;
73
+ this.onmessage?.(parsed.message);
74
+ }
75
+ catch (error) {
76
+ this.onerror?.(error instanceof Error ? error : new Error(String(error)));
77
+ this.buffer = Buffer.alloc(0);
78
+ return;
79
+ }
80
+ }
81
+ }
82
+ trimLeadingNewlines() {
83
+ while (this.buffer.length > 0) {
84
+ if (this.buffer[0] === 0x0a) {
85
+ this.buffer = this.buffer.subarray(1);
86
+ continue;
87
+ }
88
+ if (this.buffer.length > 1 && this.buffer[0] === 0x0d && this.buffer[1] === 0x0a) {
89
+ this.buffer = this.buffer.subarray(2);
90
+ continue;
91
+ }
92
+ return;
93
+ }
94
+ }
95
+ readMessage() {
96
+ const framing = this.clientFraming ?? this.detectFraming();
97
+ if (framing === null) {
98
+ return null;
99
+ }
100
+ return framing === 'content-length'
101
+ ? this.readContentLengthMessage()
102
+ : this.readJsonLineMessage();
103
+ }
104
+ detectFraming() {
105
+ if (this.buffer.length === 0) {
106
+ return null;
107
+ }
108
+ const preview = this.buffer
109
+ .subarray(0, Math.min(this.buffer.length, 32))
110
+ .toString('utf8')
111
+ .toLowerCase();
112
+ if (preview.startsWith(CONTENT_LENGTH_HEADER)) {
113
+ return 'content-length';
114
+ }
115
+ if (preview.startsWith('{') || preview.startsWith('[')) {
116
+ return 'jsonl';
117
+ }
118
+ const newlineIndex = this.buffer.indexOf(0x0a);
119
+ if (newlineIndex === -1) {
120
+ return null;
121
+ }
122
+ const firstLine = this.buffer.toString('utf8', 0, newlineIndex).replace(/\r$/, '').trimStart();
123
+ if (firstLine.toLowerCase().startsWith(CONTENT_LENGTH_HEADER)) {
124
+ return 'content-length';
125
+ }
126
+ if (firstLine.startsWith('{') || firstLine.startsWith('[')) {
127
+ return 'jsonl';
128
+ }
129
+ throw new Error(`Unsupported MCP stdio framing: ${JSON.stringify(firstLine.slice(0, 80))}`);
130
+ }
131
+ readJsonLineMessage() {
132
+ const newlineIndex = this.buffer.indexOf(0x0a);
133
+ if (newlineIndex === -1) {
134
+ return null;
135
+ }
136
+ const rawLine = this.buffer.toString('utf8', 0, newlineIndex).replace(/\r$/, '');
137
+ this.buffer = this.buffer.subarray(newlineIndex + 1);
138
+ if (rawLine.trim() === '') {
139
+ return this.readMessage();
140
+ }
141
+ return {
142
+ framing: 'jsonl',
143
+ message: JSON.parse(rawLine),
144
+ };
145
+ }
146
+ readContentLengthMessage() {
147
+ const headerBoundary = this.findHeaderBoundary();
148
+ if (headerBoundary === null) {
149
+ return null;
150
+ }
151
+ const { bodyStart, headerEnd } = headerBoundary;
152
+ const headerText = this.buffer.toString('utf8', 0, headerEnd);
153
+ const contentLength = this.parseContentLength(headerText);
154
+ if (this.buffer.length < bodyStart + contentLength) {
155
+ return null;
156
+ }
157
+ const bodyText = this.buffer.toString('utf8', bodyStart, bodyStart + contentLength);
158
+ this.buffer = this.buffer.subarray(bodyStart + contentLength);
159
+ return {
160
+ framing: 'content-length',
161
+ message: JSON.parse(bodyText),
162
+ };
163
+ }
164
+ findHeaderBoundary() {
165
+ const crlfBoundary = this.buffer.indexOf('\r\n\r\n');
166
+ if (crlfBoundary !== -1) {
167
+ return {
168
+ bodyStart: crlfBoundary + 4,
169
+ headerEnd: crlfBoundary,
170
+ };
171
+ }
172
+ const lfBoundary = this.buffer.indexOf('\n\n');
173
+ if (lfBoundary !== -1) {
174
+ return {
175
+ bodyStart: lfBoundary + 2,
176
+ headerEnd: lfBoundary,
177
+ };
178
+ }
179
+ return null;
180
+ }
181
+ parseContentLength(headerText) {
182
+ const headers = headerText.split(/\r?\n/);
183
+ for (const header of headers) {
184
+ const separatorIndex = header.indexOf(':');
185
+ if (separatorIndex === -1) {
186
+ continue;
187
+ }
188
+ const name = header.slice(0, separatorIndex).trim().toLowerCase();
189
+ if (name !== 'content-length') {
190
+ continue;
191
+ }
192
+ const value = Number.parseInt(header.slice(separatorIndex + 1).trim(), 10);
193
+ if (!Number.isFinite(value) || value < 0) {
194
+ throw new Error(`Invalid Content-Length header: ${header}`);
195
+ }
196
+ return value;
197
+ }
198
+ throw new Error('Missing Content-Length header');
199
+ }
200
+ }
package/dist/types.d.ts CHANGED
@@ -1,160 +1,4 @@
1
- import type { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { Implementation } from '@modelcontextprotocol/sdk/types.js';
3
- import type { GetAccountsOutput, GetFinancialStateOutput, GetTransactionsInput, GetTransactionsOutput, VerifyKeyOutput } from './schemas.js';
4
1
  export declare const WARM_TOOL_NAMES: readonly ["get_accounts", "get_transactions", "get_financial_state", "verify_key"];
5
2
  export type WarmToolName = (typeof WARM_TOOL_NAMES)[number];
6
- export interface WarmApiClientOptions {
7
- apiUrl?: string;
8
- requestTimeoutMs?: number;
9
- fetchImplementation?: typeof fetch;
10
- apiKeyResolver?: () => string | null;
11
- }
12
- export interface WarmServerOptions extends WarmApiClientOptions {
13
- serverInfo?: Implementation;
14
- }
15
- export type WarmToolRegistrationTarget = McpServer;
16
- export type RegisteredWarmTools = Record<WarmToolName, RegisteredTool>;
17
- export interface WarmApiClient {
18
- getAccounts(): Promise<GetAccountsOutput>;
19
- getFinancialState(): Promise<GetFinancialStateOutput>;
20
- getTransactions(input: GetTransactionsInput): Promise<GetTransactionsOutput>;
21
- verifyKey(): Promise<VerifyKeyOutput>;
22
- }
23
- export interface WarmApiAccount {
24
- account_id?: string | null;
25
- name?: string | null;
26
- type?: string | null;
27
- subtype?: string | null;
28
- current_balance?: number | null;
29
- available_balance?: number | null;
30
- institution_name?: string | null;
31
- mask?: string | null;
32
- }
33
- export interface WarmApiAccountsResponse {
34
- accounts?: WarmApiAccount[];
35
- generated_at?: string;
36
- }
37
- export interface WarmApiTransaction {
38
- id?: string | null;
39
- account_id?: string | null;
40
- date?: string | null;
41
- amount?: number | null;
42
- merchant_name?: string | null;
43
- name?: string | null;
44
- primary_category?: string | null;
45
- detailed_category?: string | null;
46
- }
47
- export interface WarmApiTransactionsResponse {
48
- generated_at?: string;
49
- next_knowledge?: string;
50
- transactions?: WarmApiTransaction[];
51
- pagination?: {
52
- limit?: number | null;
53
- next_cursor?: string | null;
54
- };
55
- }
56
- export interface WarmApiSnapshot {
57
- snapshot_date?: string | null;
58
- period_end?: string | null;
59
- net_worth?: number | null;
60
- total_assets?: number | null;
61
- total_liabilities?: number | null;
62
- total_cash?: number | null;
63
- investment_value?: number | null;
64
- asset_value?: number | null;
65
- total_debt?: number | null;
66
- liability_value?: number | null;
67
- }
68
- export interface WarmApiSnapshotsResponse {
69
- snapshots?: WarmApiSnapshot[];
70
- generated_at?: string;
71
- }
72
- export interface WarmApiRecurring {
73
- average_amount?: number | null;
74
- description?: string | null;
75
- frequency?: string | null;
76
- is_active?: boolean | null;
77
- last_amount?: number | null;
78
- merchant_name?: string | null;
79
- next_date?: string | null;
80
- stream_type?: string | null;
81
- }
82
- export interface WarmApiRecurringResponse {
83
- recurring_transactions?: WarmApiRecurring[];
84
- generated_at?: string;
85
- }
86
- export interface WarmApiBudget {
87
- amount?: number | null;
88
- name?: string | null;
89
- period?: string | null;
90
- percent_used?: number | null;
91
- remaining?: number | null;
92
- spent?: number | null;
93
- status?: string | null;
94
- }
95
- export interface WarmApiBudgetsResponse {
96
- budgets?: WarmApiBudget[];
97
- generated_at?: string;
98
- }
99
- export interface WarmApiGoal {
100
- category?: string | null;
101
- current?: number | null;
102
- monthly_contribution_needed?: number | null;
103
- name?: string | null;
104
- progress_percent?: number | null;
105
- status?: string | null;
106
- target?: number | null;
107
- target_date?: string | null;
108
- }
109
- export interface WarmApiGoalsResponse {
110
- goals?: WarmApiGoal[];
111
- generated_at?: string;
112
- }
113
- export interface WarmApiHealthResponse {
114
- score?: number | null;
115
- label?: string | null;
116
- pillars?: {
117
- spend?: number | null;
118
- save?: number | null;
119
- borrow?: number | null;
120
- build?: number | null;
121
- } | null;
122
- data_completeness?: number | null;
123
- message?: string | null;
124
- generated_at?: string;
125
- }
126
- export interface WarmApiLiability {
127
- account_id?: string | null;
128
- type?: string | null;
129
- balance?: number | null;
130
- apr_percentage?: number | null;
131
- interest_rate_percentage?: number | null;
132
- interest_rate_type?: string | null;
133
- minimum_payment?: number | null;
134
- next_payment_due_date?: string | null;
135
- expected_payoff_date?: string | null;
136
- origination_principal_amount?: number | null;
137
- is_overdue?: boolean | null;
138
- }
139
- export interface WarmApiLiabilitiesResponse {
140
- liabilities?: WarmApiLiability[];
141
- generated_at?: string;
142
- }
143
- export interface WarmApiHolding {
144
- account_id?: string | null;
145
- security_name?: string | null;
146
- symbol?: string | null;
147
- type?: string | null;
148
- quantity?: number | null;
149
- value?: number | null;
150
- cost_basis?: number | null;
151
- }
152
- export interface WarmApiHoldingsResponse {
153
- holdings?: WarmApiHolding[];
154
- generated_at?: string;
155
- }
156
- export interface WarmApiVerifyResponse {
157
- valid?: boolean;
158
- status?: string;
159
- error?: string;
160
- }
3
+ export type { GetAccountsOutput, GetTransactionsInput, GetTransactionsOutput, VerifyKeyOutput, WarmApiAccount, WarmApiBudget, WarmApiClient, WarmApiClientOptions, WarmApiGoal, WarmApiHolding, WarmApiLiability, WarmApiTransaction, WarmFinancialState, } from './warm-api-client.js';
4
+ export type { WarmServerOptions } from './server.js';
@@ -0,0 +1,184 @@
1
+ type NormalizedAccountType = 'depository' | 'credit' | 'loan' | 'investment' | 'other';
2
+ export interface WarmApiClientOptions {
3
+ apiKeyResolver?: () => string | null;
4
+ apiUrl?: string;
5
+ fetchImplementation?: typeof fetch;
6
+ requestTimeoutMs?: number;
7
+ }
8
+ export interface WarmApiAccount {
9
+ account_id?: string | null;
10
+ available_balance?: number | null;
11
+ current_balance?: number | null;
12
+ institution_name?: string | null;
13
+ mask?: string | null;
14
+ name?: string | null;
15
+ subtype?: string | null;
16
+ type?: string | null;
17
+ }
18
+ export interface WarmApiTransaction {
19
+ account_id?: string | null;
20
+ amount?: number | null;
21
+ date?: string | null;
22
+ detailed_category?: string | null;
23
+ id?: string | null;
24
+ merchant_name?: string | null;
25
+ name?: string | null;
26
+ primary_category?: string | null;
27
+ }
28
+ export interface WarmApiBudget {
29
+ amount?: number | null;
30
+ name?: string | null;
31
+ period?: string | null;
32
+ percent_used?: number | null;
33
+ remaining?: number | null;
34
+ spent?: number | null;
35
+ status?: string | null;
36
+ }
37
+ export interface WarmApiGoal {
38
+ category?: string | null;
39
+ current?: number | null;
40
+ monthly_contribution_needed?: number | null;
41
+ name?: string | null;
42
+ progress_percent?: number | null;
43
+ status?: string | null;
44
+ target?: number | null;
45
+ target_date?: string | null;
46
+ }
47
+ export interface WarmFinancialState extends Record<string, unknown> {
48
+ budgets: Array<{
49
+ amount: number;
50
+ name: string;
51
+ percent_used: number;
52
+ period: string;
53
+ remaining: number;
54
+ spent: number;
55
+ status: string | null;
56
+ }>;
57
+ category_spending: Array<{
58
+ amount: number;
59
+ category: string;
60
+ }>;
61
+ generated_at: string;
62
+ goals: Array<{
63
+ category: string | null;
64
+ current: number;
65
+ monthly_contribution_needed: number | null;
66
+ name: string;
67
+ progress_percent: number;
68
+ status: string | null;
69
+ target: number;
70
+ target_date: string | null;
71
+ }>;
72
+ health: {
73
+ data_completeness: number | null;
74
+ label: string | null;
75
+ message: string | null;
76
+ pillars: {
77
+ borrow: number | null;
78
+ build: number | null;
79
+ save: number | null;
80
+ spend: number | null;
81
+ } | null;
82
+ score: number | null;
83
+ };
84
+ holdings: Array<{
85
+ account_id: string;
86
+ cost_basis: number | null;
87
+ quantity: number;
88
+ security_name: string | null;
89
+ symbol: string | null;
90
+ type: string | null;
91
+ value: number | null;
92
+ }>;
93
+ liabilities: Array<{
94
+ account_id: string;
95
+ apr_percentage: number | null;
96
+ balance: number | null;
97
+ is_overdue: boolean | null;
98
+ minimum_payment: number | null;
99
+ next_payment_due_date: string | null;
100
+ type: string;
101
+ }>;
102
+ recurring: Array<{
103
+ active: boolean;
104
+ amount: number;
105
+ frequency: string;
106
+ merchant: string;
107
+ next_date: string | null;
108
+ type: string | null;
109
+ }>;
110
+ snapshots: Array<{
111
+ date: string;
112
+ net_worth: number;
113
+ total_assets: number;
114
+ total_liabilities: number;
115
+ }>;
116
+ }
117
+ export interface GetTransactionsInput {
118
+ cursor?: string;
119
+ last_knowledge?: string;
120
+ limit: number;
121
+ search?: string;
122
+ }
123
+ export interface GetTransactionsOutput extends Record<string, unknown> {
124
+ generated_at: string | null;
125
+ next_knowledge: string | null;
126
+ pagination: {
127
+ has_more: boolean;
128
+ limit: number;
129
+ next_cursor: string | null;
130
+ };
131
+ txns: Array<{
132
+ amount: number;
133
+ category: string | null;
134
+ date: string | null;
135
+ description: string | null;
136
+ detailed_category: string | null;
137
+ id: string | null;
138
+ merchant: string | null;
139
+ }>;
140
+ }
141
+ export interface GetAccountsOutput extends Record<string, unknown> {
142
+ accounts: Array<{
143
+ balance: number;
144
+ institution: string | null;
145
+ mask: string | null;
146
+ name: string;
147
+ subtype: string | null;
148
+ type: NormalizedAccountType;
149
+ }>;
150
+ }
151
+ export interface VerifyKeyOutput extends Record<string, unknown> {
152
+ status: string;
153
+ valid: boolean;
154
+ }
155
+ export interface WarmApiClient {
156
+ getAccounts(): Promise<GetAccountsOutput>;
157
+ getFinancialState(): Promise<WarmFinancialState>;
158
+ getTransactions(input: GetTransactionsInput): Promise<GetTransactionsOutput>;
159
+ verifyKey(): Promise<VerifyKeyOutput>;
160
+ }
161
+ export interface WarmApiLiability {
162
+ account_id?: string | null;
163
+ apr_percentage?: number | null;
164
+ balance?: number | null;
165
+ is_overdue?: boolean | null;
166
+ minimum_payment?: number | null;
167
+ next_payment_due_date?: string | null;
168
+ type?: string | null;
169
+ }
170
+ export interface WarmApiHolding {
171
+ account_id?: string | null;
172
+ cost_basis?: number | null;
173
+ quantity?: number | null;
174
+ security_name?: string | null;
175
+ symbol?: string | null;
176
+ type?: string | null;
177
+ value?: number | null;
178
+ }
179
+ export declare function getConfiguredApiKey(): string | null;
180
+ export declare function resetConfiguredApiKeyCache(): void;
181
+ export declare function apiRequest<TResponse>(endpoint: string, params?: Record<string, string | undefined>, options?: WarmApiClientOptions): Promise<TResponse>;
182
+ export declare function createWarmApiClient(options?: WarmApiClientOptions): WarmApiClient;
183
+ export declare function verifyWarmApiKey(apiKey: string): Promise<VerifyKeyOutput>;
184
+ export {};