add_txn
Record a stock transaction: buy, sell, deposit, dividend, or tax. Specify ticker, date, shares, price, and optional reason for trade analysis.
Instructions
Insert one stock transaction. For bulk imports: first show the user your detected column mapping and total row count, wait for their confirmation, then call once per row in chronological order (average cost depends on insertion order). Types: buy/sell move shares; deposit adds shares (set price=0 for grants/transfers, or actual cost basis); dividend/tax record cash events (price = total amount, shares = 1). For buy/sell, capture the user's reason (the why behind the trade) when they share it — even one short sentence helps later analysis cross-check thesis vs outcome. The currency arg is the trade's native currency, not the user's home currency.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ticker | Yes | Stock ticker symbol (e.g. AAPL) | |
| date | Yes | Transaction date (YYYY-MM-DD) | |
| type | Yes | ||
| shares | Yes | ||
| price | Yes | Price per share in USD (use 0 for price=unknown deposits) | |
| currency | No | USD | |
| reason | No | Why this trade? (free-form). Most useful on buy/sell — leave blank for dividends/grants/etc. |
Implementation Reference
- apps/mcp/src/tools/mutate.ts:12-47 (handler)The 'add_txn' tool handler: registers the MCP tool with server.tool(), defines the Zod schema (ticker, date, type, shares, price, currency, reason), and implements the logic to insert a stock transaction into the database via Drizzle ORM. Returns the inserted transaction id and fields.
export function registerMutateTools(server: McpServer): void { server.tool( 'add_txn', "Insert one stock transaction. For bulk imports: first show the user your detected column mapping and total row count, wait for their confirmation, then call once per row in chronological order (average cost depends on insertion order). Types: buy/sell move shares; deposit adds shares (set price=0 for grants/transfers, or actual cost basis); dividend/tax record cash events (price = total amount, shares = 1). For buy/sell, capture the user's reason (the why behind the trade) when they share it — even one short sentence helps later analysis cross-check thesis vs outcome. The currency arg is the trade's native currency, not the user's home currency.", { ticker: z.string().describe('Stock ticker symbol (e.g. AAPL)'), date: z.string().describe('Transaction date (YYYY-MM-DD)'), type: z.enum(['buy', 'sell', 'deposit', 'dividend', 'tax']), shares: z.number().positive(), price: z.number().min(0).describe('Price per share in USD (use 0 for price=unknown deposits)'), currency: z.string().default('USD'), reason: z .string() .optional() .describe( 'Why this trade? (free-form). Most useful on buy/sell — leave blank for dividends/grants/etc.', ), }, async ({ ticker, date, type, shares, price, currency, reason }) => { const db = getDb(); const result = db .insert(transactions) .values({ ticker: ticker.toUpperCase(), date, type, shares, price, currency, reason: reason ?? null, }) .returning({ id: transactions.id }) .get(); return ok({ id: result?.id, ticker: ticker.toUpperCase(), date, type, shares, price }); }, ); - apps/mcp/src/tools/mutate.ts:12-47 (schema)Zod schema definition for add_txn: ticker (string), date (YYYY-MM-DD), type (enum: buy/sell/deposit/dividend/tax), shares (positive number), price (non-negative number, USD), currency (default USD), reason (optional string).
export function registerMutateTools(server: McpServer): void { server.tool( 'add_txn', "Insert one stock transaction. For bulk imports: first show the user your detected column mapping and total row count, wait for their confirmation, then call once per row in chronological order (average cost depends on insertion order). Types: buy/sell move shares; deposit adds shares (set price=0 for grants/transfers, or actual cost basis); dividend/tax record cash events (price = total amount, shares = 1). For buy/sell, capture the user's reason (the why behind the trade) when they share it — even one short sentence helps later analysis cross-check thesis vs outcome. The currency arg is the trade's native currency, not the user's home currency.", { ticker: z.string().describe('Stock ticker symbol (e.g. AAPL)'), date: z.string().describe('Transaction date (YYYY-MM-DD)'), type: z.enum(['buy', 'sell', 'deposit', 'dividend', 'tax']), shares: z.number().positive(), price: z.number().min(0).describe('Price per share in USD (use 0 for price=unknown deposits)'), currency: z.string().default('USD'), reason: z .string() .optional() .describe( 'Why this trade? (free-form). Most useful on buy/sell — leave blank for dividends/grants/etc.', ), }, async ({ ticker, date, type, shares, price, currency, reason }) => { const db = getDb(); const result = db .insert(transactions) .values({ ticker: ticker.toUpperCase(), date, type, shares, price, currency, reason: reason ?? null, }) .returning({ id: transactions.id }) .get(); return ok({ id: result?.id, ticker: ticker.toUpperCase(), date, type, shares, price }); }, ); - apps/mcp/src/index.ts:7-7 (registration)Import of registerMutateTools from the mutate.ts module where add_txn is defined.
import { registerMutateTools } from './tools/mutate.ts'; - apps/mcp/src/index.ts:21-21 (registration)Registration call: registerMutateTools(server) which registers the add_txn tool on the MCP server.
registerMutateTools(server); - apps/mcp/src/helpers.ts:57-57 (helper)Instruction referencing add_txn in the server instructions: 'A buy/sell is recorded via add_txn and the conversation contains a rationale → save the decision + reason as a project note.'
- A buy/sell is recorded via add_txn and the conversation contains a rationale → save the decision + reason as a project note.