Investbrain MCP
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Investbrain MCP
Remote Model Context Protocol server for a self-hosted Investbrain portfolio tracker. It runs on Cloudflare Workers (free plan) and calls your Investbrain /api routes with the Sanctum token your MCP client sends on each request.
The Worker does not store that token.
Keywords: MCP, Model Context Protocol, Investbrain, portfolio tracker, holdings, transactions, dividends, market data, Laravel Sanctum, Cloudflare Workers, self-hosted.
Tools
One tool per Investbrain API route under auth:sanctum. Update and delete tools are marked destructive in the MCP tool annotations and in the description.
Tool | Method | Path |
| GET |
|
| GET |
|
| GET |
|
| POST |
|
| PUT |
|
| DELETE |
|
| GET |
|
| GET |
|
| POST |
|
| PUT |
|
| DELETE |
|
| GET |
|
| GET |
|
| PUT |
|
| GET |
|
| GET |
|
| POST |
|
| PUT |
|
| DELETE |
|
| GET |
|
Portfolio, transaction, and holding list tools accept Investbrain's filter query: page, itemsPerPage (default 15, or -1 for every row), search, sortBy, and sortDesc. Portfolio search matches title and notes. Transaction and holding search match the symbol. investbrain_list_portfolios can also filter with holdings_symbol (filter[holdings.symbol]).
investbrain_list_dividends uses a different query. It accepts page, itemsPerPage (1–100, default 15; -1 is not valid), search (symbol, max 255 characters), sort (comma-separated columns symbol, date, dividend_amount, source, created_at, updated_at, with - for descending, for example -date), symbol (filter[symbol], one ticker or a comma-separated list), source (filter[source]: sync or manual), and date (filter[date], exact YYYY-MM-DD). Default order is date descending, then id ascending. The list is paginated (data, links, meta). A single dividend is not wrapped.
A dividend date is the inclusive entitlement date. For B3 that is data-com, not the payment date and not Yahoo's ex-date. Shares bought or sold on that date count. dividend_amount is the gross per-share amount in the trading currency. The app does not withhold tax. One row exists per symbol per date, so same-day proventos (dividendo, JCP, and others) are summed into that amount. Create and update store source=manual. source and dividend_amount_base sent on a write are ignored. Manual rows survive the Yahoo sync. A delete is remembered, so sync does not recreate that symbol and date. Each write recalculates dividends_earned. A duplicate symbol and date, or another validation failure, is a 422. The tool returns Investbrain's message and field errors.
Portfolio, transaction, and dividend ids are UUIDs. Create and update fields follow Investbrain's form requests: portfolio title is 5–255 characters; a transaction is BUY (requires cost_basis) or SELL (requires sale_price); quantity must be greater than 0; a sell cannot exceed shares owned before that date; holding update only sets reinvest_dividends. A dividend create requires symbol, date, and dividend_amount greater than 0. A dividend update sends any subset of those fields.
Laravel registers PUT and PATCH for each update action. These tools send PUT.
Related MCP server: IB MCP Server
Quick start
Fork this repository.
In the Cloudflare dashboard, open Workers & Pages and connect the fork with Workers Builds. Production branch:
main.In the Worker, open Settings → Builds:
Deploy command:
pnpm run deployBuild variable
INVESTBRAIN_BASE_URL: public origin of your Investbrain app, for examplehttps://investbrain.example.comOptional build variable
ALLOWED_HOSTNAMES: comma-separated hostnames, for examplemcp.example.com, with no scheme or port
A push to
maindeploys. Workers Builds does not copy a build variable into the Worker.pnpm run deploydoes, withwrangler deploy --var. The default commandnpx wrangler deploywould upload the empty placeholder.Leave non-production branch builds off. Their default command does not apply that variable.
Wrangler prints the workers.dev host. The MCP endpoint is
https://investbrain-mcp.<account-subdomain>.workers.dev/mcp.Create an Investbrain API token (below) and put it in the MCP client. Do not add that token as a GitHub secret, a Worker secret, or a build variable.
Manual deploy
GitHub Actions can deploy the same script. The workflow runs only when you start it. A push does not start it.
Create a Cloudflare API token from the "Edit Cloudflare Workers" template, and copy the account ID. Workers Builds does not need this token.
In the GitHub fork, add repository secrets (Settings → Secrets and variables → Actions):
Secret | Value |
| API token from step 1 |
| Cloudflare account ID |
| Same origin as the Workers Builds variable |
| Optional. Same comma-separated hostnames. |
Actions → Deploy → Run workflow.
Configuration
Name | Where it is set | Purpose |
| Workers Builds build variable, or the GitHub Actions secret. Locally, | Origin of Investbrain. |
| Optional build variable or GitHub secret. Locally, | Extra Host and Origin names, comma-separated. Empty keeps |
| GitHub secret for the manual workflow only | Wrangler deploy. Workers Builds creates its own token. |
| GitHub secret for the manual workflow only | Wrangler deploy |
|
| Failed auth attempts per client IP before lockout |
|
| Window those failures are counted in |
|
| How long the IP stays locked |
The committed INVESTBRAIN_BASE_URL and ALLOWED_HOSTNAMES values are empty placeholders. pnpm run deploy reads those names from the environment and writes them onto the Worker with wrangler deploy --var. In Workers Builds, set them as build variables. A value saved only under Settings → Variables and Secrets is replaced on the next deploy. An empty or invalid base URL makes /mcp return 503.
Create an API token
Sign in to your Investbrain instance.
Open
/user/api-tokens.Create a token and name it, for example
MCP.Copy the plaintext token. Investbrain shows it once.
The API routes use Laravel Sanctum (auth:sanctum) and do not check token abilities. If the form lists permissions, leave them unset or pick any.
Send that token only from the MCP client:
Authorization: Bearer <investbrain-api-token>There is no token argument on the tools. The Worker reads this header, checks it with GET /api/me, caches a successful check for 60 seconds, and forwards the same header to Investbrain.
MCP client
Streamable HTTP, endpoint path /mcp.
{
"mcpServers": {
"investbrain": {
"type": "http",
"url": "https://investbrain-mcp.<account-subdomain>.workers.dev/mcp",
"headers": {
"Authorization": "Bearer <investbrain-api-token>"
}
}
}
}The client must send that header on every request to /mcp, including tool calls. A missing or rejected token is HTTP 401. Repeated failures from the same IP are HTTP 429.
The client has to support custom headers. A connector that only offers OAuth cannot send this static bearer token.
/health needs no token.
Security model
The Investbrain token is not a Worker secret, not a GitHub secret, and not written to Durable Object storage, logs, or tool arguments.
Each request carries
Authorization: Bearer <token>. The Worker copies that header to Investbrain and drops it when the request ends.Before the MCP handler runs, the Worker calls
GET /api/mewith the same header. A successful check is cached in that isolate for 60 seconds, keyed by the SHA-256 hash of the token, with at most 256 entries. The raw token is not stored. The cache is not shared across isolates and is not written to Durable Object storage. Investbrain401on the probe becomes HTTP 401 and is not cached. Other upstream failures become HTTP 502 and are not counted as bad tokens. Tool calls still request Investbrain, so an upstream401is a tool error even when the probe was cached. A revoked token can pass the gate until that entry expires.Missing and rejected tokens are counted per
CF-Connecting-IP(Cloudflare overwrites a client-supplied value). The default is 10 failures in 15 minutes, then a 30 minute lockout. The counter is a SQLite Durable Object, which the Workers free plan includes. The in-memory cache stores the IP and the lockout expiry, not the token.Redirects are not followed, so the token is not sent to another host.
INVESTBRAIN_BASE_URLis the address of your Investbrain app. It is not an API key.
Local development
Requires Node.js 22 or newer and pnpm 10.
pnpm install
cp .dev.vars.example .dev.vars
# edit INVESTBRAIN_BASE_URL
pnpm dev
pnpm test
pnpm type-checkpnpm dev serves the Worker on your machine (Wrangler prints the port, usually 8787). Point a client at http://127.0.0.1:8787/mcp with the same Authorization header. .dev.vars overrides the empty placeholders and is gitignored.
From your laptop, INVESTBRAIN_BASE_URL can be an Investbrain app that only your machine can reach. A deployed Worker cannot. The deployed URL has to be reachable from Cloudflare.
Regenerate Worker types after a wrangler.jsonc change with pnpm cf-typegen.
CI
Pull requests and pushes run pnpm install --frozen-lockfile, pnpm type-check, and pnpm test. That workflow has no secrets and does not deploy. The manual Deploy workflow repeats those checks, then runs pnpm run deploy.
Limitations
Transport is stateless Streamable HTTP at
/mcp. There is no SSE session. A session would have to keep the API token on the server.A successful
GET /api/mecheck is reused for 60 seconds in that isolate.tools/callstill requests Investbrain.Clients behind one public IP share the auth-failure lockout.
Deleting a portfolio follows Investbrain and cascades to that portfolio's holdings and transactions.
Market data is whatever that Investbrain instance has configured. Unknown symbols are a 404 from Investbrain, returned as a tool error.
This uses the Workers free plan: the Worker script and one SQLite Durable Object. It does not use Workers AI, Queues, or the Rate Limiting binding.
The default Host check allows
workers.devand localhost. SetALLOWED_HOSTNAMESto add a custom hostname. Those names are also accepted as browser Origin hosts. Requests with no Origin stay valid.Dividend
dateis the entitlement date (B3 data-com). Yahoo's historical dividend date can differ from data-com and from the payment date. These tools do not convert one date into the other.
License
Related MCP Connectors
Query your real net worth, spending, transactions, budgets and portfolio from any MCP client.
Create and manage trading strategies, bots, webhooks and signals from any MCP client.
Market Data App MCP — wraps the Market Data App API (marketdata.app)
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
Related MCP Servers
- FlicenseNot gradedqualityAmaintenanceEnables MCP clients to interact with Interactive Brokers' official Web API through a Client Portal Gateway, exposing brokerage account, market data, and trading functionality via authenticated HTTP endpoints.-
- FlicenseNot gradedqualityAmaintenanceEnables MCP clients to interact with Interactive Brokers' Web API for brokerage session management, account information, and trading-related endpoints. It uses the Client Portal Gateway for authentication and supports local or Docker-based deployment.-
- AlicenseNot gradedqualityBmaintenanceEnables MCP clients to read and edit a portfolio journal, including recording buys and sells, managing buy theses and checkable claims, watchlists, tags, and DCA plans, with OAuth or API key authentication.MIT
- FlicenseNot gradedqualityBmaintenanceEnables MCP-compatible clients to interact with Interactive Brokers through its official Web API, supporting account management, portfolio reporting, market data, and trading operations.-