@striderlabs/mcp-chase 1.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/README.md +100 -0
- package/dist/auth.js +72 -0
- package/dist/browser.js +394 -0
- package/dist/index.js +398 -0
- package/package.json +39 -0
- package/server.json +20 -0
- package/src/auth.ts +83 -0
- package/src/browser.ts +530 -0
- package/src/index.ts +460 -0
- package/tsconfig.json +15 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Strider Labs Chase Bank MCP Server
|
|
4
|
+
*
|
|
5
|
+
* MCP server that gives AI agents the ability to check accounts,
|
|
6
|
+
* view transactions, and manage banking operations.
|
|
7
|
+
* https://striderlabs.ai
|
|
8
|
+
*/
|
|
9
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
10
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
12
|
+
import { checkAuth, getAccounts, getTransactions, getBalance, getBills, getTransfers, getStatements, getRewards, initiateTransfer, payBill, getLoginUrl, cleanup, } from "./browser.js";
|
|
13
|
+
import { hasStoredCookies, clearCookies, getCookiesPath } from "./auth.js";
|
|
14
|
+
// Initialize server
|
|
15
|
+
const server = new Server({
|
|
16
|
+
name: "strider-chase",
|
|
17
|
+
version: "1.0.0",
|
|
18
|
+
}, {
|
|
19
|
+
capabilities: {
|
|
20
|
+
tools: {},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
// Tool definitions
|
|
24
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
25
|
+
return {
|
|
26
|
+
tools: [
|
|
27
|
+
{
|
|
28
|
+
name: "chase_auth_check",
|
|
29
|
+
description: "Check if user is logged in to Chase. Returns login status and instructions if not authenticated. Call this before any other Chase operations.",
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "chase_auth_clear",
|
|
37
|
+
description: "Clear stored Chase session cookies. Use this to log out or reset authentication state.",
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: "object",
|
|
40
|
+
properties: {},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "chase_accounts",
|
|
45
|
+
description: "Get all Chase accounts - checking, savings, credit cards, investments, and loans.",
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: "object",
|
|
48
|
+
properties: {},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "chase_balance",
|
|
53
|
+
description: "Get the current balance for a specific account.",
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: "object",
|
|
56
|
+
properties: {
|
|
57
|
+
accountId: {
|
|
58
|
+
type: "string",
|
|
59
|
+
description: "The account ID to check balance for",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
required: ["accountId"],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "chase_transactions",
|
|
67
|
+
description: "Get recent transactions for an account.",
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: {
|
|
71
|
+
accountId: {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: "The account ID to get transactions for",
|
|
74
|
+
},
|
|
75
|
+
limit: {
|
|
76
|
+
type: "number",
|
|
77
|
+
description: "Maximum number of transactions to return (default: 25)",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
required: ["accountId"],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "chase_bills",
|
|
85
|
+
description: "Get bills and payees set up in Chase Bill Pay.",
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: "object",
|
|
88
|
+
properties: {},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "chase_transfers",
|
|
93
|
+
description: "Get recent transfer history.",
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: {},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "chase_statements",
|
|
101
|
+
description: "Get available statements for an account.",
|
|
102
|
+
inputSchema: {
|
|
103
|
+
type: "object",
|
|
104
|
+
properties: {
|
|
105
|
+
accountId: {
|
|
106
|
+
type: "string",
|
|
107
|
+
description: "The account ID to get statements for",
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
required: ["accountId"],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "chase_rewards",
|
|
115
|
+
description: "Get Ultimate Rewards points and cash back balance.",
|
|
116
|
+
inputSchema: {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: "chase_transfer_preview",
|
|
123
|
+
description: "Preview a transfer between accounts. Does NOT execute the transfer - provides instructions for completing it manually.",
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: {
|
|
127
|
+
fromAccountId: {
|
|
128
|
+
type: "string",
|
|
129
|
+
description: "Source account ID",
|
|
130
|
+
},
|
|
131
|
+
toAccountId: {
|
|
132
|
+
type: "string",
|
|
133
|
+
description: "Destination account ID",
|
|
134
|
+
},
|
|
135
|
+
amount: {
|
|
136
|
+
type: "number",
|
|
137
|
+
description: "Amount to transfer",
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
required: ["fromAccountId", "toAccountId", "amount"],
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "chase_bill_pay_preview",
|
|
145
|
+
description: "Preview a bill payment. Does NOT execute the payment - provides instructions for completing it manually.",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
type: "object",
|
|
148
|
+
properties: {
|
|
149
|
+
payeeId: {
|
|
150
|
+
type: "string",
|
|
151
|
+
description: "Payee ID to pay",
|
|
152
|
+
},
|
|
153
|
+
amount: {
|
|
154
|
+
type: "number",
|
|
155
|
+
description: "Amount to pay",
|
|
156
|
+
},
|
|
157
|
+
date: {
|
|
158
|
+
type: "string",
|
|
159
|
+
description: "Payment date (optional, defaults to immediately)",
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
required: ["payeeId", "amount"],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
// Tool execution
|
|
169
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
170
|
+
const { name, arguments: args } = request.params;
|
|
171
|
+
try {
|
|
172
|
+
switch (name) {
|
|
173
|
+
case "chase_auth_check": {
|
|
174
|
+
const hasCookies = hasStoredCookies();
|
|
175
|
+
if (!hasCookies) {
|
|
176
|
+
const loginInfo = await getLoginUrl();
|
|
177
|
+
return {
|
|
178
|
+
content: [
|
|
179
|
+
{
|
|
180
|
+
type: "text",
|
|
181
|
+
text: JSON.stringify({
|
|
182
|
+
success: true,
|
|
183
|
+
isLoggedIn: false,
|
|
184
|
+
message: "Not logged in to Chase.",
|
|
185
|
+
loginUrl: loginInfo.url,
|
|
186
|
+
instructions: loginInfo.instructions,
|
|
187
|
+
cookiesPath: getCookiesPath(),
|
|
188
|
+
}),
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const authState = await checkAuth();
|
|
194
|
+
if (!authState.isLoggedIn) {
|
|
195
|
+
const loginInfo = await getLoginUrl();
|
|
196
|
+
return {
|
|
197
|
+
content: [
|
|
198
|
+
{
|
|
199
|
+
type: "text",
|
|
200
|
+
text: JSON.stringify({
|
|
201
|
+
success: true,
|
|
202
|
+
isLoggedIn: false,
|
|
203
|
+
message: "Session expired. Please log in again.",
|
|
204
|
+
loginUrl: loginInfo.url,
|
|
205
|
+
instructions: loginInfo.instructions,
|
|
206
|
+
}),
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
content: [
|
|
213
|
+
{
|
|
214
|
+
type: "text",
|
|
215
|
+
text: JSON.stringify({
|
|
216
|
+
success: true,
|
|
217
|
+
isLoggedIn: true,
|
|
218
|
+
message: "Logged in to Chase.",
|
|
219
|
+
accountHolder: authState.accountHolder,
|
|
220
|
+
}),
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
case "chase_auth_clear": {
|
|
226
|
+
clearCookies();
|
|
227
|
+
return {
|
|
228
|
+
content: [
|
|
229
|
+
{
|
|
230
|
+
type: "text",
|
|
231
|
+
text: JSON.stringify({
|
|
232
|
+
success: true,
|
|
233
|
+
message: "Chase session cleared. You will need to log in again.",
|
|
234
|
+
}),
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
case "chase_accounts": {
|
|
240
|
+
const result = await getAccounts();
|
|
241
|
+
return {
|
|
242
|
+
content: [
|
|
243
|
+
{
|
|
244
|
+
type: "text",
|
|
245
|
+
text: JSON.stringify(result),
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
isError: !result.success,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
case "chase_balance": {
|
|
252
|
+
const { accountId } = args;
|
|
253
|
+
const result = await getBalance(accountId);
|
|
254
|
+
return {
|
|
255
|
+
content: [
|
|
256
|
+
{
|
|
257
|
+
type: "text",
|
|
258
|
+
text: JSON.stringify(result),
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
isError: !result.success,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
case "chase_transactions": {
|
|
265
|
+
const { accountId, limit } = args;
|
|
266
|
+
const result = await getTransactions(accountId, limit);
|
|
267
|
+
return {
|
|
268
|
+
content: [
|
|
269
|
+
{
|
|
270
|
+
type: "text",
|
|
271
|
+
text: JSON.stringify(result),
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
isError: !result.success,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
case "chase_bills": {
|
|
278
|
+
const result = await getBills();
|
|
279
|
+
return {
|
|
280
|
+
content: [
|
|
281
|
+
{
|
|
282
|
+
type: "text",
|
|
283
|
+
text: JSON.stringify(result),
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
isError: !result.success,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
case "chase_transfers": {
|
|
290
|
+
const result = await getTransfers();
|
|
291
|
+
return {
|
|
292
|
+
content: [
|
|
293
|
+
{
|
|
294
|
+
type: "text",
|
|
295
|
+
text: JSON.stringify(result),
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
isError: !result.success,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
case "chase_statements": {
|
|
302
|
+
const { accountId } = args;
|
|
303
|
+
const result = await getStatements(accountId);
|
|
304
|
+
return {
|
|
305
|
+
content: [
|
|
306
|
+
{
|
|
307
|
+
type: "text",
|
|
308
|
+
text: JSON.stringify(result),
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
isError: !result.success,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
case "chase_rewards": {
|
|
315
|
+
const result = await getRewards();
|
|
316
|
+
return {
|
|
317
|
+
content: [
|
|
318
|
+
{
|
|
319
|
+
type: "text",
|
|
320
|
+
text: JSON.stringify(result),
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
isError: !result.success,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
case "chase_transfer_preview": {
|
|
327
|
+
const { fromAccountId, toAccountId, amount } = args;
|
|
328
|
+
const result = await initiateTransfer(fromAccountId, toAccountId, amount);
|
|
329
|
+
return {
|
|
330
|
+
content: [
|
|
331
|
+
{
|
|
332
|
+
type: "text",
|
|
333
|
+
text: JSON.stringify(result),
|
|
334
|
+
},
|
|
335
|
+
],
|
|
336
|
+
isError: !result.success,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
case "chase_bill_pay_preview": {
|
|
340
|
+
const { payeeId, amount, date } = args;
|
|
341
|
+
const result = await payBill(payeeId, amount, date);
|
|
342
|
+
return {
|
|
343
|
+
content: [
|
|
344
|
+
{
|
|
345
|
+
type: "text",
|
|
346
|
+
text: JSON.stringify(result),
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
isError: !result.success,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
default:
|
|
353
|
+
return {
|
|
354
|
+
content: [
|
|
355
|
+
{
|
|
356
|
+
type: "text",
|
|
357
|
+
text: JSON.stringify({
|
|
358
|
+
success: false,
|
|
359
|
+
error: `Unknown tool: ${name}`,
|
|
360
|
+
}),
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
isError: true,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
369
|
+
return {
|
|
370
|
+
content: [
|
|
371
|
+
{
|
|
372
|
+
type: "text",
|
|
373
|
+
text: JSON.stringify({
|
|
374
|
+
success: false,
|
|
375
|
+
error: errorMessage,
|
|
376
|
+
}),
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
isError: true,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
// Cleanup on exit
|
|
384
|
+
process.on("SIGINT", async () => {
|
|
385
|
+
await cleanup();
|
|
386
|
+
process.exit(0);
|
|
387
|
+
});
|
|
388
|
+
process.on("SIGTERM", async () => {
|
|
389
|
+
await cleanup();
|
|
390
|
+
process.exit(0);
|
|
391
|
+
});
|
|
392
|
+
// Start server
|
|
393
|
+
async function main() {
|
|
394
|
+
const transport = new StdioServerTransport();
|
|
395
|
+
await server.connect(transport);
|
|
396
|
+
console.error("Strider Chase MCP server running");
|
|
397
|
+
}
|
|
398
|
+
main().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@striderlabs/mcp-chase",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Chase Bank - let AI agents check accounts and manage finances",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"striderlabs-mcp-chase": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"chase",
|
|
17
|
+
"banking",
|
|
18
|
+
"finance",
|
|
19
|
+
"ai-agent",
|
|
20
|
+
"strider",
|
|
21
|
+
"model-context-protocol"
|
|
22
|
+
],
|
|
23
|
+
"author": "Strider Labs <hello@striderlabs.ai>",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/markswendsen-code/mcp-chase"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://striderlabs.ai",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
32
|
+
"patchright": "^1.58.2"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.11.0",
|
|
36
|
+
"typescript": "^5.3.3"
|
|
37
|
+
},
|
|
38
|
+
"mcpName": "io.github.markswendsen-code/chase"
|
|
39
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.markswendsen-code/chase",
|
|
4
|
+
"description": "MCP server for Chase Bank - let AI agents check accounts and manage finances",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/markswendsen-code/mcp-chase",
|
|
7
|
+
"source": "github"
|
|
8
|
+
},
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"packages": [
|
|
11
|
+
{
|
|
12
|
+
"registryType": "npm",
|
|
13
|
+
"identifier": "@striderlabs/mcp-chase",
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"transport": {
|
|
16
|
+
"type": "stdio"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
package/src/auth.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chase Bank Authentication Utilities
|
|
3
|
+
*
|
|
4
|
+
* Manages cookie storage and session persistence.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
import * as os from "os";
|
|
10
|
+
import { BrowserContext } from "patchright";
|
|
11
|
+
|
|
12
|
+
const COOKIES_DIR = path.join(os.homedir(), ".strider", "chase");
|
|
13
|
+
const COOKIES_PATH = path.join(COOKIES_DIR, "cookies.json");
|
|
14
|
+
|
|
15
|
+
export interface AuthState {
|
|
16
|
+
isLoggedIn: boolean;
|
|
17
|
+
accountHolder?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get the path to the cookies file
|
|
22
|
+
*/
|
|
23
|
+
export function getCookiesPath(): string {
|
|
24
|
+
return COOKIES_PATH;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if cookies file exists
|
|
29
|
+
*/
|
|
30
|
+
export function hasStoredCookies(): boolean {
|
|
31
|
+
return fs.existsSync(COOKIES_PATH);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Save cookies to disk
|
|
36
|
+
*/
|
|
37
|
+
export async function saveCookies(context: BrowserContext): Promise<void> {
|
|
38
|
+
try {
|
|
39
|
+
if (!fs.existsSync(COOKIES_DIR)) {
|
|
40
|
+
fs.mkdirSync(COOKIES_DIR, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
const cookies = await context.cookies();
|
|
43
|
+
fs.writeFileSync(COOKIES_PATH, JSON.stringify(cookies, null, 2));
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error("Failed to save cookies:", error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Load cookies from disk into context
|
|
51
|
+
*/
|
|
52
|
+
export async function loadCookies(context: BrowserContext): Promise<void> {
|
|
53
|
+
try {
|
|
54
|
+
if (fs.existsSync(COOKIES_PATH)) {
|
|
55
|
+
const cookies = JSON.parse(fs.readFileSync(COOKIES_PATH, "utf-8"));
|
|
56
|
+
await context.addCookies(cookies);
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("Failed to load cookies:", error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Clear stored cookies
|
|
65
|
+
*/
|
|
66
|
+
export function clearCookies(): void {
|
|
67
|
+
try {
|
|
68
|
+
if (fs.existsSync(COOKIES_PATH)) {
|
|
69
|
+
fs.unlinkSync(COOKIES_PATH);
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error("Failed to clear cookies:", error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get current auth state
|
|
78
|
+
*/
|
|
79
|
+
export function getAuthState(): AuthState {
|
|
80
|
+
return {
|
|
81
|
+
isLoggedIn: hasStoredCookies(),
|
|
82
|
+
};
|
|
83
|
+
}
|