@warmio/mcp 5.0.1 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/schemas.js CHANGED
@@ -1,160 +1,247 @@
1
1
  import { z } from 'zod';
2
+ const monthSchema = z.string().regex(/^\d{4}-\d{2}$/, 'Expected YYYY-MM month.');
2
3
  const dateSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Expected YYYY-MM-DD date.');
3
4
  const dateTimeSchema = z.string().datetime({ offset: true });
4
5
  export const emptyInputSchema = {};
5
- export const accountTypeSchema = z.enum([
6
- 'depository',
7
- 'credit',
8
- 'loan',
9
- 'investment',
10
- 'other',
11
- ]);
12
- export const getAccountsOutputSchema = {
13
- accounts: z.array(z
14
- .object({
15
- balance: z.number().finite(),
16
- institution: z.string().nullable(),
17
- mask: z.string().nullable(),
18
- name: z.string(),
19
- subtype: z.string().nullable(),
20
- type: accountTypeSchema,
21
- })
22
- .strict()),
6
+ export const automationInputSchema = z
7
+ .object({
8
+ body: z.record(z.unknown()).optional(),
9
+ params: z.record(z.string()).optional(),
10
+ })
11
+ .strict();
12
+ export const searchOperationsInputSchema = {
13
+ query: z.string().max(200).optional().describe('Words describing the action or data you need.'),
23
14
  };
24
- export const getTransactionsInputSchema = {
25
- cursor: z
26
- .string()
27
- .min(1)
15
+ export const describeOperationInputSchema = {
16
+ operation_id: z.string().min(1),
17
+ input: automationInputSchema
28
18
  .optional()
29
- .describe('Opaque pagination cursor from a prior get_transactions response.'),
30
- last_knowledge: dateTimeSchema
31
- .optional()
32
- .describe('Incremental sync checkpoint. Cannot be combined with cursor.'),
33
- limit: z
34
- .number()
35
- .int()
36
- .min(1)
37
- .max(1000)
38
- .default(500)
39
- .describe('Maximum transactions to return (default 500, max 1000).'),
40
- search: z
19
+ .describe('Exact intended input. Required to obtain approval for a write operation.'),
20
+ };
21
+ export const invokeOperationInputSchema = {
22
+ operation_id: z.string().min(1),
23
+ input: automationInputSchema.optional(),
24
+ approval_id: z
41
25
  .string()
42
- .min(1)
43
- .max(120)
26
+ .uuid()
44
27
  .optional()
45
- .describe('Optional merchant or description search query.'),
46
- };
47
- export const getTransactionsOutputSchema = {
48
- generated_at: dateTimeSchema.nullable(),
49
- next_knowledge: dateTimeSchema.nullable(),
50
- pagination: z
51
- .object({
52
- has_more: z.boolean(),
53
- limit: z.number().int().min(1).max(1000),
54
- next_cursor: z.string().nullable(),
55
- })
56
- .strict(),
57
- txns: z.array(z
58
- .object({
59
- amount: z.number().finite(),
60
- category: z.string().nullable(),
61
- date: dateSchema.nullable(),
62
- description: z.string().nullable(),
63
- detailed_category: z.string().nullable(),
64
- id: z.string().nullable(),
65
- merchant: z.string().nullable(),
66
- })
67
- .strict()),
28
+ .describe('Approval ID returned by describe_operation for the exact write input.'),
68
29
  };
69
- export const getFinancialStateOutputSchema = {
70
- budgets: z.array(z
30
+ export const transactionIndexSchema = z
31
+ .object({
32
+ total: z.number().int().nonnegative(),
33
+ months: z.array(z
71
34
  .object({
72
- amount: z.number().finite(),
73
- name: z.string(),
74
- percent_used: z.number().finite(),
75
- period: z.string(),
76
- remaining: z.number().finite(),
77
- spent: z.number().finite(),
78
- status: z.string().nullable(),
35
+ month: monthSchema,
36
+ count: z.number().int().nonnegative(),
79
37
  })
80
38
  .strict()),
81
- category_spending: z.array(z
39
+ })
40
+ .strict();
41
+ export const positionSchema = z
42
+ .object({
43
+ date: dateSchema.nullable(),
44
+ net_worth: z.number().finite().nullable(),
45
+ cash: z.number().finite().nullable(),
46
+ debt: z.number().finite().nullable(),
47
+ investments: z.number().finite().nullable(),
48
+ other_assets: z.number().finite().nullable(),
49
+ total_assets: z.number().finite().nullable(),
50
+ })
51
+ .strict();
52
+ export const accountSchema = z
53
+ .object({
54
+ id: z.string(),
55
+ name: z.string(),
56
+ type: z.enum(['depository', 'credit', 'loan', 'investment', 'brokerage', 'other']),
57
+ subtype: z.string().nullable(),
58
+ group: z.enum(['cash', 'debt', 'investments']).nullable(),
59
+ institution: z.string().nullable(),
60
+ mask: z.string().nullable(),
61
+ balance: z.number().finite().nullable(),
62
+ available: z.number().finite().nullable(),
63
+ currency: z.string().nullable(),
64
+ updated_at: dateTimeSchema.nullable(),
65
+ })
66
+ .strict();
67
+ export const statusSchema = z
68
+ .object({
69
+ position: positionSchema.nullable(),
70
+ accounts: z.array(accountSchema),
71
+ })
72
+ .strict();
73
+ export const recurringSchema = z
74
+ .object({
75
+ id: z.string(),
76
+ account_id: z.string().nullable(),
77
+ direction: z.enum(['inflow', 'outflow']).nullable(),
78
+ frequency: z
79
+ .enum(['WEEKLY', 'BIWEEKLY', 'MONTHLY', 'QUARTERLY', 'SEMI_ANNUALLY', 'ANNUALLY'])
80
+ .nullable(),
81
+ status: z.enum(['active', 'inactive', 'dismissed']).nullable(),
82
+ merchant: z.string().nullable(),
83
+ amount: z.number().finite().nullable(),
84
+ next_date: dateSchema.nullable(),
85
+ })
86
+ .strict();
87
+ export const budgetSchema = z
88
+ .object({
89
+ id: z.string(),
90
+ name: z.string(),
91
+ type: z.enum(['category', 'merchant']),
92
+ period: z.enum(['weekly', 'biweekly', 'monthly']),
93
+ status: z.enum(['under', 'warning', 'over']),
94
+ amount: z.number().finite(),
95
+ spent: z.number().finite(),
96
+ remaining: z.number().finite(),
97
+ used_percent: z.number().finite(),
98
+ })
99
+ .strict();
100
+ export const goalSchema = z
101
+ .object({
102
+ id: z.string(),
103
+ name: z.string(),
104
+ category: z.string(),
105
+ status: z.enum(['not_started', 'in_progress', 'on_track', 'behind', 'completed']),
106
+ target: z.number().finite(),
107
+ current: z.number().finite(),
108
+ progress_percent: z.number().finite(),
109
+ target_date: dateSchema.nullable(),
110
+ })
111
+ .strict();
112
+ export const snapshotSchema = z
113
+ .object({
114
+ date: dateSchema,
115
+ net_worth: z.number().finite().nullable(),
116
+ cash: z.number().finite().nullable(),
117
+ debt: z.number().finite().nullable(),
118
+ income: z.number().finite().nullable(),
119
+ expenses: z.number().finite().nullable(),
120
+ cash_flow: z.number().finite().nullable(),
121
+ savings_rate: z.number().finite().nullable(),
122
+ investments: z.number().finite().nullable(),
123
+ assets: z.number().finite().nullable(),
124
+ health_score: z.number().finite().nullable(),
125
+ })
126
+ .strict();
127
+ export const liabilitySchema = z
128
+ .object({
129
+ account_id: z.string().nullable(),
130
+ type: z.enum(['credit', 'student', 'mortgage', 'other']).nullable(),
131
+ balance: z.number().finite().nullable(),
132
+ minimum_payment: z.number().finite().nullable(),
133
+ due_date: dateSchema.nullable(),
134
+ interest_rate: z.number().finite().nullable(),
135
+ rate_type: z.enum(['fixed', 'variable']).nullable(),
136
+ overdue: z.boolean().nullable(),
137
+ })
138
+ .strict();
139
+ export const holdingSchema = z
140
+ .object({
141
+ account_id: z.string().nullable(),
142
+ symbol: z.string().nullable(),
143
+ name: z.string().nullable(),
144
+ type: z
145
+ .enum(['cash', 'derivative', 'equity', 'etf', 'fixed_income', 'mutual_fund', 'other'])
146
+ .nullable(),
147
+ quantity: z.number().finite().nullable(),
148
+ value: z.number().finite().nullable(),
149
+ cost_basis: z.number().finite().nullable(),
150
+ })
151
+ .strict();
152
+ export const healthSchema = z
153
+ .object({
154
+ score: z.number().finite(),
155
+ label: z.enum(['Needs Attention', 'Good', 'Strong']),
156
+ level: z.enum(['critical', 'poor', 'fair', 'good', 'strong']),
157
+ summary: z.string(),
158
+ data_completeness: z.number().finite(),
159
+ pillars: z
82
160
  .object({
83
- amount: z.number().finite(),
84
- category: z.string(),
161
+ spend: z.number().finite().nullable(),
162
+ save: z.number().finite().nullable(),
163
+ borrow: z.number().finite().nullable(),
164
+ build: z.number().finite().nullable(),
85
165
  })
86
- .strict()),
166
+ .strict()
167
+ .nullable(),
168
+ })
169
+ .strict();
170
+ export const financialContextSchema = z
171
+ .object({
172
+ version: z.literal('v1'),
173
+ updated_at: dateTimeSchema,
174
+ currency: z.literal('USD'),
175
+ status: statusSchema,
176
+ transactions: transactionIndexSchema,
177
+ recurring: z.array(recurringSchema),
178
+ budgets: z.array(budgetSchema),
179
+ goals: z.array(goalSchema),
180
+ snapshots: z.array(snapshotSchema),
181
+ liabilities: z.array(liabilitySchema),
182
+ holdings: z.array(holdingSchema),
183
+ health: healthSchema.nullable(),
184
+ })
185
+ .strict();
186
+ export const transactionSchema = z
187
+ .object({
188
+ id: z.string(),
189
+ account_id: z.string().nullable(),
190
+ date: dateSchema.nullable(),
191
+ amount: z.number().finite(),
192
+ merchant: z.string().nullable(),
193
+ name: z.string().nullable(),
194
+ category: z.string().nullable(),
195
+ subcategory: z.string().nullable(),
196
+ pending: z.boolean().nullable(),
197
+ currency: z.string().nullable(),
198
+ })
199
+ .strict();
200
+ export const transactionMonthSchema = z
201
+ .object({
202
+ month: monthSchema,
203
+ start_date: dateSchema,
204
+ end_date: dateSchema,
205
+ count: z.number().int().nonnegative(),
206
+ items: z.array(transactionSchema),
207
+ })
208
+ .strict();
209
+ export const latestTransactionsSchema = z
210
+ .object({
211
+ since: dateSchema,
212
+ window_days: z.literal(10),
213
+ count: z.number().int().nonnegative(),
214
+ items: z.array(transactionSchema),
215
+ })
216
+ .strict();
217
+ export const getTransactionsInputSchema = {
218
+ month: monthSchema
219
+ .optional()
220
+ .describe('Transaction month in YYYY-MM format. Mutually exclusive with latest. Months outside the covered range return an error.'),
221
+ latest: z
222
+ .boolean()
223
+ .optional()
224
+ .describe('When true, return the fixed 10-day latest window. Mutually exclusive with month. A bare call with no arguments defaults to latest: true.'),
225
+ };
226
+ export const financialContextMetaSchema = z
227
+ .object({
228
+ version: z.literal('v1'),
229
+ user_id: z.string(),
230
+ context_id: z.string(),
87
231
  generated_at: dateTimeSchema,
88
- goals: z.array(z
232
+ updated_at: dateTimeSchema,
233
+ content_hash: z.string(),
234
+ byte_length: z.number().int().nonnegative(),
235
+ counts: z
89
236
  .object({
90
- category: z.string().nullable(),
91
- current: z.number().finite(),
92
- monthly_contribution_needed: z.number().finite().nullable(),
93
- name: z.string(),
94
- progress_percent: z.number().finite(),
95
- status: z.string().nullable(),
96
- target: z.number().finite(),
97
- target_date: dateSchema.nullable(),
98
- })
99
- .strict()),
100
- health: z
101
- .object({
102
- data_completeness: z.number().finite().nullable(),
103
- label: z.string().nullable(),
104
- message: z.string().nullable(),
105
- pillars: z
106
- .object({
107
- borrow: z.number().finite().nullable(),
108
- build: z.number().finite().nullable(),
109
- save: z.number().finite().nullable(),
110
- spend: z.number().finite().nullable(),
111
- })
112
- .strict()
113
- .nullable(),
114
- score: z.number().finite().nullable(),
237
+ accounts: z.number().int().nonnegative(),
238
+ transaction_months: z.number().int().nonnegative(),
239
+ transactions: z.number().int().nonnegative(),
240
+ snapshots: z.number().int().nonnegative(),
115
241
  })
116
242
  .strict(),
117
- holdings: z.array(z
118
- .object({
119
- account_id: z.string(),
120
- cost_basis: z.number().finite().nullable(),
121
- quantity: z.number().finite(),
122
- security_name: z.string().nullable(),
123
- symbol: z.string().nullable(),
124
- type: z.string().nullable(),
125
- value: z.number().finite().nullable(),
126
- })
127
- .strict()),
128
- liabilities: z.array(z
129
- .object({
130
- account_id: z.string(),
131
- apr_percentage: z.number().finite().nullable(),
132
- balance: z.number().finite().nullable(),
133
- is_overdue: z.boolean().nullable(),
134
- minimum_payment: z.number().finite().nullable(),
135
- next_payment_due_date: dateSchema.nullable(),
136
- type: z.string(),
137
- })
138
- .strict()),
139
- recurring: z.array(z
140
- .object({
141
- active: z.boolean(),
142
- amount: z.number().finite(),
143
- frequency: z.string(),
144
- merchant: z.string(),
145
- next_date: dateSchema.nullable(),
146
- type: z.string().nullable(),
147
- })
148
- .strict()),
149
- snapshots: z.array(z
150
- .object({
151
- date: dateSchema,
152
- net_worth: z.number().finite(),
153
- total_assets: z.number().finite(),
154
- total_liabilities: z.number().finite(),
155
- })
156
- .strict()),
157
- };
243
+ })
244
+ .strict();
158
245
  export const verifyKeyOutputSchema = {
159
246
  status: z.string(),
160
247
  valid: z.boolean(),
package/dist/server.d.ts CHANGED
@@ -1,15 +1,17 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { type Implementation } from '@modelcontextprotocol/sdk/types.js';
3
- import { createWarmApiClient, type WarmApiClientOptions, getConfiguredApiKey, verifyWarmApiKey, apiRequest } from './warm-api-client.js';
4
- export { apiRequest, createWarmApiClient, getConfiguredApiKey, verifyWarmApiKey };
3
+ import { createWarmApiClient, type WarmApiClientOptions, getConfiguredApiKey, verifyWarmApiKey, WarmApiError } from './warm-api-client.js';
4
+ export { createWarmApiClient, getConfiguredApiKey, verifyWarmApiKey, WarmApiError };
5
5
  export type { WarmApiClientOptions } from './warm-api-client.js';
6
6
  export interface WarmServerOptions extends WarmApiClientOptions {
7
+ mode?: WarmServerMode;
7
8
  serverInfo?: Implementation;
8
9
  }
10
+ export type WarmServerMode = 'automation' | 'context';
9
11
  export declare const API_URL: string;
10
12
  export declare const WARM_SERVER_INFO: {
11
13
  name: string;
12
14
  version: string;
13
15
  };
14
- export declare function registerWarmTools(server: McpServer, options?: WarmApiClientOptions): void;
16
+ export declare function registerWarmTools(server: McpServer, options?: WarmApiClientOptions, mode?: WarmServerMode): void;
15
17
  export declare function createWarmServer(options?: WarmServerOptions): McpServer;
package/dist/server.js CHANGED
@@ -3,10 +3,10 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { toJsonSchemaCompat } from '@modelcontextprotocol/sdk/server/zod-json-schema-compat.js';
4
4
  import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
5
5
  import { z } from 'zod';
6
- import { createWarmApiClient, getConfiguredApiKey, verifyWarmApiKey, apiRequest, } from './warm-api-client.js';
7
- import { emptyInputSchema, getTransactionsInputSchema, } from './schemas.js';
8
- export { apiRequest, createWarmApiClient, getConfiguredApiKey, verifyWarmApiKey };
9
- export const API_URL = process.env.WARM_API_URL || 'https://warm.io';
6
+ import { createWarmApiClient, getConfiguredApiKey, verifyWarmApiKey, WarmApiError, } from './warm-api-client.js';
7
+ import { describeOperationInputSchema, emptyInputSchema, getTransactionsInputSchema, invokeOperationInputSchema, searchOperationsInputSchema, } from './schemas.js';
8
+ export { createWarmApiClient, getConfiguredApiKey, verifyWarmApiKey, WarmApiError };
9
+ export const API_URL = process.env.WARM_API_URL || 'https://app.warm.io';
10
10
  function getPackageVersion() {
11
11
  try {
12
12
  const packageJson = fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8');
@@ -21,7 +21,7 @@ export const WARM_SERVER_INFO = {
21
21
  name: 'warm',
22
22
  version: getPackageVersion(),
23
23
  };
24
- function createStructuredToolResult(structuredContent) {
24
+ function createStructuredToolResult(structuredContent, isError = false) {
25
25
  return {
26
26
  content: [
27
27
  {
@@ -29,48 +29,91 @@ function createStructuredToolResult(structuredContent) {
29
29
  text: JSON.stringify(structuredContent, null, 2),
30
30
  },
31
31
  ],
32
+ ...(isError ? { isError: true } : {}),
32
33
  structuredContent,
33
34
  };
34
35
  }
35
- export function registerWarmTools(server, options = {}) {
36
- const client = createWarmApiClient(options);
37
- const tools = [
36
+ function createToolFailure(error) {
37
+ if (error instanceof WarmApiError) {
38
+ return createStructuredToolResult({
39
+ body: error.body,
40
+ headers: error.headers,
41
+ message: error.message,
42
+ status: error.status,
43
+ }, true);
44
+ }
45
+ return createStructuredToolResult({
46
+ error: {
47
+ message: error instanceof Error ? error.message : String(error),
48
+ },
49
+ }, true);
50
+ }
51
+ function parseGetTransactionsArgs(args) {
52
+ const hasMonth = typeof args.month === 'string';
53
+ const hasLatestKey = Object.prototype.hasOwnProperty.call(args, 'latest');
54
+ const hasLatest = args.latest === true;
55
+ if (hasMonth && hasLatestKey) {
56
+ throw new Error('`month` and `latest` are mutually exclusive.');
57
+ }
58
+ if (hasMonth) {
59
+ return { month: args.month };
60
+ }
61
+ if (hasLatest || Object.keys(args).length === 0) {
62
+ return { latest: true };
63
+ }
64
+ throw new Error('Call get_transactions with `month` in YYYY-MM format, `latest: true`, or no arguments.');
65
+ }
66
+ export function registerWarmTools(server, options = {}, mode = 'context') {
67
+ const client = createWarmApiClient({ ...options, audience: mode });
68
+ const contextTools = [
38
69
  {
39
- description: 'Read-only list of connected financial accounts with balances, subtypes, institutions, and masks when available.',
40
- handler: async () => await client.getAccounts(),
70
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
71
+ description: 'Read-only compact FinancialContext JSON. Includes status.position, status.accounts, transaction index total/months, recurring, budgets, goals, snapshots, liabilities, holdings, and health. Transaction items are not inline; use get_transactions for items.',
72
+ handler: async () => await client.getFinancialContext(),
41
73
  inputShape: emptyInputSchema,
42
- name: 'get_accounts',
74
+ name: 'get_financial_context',
43
75
  },
44
76
  {
45
- description: 'Read-only transaction export with strict opaque cursor pagination and optional last_knowledge incremental sync.',
77
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
78
+ description: 'Read-only transactions from the FinancialContext artifact. Pass exactly one selector: `month` in YYYY-MM format for a month page, or `latest: true` for the fixed latest window. A bare call with no arguments defaults to `latest: true`. The latest window is fixed at 10 days and is not caller-configurable. `month` and `latest` are mutually exclusive. Months outside the covered range return an error.',
46
79
  handler: async (args) => {
47
- const transactionArgs = {
48
- cursor: typeof args.cursor === 'string' ? args.cursor : undefined,
49
- last_knowledge: typeof args.last_knowledge === 'string' ? args.last_knowledge : undefined,
50
- limit: typeof args.limit === 'number' ? args.limit : 500,
51
- search: typeof args.search === 'string' ? args.search : undefined,
52
- };
53
- if (transactionArgs.cursor && transactionArgs.last_knowledge) {
54
- throw new Error('`cursor` cannot be combined with `last_knowledge`.');
55
- }
56
- return await client.getTransactions(transactionArgs);
80
+ return await client.getTransactions(parseGetTransactionsArgs(args));
57
81
  },
58
82
  inputShape: getTransactionsInputSchema,
59
83
  name: 'get_transactions',
60
84
  },
61
85
  {
62
- description: 'Read-only broad financial state bundle with snapshots, recurring payments, budgets, goals, financial health, liabilities, holdings, and category spending.',
63
- handler: async () => await client.getFinancialState(),
64
- inputShape: emptyInputSchema,
65
- name: 'get_financial_state',
66
- },
67
- {
86
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
68
87
  description: 'Read-only API key validation for the configured Warm account.',
69
- handler: async () => await client.verifyKey(),
88
+ handler: async () => await client.validateAudience(),
70
89
  inputShape: emptyInputSchema,
71
90
  name: 'verify_key',
72
91
  },
73
92
  ];
93
+ const automationTools = [
94
+ {
95
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
96
+ description: 'Search the supported Warm operation catalog. Use this first to find the smallest matching operation.',
97
+ handler: async (args) => await client.searchOperations(typeof args.query === 'string' ? args.query : undefined),
98
+ inputShape: searchOperationsInputSchema,
99
+ name: 'search_operations',
100
+ },
101
+ {
102
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
103
+ description: 'Describe one operation, including its input schema and risk. For write operations, pass the exact intended input to receive an approval with its ID, status, URL, and expiry.',
104
+ handler: async (args) => await client.describeOperation(args.operation_id, args.input),
105
+ inputShape: describeOperationInputSchema,
106
+ name: 'describe_operation',
107
+ },
108
+ {
109
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
110
+ description: 'Invoke one previously discovered Warm operation. Every write operation requires the approval_id returned for the exact same input by describe_operation.',
111
+ handler: async (args) => await client.invokeOperation(args.operation_id, args.input, typeof args.approval_id === 'string' ? args.approval_id : undefined),
112
+ inputShape: invokeOperationInputSchema,
113
+ name: 'invoke_operation',
114
+ },
115
+ ];
116
+ const tools = mode === 'automation' ? automationTools : contextTools;
74
117
  server.server.registerCapabilities({
75
118
  tools: {
76
119
  listChanged: true,
@@ -79,6 +122,7 @@ export function registerWarmTools(server, options = {}) {
79
122
  server.server.setRequestHandler(ListToolsRequestSchema, () => ({
80
123
  tools: tools.map((tool) => ({
81
124
  description: tool.description,
125
+ annotations: tool.annotations,
82
126
  inputSchema: toJsonSchemaCompat(z.object(tool.inputShape).strict(), {
83
127
  pipeStrategy: 'input',
84
128
  strictUnions: true,
@@ -96,12 +140,32 @@ export function registerWarmTools(server, options = {}) {
96
140
  if (!parseResult.success) {
97
141
  throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for tool ${request.params.name}: ${parseResult.error.message}`);
98
142
  }
99
- return createStructuredToolResult(await tool.handler(parseResult.data));
143
+ if (tool.name === 'get_transactions') {
144
+ try {
145
+ parseGetTransactionsArgs(parseResult.data);
146
+ }
147
+ catch (error) {
148
+ throw new McpError(ErrorCode.InvalidParams, error instanceof Error ? error.message : String(error));
149
+ }
150
+ }
151
+ try {
152
+ if (tool.name !== 'verify_key') {
153
+ await client.validateAudience();
154
+ }
155
+ const result = await tool.handler(parseResult.data);
156
+ const isFailedAutomationOperation = tool.name === 'invoke_operation' &&
157
+ typeof result.status === 'number' &&
158
+ result.status >= 400;
159
+ return createStructuredToolResult(result, isFailedAutomationOperation);
160
+ }
161
+ catch (error) {
162
+ return createToolFailure(error);
163
+ }
100
164
  });
101
165
  }
102
166
  export function createWarmServer(options = {}) {
103
- const { serverInfo, ...clientOptions } = options;
167
+ const { mode = 'context', serverInfo, ...clientOptions } = options;
104
168
  const server = new McpServer(serverInfo || WARM_SERVER_INFO);
105
- registerWarmTools(server, clientOptions);
169
+ registerWarmTools(server, clientOptions, mode);
106
170
  return server;
107
171
  }
package/dist/types.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- export declare const WARM_TOOL_NAMES: readonly ["get_accounts", "get_transactions", "get_financial_state", "verify_key"];
1
+ export declare const WARM_CONTEXT_TOOL_NAMES: readonly ["get_financial_context", "get_transactions", "verify_key"];
2
+ export declare const WARM_AUTOMATION_TOOL_NAMES: readonly ["search_operations", "describe_operation", "invoke_operation"];
3
+ export declare const WARM_TOOL_NAMES: readonly ["get_financial_context", "get_transactions", "verify_key", "search_operations", "describe_operation", "invoke_operation"];
2
4
  export type WarmToolName = (typeof WARM_TOOL_NAMES)[number];
3
- export type { GetAccountsOutput, GetTransactionsInput, GetTransactionsOutput, VerifyKeyOutput, WarmApiAccount, WarmApiBudget, WarmApiClient, WarmApiClientOptions, WarmApiGoal, WarmApiHolding, WarmApiLiability, WarmApiTransaction, WarmFinancialState, } from './warm-api-client.js';
5
+ export type { Account, AutomationInput, AutomationOperation, AutomationOperationDescription, Budget, DescribeOperationOutput, FinancialContext, FinancialContextMeta, GetTransactionsInput, GetTransactionsOutput, Goal, Health, Holding, InvokeOperationOutput, LatestTransactions, Liability, Position, Recurring, Snapshot, Status, Transaction, TransactionIndex, TransactionMonth, VerifyKeyOutput, WarmApiClient, WarmApiClientOptions, } from './warm-api-client.js';
6
+ export type { WarmApiAudience } from './config-paths.js';
4
7
  export type { WarmServerOptions } from './server.js';
package/dist/types.js CHANGED
@@ -1,6 +1,11 @@
1
- export const WARM_TOOL_NAMES = [
2
- 'get_accounts',
1
+ export const WARM_CONTEXT_TOOL_NAMES = [
2
+ 'get_financial_context',
3
3
  'get_transactions',
4
- 'get_financial_state',
5
4
  'verify_key',
6
5
  ];
6
+ export const WARM_AUTOMATION_TOOL_NAMES = [
7
+ 'search_operations',
8
+ 'describe_operation',
9
+ 'invoke_operation',
10
+ ];
11
+ export const WARM_TOOL_NAMES = [...WARM_CONTEXT_TOOL_NAMES, ...WARM_AUTOMATION_TOOL_NAMES];