@warmio/mcp 3.0.2 → 4.1.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/dist/install.js CHANGED
@@ -1,7 +1,8 @@
1
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
2
- import { join, dirname, resolve } from 'path';
3
- import { homedir, platform } from 'os';
4
1
  import { createInterface } from 'readline';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
3
+ import { dirname, join, resolve } from 'path';
4
+ import { homedir, platform } from 'os';
5
+ import { verifyWarmApiKey } from './server.js';
5
6
  const HOME = homedir();
6
7
  const CWD = process.cwd();
7
8
  function isRecord(value) {
@@ -17,19 +18,29 @@ function getClaudeDesktopPath() {
17
18
  return join(HOME, '.config', 'claude', 'claude_desktop_config.json');
18
19
  }
19
20
  const GLOBAL_CLIENTS = [
20
- { name: 'Claude Code', configPath: join(HOME, '.claude.json'), format: 'json', alwaysInclude: true },
21
+ {
22
+ name: 'Claude Code',
23
+ configPath: join(HOME, '.claude.json'),
24
+ format: 'json',
25
+ alwaysInclude: true,
26
+ },
21
27
  { name: 'Claude Desktop', configPath: getClaudeDesktopPath(), format: 'json' },
22
28
  { name: 'Cursor', configPath: join(HOME, '.cursor', 'mcp.json'), format: 'json' },
23
- { name: 'Windsurf', configPath: join(HOME, '.codeium', 'windsurf', 'mcp_config.json'), format: 'json' },
29
+ {
30
+ name: 'Windsurf',
31
+ configPath: join(HOME, '.codeium', 'windsurf', 'mcp_config.json'),
32
+ format: 'json',
33
+ },
24
34
  { name: 'OpenCode', configPath: join(HOME, '.config', 'opencode', 'opencode.json'), format: 'json' },
25
35
  { name: 'Codex CLI', configPath: join(HOME, '.codex', 'config.toml'), format: 'toml' },
26
- { name: 'Antigravity', configPath: join(HOME, '.gemini', 'antigravity', 'mcp_config.json'), format: 'json' },
36
+ {
37
+ name: 'Antigravity',
38
+ configPath: join(HOME, '.gemini', 'antigravity', 'mcp_config.json'),
39
+ format: 'json',
40
+ },
27
41
  { name: 'Gemini CLI', configPath: join(HOME, '.gemini', 'settings.json'), format: 'json' },
28
42
  ];
29
- // Project-level MCP config files (checked in CWD)
30
43
  const PROJECT_CONFIGS = ['.mcp.json', '.cursor/mcp.json', '.vscode/mcp.json'];
31
- // On Windows, npx doesn't forward stdin/stdout properly for MCP's JSON-RPC protocol.
32
- // Using cmd /c npx ... fixes the pipe forwarding.
33
44
  const MCP_CONFIG = platform() === 'win32'
34
45
  ? { command: 'cmd', args: ['/c', 'npx', '-y', '@warmio/mcp', '--server'] }
35
46
  : { command: 'npx', args: ['-y', '@warmio/mcp', '--server'] };
@@ -49,17 +60,20 @@ function detectProjectClients() {
49
60
  return found;
50
61
  }
51
62
  function isDetected(client) {
52
- if (client.alwaysInclude)
63
+ if (client.alwaysInclude) {
53
64
  return true;
65
+ }
54
66
  return existsSync(dirname(client.configPath));
55
67
  }
56
68
  function isConfigured(client) {
57
- if (!existsSync(client.configPath))
69
+ if (!existsSync(client.configPath)) {
58
70
  return false;
71
+ }
59
72
  try {
60
73
  const content = readFileSync(client.configPath, 'utf-8');
61
- if (client.format === 'toml')
74
+ if (client.format === 'toml') {
62
75
  return content.includes('[mcp_servers.warm]');
76
+ }
63
77
  return !!JSON.parse(content)?.mcpServers?.warm;
64
78
  }
65
79
  catch {
@@ -72,19 +86,28 @@ function configureJson(client, apiKey) {
72
86
  try {
73
87
  config = JSON.parse(readFileSync(client.configPath, 'utf-8'));
74
88
  }
75
- catch { /* start fresh */ }
89
+ catch {
90
+ // Start from an empty config if the existing file is invalid.
91
+ }
76
92
  }
77
- if (!config.mcpServers)
93
+ if (!config.mcpServers) {
78
94
  config.mcpServers = {};
95
+ }
79
96
  const servers = config.mcpServers;
80
97
  const existing = isRecord(servers.warm) ? servers.warm : undefined;
81
98
  const existingEnv = isRecord(existing?.env) ? existing.env : {};
82
- // For project-level configs, preserve existing command/args if present — only inject the key.
83
99
  if (client.isProjectLevel && existing?.command) {
84
- servers.warm = { ...existing, env: { ...existingEnv, WARM_API_KEY: apiKey } };
100
+ servers.warm = {
101
+ ...existing,
102
+ env: { ...existingEnv, WARM_API_KEY: apiKey },
103
+ };
85
104
  }
86
105
  else {
87
- servers.warm = { ...existing, ...MCP_CONFIG, env: { ...existingEnv, WARM_API_KEY: apiKey } };
106
+ servers.warm = {
107
+ ...existing,
108
+ ...MCP_CONFIG,
109
+ env: { ...existingEnv, WARM_API_KEY: apiKey },
110
+ };
88
111
  }
89
112
  mkdirSync(dirname(client.configPath), { recursive: true });
90
113
  writeFileSync(client.configPath, JSON.stringify(config, null, 2) + '\n');
@@ -93,8 +116,9 @@ function configureToml(client, apiKey) {
93
116
  let content = '';
94
117
  if (existsSync(client.configPath)) {
95
118
  content = readFileSync(client.configPath, 'utf-8');
96
- if (!content.endsWith('\n'))
119
+ if (!content.endsWith('\n')) {
97
120
  content += '\n';
121
+ }
98
122
  }
99
123
  const tomlCommand = platform() === 'win32' ? 'cmd' : 'npx';
100
124
  const tomlArgs = platform() === 'win32'
@@ -111,35 +135,67 @@ function configureToml(client, apiKey) {
111
135
  writeFileSync(client.configPath, nextContent.endsWith('\n') ? nextContent : `${nextContent}\n`);
112
136
  }
113
137
  function configure(client, apiKey) {
114
- if (client.format === 'json')
138
+ if (client.format === 'json') {
115
139
  configureJson(client, apiKey);
116
- else
117
- configureToml(client, apiKey);
140
+ return;
141
+ }
142
+ configureToml(client, apiKey);
118
143
  }
119
- function shortPath(p) {
120
- return p.replace(HOME, '~').replace(CWD, '.');
144
+ function shortPath(filePath) {
145
+ return filePath.replace(HOME, '~').replace(CWD, '.');
121
146
  }
122
147
  function prompt(question) {
123
- return new Promise((resolve) => {
148
+ return new Promise((resolvePrompt) => {
124
149
  const rl = createInterface({ input: process.stdin, output: process.stdout });
125
150
  rl.question(question, (answer) => {
126
151
  rl.close();
127
- resolve(answer.trim());
152
+ resolvePrompt(answer.trim());
128
153
  });
129
154
  });
130
155
  }
131
- export async function install() {
132
- const force = process.argv.includes('--force');
156
+ function isBlockingValidationError(message) {
157
+ const normalized = message.toLowerCase();
158
+ return (normalized.includes('invalid or expired api key') ||
159
+ normalized.includes('pro subscription required'));
160
+ }
161
+ async function validateApiKey(apiKey) {
162
+ console.log(' Validating API key...');
163
+ try {
164
+ const result = await verifyWarmApiKey(apiKey);
165
+ if (!result.valid) {
166
+ console.log(` Validation failed: ${result.status}`);
167
+ console.log(' Check https://warm.io/settings and try again.');
168
+ console.log('');
169
+ return false;
170
+ }
171
+ console.log(' API key verified.');
172
+ console.log('');
173
+ return true;
174
+ }
175
+ catch (error) {
176
+ const message = error instanceof Error ? error.message : String(error);
177
+ if (isBlockingValidationError(message)) {
178
+ console.log(` Validation failed: ${message}`);
179
+ console.log('');
180
+ return false;
181
+ }
182
+ console.log(` Could not validate right now: ${message}`);
183
+ console.log(' Continuing setup with the provided key.');
184
+ console.log('');
185
+ return true;
186
+ }
187
+ }
188
+ export async function install(options = {}) {
189
+ const force = options.force ?? false;
190
+ const shouldValidateApiKey = options.validateApiKey ?? true;
133
191
  console.log('');
134
192
  console.log(' Warm MCP Server Installer');
135
193
  console.log(' -------------------------');
136
194
  console.log('');
137
- // Detect global + project-level clients
138
195
  const globalClients = GLOBAL_CLIENTS.filter(isDetected);
139
196
  const projectClients = detectProjectClients();
140
197
  const allClients = [...globalClients, ...projectClients];
141
- const needsSetup = allClients.filter((c) => !isConfigured(c) || force);
142
- // Show all detected clients
198
+ const needsSetup = allClients.filter((client) => !isConfigured(client) || force);
143
199
  console.log(' MCP clients found:');
144
200
  allClients.forEach((client) => {
145
201
  const configured = isConfigured(client);
@@ -147,14 +203,12 @@ export async function install() {
147
203
  console.log(` ${client.name.padEnd(22)} ${shortPath(client.configPath).padEnd(55)} ${status}`);
148
204
  });
149
205
  console.log('');
150
- // Nothing to do
151
206
  if (needsSetup.length === 0) {
152
207
  console.log(' All clients already configured!');
153
208
  console.log(' Run with --force to update the API key.');
154
209
  console.log('');
155
210
  return;
156
211
  }
157
- // Prompt for API key
158
212
  const apiKey = await prompt(' Warm API key: ');
159
213
  if (!apiKey) {
160
214
  console.log('');
@@ -162,7 +216,12 @@ export async function install() {
162
216
  console.log('');
163
217
  return;
164
218
  }
165
- console.log('');
219
+ if (shouldValidateApiKey) {
220
+ const isValid = await validateApiKey(apiKey);
221
+ if (!isValid) {
222
+ return;
223
+ }
224
+ }
166
225
  console.log(' Configuring...');
167
226
  console.log('');
168
227
  needsSetup.forEach((client) => {
@@ -170,8 +229,9 @@ export async function install() {
170
229
  configure(client, apiKey);
171
230
  console.log(` ${client.name.padEnd(22)} done`);
172
231
  }
173
- catch (err) {
174
- console.log(` ${client.name.padEnd(22)} failed: ${err instanceof Error ? err.message : String(err)}`);
232
+ catch (error) {
233
+ const message = error instanceof Error ? error.message : String(error);
234
+ console.log(` ${client.name.padEnd(22)} failed: ${message}`);
175
235
  }
176
236
  });
177
237
  console.log('');
@@ -0,0 +1,225 @@
1
+ import * as z from 'zod/v4';
2
+ export declare const emptyInputSchema: z.ZodDefault<z.ZodObject<{}, z.core.$strict>>;
3
+ export declare const accountTypeSchema: z.ZodEnum<{
4
+ depository: "depository";
5
+ credit: "credit";
6
+ loan: "loan";
7
+ investment: "investment";
8
+ other: "other";
9
+ }>;
10
+ export declare const accountSchema: z.ZodObject<{
11
+ name: z.ZodString;
12
+ type: z.ZodEnum<{
13
+ depository: "depository";
14
+ credit: "credit";
15
+ loan: "loan";
16
+ investment: "investment";
17
+ other: "other";
18
+ }>;
19
+ subtype: z.ZodNullable<z.ZodString>;
20
+ balance: z.ZodNumber;
21
+ institution: z.ZodNullable<z.ZodString>;
22
+ mask: z.ZodNullable<z.ZodString>;
23
+ }, z.core.$strict>;
24
+ export declare const getAccountsOutputSchema: z.ZodObject<{
25
+ accounts: z.ZodArray<z.ZodObject<{
26
+ name: z.ZodString;
27
+ type: z.ZodEnum<{
28
+ depository: "depository";
29
+ credit: "credit";
30
+ loan: "loan";
31
+ investment: "investment";
32
+ other: "other";
33
+ }>;
34
+ subtype: z.ZodNullable<z.ZodString>;
35
+ balance: z.ZodNumber;
36
+ institution: z.ZodNullable<z.ZodString>;
37
+ mask: z.ZodNullable<z.ZodString>;
38
+ }, z.core.$strict>>;
39
+ }, z.core.$strict>;
40
+ export declare const getTransactionsInputSchema: z.ZodObject<{
41
+ limit: z.ZodDefault<z.ZodInt>;
42
+ cursor: z.ZodOptional<z.ZodString>;
43
+ last_knowledge: z.ZodOptional<z.ZodString>;
44
+ }, z.core.$strict>;
45
+ export declare const transactionSchema: z.ZodObject<{
46
+ id: z.ZodNullable<z.ZodString>;
47
+ date: z.ZodNullable<z.ZodString>;
48
+ amount: z.ZodNumber;
49
+ merchant: z.ZodNullable<z.ZodString>;
50
+ description: z.ZodNullable<z.ZodString>;
51
+ category: z.ZodNullable<z.ZodString>;
52
+ detailed_category: z.ZodNullable<z.ZodString>;
53
+ }, z.core.$strict>;
54
+ export declare const getTransactionsOutputSchema: z.ZodObject<{
55
+ generated_at: z.ZodNullable<z.ZodString>;
56
+ next_knowledge: z.ZodNullable<z.ZodString>;
57
+ txns: z.ZodArray<z.ZodObject<{
58
+ id: z.ZodNullable<z.ZodString>;
59
+ date: z.ZodNullable<z.ZodString>;
60
+ amount: z.ZodNumber;
61
+ merchant: z.ZodNullable<z.ZodString>;
62
+ description: z.ZodNullable<z.ZodString>;
63
+ category: z.ZodNullable<z.ZodString>;
64
+ detailed_category: z.ZodNullable<z.ZodString>;
65
+ }, z.core.$strict>>;
66
+ pagination: z.ZodObject<{
67
+ limit: z.ZodInt;
68
+ next_cursor: z.ZodNullable<z.ZodString>;
69
+ has_more: z.ZodBoolean;
70
+ }, z.core.$strict>;
71
+ }, z.core.$strict>;
72
+ export declare const snapshotSchema: z.ZodObject<{
73
+ date: z.ZodString;
74
+ net_worth: z.ZodNumber;
75
+ total_assets: z.ZodNumber;
76
+ total_liabilities: z.ZodNumber;
77
+ }, z.core.$strict>;
78
+ export declare const recurringSchema: z.ZodObject<{
79
+ merchant: z.ZodString;
80
+ amount: z.ZodNumber;
81
+ frequency: z.ZodString;
82
+ next_date: z.ZodNullable<z.ZodString>;
83
+ type: z.ZodNullable<z.ZodString>;
84
+ active: z.ZodBoolean;
85
+ }, z.core.$strict>;
86
+ export declare const budgetSchema: z.ZodObject<{
87
+ name: z.ZodString;
88
+ amount: z.ZodNumber;
89
+ spent: z.ZodNumber;
90
+ remaining: z.ZodNumber;
91
+ percent_used: z.ZodNumber;
92
+ period: z.ZodString;
93
+ status: z.ZodNullable<z.ZodString>;
94
+ }, z.core.$strict>;
95
+ export declare const goalSchema: z.ZodObject<{
96
+ name: z.ZodString;
97
+ target: z.ZodNumber;
98
+ current: z.ZodNumber;
99
+ progress_percent: z.ZodNumber;
100
+ target_date: z.ZodNullable<z.ZodString>;
101
+ status: z.ZodNullable<z.ZodString>;
102
+ category: z.ZodNullable<z.ZodString>;
103
+ monthly_contribution_needed: z.ZodNullable<z.ZodNumber>;
104
+ }, z.core.$strict>;
105
+ export declare const healthPillarsSchema: z.ZodObject<{
106
+ spend: z.ZodNullable<z.ZodNumber>;
107
+ save: z.ZodNullable<z.ZodNumber>;
108
+ borrow: z.ZodNullable<z.ZodNumber>;
109
+ build: z.ZodNullable<z.ZodNumber>;
110
+ }, z.core.$strict>;
111
+ export declare const liabilitySchema: z.ZodObject<{
112
+ account_id: z.ZodString;
113
+ type: z.ZodString;
114
+ balance: z.ZodNullable<z.ZodNumber>;
115
+ apr_percentage: z.ZodNullable<z.ZodNumber>;
116
+ minimum_payment: z.ZodNullable<z.ZodNumber>;
117
+ next_payment_due_date: z.ZodNullable<z.ZodString>;
118
+ is_overdue: z.ZodNullable<z.ZodBoolean>;
119
+ }, z.core.$strict>;
120
+ export declare const holdingSchema: z.ZodObject<{
121
+ account_id: z.ZodString;
122
+ security_name: z.ZodNullable<z.ZodString>;
123
+ symbol: z.ZodNullable<z.ZodString>;
124
+ type: z.ZodNullable<z.ZodString>;
125
+ quantity: z.ZodNumber;
126
+ value: z.ZodNullable<z.ZodNumber>;
127
+ cost_basis: z.ZodNullable<z.ZodNumber>;
128
+ }, z.core.$strict>;
129
+ export declare const categorySpendingSchema: z.ZodObject<{
130
+ category: z.ZodString;
131
+ amount: z.ZodNumber;
132
+ }, z.core.$strict>;
133
+ export declare const financialHealthSchema: z.ZodObject<{
134
+ score: z.ZodNullable<z.ZodNumber>;
135
+ label: z.ZodNullable<z.ZodString>;
136
+ data_completeness: z.ZodNullable<z.ZodNumber>;
137
+ pillars: z.ZodNullable<z.ZodObject<{
138
+ spend: z.ZodNullable<z.ZodNumber>;
139
+ save: z.ZodNullable<z.ZodNumber>;
140
+ borrow: z.ZodNullable<z.ZodNumber>;
141
+ build: z.ZodNullable<z.ZodNumber>;
142
+ }, z.core.$strict>>;
143
+ message: z.ZodNullable<z.ZodString>;
144
+ }, z.core.$strict>;
145
+ export declare const getFinancialStateOutputSchema: z.ZodObject<{
146
+ generated_at: z.ZodString;
147
+ snapshots: z.ZodArray<z.ZodObject<{
148
+ date: z.ZodString;
149
+ net_worth: z.ZodNumber;
150
+ total_assets: z.ZodNumber;
151
+ total_liabilities: z.ZodNumber;
152
+ }, z.core.$strict>>;
153
+ recurring: z.ZodArray<z.ZodObject<{
154
+ merchant: z.ZodString;
155
+ amount: z.ZodNumber;
156
+ frequency: z.ZodString;
157
+ next_date: z.ZodNullable<z.ZodString>;
158
+ type: z.ZodNullable<z.ZodString>;
159
+ active: z.ZodBoolean;
160
+ }, z.core.$strict>>;
161
+ budgets: z.ZodArray<z.ZodObject<{
162
+ name: z.ZodString;
163
+ amount: z.ZodNumber;
164
+ spent: z.ZodNumber;
165
+ remaining: z.ZodNumber;
166
+ percent_used: z.ZodNumber;
167
+ period: z.ZodString;
168
+ status: z.ZodNullable<z.ZodString>;
169
+ }, z.core.$strict>>;
170
+ goals: z.ZodArray<z.ZodObject<{
171
+ name: z.ZodString;
172
+ target: z.ZodNumber;
173
+ current: z.ZodNumber;
174
+ progress_percent: z.ZodNumber;
175
+ target_date: z.ZodNullable<z.ZodString>;
176
+ status: z.ZodNullable<z.ZodString>;
177
+ category: z.ZodNullable<z.ZodString>;
178
+ monthly_contribution_needed: z.ZodNullable<z.ZodNumber>;
179
+ }, z.core.$strict>>;
180
+ health: z.ZodObject<{
181
+ score: z.ZodNullable<z.ZodNumber>;
182
+ label: z.ZodNullable<z.ZodString>;
183
+ data_completeness: z.ZodNullable<z.ZodNumber>;
184
+ pillars: z.ZodNullable<z.ZodObject<{
185
+ spend: z.ZodNullable<z.ZodNumber>;
186
+ save: z.ZodNullable<z.ZodNumber>;
187
+ borrow: z.ZodNullable<z.ZodNumber>;
188
+ build: z.ZodNullable<z.ZodNumber>;
189
+ }, z.core.$strict>>;
190
+ message: z.ZodNullable<z.ZodString>;
191
+ }, z.core.$strict>;
192
+ liabilities: z.ZodArray<z.ZodObject<{
193
+ account_id: z.ZodString;
194
+ type: z.ZodString;
195
+ balance: z.ZodNullable<z.ZodNumber>;
196
+ apr_percentage: z.ZodNullable<z.ZodNumber>;
197
+ minimum_payment: z.ZodNullable<z.ZodNumber>;
198
+ next_payment_due_date: z.ZodNullable<z.ZodString>;
199
+ is_overdue: z.ZodNullable<z.ZodBoolean>;
200
+ }, z.core.$strict>>;
201
+ holdings: z.ZodArray<z.ZodObject<{
202
+ account_id: z.ZodString;
203
+ security_name: z.ZodNullable<z.ZodString>;
204
+ symbol: z.ZodNullable<z.ZodString>;
205
+ type: z.ZodNullable<z.ZodString>;
206
+ quantity: z.ZodNumber;
207
+ value: z.ZodNullable<z.ZodNumber>;
208
+ cost_basis: z.ZodNullable<z.ZodNumber>;
209
+ }, z.core.$strict>>;
210
+ category_spending: z.ZodArray<z.ZodObject<{
211
+ category: z.ZodString;
212
+ amount: z.ZodNumber;
213
+ }, z.core.$strict>>;
214
+ }, z.core.$strict>;
215
+ export declare const verifyKeyOutputSchema: z.ZodObject<{
216
+ valid: z.ZodBoolean;
217
+ status: z.ZodString;
218
+ }, z.core.$strict>;
219
+ export type Account = z.infer<typeof accountSchema>;
220
+ export type FinancialHealth = z.infer<typeof financialHealthSchema>;
221
+ export type GetAccountsOutput = z.infer<typeof getAccountsOutputSchema>;
222
+ export type GetFinancialStateOutput = z.infer<typeof getFinancialStateOutputSchema>;
223
+ export type GetTransactionsInput = z.infer<typeof getTransactionsInputSchema>;
224
+ export type GetTransactionsOutput = z.infer<typeof getTransactionsOutputSchema>;
225
+ export type VerifyKeyOutput = z.infer<typeof verifyKeyOutputSchema>;
@@ -0,0 +1,186 @@
1
+ import * as z from 'zod/v4';
2
+ const dateSchema = z
3
+ .string()
4
+ .regex(/^\d{4}-\d{2}-\d{2}$/, 'Expected date in YYYY-MM-DD format.');
5
+ const dateTimeSchema = z.string().datetime({ offset: true });
6
+ export const emptyInputSchema = z.object({}).strict().default({});
7
+ export const accountTypeSchema = z.enum([
8
+ 'depository',
9
+ 'credit',
10
+ 'loan',
11
+ 'investment',
12
+ 'other',
13
+ ]);
14
+ export const accountSchema = z
15
+ .object({
16
+ name: z.string(),
17
+ type: accountTypeSchema,
18
+ subtype: z.string().nullable(),
19
+ balance: z.number().finite(),
20
+ institution: z.string().nullable(),
21
+ mask: z.string().nullable(),
22
+ })
23
+ .strict();
24
+ export const getAccountsOutputSchema = z
25
+ .object({
26
+ accounts: z.array(accountSchema),
27
+ })
28
+ .strict();
29
+ const getTransactionsInputObjectSchema = z
30
+ .object({
31
+ limit: z
32
+ .int()
33
+ .min(1)
34
+ .max(1000)
35
+ .default(500)
36
+ .describe('Maximum transactions to return in this page (default 500, max 1000).'),
37
+ cursor: z
38
+ .string()
39
+ .min(1)
40
+ .optional()
41
+ .describe('Opaque pagination cursor from a previous get_transactions response.'),
42
+ last_knowledge: dateTimeSchema
43
+ .optional()
44
+ .describe('Incremental sync checkpoint from a prior get_transactions response. Cannot be combined with cursor.'),
45
+ })
46
+ .strict()
47
+ .superRefine((value, ctx) => {
48
+ if (value.cursor && value.last_knowledge) {
49
+ ctx.addIssue({
50
+ code: z.ZodIssueCode.custom,
51
+ path: ['cursor'],
52
+ message: '`cursor` cannot be combined with `last_knowledge`.',
53
+ });
54
+ }
55
+ });
56
+ export const getTransactionsInputSchema = getTransactionsInputObjectSchema;
57
+ export const transactionSchema = z
58
+ .object({
59
+ id: z.string().nullable(),
60
+ date: dateSchema.nullable(),
61
+ amount: z.number().finite(),
62
+ merchant: z.string().nullable(),
63
+ description: z.string().nullable(),
64
+ category: z.string().nullable(),
65
+ detailed_category: z.string().nullable(),
66
+ })
67
+ .strict();
68
+ export const getTransactionsOutputSchema = z
69
+ .object({
70
+ generated_at: dateTimeSchema.nullable(),
71
+ next_knowledge: dateTimeSchema.nullable(),
72
+ txns: z.array(transactionSchema),
73
+ pagination: z
74
+ .object({
75
+ limit: z.int().min(1).max(1000),
76
+ next_cursor: z.string().nullable(),
77
+ has_more: z.boolean(),
78
+ })
79
+ .strict(),
80
+ })
81
+ .strict();
82
+ export const snapshotSchema = z
83
+ .object({
84
+ date: dateSchema,
85
+ net_worth: z.number().finite(),
86
+ total_assets: z.number().finite(),
87
+ total_liabilities: z.number().finite(),
88
+ })
89
+ .strict();
90
+ export const recurringSchema = z
91
+ .object({
92
+ merchant: z.string(),
93
+ amount: z.number().finite(),
94
+ frequency: z.string(),
95
+ next_date: dateSchema.nullable(),
96
+ type: z.string().nullable(),
97
+ active: z.boolean(),
98
+ })
99
+ .strict();
100
+ export const budgetSchema = z
101
+ .object({
102
+ name: z.string(),
103
+ amount: z.number().finite(),
104
+ spent: z.number().finite(),
105
+ remaining: z.number().finite(),
106
+ percent_used: z.number().finite(),
107
+ period: z.string(),
108
+ status: z.string().nullable(),
109
+ })
110
+ .strict();
111
+ export const goalSchema = z
112
+ .object({
113
+ name: z.string(),
114
+ target: z.number().finite(),
115
+ current: z.number().finite(),
116
+ progress_percent: z.number().finite(),
117
+ target_date: dateSchema.nullable(),
118
+ status: z.string().nullable(),
119
+ category: z.string().nullable(),
120
+ monthly_contribution_needed: z.number().finite().nullable(),
121
+ })
122
+ .strict();
123
+ export const healthPillarsSchema = z
124
+ .object({
125
+ spend: z.number().finite().nullable(),
126
+ save: z.number().finite().nullable(),
127
+ borrow: z.number().finite().nullable(),
128
+ build: z.number().finite().nullable(),
129
+ })
130
+ .strict();
131
+ export const liabilitySchema = z
132
+ .object({
133
+ account_id: z.string(),
134
+ type: z.string(),
135
+ balance: z.number().finite().nullable(),
136
+ apr_percentage: z.number().finite().nullable(),
137
+ minimum_payment: z.number().finite().nullable(),
138
+ next_payment_due_date: dateSchema.nullable(),
139
+ is_overdue: z.boolean().nullable(),
140
+ })
141
+ .strict();
142
+ export const holdingSchema = z
143
+ .object({
144
+ account_id: z.string(),
145
+ security_name: z.string().nullable(),
146
+ symbol: z.string().nullable(),
147
+ type: z.string().nullable(),
148
+ quantity: z.number().finite(),
149
+ value: z.number().finite().nullable(),
150
+ cost_basis: z.number().finite().nullable(),
151
+ })
152
+ .strict();
153
+ export const categorySpendingSchema = z
154
+ .object({
155
+ category: z.string(),
156
+ amount: z.number().finite(),
157
+ })
158
+ .strict();
159
+ export const financialHealthSchema = z
160
+ .object({
161
+ score: z.number().finite().nullable(),
162
+ label: z.string().nullable(),
163
+ data_completeness: z.number().finite().nullable(),
164
+ pillars: healthPillarsSchema.nullable(),
165
+ message: z.string().nullable(),
166
+ })
167
+ .strict();
168
+ export const getFinancialStateOutputSchema = z
169
+ .object({
170
+ generated_at: dateTimeSchema,
171
+ snapshots: z.array(snapshotSchema),
172
+ recurring: z.array(recurringSchema),
173
+ budgets: z.array(budgetSchema),
174
+ goals: z.array(goalSchema),
175
+ health: financialHealthSchema,
176
+ liabilities: z.array(liabilitySchema),
177
+ holdings: z.array(holdingSchema),
178
+ category_spending: z.array(categorySpendingSchema),
179
+ })
180
+ .strict();
181
+ export const verifyKeyOutputSchema = z
182
+ .object({
183
+ valid: z.boolean(),
184
+ status: z.string(),
185
+ })
186
+ .strict();
package/dist/server.d.ts CHANGED
@@ -1,10 +1,8 @@
1
1
  /**
2
- * Warm MCP Server
2
+ * Warm MCP stdio entrypoint and compatibility exports.
3
3
  *
4
- * Provides financial data from the Warm API as MCP tools.
5
- * Reads API key from WARM_API_KEY env var or ~/.config/warm/api_key.
6
- *
7
- * Four read-only tools: get_accounts, get_transactions, get_snapshots, verify_key.
8
- * The AI client handles all analysis — no sandbox needed.
4
+ * The typed MCP contract and Warm API helpers live in `warm-server.ts`.
9
5
  */
10
- export {};
6
+ import { API_URL, WARM_SERVER_INFO, apiRequest, createWarmApiClient, createWarmServer, getConfiguredApiKey, verifyWarmApiKey } from './warm-server.js';
7
+ export { API_URL, WARM_SERVER_INFO, apiRequest, createWarmApiClient, createWarmServer, getConfiguredApiKey, verifyWarmApiKey, };
8
+ export declare function startStdioServer(): Promise<import("@modelcontextprotocol/sdk/server/mcp.js").McpServer>;