monobank-mcp-server 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Anton Kutishevsky
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # Monobank MCP Server
2
+
3
+ MCP (Model Context Protocol) server for integrating with Monobank API.
4
+
5
+ ## Prerequisites
6
+
7
+ - Node.js 18.0.0 or higher
8
+ - A Monobank API token. You can obtain it at: https://api.monobank.ua/
9
+
10
+ **Note:** Your API token is only used locally and is never tracked or transmitted anywhere except to Monobank's API.
11
+
12
+ ## Available Tools
13
+
14
+ The server provides three tools:
15
+
16
+ 1. **get_currency_rates** - Get currency exchange rates (rate limited to once per 5 minutes)
17
+ 2. **get_client_info** - Get client information and account details (rate limited to once per 60 seconds)
18
+ 3. **get_statement** - Get account statements for a specified date range (rate limited to once per 60 seconds, max 31 days + 1 hour)
19
+
20
+ ## MCP Configuration
21
+
22
+ To use this server with an MCP client, add it to your configuration:
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "monobank": {
28
+ "command": "npx",
29
+ "args": ["monobank-mcp-server"],
30
+ "env": {
31
+ "MONOBANK_API_TOKEN": "your_token_here"
32
+ }
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ ## Example Prompts
39
+
40
+ ### English
41
+ - "Show me the current USD to UAH exchange rate"
42
+ - "Get my account balance and recent transactions"
43
+ - "Show me all transactions from the last week"
44
+ - "What did I spend money on yesterday?"
45
+ - "Show my statement for October 2024"
46
+ - "Get all my credit card transactions from the last 3 days"
47
+
48
+ ### Українською
49
+ - "Покажи поточний курс долара"
50
+ - "Скільки грошей на моїх рахунках?"
51
+ - "Покажи виписку за вчора"
52
+ - "На що я витратив гроші цього тижня?"
53
+ - "Покажи всі транзакції за жовтень"
54
+ - "Скільки я витратив на їжу за останній місяць?"
55
+
56
+ ## License
57
+
58
+ MIT
@@ -0,0 +1,17 @@
1
+ let config = null;
2
+ export function initializeConfig() {
3
+ if (!process.env.MONOBANK_API_TOKEN) {
4
+ throw new Error("Failed to get the MONOBANK_API_TOKEN. Probably it wasn't added during the server configuration.");
5
+ }
6
+ config = {
7
+ monobankApiToken: process.env.MONOBANK_API_TOKEN,
8
+ baseUrl: "https://api.monobank.ua",
9
+ };
10
+ return config;
11
+ }
12
+ export function getConfig() {
13
+ if (!config) {
14
+ throw new Error("Configuration not initialized. Call initializeConfig() first.");
15
+ }
16
+ return config;
17
+ }
@@ -0,0 +1,81 @@
1
+ import { z } from "zod";
2
+ export function createErrorResponse(message) {
3
+ return {
4
+ content: [
5
+ {
6
+ type: "text",
7
+ text: message,
8
+ },
9
+ ],
10
+ };
11
+ }
12
+ export function createSuccessResponse(data) {
13
+ return {
14
+ content: [
15
+ {
16
+ type: "text",
17
+ text: typeof data === "string" ? data : JSON.stringify(data, null, 2),
18
+ },
19
+ ],
20
+ };
21
+ }
22
+ export function formatZodError(error) {
23
+ return error.errors
24
+ .map((e) => `${e.path.join(".")}: ${e.message}`)
25
+ .join(", ");
26
+ }
27
+ export async function fetchWithErrorHandling(url, options) {
28
+ const response = await fetch(url, options);
29
+ if (!response.ok) {
30
+ const errorText = await response
31
+ .text()
32
+ .catch(() => response.statusText);
33
+ throw new Error(`HTTP ${response.status} - ${errorText}`);
34
+ }
35
+ return response;
36
+ }
37
+ export async function parseJsonResponse(response) {
38
+ try {
39
+ return await response.json();
40
+ }
41
+ catch (error) {
42
+ throw new Error(`Failed to parse response as JSON: ${error instanceof Error ? error.message : "Unknown JSON error"}`);
43
+ }
44
+ }
45
+ export function formatErrorAsToolResponse(error, context) {
46
+ if (error instanceof z.ZodError) {
47
+ return createErrorResponse(`Invalid ${context} format: ${formatZodError(error)}`);
48
+ }
49
+ if (error instanceof Error) {
50
+ return createErrorResponse(error.message.startsWith("HTTP")
51
+ ? `Failed to ${context}: ${error.message}`
52
+ : `Error ${context}: ${error.message}`);
53
+ }
54
+ return createErrorResponse(`Error ${context}: Unknown error`);
55
+ }
56
+ export function validateStatementDates(from, to) {
57
+ const fromDate = new Date(from);
58
+ const toDate = to ? new Date(to) : new Date();
59
+ if (isNaN(fromDate.getTime())) {
60
+ return createErrorResponse(`Invalid 'from' date format: ${from}`);
61
+ }
62
+ if (to && isNaN(toDate.getTime())) {
63
+ return createErrorResponse(`Invalid 'to' date format: ${to}`);
64
+ }
65
+ const fromInSeconds = Math.floor(fromDate.getTime() / 1000);
66
+ const toInSeconds = Math.floor(toDate.getTime() / 1000);
67
+ // Validate time range (max 31 days + 1 hour = 2682000 seconds)
68
+ if (toInSeconds - fromInSeconds > 2682000) {
69
+ return createErrorResponse("Time range exceeds maximum allowed (31 days + 1 hour). Please use a smaller date range.");
70
+ }
71
+ return { fromInSeconds, toInSeconds };
72
+ }
73
+ export function formatStatementItems(items) {
74
+ return items.map((item) => ({
75
+ ...item,
76
+ amount: item.amount / 100,
77
+ operationAmount: item.operationAmount / 100,
78
+ cashbackAmount: item.cashbackAmount / 100,
79
+ balance: item.balance / 100,
80
+ }));
81
+ }
package/build/index.js ADDED
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { CurrencyRatesResponseSchema, StatementItemSchema } from "./schemas.js";
6
+ import { createSuccessResponse, fetchWithErrorHandling, parseJsonResponse, formatErrorAsToolResponse, validateStatementDates, formatStatementItems, } from "./helpers.js";
7
+ import { initializeConfig, getConfig } from "./config.js";
8
+ const server = new McpServer({
9
+ name: "monobank-mcp-server",
10
+ version: "1.0.0",
11
+ capabilities: {
12
+ tools: {},
13
+ },
14
+ });
15
+ server.tool("get_currency_rates", "Get a basic list of currency rates from Monobank. The information can be refreshed once per 5 minutes, otherwise an error will be thrown.", {}, async () => {
16
+ try {
17
+ const { baseUrl } = getConfig();
18
+ const response = await fetchWithErrorHandling(`${baseUrl}/bank/currency`);
19
+ const result = await parseJsonResponse(response);
20
+ const currencyRates = CurrencyRatesResponseSchema.parse(result);
21
+ return createSuccessResponse(currencyRates);
22
+ }
23
+ catch (error) {
24
+ return formatErrorAsToolResponse(error, "get currency rates");
25
+ }
26
+ });
27
+ server.tool("get_client_info", "Get information about a client and a list of their accounts and jars. The tool can be called not more than 1 time per 60 seconds, otherwise an error will be thrown.", {}, async () => {
28
+ try {
29
+ const { baseUrl, monobankApiToken } = getConfig();
30
+ const response = await fetchWithErrorHandling(`${baseUrl}/personal/client-info`, {
31
+ headers: {
32
+ "X-Token": monobankApiToken,
33
+ },
34
+ });
35
+ const clientInfo = await parseJsonResponse(response);
36
+ return createSuccessResponse(clientInfo);
37
+ }
38
+ catch (error) {
39
+ return formatErrorAsToolResponse(error, "get client info");
40
+ }
41
+ });
42
+ server.tool("get_statement", "Get Monobank statement for the time from {from} to {to} time in seconds in Unix time format. The maximum time for which it is posssible to obtain a statement is 31 days + 1 hour (2682000 seconds). The statement can be retrieved not more than once per 60 seconds, otherwise an error will be thrown.", {
43
+ input: z.object({
44
+ account: z
45
+ .string()
46
+ .nonempty()
47
+ .describe("A unique indentificator of the Monobank account or a jar from the Statement list. If not provided, then a defaukt account is used, which is equal to '0'."),
48
+ from: z
49
+ .string()
50
+ .nonempty()
51
+ .describe("A date in ISO 8601 YYYY-MM-DD format."),
52
+ to: z
53
+ .string()
54
+ .optional()
55
+ .describe("A date in ISO 8601 YYYY-MM-DD format."),
56
+ }),
57
+ }, async ({ input }) => {
58
+ try {
59
+ const { account, from, to } = input;
60
+ const dateValidation = validateStatementDates(from, to);
61
+ if ("content" in dateValidation) {
62
+ return dateValidation;
63
+ }
64
+ const { fromInSeconds, toInSeconds } = dateValidation;
65
+ const { baseUrl, monobankApiToken } = getConfig();
66
+ const response = await fetchWithErrorHandling(`${baseUrl}/personal/statement/${account}/${fromInSeconds}/${toInSeconds}`, {
67
+ headers: {
68
+ "X-Token": monobankApiToken,
69
+ },
70
+ });
71
+ const data = await parseJsonResponse(response);
72
+ const statement = z.array(StatementItemSchema).parse(data);
73
+ const formattedStatement = formatStatementItems(statement);
74
+ return createSuccessResponse(formattedStatement);
75
+ }
76
+ catch (error) {
77
+ return formatErrorAsToolResponse(error, "fetch statement");
78
+ }
79
+ });
80
+ async function main() {
81
+ initializeConfig();
82
+ const transport = new StdioServerTransport();
83
+ await server.connect(transport);
84
+ console.error("Monobank MCP Server running on stdio");
85
+ }
86
+ main().catch((error) => {
87
+ console.error("Fatal error in main():", error);
88
+ process.exit(1);
89
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ export const CurrencyRateSchema = z.object({
3
+ currencyCodeA: z.number(),
4
+ currencyCodeB: z.number(),
5
+ date: z.number(),
6
+ rateBuy: z.number().optional(),
7
+ rateSell: z.number().optional(),
8
+ rateCross: z.number().optional(),
9
+ });
10
+ export const CurrencyRatesResponseSchema = z.array(CurrencyRateSchema);
11
+ export const StatementItemSchema = z.object({
12
+ id: z.string(),
13
+ time: z.number(),
14
+ description: z.string(),
15
+ mcc: z.number(),
16
+ originalMcc: z.number(),
17
+ hold: z.boolean(),
18
+ amount: z.number().describe("Amount in cents, multiply by 100"),
19
+ operationAmount: z.number().describe("Amount in cents, multiply by 100"),
20
+ currencyCode: z.number(),
21
+ commissionRate: z.number(),
22
+ cashbackAmount: z.number().describe("Amount in cents, multiply by 100"),
23
+ balance: z.number().describe("Amount in cents, multiply by 100"),
24
+ comment: z.string().optional(),
25
+ receiptId: z.string().optional(),
26
+ invoiceId: z.string().optional(),
27
+ counterEdrpou: z.string().optional(),
28
+ counterIban: z.string().optional(),
29
+ counterName: z.string().optional(),
30
+ });
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "monobank-mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Monobank API integration",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "monobank-mcp-server": "./build/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc && chmod +x build/index.js",
11
+ "prepublishOnly": "npm run build"
12
+ },
13
+ "files": [
14
+ "build"
15
+ ],
16
+ "keywords": [
17
+ "mcp",
18
+ "monobank",
19
+ "api",
20
+ "banking",
21
+ "model-context-protocol"
22
+ ],
23
+ "author": "",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/akutishevsky/monobank-mcp-server.git"
28
+ },
29
+ "homepage": "https://github.com/akutishevsky/monobank-mcp-server#readme",
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "type": "module",
34
+ "dependencies": {
35
+ "@modelcontextprotocol/sdk": "^1.17.0",
36
+ "zod": "^3.25.76"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^24.1.0",
40
+ "typescript": "^5.8.3"
41
+ }
42
+ }