Skip to main content
Glama
wilderfield

plaid-mcp

by wilderfield

plaid-mcp

为运行在临时容器中的 AI 助手 (Elowen) 提供的持久化 Plaid MCP 服务器。

plaid-mcp 是一个长期运行的外部托管服务,它持有 Plaid 密钥以及每个关联机构的加密访问令牌。助手在运行时调用 mcp__plaid__* 工具;它永远不会看到原始访问令牌,只会看到 Plaid 已经视为公开的不透明 item_idaccount_id 值。

Elowen (ephemeral container)
  └─ calls mcp__plaid__* tools
        └─ plaid-mcp (persistent, nanoclaw-hosted)
              ├─ Plaid SDK + PLAID_SECRET (never leaves this service)
              ├─ access_token store (SQLite, AES-256-GCM at rest)
              └─ /link/start, /link/callback (HTTPS, browser-facing)
                    └─ Plaid REST API / Plaid Link JS

界面

单个 Node.js 进程暴露两个完全独立的界面:

  1. MCP 服务器。 可以是 stdio(代理将此二进制文件作为子进程生成)或 httpPOST /mcp 上的流式 HTTP,受 Bearer 令牌保护)。使用 MCP_TRANSPORT 进行选择。对于上述家庭预算用例,建议使用 http,以便一组临时代理容器可以共享一个持久化服务器。

  2. HTTPS 链接微型应用,位于 /link/*仅在一次性银行链接流程中使用 —— 用户打开助手提供的 URL,在 Plaid Link 中登录其银行,然后就完成了。此后,该机构无需再次使用浏览器。

Related MCP server: plaid-mcp

MCP 工具

工具

功能

list_linked_institutions()

每个关联的 Item,带有 needs_relink 健康标志(每个 Item 调用 /item/get)。

list_accounts(item_id?)

一个或所有机构的缓存账户列表(类型、子类型、掩码、最后余额)。

get_balances(account_ids?)

通过 /accounts/balance/get 获取实时余额(付费 Plaid 端点)。

get_transactions(start_date, end_date, account_ids?, cursor?)

日期范围内的交易,每页约 250 条,不透明的分页游标。

search_transactions(query, since?, until?, min_amount?, max_amount?, category?)

服务器端过滤的交易搜索。返回紧凑的行。

get_monthly_summary(month, group_by?)

categorymerchant 分组的预聚合月度总计。保持 LLM 上下文精简。

get_investment_holdings(account_ids?)

持仓快照(代码、数量、市值、成本基础)。

get_investment_transactions(start_date, end_date, account_ids?)

窗口期内的买入/卖出/股息。

get_liabilities(account_ids?)

信用卡 APR/账单、学生贷款、抵押贷款详情。

initiate_link(institution_hint?)

返回 { url, session_id, expires_at } —— 将 URL 提供给用户。

link_status(session_id)

轮询直到 succeeded(带有新的 item_id)、failedexpired

remove_institution(item_id)

撤销 Plaid Item 并删除本地令牌。

所有工具响应均为单个 text 内容项内的 JSON(适用于所有 MCP 客户端,包括那些不支持 structuredContent 的客户端)。

一次性链接流程

  1. Elowen 调用 initiate_link({ institution_hint: "Chase" })。服务器:

    • 调用 Plaid /link/token/create

    • 存储一行 link_sessions(状态为 pending),

    • 返回 { url: "https://<LINK_BASE_URL>/link/start?s=<uuid>&sig=<hmac>", session_id, expires_at }

  2. Elowen 将 URL 发送给用户。

  3. 用户在浏览器中打开它。页面使用该 link_token 从官方 CDN 加载 Plaid Link JS,并显示“打开 Plaid Link”按钮。

  4. Plaid Link 的 onSuccess{ public_token, institution } 以及签名的会话 ID POST 回 /link/callback

  5. /link/callbackpublic_token 交换为 access_token + item_id,使用 AES-256-GCM 加密访问令牌,持久化存储,并将该会话标记为 succeeded

  6. Elowen 轮询 link_status(session_id),看到 succeeded 以及 item_id,然后继续执行。

签名的 URL 参数 (s, sig) 由 LINK_SESSION_SECRET 进行 HMAC-SHA256 密钥处理。数据库行是事实来源 —— HMAC 只是在接触 SQLite 之前廉价地拒绝垃圾请求。

配置

所有配置均通过环境变量(从 .env 加载)进行。

变量

必需

默认

描述

PLAID_CLIENT_ID

来自 Plaid 仪表板

PLAID_SECRET

来自 Plaid 仪表板。永远不会离开此服务。

PLAID_ENV

sandbox

sandbox

development

production

PLAID_API_VERSION

2020-09-14

固定 API 版本

PLAID_PRODUCTS

transactions

逗号分隔列表。常见:transactions,investments,liabilities

PLAID_COUNTRY_CODES

US

ISO 国家代码的逗号分隔列表

PLAID_USER_ID

family-default

发送给 Plaid 的稳定 client_user_id

PLAID_ENCRYPTION_KEY

32 字节十六进制 (openssl rand -hex 32)。用于静态令牌的 AES-256-GCM 密钥。

LINK_SESSION_SECRET

≥ 32 字节十六进制。用于签名链接 URL 的 HMAC 密钥。

LINK_SESSION_TTL_SECONDS

900

链接会话生命周期

LINK_BASE_URL

浏览器将访问的公共 HTTPS 基础 URL(例如 https://plaid.example.com

PORT

3333

HTTP 端口。TLS 在 nanoclaw 上游终止。

ADMIN_TOKEN

如果设置,则保护 /link/admin/* 内省路由

MCP_TRANSPORT

http

stdio

http

MCP_BEARER_TOKEN

如果 MCP_TRANSPORT=http 则必需

POST /mcp 上必需的 Bearer 令牌

DB_PATH

./data/plaid-mcp.sqlite (Docker: /data/plaid-mcp.sqlite)

SQLite 路径。在此处挂载持久化卷。

LOG_LEVEL

info

Pino 日志级别。所有日志发送到 stderr。

使用以下命令生成密钥:

make keys

存储

位于 $DB_PATH 的 SQLite (better-sqlite3)。两个表很重要:

  • itemsitem_id 主键,加密的 access_token_blob BLOB,机构名称/ID,状态,同意过期时间。

  • link_sessions — 短期有效,在读取时如果超过 expires_at 且在 60 秒后台扫描期间会自动过期。

访问令牌存储为 [1 字节版本][12 字节 IV][16 字节 GCM 标签][N 字节密文]。如果 GCM 标签验证失败,解密将关闭。

安全模型

  • MCP HTTP 传输在每个请求上都需要 Authorization: Bearer $MCP_BEARER_TOKEN。如果没有它,代理集群会将每个关联的银行账户暴露给互联网。

  • 面向浏览器的 /link/* 路由是签名的 (HMAC),并绑定到基于数据库的短期会话。

  • TLS 预期在上游(在 nanoclaw / Caddy / 您的边缘设备处)终止。容器内部使用纯 HTTP;仅通过代理暴露它。

  • 每个 Plaid 令牌在静态存储时都是加密的。即使拿到 SQLite 文件,没有 PLAID_ENCRYPTION_KEY 的攻击者也无法使用这些令牌。

  • MCP 工具永远不会将访问令牌返回给代理。只有不透明的 item_id / account_id 字符串会跨越 MCP 边界。

本地开发

npm install
make setup           # creates .env from env.example
make keys >> .env    # append fresh PLAID_ENCRYPTION_KEY / LINK_SESSION_SECRET / MCP_BEARER_TOKEN
# edit .env: PLAID_CLIENT_ID, PLAID_SECRET, LINK_BASE_URL
npm run dev          # tsx with hot reload

对于本地链接测试,您需要一个 HTTPS 隧道(Plaid Link onSuccess 不会从 http://localhost 触发)。cloudflaredngrok 或真实的 Caddy 反向代理都可以;它们给您的任何公共主机名都应填入 LINK_BASE_URL

Docker

make build
make up
make logs

Compose 文件挂载了 ./data:/data,以便 SQLite 数据库在重启后依然存在。在 nanoclaw 部署中,将该绑定挂载替换为集群管理的持久化卷。

将代理连接到托管实例

在代理容器的 MCP 客户端配置中:

{
  "mcpServers": {
    "plaid": {
      "url": "https://plaid-mcp.your-domain.example/mcp",
      "headers": {
        "Authorization": "Bearer <MCP_BEARER_TOKEN>"
      }
    }
  }
}

代理通过 nanoclaw 用于其其他代理密钥的任何密钥注入机制获取 Bearer 令牌。它永远不会看到 PLAID_SECRET 或任何访问令牌。

许可证

内部使用。

Maintenance

ActivityInactive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Self-hosted MCP server enabling Claude to query bank accounts, balances, and transactions through Plaid with OAuth and TLS.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    A local MCP server that provides read-only SQL access to financial accounts via Plaid, enabling natural language queries about transactions, balances, and holdings.
    MIT
  • F
    license
    A
    quality
    C
    maintenance
    Personal finance MCP server that integrates Plaid bank data with local SQLite memory for conversational budgeting, goal tracking, and transaction management.
    15
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP server that exposes banking data (connections, accounts, balances, transactions) and agent skills, allowing AI agents to query and refresh financial data via stdio.
    139
    6
    Apache 2.0