Skip to main content
Glama

Bunq MCP

by WilcoKruijer
index.ts5.44 kB
import OAuthProvider from "@cloudflare/workers-oauth-provider"; import { McpAgent } from "agents/mcp"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { createOAuthHandler } from "./bunq/OAuthHandler"; import { getBunqClient, getBunqClientIfInitialized } from "./bunq/BunqClient"; import type { BunqAuthProps } from "./bunq/BunqClient"; import { registerTools } from "./tools"; import { env as workersEnv } from "cloudflare:workers"; import cookie from "./keys/cookie.txt"; type BunqCredentials = | { type: "oauth"; clientId: string; clientSecret: string; } | { type: "api-key"; apiKey: string; }; // All of this is a shitty hack, we can revisit later. const viteEnv = (import.meta as any).env; const clientId = workersEnv["BUNQ_CLIENT_ID"] || process.env["BUNQ_CLIENT_ID"] || viteEnv.BUNQ_CLIENT_ID; const clientSecret = workersEnv["BUNQ_CLIENT_SECRET"] || process.env["BUNQ_CLIENT_SECRET"] || viteEnv.BUNQ_CLIENT_SECRET; const apiKey = workersEnv["BUNQ_API_KEY"] || process.env["BUNQ_API_KEY"] || viteEnv.BUNQ_API_KEY; let credentials: BunqCredentials; if (apiKey) { console.log("Using API key"); credentials = { type: "api-key", apiKey, }; } else if (clientId && clientSecret) { console.log("Using OAuth flow with client ID and secret"); credentials = { type: "oauth", clientId, clientSecret, }; } else { console.error(` Bunq client ID and secret or API key is not set. Please either set both a clientId and clientSecret in the following manner: - Set a clientId using --bunq-client-id or BUNQ_CLIENT_ID environment variable. - Set a clientSecret using --bunq-client-secret or BUNQ_CLIENT_SECRET environment variable. Or set an apiKey: - Set an apiKey using --bunq-api-key or BUNQ_API_KEY environment variable. See usage instructions at https://npmjs.com/package/bunq-mcp `); throw new Error("Bunq credentials missing."); } // Add the bunq props that we'll store in the token type ExtendedProps = BunqAuthProps; export class MyMCP extends McpAgent<ExtendedProps, Env> { server = new McpServer({ name: "Bunq MCP", version: "1.0.0", }); async init() { const { accessToken: oauthAccessToken, apiKeyAccessToken } = this.props as ExtendedProps; let accessToken = credentials.type === "oauth" ? oauthAccessToken : apiKeyAccessToken; if (!accessToken && credentials.type === "api-key") { accessToken = credentials.apiKey; } const bunqClient = getBunqClient(accessToken); const res = await bunqClient.initialize(); if (credentials.type === "api-key") { console.log("Setting access token"); this.props["accessToken"] = res.accessToken; } const tools = registerTools(bunqClient); for (const tool of tools) { if (tool.requiresApiKey && credentials.type === "oauth") { continue; } this.server.tool( tool.name, tool.description, tool.parameters, async (args: any, extra: any) => { // We need to properly handle the args and wrap handler for type safety return await tool.handler(args, extra); }, ); } } } interface WorkerExport { fetch(request: Request, env: any, ctx: ExecutionContext): Promise<Response>; } let workerExport: WorkerExport; async function buildInstructionText() { let text = `bunq-mcp up and running.\n`; if (credentials.type === "oauth") { text += `\n - Client ID: '${credentials.clientId}'`; } else { text += `\n - API Key: (hidden)`; } text += `\n - Cookie: '${cookie}'`; const bunqClient = getBunqClientIfInitialized(); if (bunqClient) { text += `\n\nBunq client initialized successfully.`; const [firstAccount] = await bunqClient.account.getMonetaryBankAccounts(); if (firstAccount) { text += `\n - Found Bunq account: '${firstAccount.description}'`; } else { text += `\n - No Bunq (monetary bank) accounts found`; } } else { text += `\n\nBunq client NOT initialized.`; } text += `\n\nSee usage instructions at https://npmjs.com/package/bunq-mcp`; return text; } if (credentials.type === "oauth") { const bunqHandler = createOAuthHandler(credentials.clientId, credentials.clientSecret); bunqHandler.get("/", async (c) => { return c.text(await buildInstructionText()); }); workerExport = new OAuthProvider({ apiRoute: "/sse", apiHandler: MyMCP.mount("/sse") as any, defaultHandler: bunqHandler as any, authorizeEndpoint: "/authorize", tokenEndpoint: "/token", clientRegistrationEndpoint: "/register", }); } else { // using api key workerExport = { async fetch(request: Request, env: Env, ctx: ExecutionContext) { const url = new URL(request.url); if (url.pathname === "/sse" || url.pathname === "/sse/message") { // @ts-ignore return MyMCP.serveSSE("/sse").fetch(request, env, ctx); } if (url.pathname === "/mcp") { // @ts-ignore return MyMCP.serve("/mcp").fetch(request, env, ctx); } if (url.pathname === "/") { const bunqClient = getBunqClient(credentials.apiKey); await bunqClient.initialize(); return new Response(await buildInstructionText()); } return new Response("Not found", { status: 404 }); }, }; } export default workerExport;

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/WilcoKruijer/bunq-mcp'

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