Skip to main content
Glama
server.js9.19 kB
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import axios from "axios"; import dotenv from "dotenv"; dotenv.config(); const UPSTOX_BASE_URL = "https://api.upstox.com"; const ACCESS_TOKEN = process.env.UPSTOX_ACCESS_TOKEN; const server = new Server( { name: "upstox-trading-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); const tools = [ { name: "get_holdings", description: "Retrieve long-term holdings (DEMAT positions) of the user. Returns list of stocks held in the portfolio.", inputSchema: { type: "object", properties: {}, required: [], }, }, { name: "get_positions", description: "Get current intraday and delivery positions. Returns list of open positions with profit/loss details.", inputSchema: { type: "object", properties: {}, required: [], }, }, { name: "get_mtf_positions", description: "Retrieve Margin Trade Funding (MTF) positions. Returns list of leveraged positions.", inputSchema: { type: "object", properties: {}, required: [], }, }, { name: "calculate_margin", description: "Calculate required margin for instruments. Provide instrument details to get margin requirement.", inputSchema: { type: "object", properties: { instruments: { type: "array", items: { type: "object", properties: { instrument_token: { type: "string", description: "Instrument key (e.g., NSE_EQ|INE528G01035 for equity or NSE_FO|35000 for derivatives)", }, quantity: { type: "number", description: "Order quantity", }, product: { type: "string", enum: ["D", "I", "CO", "MTF"], description: "Product type: D=Delivery, I=Intraday, CO=Cover Order, MTF=Margin", }, transaction_type: { type: "string", enum: ["BUY", "SELL"], description: "BUY or SELL", }, }, required: ["instrument_token", "quantity", "product", "transaction_type"], }, description: "Array of instruments to calculate margin for (max 20)", }, }, required: ["instruments"], }, }, { name: "get_market_quote", description: "Retrieve full market quotes including OHLC data, depth, volume for instruments.", inputSchema: { type: "object", properties: { instrument_keys: { type: "array", items: { type: "string", }, description: "Array of instrument keys (e.g., [NSE_EQ|INE528G01035, NSE_EQ|INE002A01018])", }, }, required: ["instrument_keys"], }, }, { name: "get_ltp_quotes", description: "Get Last Traded Price (LTP) for instruments.", inputSchema: { type: "object", properties: { instrument_keys: { type: "array", items: { type: "string", }, description: "Array of instrument keys", }, }, required: ["instrument_keys"], }, }, { name: "place_order", description: "Execute a new order on the exchange. Supports market, limit, stop-loss and bracket orders.", inputSchema: { type: "object", properties: { instrument_token: { type: "string", description: "Instrument token (e.g., NSE_EQ|INE528G01035)", }, quantity: { type: "number", description: "Order quantity", }, order_type: { type: "string", enum: ["MARKET", "LIMIT", "SL", "SL-M"], description: "Order type: MARKET, LIMIT, SL (Stop Loss), SL-M (Stop Loss Market)", }, transaction_type: { type: "string", enum: ["BUY", "SELL"], description: "BUY or SELL", }, product: { type: "string", enum: ["D", "I", "CO", "MTF"], description: "Product type: D=Delivery, I=Intraday, CO=Cover Order, MTF=Margin", }, price: { type: "number", description: "Price for LIMIT orders, 0 for MARKET orders", }, trigger_price: { type: "number", description: "Trigger price for stop-loss orders", }, validity: { type: "string", enum: ["DAY", "IOC"], description: "Order validity: DAY or IOC (Immediate or Cancel)", }, disclosed_quantity: { type: "number", description: "Quantity to display in order book (optional)", }, is_amo: { type: "boolean", description: "Place order as After Market Order (optional)", }, tag: { type: "string", description: "Order tag for tracking (optional)", }, }, required: [ "instrument_token", "quantity", "order_type", "transaction_type", "product", "price", "trigger_price", "validity", ], }, }, { name: "cancel_order", description: "Cancel an existing pending order.", inputSchema: { type: "object", properties: { order_id: { type: "string", description: "Order ID to cancel", }, }, required: ["order_id"], }, }, ]; // API call helper async function callUpstoxAPI(endpoint, method = "GET", data = null) { try { if (!ACCESS_TOKEN) { throw new Error("UPSTOX_ACCESS_TOKEN is not set in environment variables"); } const config = { method, url: `${UPSTOX_BASE_URL}${endpoint}`, headers: { Authorization: `Bearer ${ACCESS_TOKEN}`, "Content-Type": "application/json", Accept: "application/json", }, }; if (data) { config.data = data; } const response = await axios(config); return response.data; } catch (error) { const errorMessage = error.response?.data?.message || error.message; throw new Error(`Upstox API Error: ${errorMessage}`); } } async function handleToolCall(toolName, toolInput) { try { switch (toolName) { case "get_holdings": return await callUpstoxAPI("/v2/portfolio/long-term-holdings"); case "get_positions": return await callUpstoxAPI("/v3/portfolio/short-term-positions"); case "get_mtf_positions": return await callUpstoxAPI("/v3/portfolio/mtf-positions"); case "calculate_margin": { const marginData = { instruments: toolInput.instruments, }; return await callUpstoxAPI("/v2/charges/margin", "POST", marginData); } case "get_market_quote": { const keys = toolInput.instrument_keys.join(","); return await callUpstoxAPI(`/v2/market-quote/quotes?instrument_key=${keys}`); } case "get_ltp_quotes": { const keys = toolInput.instrument_keys.join(","); return await callUpstoxAPI(`/v2/market-quote/ltp?instrument_key=${keys}`); } case "place_order": { const orderData = { quantity: toolInput.quantity, product: toolInput.product, validity: toolInput.validity, price: toolInput.price, instrument_token: toolInput.instrument_token, order_type: toolInput.order_type, transaction_type: toolInput.transaction_type, disclosed_quantity: toolInput.disclosed_quantity || 0, trigger_price: toolInput.trigger_price || 0, is_amo: toolInput.is_amo || false, tag: toolInput.tag || "", }; return await callUpstoxAPI("/v2/order/place", "POST", orderData); } case "cancel_order": { const { order_id } = toolInput; return await callUpstoxAPI(`/v3/order/cancel?order_id=${order_id}`, "DELETE"); } default: throw new Error(`Unknown tool: ${toolName}`); } } catch (error) { return { status: "error", message: error.message, }; } } server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const result = await handleToolCall(name, args || {}); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; }); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Upstox MCP Server running on stdio"); } main().catch((error) => { console.error("Fatal error in main():", error); process.exit(1); });

Latest Blog Posts

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/dileepgaganr/upstox-mcp'

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