paper-broker-mcp
A simulated (paper) NSE equity trading server: look up stocks, read quotes and account state, and place orders only through a preview-then-confirm flow.
search_instruments(query) — turn a spoken name, nickname or symbol ("state bank", "L and T", "infy") into an NSE symbol.
get_quote(symbol) — last price, previous close, day change, day high/low (simulated).
get_funds() — cash, cash blocked by open buys, available cash, holdings value, P&L.
get_positions() — holdings with quantity, average price, current price, P&L.
get_orders() — recent orders newest first, with status (OPEN/FILLED/CANCELLED/REJECTED).
preview_order(symbol, side, quantity, order_type, limit_price?) — validate a BUY/SELL market or limit order (qty 1–10,000, limit within 20% of last price) and return a
preview_idvalid for 60 seconds; places nothing.place_order(preview_id) — place a previously previewed order, only after explicit user confirmation; fails if the preview is missing or expired.
cancel_order(order_id) — cancel an open limit order by id (filled orders can't be cancelled).
All state is in-memory/plain-JSON paper money — no broker, exchange, or real funds involved.
Fetches this week's real news headlines for a stock, topic or the market from Google News RSS, returning title, source, publish time and link (headlines only, never article text), tagged with red flags for hype or tip-style language. Results are cached for 10 minutes and the tool degrades gracefully if Google News is unreachable.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@paper-broker-mcp buy 10 shares of Reliance, show me the preview first"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
paper-broker-mcp
An MCP server for a simulated stock trading account, plus a small agent loop that talks to it. You say "buy 10 Reliance", the agent looks up the stock, previews the order, reads it back and asks "Shall I place it?". Only after you say yes does anything get placed.
It can also check your portfolio and prices, summarise this week's real headlines about a stock without the hype, and point out the warning signs in a forwarded stock tip. It works in English and Hindi.
This is the backend for the voice trading agent on my portfolio: https://www.anantdubey.com/work/voice-trading-agent. The idea comes from my Ideathon 2026 entry. This repo is a from-scratch rebuild done in public and shares no code with the original.
Prices are simulated and no real money is involved. It isn't connected to any broker or exchange, and nothing here is investment advice.
How it fits together
browser mic ──speech-to-text──┐ ┌── text-to-speech ──> speaker
v │
runAgentTurn(messages, state)
│
model (Claude, Gemini, or offline rules)
│ tool calls
v
MCP client ══ in-memory transport ══ MCP server
│
v
PaperBroker (plain JSON state)
prices = f(symbol, time)
+ Google News RSS (headlines only)The same MCP server also runs over stdio, so Claude Desktop or any other MCP client can use it directly.
Related MCP server: trclient-mcp
Tools
Tool | What it does |
| Turns a spoken name ("state bank", "L and T", "infy") into an NSE symbol. |
| Last price, previous close, change, day high and low. Always marked |
| Cash, cash blocked by open buy orders, available cash, holdings value, P&L. |
| Holdings with average price, current price and P&L. |
| Recent orders, newest first. |
| Checks an order and returns |
| Places a previewed order. Fails if the preview doesn't exist or has expired. |
| Cancels an open limit order. |
| This week's real headlines from Google News for a stock, a topic or the market: title, source, time and link, each tagged with red flags such as hype or tip-style language. |
| Looks for the warning signs of a pump-and-dump or tip scam in a message: guaranteed returns, short-term price targets, urgency, insider or "operator" claims, paid groups, OTP or fee requests, penny stocks. Returns a risk level, each flag with the words that triggered it, and what to do instead. |
Each tool returns a short text line (written to be read aloud) and the same data as structured JSON.
Preview, then confirm
There is no tool that places an order from raw parameters. An order can only come from a preview, and a preview only lives for 60 seconds. That gives two layers of protection:
The server.
place_ordertakes nothing but apreview_id. The model can't invent an order the user never heard, and a stale preview can't be replayed. Its description says to call it only after the user has explicitly confirmed the preview.The agent loop.
runAgentTurnonly letsplace_orderthrough for the preview that was pending before the user's latest message. A preview created during the current turn is refused withNOT_CONFIRMED, whatever the model decides, so the user always hears the preview and answers before anything is placed. If the user says anything other than a confirmation, the pending preview is dropped.
News and tip checks
Stock tips spread through WhatsApp and Telegram groups, and many of them are pump-and-dump schemes. Two tools are there to slow a user down before they act on one:
get_newsfetches real headlines (titles, source and link only, never article text) and flags the ones written like a tip. The agent is told to summarise them neutrally, keep what was reported apart from opinion, and never suggest a trade because of news. Results are cached for 10 minutes; if Google News can't be reached the tool says so instead of failing. Headlines are real; prices in this demo are simulated, so the two won't match.check_tipis deterministic, so it works offline and gives the same answer every time. It never calls a message a scam as a fact. It says how many common warning signs it found, and points to checking an advisor on SEBI's intermediary search, reading the company's NSE or BSE announcements, never paying for tips or sharing an OTP, and reporting on SEBI SCORES.
Hindi
Pass language: "hi" to runAgentTurn, or just write in Devanagari, and the agent replies in simple spoken Hindi. News and tip checks follow the same language. The offline parser understands Hindi and Hinglish too: Hindi names for all twenty stocks (रिलायंस, टीसीएस, इंफोसिस, स्टेट बैंक…), Hindi number words, and commands like "10 रिलायंस खरीदो", "इंफोसिस का भाव", "टाटा मोटर्स की खबर", "मेरा पोर्टफोलियो" and "हाँ" / "नहीं".
Quickstart
Claude Desktop
Add this to claude_desktop_config.json and restart Claude Desktop:
{
"mcpServers": {
"paper-broker": {
"command": "npx",
"args": ["-y", "github:dubeyanant/paper-broker-mcp"]
}
}
}The account lives in memory and resets when the server restarts. To keep it, add "--state", "/path/to/paper-account.json" to args. The file is created on the first change.
Terminal chat
git clone https://github.com/dubeyanant/paper-broker-mcp
cd paper-broker-mcp
npm install
npm run chatTool calls are printed dimmed above each reply. Things to try: buy 10 reliance, yes, sell 5 TCS at 4,000, buy twenty shares of infosys, price of HDFC bank, how is Tata Motors doing, news on infosys, check this tip: XYZ is a multibagger, target 500 in 2 days, join our VIP group, show my portfolio, how much cash do I have, my orders, cancel my last order, 10 रिलायंस खरीदो. Add -- --provider rules to force the offline parser, or -- --language hi for Hindi replies.
The chat script and tests run the TypeScript sources directly, which needs Node 22.18 or newer. The built package in dist/ runs on Node 20.
From code
import { runAgentTurn } from "paper-broker-mcp/agent";
const first = await runAgentTurn({
messages: [{ role: "user", content: "buy 10 reliance" }],
state: null, // fresh account with 10 lakh rupees of virtual cash
});
first.reply; // "Buy 10 shares of Reliance at market, about 29,425 rupees. Shall I place it?"
first.events; // [{ type: "tool_call", name: "search_instruments", args: {...} }, { type: "tool_result", ... }, ...]
const second = await runAgentTurn({
messages: [...first.messages, { role: "user", content: "yes" }],
state: first.state, // plain JSON, safe to store anywhere between turns
});
second.reply; // "Done. Bought 10 shares of Reliance at 2,942 rupees 45 paise."
// Hindi: set language, or write in Devanagari.
const hindi = await runAgentTurn({ messages: [{ role: "user", content: "टाटा मोटर्स की खबर" }], state: null, language: "hi" });state is plain JSON, so a web route can send it to the browser and get it back on the next request. Validate before trusting it: runAgentTurn and PaperBroker.fromState both reject malformed state.
The broker and server are available on their own too:
import { PaperBroker, createServer } from "paper-broker-mcp";
const broker = PaperBroker.fromState(savedState); // or null
const server = createServer(broker); // an McpServer; connect it to any transport
// ...after tool calls:
const next = broker.toState();API keys
Copy .env.example to .env. Every value is optional.
Variable | Default | Notes |
| Uses Claude when set. | |
|
| |
| Uses Gemini when set and there is no Anthropic key. | |
|
| Tried first. If it is busy, slow or out of quota, the agent retries once and then falls back to |
|
|
|
Either key works. With neither, the agent uses a built-in rules parser that makes no network calls and drives the same MCP tools, so the events look the same. If the model fails partway through a turn, the turn is replayed from the original state with the rules parser, and the result includes an error field. The one exception is a failure after an order was already placed or cancelled: that change stands, and the reply describes it.
How the simulation works
Universe. Twenty NSE large caps, each with spoken aliases ("RIL", "tata consultancy", "airtel", "L and T"). Equity delivery only.
Prices. A pure function of symbol and time: a base price times a sum of slow sine waves (periods from 47 minutes to 71 days, with per-symbol phases) plus a little hashed noise that changes once a second, rounded to the 0.05 tick. Nothing is stored, so any process computes the same price for the same second. The market never closes; "previous close" is the price just before midnight IST.
Account. Starts with 10 lakh rupees of virtual cash. No brokerage, taxes or slippage.
Rules. Quantity 1 to 10,000. Buys need enough available cash. No short selling. Limit prices must be within 20% of the last price. Market orders fill at the last price. Limit orders rest, block cash (buys) or shares (sells), and fill at the limit price the first time the price path touches it. Open orders are checked whenever the account is used, by walking the price path since the order was placed.
Development
npm test # node:test, runs the TypeScript sources directly
npm run typecheck # src, tests and examples
npm run build # compile src to dist/dist/ is committed on purpose so the package can be installed straight from GitHub without a build step. Run npm run build and commit the result after changing anything in src/.
Disclaimer
Simulated prices, no real money, not investment advice. This project is not affiliated with any broker or exchange. Stock names and symbols are used only to make the demo recognisable.
License
MIT. See LICENSE.
Available Tools
8 toolscancel_orderCancel orderADestructive
Cancel an open limit order by its id, for example ORD-3. Filled orders cannot be cancelled.
Inputs: order_id (required).
get_fundsGet fundsBRead-only
Cash, cash blocked by open buy orders, available cash, holdings value and profit or loss for the account.
Inputs: no named parameters.
get_ordersGet ordersBRead-only
Recent orders, newest first, with status (OPEN, FILLED, CANCELLED or REJECTED).
Inputs: no named parameters.
get_positionsGet positionsARead-only
Stocks held in the account with quantity, average price, current price and profit or loss.
Inputs: no named parameters.
get_quoteGet quoteARead-only
Current simulated price for one stock: last price, previous close, day change, day high and low.
Inputs: symbol (required).
place_orderPlace orderA
Place an order from a preview made by preview_order. Call only after the user has explicitly confirmed the preview (for example 'yes' or 'go ahead'). Never call it in the same step as preview_order. Fails if the preview does not exist or has expired.
Inputs: preview_id (required).
preview_orderPreview orderA
Check an order and get a preview to read back to the user. Does not place anything. Returns a preview_id that place_order needs. Previews expire after 60 seconds.
Inputs: side (required), symbol (required), quantity (required), order_type (required), +1 more.
search_instrumentsSearch instrumentsARead-only
Find NSE stocks by company name, spoken name or symbol (for example 'reliance', 'state bank', 'L and T', 'infy'). Use this to turn what the user said into a symbol before quoting or ordering.
Inputs: query (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 8 tools
Each tool targets a distinct resource+action pair: search_instruments resolves names to symbols, get_quote prices a single stock, get_funds/get_positions/get_orders read distinct account views, and cancel_order/preview_order/place_order cover separate order lifecycle steps. The preview_order/place_order split is explicitly documented as a two-step gate with a preview_id handshake, so misselection risk is low. No two tools appear to do the same thing.
Every tool is snake_case verb_noun (search_instruments, get_quote, get_funds, get_positions, get_orders, preview_order, place_order, cancel_order). The verb choices are meaningful and predictable: get_* for reads, preview/place/cancel_* for lifecycle actions. No mixed conventions or vague verbs.
Eight tools is well-scoped for a paper-trading broker: three read-only account views, one instrument lookup, one quote, and a three-step order lifecycle. Each tool earns its place and there is no filler or redundant surface.
The surface covers the full retail trading loop: discovery (search_instruments), pricing (get_quote), account state (funds/positions/orders), and order creation and cancellation with a preview gate. Minor gaps remain, such as no modify-order operation, no single-order status lookup by id, and no market-hours/status signal, but agents can work around these using get_orders and cancel-then-replace.
Maintenance
Related MCP Connectors
Trading MCP server for AI agents, with live market data, account reads and controlled execution.
NSE/BSE stock data via Tickertape as JSON: index lists, screener, 10-year fundamentals.
Open-source MCP server for Zerodha Kite Connect. Portfolio, market data, backtesting, alerts.
Paper trading for AI: live quotes, indicators, and virtual trades on stocks, crypto, and forex.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceEnables language models to trade on a Zerodha account by placing and canceling orders and reading orders and portfolio positions over stdio.-
- AlicenseAqualityBmaintenanceEnables local management of Trade Republic accounts, including portfolio, cash, orders, documents, and market data, with dry-run order previews and safety guardrails.22MIT
- AlicenseNot gradedqualityBmaintenanceEnables reading a Trading 212 Stocks ISA and safely previewing, approving, placing, and cancelling orders, with account, position, order-history, and instrument-search tools.23 PyPIMIT
- AlicenseNot gradedqualityCmaintenanceEnables users to structure supported paper-trade ideas, check them against disclosed limits, enforce a server-side cooldown, require exact manual confirmation, and review a local audit trail without broker integration or real-money execution.MIT