├── tsconfig.json ├── .gitignore ├── package.json ├── README.md └── src └── server.ts /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "outDir": "./build", 6 | "rootDir": "./src", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "skipLibCheck": true, 10 | "forceConsistentCasingInFileNames": true 11 | }, 12 | "include": ["src/**/*"], 13 | "exclude": ["node_modules"] 14 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | package-lock.json 4 | 5 | # TypeScript 6 | dist/ 7 | build/ 8 | *.tsbuildinfo 9 | 10 | # Environment 11 | .env 12 | .env.local 13 | .env.*.local 14 | 15 | # IDE 16 | .vscode/ 17 | .idea/ 18 | *.swp 19 | *.swo 20 | 21 | # Logs 22 | logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # OS 29 | .DS_Store 30 | Thumbs.db -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "israeli-bank-mcp", 3 | "version": "1.0.0", 4 | "description": "A project for managing Israeli bank accounts and transactions.", 5 | "main": "build/server.js", 6 | "scripts": { 7 | "start": "ts-node src/server.ts", 8 | "build": "tsc && chmod 755 build/server.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@modelcontextprotocol/sdk": "^1.0.0", 16 | "israeli-bank-scrapers": "^1.0.0", 17 | "typescript": "^5.0.0", 18 | "@types/node": "^20.0.0", 19 | "ts-node": "^10.0.0", 20 | "zod": "^3.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Israeli Bank MCP 2 | 3 | A project for managing Israeli bank accounts and transactions using the Model Context Protocol (MCP). 4 | 5 | ## Features 6 | 7 | - List available Israeli banks and credit card companies with their required credentials 8 | - Fetch transactions from any supported bank 9 | - Support for all major Israeli banks and credit card companies 10 | - Secure credential handling 11 | - Flexible transaction date ranges 12 | - Two-factor authentication support 13 | 14 | ## Getting Started 15 | 16 | 1. Install dependencies: 17 | ```bash 18 | npm install 19 | ``` 20 | 21 | 2. Build the project: 22 | ```bash 23 | npm run build 24 | ``` 25 | 26 | 3. Connect to MCP Clients 27 | 28 | ## Connecting to MCP Clients 29 | 30 | The server can be connected to any MCP-compatible client. Here's how to configure it: 31 | 32 | ### Example Configuration 33 | 34 | For clients that support configuration files (like Claude), add the following to your configuration: 35 | 36 | ```json 37 | { 38 | "mcpServers": { 39 | "israeli-bank-mcp": { 40 | "command": "node", 41 | "args": [ 42 | "/path/to/israeli-bank-mcp/build/server.js" 43 | ] 44 | } 45 | } 46 | } 47 | ``` 48 | ## Resources 49 | 50 | - **Banks** (`banks://list`) 51 | - List available banks and their required credentials 52 | 53 | ## Tools 54 | 55 | - **Fetch transactions** (`fetch-transactions`) 56 | - Fetch transactions from a bank 57 | 58 | - **2FA** (`two-factor-auth`) 59 | - 2FA authentication for banks that require that 60 | 61 | 62 | ## Supported Banks 63 | 64 | The server supports all major Israeli banks and credit card companies through the [israeli-bank-scrapers](https://github.com/eshaham/israeli-bank-scrapers) library: 65 | 66 | - Bank Hapoalim 67 | - Leumi Bank 68 | - Discount Bank 69 | - Mercantile Bank 70 | - Mizrahi Bank 71 | - Otsar Hahayal Bank 72 | - Visa Cal 73 | - Max (Formerly Leumi Card) 74 | - Isracard 75 | - Amex 76 | - Union Bank 77 | - Beinleumi 78 | - Massad 79 | - Yahav 80 | - Beyhad Bishvilha 81 | - OneZero (Experimental) 82 | - Behatsdaa 83 | 84 | ## Security 85 | 86 | - Please do not attempt this at home (I honestly don't know, it's probably not a good idea, but it's really cool) 87 | 88 | ## License 89 | 90 | MIT 91 | -------------------------------------------------------------------------------- /src/server.ts: -------------------------------------------------------------------------------- 1 | import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 3 | import { CompanyTypes, createScraper, ScraperOptions, ScraperCredentials, SCRAPERS } from 'israeli-bank-scrapers'; 4 | import { z } from "zod"; 5 | 6 | // Create an MCP server 7 | const server = new McpServer({ 8 | name: "Israeli Bank MCP", 9 | version: "1.0.0" 10 | }); 11 | 12 | // Add a resource to list available banks 13 | server.resource( 14 | "banks", 15 | "banks://list", 16 | async (uri) => ({ 17 | contents: [{ 18 | uri: uri.href, 19 | text: JSON.stringify({ 20 | banks: Object.entries(CompanyTypes).map(([key, value]) => { 21 | const scraperInfo = SCRAPERS[value]; 22 | return { 23 | id: value, 24 | name: key, 25 | requiredCredentials: scraperInfo?.loginFields || [] 26 | }; 27 | }) 28 | }) 29 | }] 30 | }) 31 | ); 32 | 33 | // Add a tool to fetch transactions from a bank 34 | server.tool( 35 | "fetch-transactions", 36 | { 37 | bankId: z.enum(Object.values(CompanyTypes) as [string, ...string[]]), 38 | credentials: z.object({ 39 | username: z.string().optional(), 40 | password: z.string(), 41 | userCode: z.string().optional(), 42 | id: z.string().optional(), 43 | num: z.string().optional(), 44 | card6Digits: z.string().optional(), 45 | nationalID: z.string().optional(), 46 | longTermTwoFactorAuthToken: z.string().optional() 47 | }), 48 | startDate: z.string().optional(), 49 | combineInstallments: z.boolean().optional(), 50 | showBrowser: z.boolean().optional() 51 | }, 52 | async ({ bankId, credentials, startDate, combineInstallments, showBrowser }) => { 53 | try { 54 | // Ensure bankId is a valid CompanyTypes value 55 | const validBankIds = new Set(Object.values(CompanyTypes)); 56 | if (!validBankIds.has(bankId as unknown as CompanyTypes)) { 57 | throw new Error(`Invalid bank ID: ${bankId}`); 58 | } 59 | 60 | const options: ScraperOptions = { 61 | companyId: bankId as unknown as CompanyTypes, 62 | startDate: startDate ? new Date(startDate) : new Date(), 63 | combineInstallments: combineInstallments ?? false, 64 | showBrowser: showBrowser ?? false 65 | }; 66 | 67 | const scraper = createScraper(options); 68 | const scrapeResult = await scraper.scrape(credentials as ScraperCredentials); 69 | 70 | if (scrapeResult.success) { 71 | return { 72 | content: [{ 73 | type: "text", 74 | text: JSON.stringify(scrapeResult) 75 | }] 76 | }; 77 | } else { 78 | return { 79 | content: [{ 80 | type: "text", 81 | text: JSON.stringify({ 82 | error: scrapeResult.errorType, 83 | message: scrapeResult.errorMessage 84 | }) 85 | }], 86 | isError: true 87 | }; 88 | } 89 | } catch (error) { 90 | return { 91 | content: [{ 92 | type: "text", 93 | text: JSON.stringify({ 94 | error: "UNKNOWN_ERROR", 95 | message: error instanceof Error ? error.message : "Unknown error occurred" 96 | }) 97 | }], 98 | isError: true 99 | }; 100 | } 101 | } 102 | ); 103 | 104 | // Add a tool for 2FA authentication 105 | server.tool( 106 | "two-factor-auth", 107 | { 108 | bankId: z.enum(Object.values(CompanyTypes) as [string, ...string[]]), 109 | phoneNumber: z.string(), 110 | action: z.enum(["trigger", "get-token"]), 111 | otpCode: z.string().optional() 112 | }, 113 | async ({ bankId, phoneNumber, action, otpCode }) => { 114 | try { 115 | const validBankIds = new Set(Object.values(CompanyTypes)); 116 | if (!validBankIds.has(bankId as unknown as CompanyTypes)) { 117 | throw new Error(`Invalid bank ID: ${bankId}`); 118 | } 119 | 120 | const scraper = createScraper({ 121 | companyId: bankId as unknown as CompanyTypes, 122 | startDate: new Date() 123 | }); 124 | 125 | if (action === "trigger") { 126 | await scraper.triggerTwoFactorAuth(phoneNumber); 127 | return { 128 | content: [{ 129 | type: "text", 130 | text: JSON.stringify({ success: true, message: "2FA code sent" }) 131 | }] 132 | }; 133 | } else if (action === "get-token" && otpCode) { 134 | const result = await scraper.getLongTermTwoFactorToken(otpCode); 135 | return { 136 | content: [{ 137 | type: "text", 138 | text: JSON.stringify(result) 139 | }] 140 | }; 141 | } else { 142 | throw new Error("Invalid action or missing OTP code"); 143 | } 144 | } catch (error) { 145 | return { 146 | content: [{ 147 | type: "text", 148 | text: JSON.stringify({ 149 | error: "UNKNOWN_ERROR", 150 | message: error instanceof Error ? error.message : "Unknown error occurred" 151 | }) 152 | }], 153 | isError: true 154 | }; 155 | } 156 | } 157 | ); 158 | 159 | // Start the server 160 | const transport = new StdioServerTransport(); 161 | server.connect(transport).catch(console.error); --------------------------------------------------------------------------------