Skip to main content
Glama
index.ts13.1 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from "@modelcontextprotocol/sdk/types.js"; interface UpApiConfig { apiToken: string; baseUrl: string; } interface MoneyObject { currencyCode: string; value: string; valueInBaseUnits: number; } interface AccountResource { type: "accounts"; id: string; attributes: { displayName: string; accountType: "SAVER" | "TRANSACTIONAL" | "HOME_LOAN"; ownershipType: "INDIVIDUAL" | "JOINT"; balance: MoneyObject; createdAt: string; }; relationships: { transactions: { links: { related: string; }; }; }; links: { self: string; }; } interface TransactionResource { type: "transactions"; id: string; attributes: { status: "HELD" | "SETTLED"; rawText: string | null; description: string; message: string | null; isCategorizable: boolean; amount: MoneyObject; foreignAmount: MoneyObject | null; settledAt: string | null; createdAt: string; transactionType: string | null; }; relationships: { account: { data: { type: "accounts"; id: string; }; }; category: { data: { type: "categories"; id: string; } | null; }; }; } interface CategoryResource { type: "categories"; id: string; attributes: { name: string; }; relationships: { parent: { data: { type: "categories"; id: string; } | null; }; children: { data: Array<{ type: "categories"; id: string; }>; }; }; } class UpApiClient { constructor(private config: UpApiConfig) {} private async makeRequest<T>(endpoint: string): Promise<T> { const url = `${this.config.baseUrl}${endpoint}`; const response = await fetch(url, { headers: { Authorization: `Bearer ${this.config.apiToken}`, "Content-Type": "application/json", }, }); if (!response.ok) { const errorText = await response.text(); throw new Error( `Up API error: ${response.status} ${response.statusText}\n${errorText}` ); } return response.json(); } async ping(): Promise<{ meta: { id: string; statusEmoji: string } }> { return this.makeRequest("/util/ping"); } async listAccounts(filters?: { accountType?: "SAVER" | "TRANSACTIONAL" | "HOME_LOAN"; ownershipType?: "INDIVIDUAL" | "JOINT"; }): Promise<{ data: AccountResource[] }> { let endpoint = "/accounts"; const params = new URLSearchParams(); if (filters?.accountType) { params.append("filter[accountType]", filters.accountType); } if (filters?.ownershipType) { params.append("filter[ownershipType]", filters.ownershipType); } if (params.toString()) { endpoint += `?${params.toString()}`; } return this.makeRequest(endpoint); } async getAccount(accountId: string): Promise<{ data: AccountResource }> { return this.makeRequest(`/accounts/${accountId}`); } async listTransactions(filters?: { accountId?: string; status?: "HELD" | "SETTLED"; since?: string; until?: string; category?: string; tag?: string; pageSize?: number; }): Promise<{ data: TransactionResource[] }> { let endpoint = filters?.accountId ? `/accounts/${filters.accountId}/transactions` : "/transactions"; const params = new URLSearchParams(); if (filters?.status) { params.append("filter[status]", filters.status); } if (filters?.since) { params.append("filter[since]", filters.since); } if (filters?.until) { params.append("filter[until]", filters.until); } if (filters?.category) { params.append("filter[category]", filters.category); } if (filters?.tag) { params.append("filter[tag]", filters.tag); } if (filters?.pageSize) { params.append("page[size]", filters.pageSize.toString()); } if (params.toString()) { endpoint += `?${params.toString()}`; } return this.makeRequest(endpoint); } async getTransaction( transactionId: string ): Promise<{ data: TransactionResource }> { return this.makeRequest(`/transactions/${transactionId}`); } async listCategories(filters?: { parentId?: string; }): Promise<{ data: CategoryResource[] }> { let endpoint = "/categories"; const params = new URLSearchParams(); if (filters?.parentId) { params.append("filter[parent]", filters.parentId); } if (params.toString()) { endpoint += `?${params.toString()}`; } return this.makeRequest(endpoint); } async getCategory(categoryId: string): Promise<{ data: CategoryResource }> { return this.makeRequest(`/categories/${categoryId}`); } } const TOOLS: Tool[] = [ { name: "up_ping", description: "Test the Up API connection and verify authentication is working", inputSchema: { type: "object", properties: {}, }, }, { name: "up_list_accounts", description: "List all accounts for the authenticated user. Returns account balances, types (SAVER, TRANSACTIONAL, HOME_LOAN), and ownership information.", inputSchema: { type: "object", properties: { accountType: { type: "string", enum: ["SAVER", "TRANSACTIONAL", "HOME_LOAN"], description: "Filter by account type", }, ownershipType: { type: "string", enum: ["INDIVIDUAL", "JOINT"], description: "Filter by ownership type", }, }, }, }, { name: "up_get_account", description: "Get details for a specific account by ID, including current balance and account information.", inputSchema: { type: "object", properties: { accountId: { type: "string", description: "The unique identifier for the account", }, }, required: ["accountId"], }, }, { name: "up_list_transactions", description: "List transactions across all accounts or for a specific account. Supports filtering by status, date range, category, and tags. Returns paginated results ordered newest first.", inputSchema: { type: "object", properties: { accountId: { type: "string", description: "Optional: Filter to transactions for a specific account", }, status: { type: "string", enum: ["HELD", "SETTLED"], description: "Filter by transaction status (pending or settled)", }, since: { type: "string", description: "Start date-time in RFC 3339 format (e.g., 2024-01-01T00:00:00+10:00)", }, until: { type: "string", description: "End date-time in RFC 3339 format (e.g., 2024-12-31T23:59:59+10:00)", }, category: { type: "string", description: "Filter by category ID (e.g., 'restaurants-and-cafes', 'good-life')", }, tag: { type: "string", description: "Filter by transaction tag", }, pageSize: { type: "number", description: "Number of records to return (default: 30, max: 100)", }, }, }, }, { name: "up_get_transaction", description: "Get detailed information about a specific transaction by ID, including amount, description, category, and related account.", inputSchema: { type: "object", properties: { transactionId: { type: "string", description: "The unique identifier for the transaction", }, }, required: ["transactionId"], }, }, { name: "up_list_categories", description: "List all spending categories in Up. Categories have a parent-child relationship. Use this to understand category IDs for filtering transactions.", inputSchema: { type: "object", properties: { parentId: { type: "string", description: "Optional: Filter to only show children of a specific parent category", }, }, }, }, { name: "up_get_category", description: "Get details about a specific category by ID, including its name and parent/child relationships.", inputSchema: { type: "object", properties: { categoryId: { type: "string", description: "The unique identifier for the category (e.g., 'restaurants-and-cafes')", }, }, required: ["categoryId"], }, }, ]; async function runServer() { const apiToken = process.env.UP_API_TOKEN; if (!apiToken) { throw new Error( "UP_API_TOKEN environment variable is required. Get your token from the Up app: Data sharing > Personal Access Token" ); } const config: UpApiConfig = { apiToken, baseUrl: "https://api.up.com.au/api/v1", }; const client = new UpApiClient(config); const server = new Server( { name: "up-banking-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: TOOLS }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { try { switch (request.params.name) { case "up_ping": { const result = await client.ping(); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } case "up_list_accounts": { const args = request.params.arguments as { accountType?: "SAVER" | "TRANSACTIONAL" | "HOME_LOAN"; ownershipType?: "INDIVIDUAL" | "JOINT"; }; const result = await client.listAccounts(args); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } case "up_get_account": { const args = request.params.arguments as { accountId: string }; const result = await client.getAccount(args.accountId); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } case "up_list_transactions": { const args = request.params.arguments as { accountId?: string; status?: "HELD" | "SETTLED"; since?: string; until?: string; category?: string; tag?: string; pageSize?: number; }; const result = await client.listTransactions(args); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } case "up_get_transaction": { const args = request.params.arguments as { transactionId: string }; const result = await client.getTransaction(args.transactionId); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } case "up_list_categories": { const args = request.params.arguments as { parentId?: string }; const result = await client.listCategories(args); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } case "up_get_category": { const args = request.params.arguments as { categoryId: string }; const result = await client.getCategory(args.categoryId); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } default: throw new Error(`Unknown tool: ${request.params.name}`); } } catch (error) { if (error instanceof Error) { return { content: [ { type: "text", text: `Error: ${error.message}`, }, ], isError: true, }; } throw error; } }); const transport = new StdioServerTransport(); await server.connect(transport); console.error("Up Banking MCP server running on stdio"); } runServer().catch((error) => { console.error("Fatal error:", error); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/alex1092/up-bank-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server