bharatstock-mcp 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -9
- package/README.md +1 -1
- package/dist/{chunk-NXHQTUBJ.js → chunk-AQVPTO4U.js} +79 -190
- package/dist/http.js +1 -1
- package/dist/index.d.ts +2 -41
- package/dist/index.js +2 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,15 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `bharatstock-mcp` are documented here (newest first).
|
|
4
4
|
|
|
5
|
-
## 0.2.
|
|
6
|
-
-
|
|
7
|
-
`BHARATSTOCK_ADMIN_SECRET` env var is set alongside the API key, the server
|
|
8
|
-
additionally registers four support-ticket tools — `get_support_tickets`,
|
|
9
|
-
`get_support_ticket`, `set_ticket_status`, `log_ticket_event` — which call
|
|
10
|
-
the hosted MCP endpoint with that secret to list, inspect, and update
|
|
11
|
-
support tickets. A normal customer config (no admin secret) never registers
|
|
12
|
-
them, and the hosted server independently re-checks the secret, so the
|
|
13
|
-
consumer surface is unchanged.
|
|
5
|
+
## 0.2.1 (2026-11-06)
|
|
6
|
+
- Maintenance release. No change to the tool surface or behaviour.
|
|
14
7
|
|
|
15
8
|
## 0.1.5 (2026-11-03)
|
|
16
9
|
- Broadened the listing metadata for discoverability: a keyword-rich
|
package/README.md
CHANGED
|
@@ -128,7 +128,7 @@ MIT
|
|
|
128
128
|
|
|
129
129
|
## Changelog
|
|
130
130
|
|
|
131
|
-
- **0.2.
|
|
131
|
+
- **0.2.1** (2026-11-06): maintenance release. No change to the tool surface or behaviour.
|
|
132
132
|
- **0.1.5** (2026-11-03): broadened the registry/npm listing metadata — a keyword-rich `description` and expanded `keywords` (NSE, BSE, NIFTY, SENSEX, mutual funds, shareholding, FII/DII, corporate actions, screener, Indian equities) so the server is easier to find. No tool/schema change.
|
|
133
133
|
- **0.1.4** (2026-11-03): published to the official MCP Registry as `com.bharatstockapi/bharatstock-mcp`. No tool/schema change.
|
|
134
134
|
- **0.1.3** (2026-11-02): picks up `bharatstock` JS SDK 0.1.12, so the financials tool output now carries the as-filed segment reconciliation fields — `segmentResultsTotal` (the filing's own segment total) and `segmentNotes` / `resultsNotes` (the narrative note blocks; the segment note carries the unallocated/elimination/exceptional reconciliation in prose). Pass-through via the SDK; no tool/schema change.
|
|
@@ -41,115 +41,9 @@ async function checkAuthorization(baseUrl, apiKey, fetchImpl = fetch) {
|
|
|
41
41
|
// src/server.ts
|
|
42
42
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
43
43
|
import { BharatStock } from "bharatstock";
|
|
44
|
-
import { z as z2 } from "zod";
|
|
45
|
-
|
|
46
|
-
// src/operator.ts
|
|
47
44
|
import { z } from "zod";
|
|
48
|
-
async function callOperatorTool(opts, name, args) {
|
|
49
|
-
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
50
|
-
const url = `${opts.baseUrl.replace(/\/$/, "")}/v1/mcp`;
|
|
51
|
-
const rpc = {
|
|
52
|
-
jsonrpc: "2.0",
|
|
53
|
-
id: Date.now(),
|
|
54
|
-
method: "tools/call",
|
|
55
|
-
params: { name, arguments: args }
|
|
56
|
-
};
|
|
57
|
-
try {
|
|
58
|
-
const resp = await fetchImpl(url, {
|
|
59
|
-
method: "POST",
|
|
60
|
-
headers: {
|
|
61
|
-
"Content-Type": "application/json",
|
|
62
|
-
"X-Admin-Secret": opts.adminSecret
|
|
63
|
-
},
|
|
64
|
-
body: JSON.stringify(rpc)
|
|
65
|
-
});
|
|
66
|
-
const bodyText = await resp.text();
|
|
67
|
-
let body;
|
|
68
|
-
try {
|
|
69
|
-
body = JSON.parse(bodyText);
|
|
70
|
-
} catch {
|
|
71
|
-
body = void 0;
|
|
72
|
-
}
|
|
73
|
-
if (!resp.ok) {
|
|
74
|
-
const msg = body?.error?.message || bodyText.slice(0, 500) || `HTTP ${resp.status}`;
|
|
75
|
-
return { isError: true, content: [{ type: "text", text: `BharatStock operator error: ${msg}` }] };
|
|
76
|
-
}
|
|
77
|
-
const rpcErr = body?.error;
|
|
78
|
-
if (rpcErr) {
|
|
79
|
-
return { isError: true, content: [{ type: "text", text: `BharatStock operator error: ${rpcErr.message}` }] };
|
|
80
|
-
}
|
|
81
|
-
const result = body?.result;
|
|
82
|
-
if (result && Array.isArray(result.content)) {
|
|
83
|
-
return result;
|
|
84
|
-
}
|
|
85
|
-
return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
86
|
-
} catch (err) {
|
|
87
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
88
|
-
return { isError: true, content: [{ type: "text", text: `BharatStock operator transport error: ${message}` }] };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
function registerOperatorTools(server, opts) {
|
|
92
|
-
server.registerTool(
|
|
93
|
-
"get_support_tickets",
|
|
94
|
-
{
|
|
95
|
-
title: "List support tickets (operator)",
|
|
96
|
-
description: "OPERATOR-ONLY. List support tickets (customer feedback/email reports) with status (open/investigating/waiting/resolved/closed, or 'all') and category filters. Default shows the live queue (excludes resolved/closed).",
|
|
97
|
-
inputSchema: {
|
|
98
|
-
status: z.string().optional(),
|
|
99
|
-
category: z.string().optional(),
|
|
100
|
-
limit: z.number().int().positive().optional(),
|
|
101
|
-
offset: z.number().int().nonnegative().optional()
|
|
102
|
-
}
|
|
103
|
-
},
|
|
104
|
-
async (args) => callOperatorTool(opts, "get_support_tickets", args)
|
|
105
|
-
);
|
|
106
|
-
server.registerTool(
|
|
107
|
-
"get_support_ticket",
|
|
108
|
-
{
|
|
109
|
-
title: "Get one support ticket in full (operator)",
|
|
110
|
-
description: "OPERATOR-ONLY. One support ticket in full: the original message, its activity trail (status changes, notes, fixes applied), and any drafted reply. Use this to pick up a ticket and see everything already tried.",
|
|
111
|
-
inputSchema: { id: z.number().int().positive() }
|
|
112
|
-
},
|
|
113
|
-
async (args) => callOperatorTool(opts, "get_support_ticket", args)
|
|
114
|
-
);
|
|
115
|
-
server.registerTool(
|
|
116
|
-
"set_ticket_status",
|
|
117
|
-
{
|
|
118
|
-
title: "Set a support ticket's status (operator, write)",
|
|
119
|
-
description: "OPERATOR-ONLY WRITE. Transition a ticket's status (open/investigating/waiting/resolved/closed) with an optional note. Moving to resolved/closed with a note records the resolution summary.",
|
|
120
|
-
inputSchema: {
|
|
121
|
-
id: z.number().int().positive(),
|
|
122
|
-
status: z.enum(["open", "investigating", "waiting", "resolved", "closed"]),
|
|
123
|
-
note: z.string().optional()
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
async (args) => callOperatorTool(opts, "set_ticket_status", args)
|
|
127
|
-
);
|
|
128
|
-
server.registerTool(
|
|
129
|
-
"log_ticket_event",
|
|
130
|
-
{
|
|
131
|
-
title: "Append an event to a ticket's trail (operator, write)",
|
|
132
|
-
description: "OPERATOR-ONLY WRITE. Append an entry to a ticket's activity trail, e.g. event_type='fix_applied', detail='ran fix_eps_sign, 4 rows, DQ green'. event_type one of status_change/note/reply_sent/fix_applied/reopened/category_change.",
|
|
133
|
-
inputSchema: {
|
|
134
|
-
id: z.number().int().positive(),
|
|
135
|
-
event_type: z.enum([
|
|
136
|
-
"status_change",
|
|
137
|
-
"note",
|
|
138
|
-
"reply_sent",
|
|
139
|
-
"fix_applied",
|
|
140
|
-
"reopened",
|
|
141
|
-
"category_change"
|
|
142
|
-
]),
|
|
143
|
-
detail: z.string().min(1)
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
async (args) => callOperatorTool(opts, "log_ticket_event", args)
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// src/server.ts
|
|
151
45
|
var SERVER_NAME = "bharatstock";
|
|
152
|
-
var SERVER_VERSION = "0.2.
|
|
46
|
+
var SERVER_VERSION = "0.2.1";
|
|
153
47
|
async function run(fn) {
|
|
154
48
|
try {
|
|
155
49
|
const data = await fn();
|
|
@@ -165,13 +59,12 @@ async function run(fn) {
|
|
|
165
59
|
}
|
|
166
60
|
}
|
|
167
61
|
function buildServer(options = {}) {
|
|
168
|
-
const
|
|
169
|
-
const client = new BharatStock(sdkOptions);
|
|
62
|
+
const client = new BharatStock(options);
|
|
170
63
|
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
|
|
171
|
-
const ticker =
|
|
172
|
-
const exchange =
|
|
173
|
-
const fromDate =
|
|
174
|
-
const toDate =
|
|
64
|
+
const ticker = z.string().describe("NSE/BSE ticker symbol (e.g. RELIANCE, TCS) or ISIN.");
|
|
65
|
+
const exchange = z.enum(["NSE", "BSE"]).optional().describe("Optional exchange to disambiguate a cross-listed stock.");
|
|
66
|
+
const fromDate = z.string().optional().describe("Start date, YYYY-MM-DD (inclusive).");
|
|
67
|
+
const toDate = z.string().optional().describe("End date, YYYY-MM-DD (inclusive).");
|
|
175
68
|
server.registerTool(
|
|
176
69
|
"get_stock_quote",
|
|
177
70
|
{
|
|
@@ -196,7 +89,7 @@ function buildServer(options = {}) {
|
|
|
196
89
|
title: "Get quotes for multiple stocks",
|
|
197
90
|
description: "Latest price snapshot for up to 50 tickers in one call. Prefer this over many get_stock_quote calls when comparing several stocks.",
|
|
198
91
|
inputSchema: {
|
|
199
|
-
symbols:
|
|
92
|
+
symbols: z.array(z.string()).min(1).max(50).describe("List of ticker symbols (max 50).")
|
|
200
93
|
}
|
|
201
94
|
},
|
|
202
95
|
async ({ symbols }) => run(() => client.stocks.quotes(symbols))
|
|
@@ -211,8 +104,8 @@ function buildServer(options = {}) {
|
|
|
211
104
|
fromDate,
|
|
212
105
|
toDate,
|
|
213
106
|
exchange,
|
|
214
|
-
page:
|
|
215
|
-
pageSize:
|
|
107
|
+
page: z.number().int().positive().optional(),
|
|
108
|
+
pageSize: z.number().int().positive().max(500).optional()
|
|
216
109
|
}
|
|
217
110
|
},
|
|
218
111
|
async ({ ticker: ticker2, fromDate: fromDate2, toDate: toDate2, exchange: exchange2, page, pageSize }) => run(() => client.stocks.prices(ticker2, { fromDate: fromDate2, toDate: toDate2, exchange: exchange2, page, pageSize }))
|
|
@@ -224,10 +117,10 @@ function buildServer(options = {}) {
|
|
|
224
117
|
description: "Quarterly or annual financial statements (P&L, balance sheet, cash flow) for a stock, as filed with the exchange. Set periodType to 'annual' for yearly figures or 'quarterly' for quarterly results.",
|
|
225
118
|
inputSchema: {
|
|
226
119
|
ticker,
|
|
227
|
-
periodType:
|
|
120
|
+
periodType: z.enum(["quarterly", "annual"]).optional().describe("Reporting cadence. Defaults to quarterly."),
|
|
228
121
|
exchange,
|
|
229
|
-
page:
|
|
230
|
-
pageSize:
|
|
122
|
+
page: z.number().int().positive().optional(),
|
|
123
|
+
pageSize: z.number().int().positive().max(50).optional()
|
|
231
124
|
}
|
|
232
125
|
},
|
|
233
126
|
async ({ ticker: ticker2, periodType, exchange: exchange2, page, pageSize }) => run(() => client.stocks.financials(ticker2, { periodType, exchange: exchange2, page, pageSize }))
|
|
@@ -251,8 +144,8 @@ function buildServer(options = {}) {
|
|
|
251
144
|
fromDate,
|
|
252
145
|
toDate,
|
|
253
146
|
exchange,
|
|
254
|
-
page:
|
|
255
|
-
pageSize:
|
|
147
|
+
page: z.number().int().positive().optional(),
|
|
148
|
+
pageSize: z.number().int().positive().max(500).optional()
|
|
256
149
|
}
|
|
257
150
|
},
|
|
258
151
|
async ({ ticker: ticker2, fromDate: fromDate2, toDate: toDate2, exchange: exchange2, page, pageSize }) => run(
|
|
@@ -267,8 +160,8 @@ function buildServer(options = {}) {
|
|
|
267
160
|
inputSchema: {
|
|
268
161
|
ticker,
|
|
269
162
|
exchange,
|
|
270
|
-
page:
|
|
271
|
-
pageSize:
|
|
163
|
+
page: z.number().int().positive().optional(),
|
|
164
|
+
pageSize: z.number().int().positive().max(50).optional()
|
|
272
165
|
}
|
|
273
166
|
},
|
|
274
167
|
async ({ ticker: ticker2, exchange: exchange2, page, pageSize }) => run(() => client.stocks.shareholding(ticker2, { exchange: exchange2, page, pageSize }))
|
|
@@ -280,10 +173,10 @@ function buildServer(options = {}) {
|
|
|
280
173
|
description: "Corporate actions for a stock: dividends, bonuses, stock splits, rights issues, and buybacks, with ex-dates.",
|
|
281
174
|
inputSchema: {
|
|
282
175
|
ticker,
|
|
283
|
-
actionType:
|
|
176
|
+
actionType: z.string().optional().describe("Filter by type, e.g. dividend, bonus, split."),
|
|
284
177
|
exchange,
|
|
285
|
-
page:
|
|
286
|
-
pageSize:
|
|
178
|
+
page: z.number().int().positive().optional(),
|
|
179
|
+
pageSize: z.number().int().positive().max(50).optional()
|
|
287
180
|
}
|
|
288
181
|
},
|
|
289
182
|
async ({ ticker: ticker2, actionType, exchange: exchange2, page, pageSize }) => run(() => client.stocks.corporateActions(ticker2, { actionType, exchange: exchange2, page, pageSize }))
|
|
@@ -295,8 +188,8 @@ function buildServer(options = {}) {
|
|
|
295
188
|
description: "Bulk deals reported for a specific stock.",
|
|
296
189
|
inputSchema: {
|
|
297
190
|
ticker,
|
|
298
|
-
page:
|
|
299
|
-
pageSize:
|
|
191
|
+
page: z.number().int().positive().optional(),
|
|
192
|
+
pageSize: z.number().int().positive().max(100).optional()
|
|
300
193
|
}
|
|
301
194
|
},
|
|
302
195
|
async ({ ticker: ticker2, page, pageSize }) => run(() => client.stocks.bulkDeals(ticker2, { page, pageSize }))
|
|
@@ -308,8 +201,8 @@ function buildServer(options = {}) {
|
|
|
308
201
|
description: "Block deals reported for a specific stock.",
|
|
309
202
|
inputSchema: {
|
|
310
203
|
ticker,
|
|
311
|
-
page:
|
|
312
|
-
pageSize:
|
|
204
|
+
page: z.number().int().positive().optional(),
|
|
205
|
+
pageSize: z.number().int().positive().max(100).optional()
|
|
313
206
|
}
|
|
314
207
|
},
|
|
315
208
|
async ({ ticker: ticker2, page, pageSize }) => run(() => client.stocks.blockDeals(ticker2, { page, pageSize }))
|
|
@@ -321,8 +214,8 @@ function buildServer(options = {}) {
|
|
|
321
214
|
description: "SEBI PIT insider-trading disclosures for a specific stock.",
|
|
322
215
|
inputSchema: {
|
|
323
216
|
ticker,
|
|
324
|
-
page:
|
|
325
|
-
pageSize:
|
|
217
|
+
page: z.number().int().positive().optional(),
|
|
218
|
+
pageSize: z.number().int().positive().max(100).optional()
|
|
326
219
|
}
|
|
327
220
|
},
|
|
328
221
|
async ({ ticker: ticker2, page, pageSize }) => run(() => client.stocks.insiderTrades(ticker2, { page, pageSize }))
|
|
@@ -333,9 +226,9 @@ function buildServer(options = {}) {
|
|
|
333
226
|
title: "Compare peers in a sector",
|
|
334
227
|
description: "Compare valuation/profitability ratios across the peers in a sector, for a quick relative-value view.",
|
|
335
228
|
inputSchema: {
|
|
336
|
-
sector:
|
|
337
|
-
sort:
|
|
338
|
-
limit:
|
|
229
|
+
sector: z.string().describe("Sector name, e.g. 'Information Technology'."),
|
|
230
|
+
sort: z.string().optional().describe("Sort key, e.g. market_cap, pe_ratio."),
|
|
231
|
+
limit: z.number().int().positive().max(100).optional()
|
|
339
232
|
}
|
|
340
233
|
},
|
|
341
234
|
async ({ sector, sort, limit }) => run(() => client.stocks.compare(sector, { sort, limit }))
|
|
@@ -346,8 +239,8 @@ function buildServer(options = {}) {
|
|
|
346
239
|
title: "Search stocks",
|
|
347
240
|
description: "Fuzzy-search listed stocks by ticker symbol or company name. Use this to resolve a company name to a ticker before calling other tools.",
|
|
348
241
|
inputSchema: {
|
|
349
|
-
q:
|
|
350
|
-
limit:
|
|
242
|
+
q: z.string().describe("Search text (symbol fragment or company name)."),
|
|
243
|
+
limit: z.number().int().positive().max(50).optional()
|
|
351
244
|
}
|
|
352
245
|
},
|
|
353
246
|
async ({ q, limit }) => run(() => client.search(q, { limit }))
|
|
@@ -358,8 +251,8 @@ function buildServer(options = {}) {
|
|
|
358
251
|
title: "Get top market movers",
|
|
359
252
|
description: "Ranked market movers: top gainers, top losers, or most-active stocks for the latest trading session. Use category to pick which.",
|
|
360
253
|
inputSchema: {
|
|
361
|
-
category:
|
|
362
|
-
limit:
|
|
254
|
+
category: z.enum(["gainers", "losers", "active"]).optional().describe("Which ranking. Defaults to gainers."),
|
|
255
|
+
limit: z.number().int().positive().max(100).optional()
|
|
363
256
|
}
|
|
364
257
|
},
|
|
365
258
|
async ({ category, limit }) => run(() => client.movers({ category, limit }))
|
|
@@ -370,9 +263,9 @@ function buildServer(options = {}) {
|
|
|
370
263
|
title: "Get price shockers",
|
|
371
264
|
description: "Stocks that moved at least a threshold percentage vs the previous close, in either direction.",
|
|
372
265
|
inputSchema: {
|
|
373
|
-
minChangePct:
|
|
374
|
-
direction:
|
|
375
|
-
limit:
|
|
266
|
+
minChangePct: z.number().positive().optional().describe("Minimum abs % move. Default 5."),
|
|
267
|
+
direction: z.enum(["up", "down", "both"]).optional(),
|
|
268
|
+
limit: z.number().int().positive().max(100).optional()
|
|
376
269
|
}
|
|
377
270
|
},
|
|
378
271
|
async ({ minChangePct, direction, limit }) => run(() => client.priceShockers({ minChangePct, direction, limit }))
|
|
@@ -383,13 +276,13 @@ function buildServer(options = {}) {
|
|
|
383
276
|
title: "Run stock screener",
|
|
384
277
|
description: "Screen stocks by fundamental filters. `filters` is a list of 'metric.operator.value' strings, e.g. ['pe_ratio.lt.15', 'roe.gt.18']. marketCap filter values are in Crores. Optionally scope by sector.",
|
|
385
278
|
inputSchema: {
|
|
386
|
-
filters:
|
|
387
|
-
sector:
|
|
279
|
+
filters: z.array(z.string()).optional().describe("Filter expressions like 'pe_ratio.lt.15'."),
|
|
280
|
+
sector: z.string().optional(),
|
|
388
281
|
exchange,
|
|
389
|
-
sortBy:
|
|
390
|
-
sortOrder:
|
|
391
|
-
page:
|
|
392
|
-
pageSize:
|
|
282
|
+
sortBy: z.string().optional().describe("Sort key, default market_cap."),
|
|
283
|
+
sortOrder: z.enum(["asc", "desc"]).optional(),
|
|
284
|
+
page: z.number().int().positive().optional(),
|
|
285
|
+
pageSize: z.number().int().positive().max(100).optional()
|
|
393
286
|
}
|
|
394
287
|
},
|
|
395
288
|
async ({ filters, sector, exchange: exchange2, sortBy, sortOrder, page, pageSize }) => run(
|
|
@@ -402,8 +295,8 @@ function buildServer(options = {}) {
|
|
|
402
295
|
title: "List indices",
|
|
403
296
|
description: "List market indices (NIFTY 50, SENSEX, sectoral indices, etc.) with their latest values.",
|
|
404
297
|
inputSchema: {
|
|
405
|
-
page:
|
|
406
|
-
pageSize:
|
|
298
|
+
page: z.number().int().positive().optional(),
|
|
299
|
+
pageSize: z.number().int().positive().max(200).optional()
|
|
407
300
|
}
|
|
408
301
|
},
|
|
409
302
|
async ({ page, pageSize }) => run(() => client.indices.list({ page, pageSize }))
|
|
@@ -414,11 +307,11 @@ function buildServer(options = {}) {
|
|
|
414
307
|
title: "Get historical index prices",
|
|
415
308
|
description: "Historical daily values for an index over a date range. Free-tier keys get a recent window; paid keys get the full archive.",
|
|
416
309
|
inputSchema: {
|
|
417
|
-
name:
|
|
310
|
+
name: z.string().describe("Index name, e.g. 'NIFTY 50'."),
|
|
418
311
|
fromDate,
|
|
419
312
|
toDate,
|
|
420
|
-
page:
|
|
421
|
-
pageSize:
|
|
313
|
+
page: z.number().int().positive().optional(),
|
|
314
|
+
pageSize: z.number().int().positive().max(500).optional()
|
|
422
315
|
}
|
|
423
316
|
},
|
|
424
317
|
async ({ name, fromDate: fromDate2, toDate: toDate2, page, pageSize }) => run(() => client.indices.prices(name, { fromDate: fromDate2, toDate: toDate2, page, pageSize }))
|
|
@@ -431,8 +324,8 @@ function buildServer(options = {}) {
|
|
|
431
324
|
inputSchema: {
|
|
432
325
|
fromDate,
|
|
433
326
|
toDate,
|
|
434
|
-
limit:
|
|
435
|
-
latest:
|
|
327
|
+
limit: z.number().int().positive().max(500).optional(),
|
|
328
|
+
latest: z.boolean().optional().describe("Return only the most recent day.")
|
|
436
329
|
}
|
|
437
330
|
},
|
|
438
331
|
async ({ fromDate: fromDate2, toDate: toDate2, limit, latest }) => run(() => client.market.fiiDii({ fromDate: fromDate2, toDate: toDate2, limit, latest }))
|
|
@@ -443,9 +336,9 @@ function buildServer(options = {}) {
|
|
|
443
336
|
title: "Get market-wide bulk deals",
|
|
444
337
|
description: "Bulk deals across the whole market for the latest sessions.",
|
|
445
338
|
inputSchema: {
|
|
446
|
-
buySell:
|
|
447
|
-
page:
|
|
448
|
-
pageSize:
|
|
339
|
+
buySell: z.enum(["BUY", "SELL"]).optional(),
|
|
340
|
+
page: z.number().int().positive().optional(),
|
|
341
|
+
pageSize: z.number().int().positive().max(100).optional()
|
|
449
342
|
}
|
|
450
343
|
},
|
|
451
344
|
async ({ buySell, page, pageSize }) => run(() => client.deals.bulk({ buySell, page, pageSize }))
|
|
@@ -456,9 +349,9 @@ function buildServer(options = {}) {
|
|
|
456
349
|
title: "Get market-wide block deals",
|
|
457
350
|
description: "Block deals across the whole market for the latest sessions.",
|
|
458
351
|
inputSchema: {
|
|
459
|
-
buySell:
|
|
460
|
-
page:
|
|
461
|
-
pageSize:
|
|
352
|
+
buySell: z.enum(["BUY", "SELL"]).optional(),
|
|
353
|
+
page: z.number().int().positive().optional(),
|
|
354
|
+
pageSize: z.number().int().positive().max(100).optional()
|
|
462
355
|
}
|
|
463
356
|
},
|
|
464
357
|
async ({ buySell, page, pageSize }) => run(() => client.deals.block({ buySell, page, pageSize }))
|
|
@@ -469,8 +362,8 @@ function buildServer(options = {}) {
|
|
|
469
362
|
title: "Get market-wide insider trades",
|
|
470
363
|
description: "SEBI PIT insider-trading disclosures across the whole market.",
|
|
471
364
|
inputSchema: {
|
|
472
|
-
page:
|
|
473
|
-
pageSize:
|
|
365
|
+
page: z.number().int().positive().optional(),
|
|
366
|
+
pageSize: z.number().int().positive().max(100).optional()
|
|
474
367
|
}
|
|
475
368
|
},
|
|
476
369
|
async ({ page, pageSize }) => run(() => client.deals.insiderTrades({ page, pageSize }))
|
|
@@ -481,14 +374,14 @@ function buildServer(options = {}) {
|
|
|
481
374
|
title: "Search mutual-fund schemes",
|
|
482
375
|
description: "List/search mutual-fund schemes across all AMCs (industry-wide AMFI dataset). Filter by name (q), AMC, category, plan (Direct/Regular), option (Growth/IDCW), or exact ISIN. Use this to find a scheme_code.",
|
|
483
376
|
inputSchema: {
|
|
484
|
-
q:
|
|
485
|
-
amc:
|
|
486
|
-
category:
|
|
487
|
-
plan:
|
|
488
|
-
option:
|
|
489
|
-
isin:
|
|
490
|
-
limit:
|
|
491
|
-
offset:
|
|
377
|
+
q: z.string().optional(),
|
|
378
|
+
amc: z.string().optional(),
|
|
379
|
+
category: z.string().optional(),
|
|
380
|
+
plan: z.string().optional(),
|
|
381
|
+
option: z.string().optional(),
|
|
382
|
+
isin: z.string().optional(),
|
|
383
|
+
limit: z.number().int().positive().max(200).optional(),
|
|
384
|
+
offset: z.number().int().nonnegative().optional()
|
|
492
385
|
}
|
|
493
386
|
},
|
|
494
387
|
async (args) => run(() => client.mutualFunds.listSchemes(args))
|
|
@@ -499,7 +392,7 @@ function buildServer(options = {}) {
|
|
|
499
392
|
title: "Get mutual-fund scheme detail",
|
|
500
393
|
description: "One scheme's identity plus its latest NAV (with as-of date).",
|
|
501
394
|
inputSchema: {
|
|
502
|
-
schemeCode:
|
|
395
|
+
schemeCode: z.string().describe("AMFI scheme code.")
|
|
503
396
|
}
|
|
504
397
|
},
|
|
505
398
|
async ({ schemeCode }) => run(() => client.mutualFunds.getScheme(schemeCode))
|
|
@@ -510,10 +403,10 @@ function buildServer(options = {}) {
|
|
|
510
403
|
title: "Get mutual-fund NAV history",
|
|
511
404
|
description: "Daily NAV history for a scheme, most recent first. Free-tier keys are limited to a recent window; paid keys get the full archive.",
|
|
512
405
|
inputSchema: {
|
|
513
|
-
schemeCode:
|
|
406
|
+
schemeCode: z.string().describe("AMFI scheme code."),
|
|
514
407
|
fromDate,
|
|
515
408
|
toDate,
|
|
516
|
-
limit:
|
|
409
|
+
limit: z.number().int().positive().max(2e3).optional()
|
|
517
410
|
}
|
|
518
411
|
},
|
|
519
412
|
async ({ schemeCode, fromDate: fromDate2, toDate: toDate2, limit }) => run(() => client.mutualFunds.nav(schemeCode, { fromDate: fromDate2, toDate: toDate2, limit }))
|
|
@@ -524,7 +417,7 @@ function buildServer(options = {}) {
|
|
|
524
417
|
title: "Get mutual-fund trailing returns",
|
|
525
418
|
description: "Trailing returns (1m/3m/6m/1y/3y/5y + since-inception) computed from the scheme's NAV series (CAGR for >=1y horizons).",
|
|
526
419
|
inputSchema: {
|
|
527
|
-
schemeCode:
|
|
420
|
+
schemeCode: z.string().describe("AMFI scheme code.")
|
|
528
421
|
}
|
|
529
422
|
},
|
|
530
423
|
async ({ schemeCode }) => run(() => client.mutualFunds.returns(schemeCode))
|
|
@@ -536,9 +429,9 @@ function buildServer(options = {}) {
|
|
|
536
429
|
description: "Which mutual-fund schemes hold a given stock (portfolio positions aggregated per stock), optionally for a specific portfolio month. Answers 'which funds own X'.",
|
|
537
430
|
inputSchema: {
|
|
538
431
|
ticker,
|
|
539
|
-
month:
|
|
540
|
-
page:
|
|
541
|
-
pageSize:
|
|
432
|
+
month: z.string().optional().describe("Portfolio month YYYY-MM-DD (month-end). Latest if omitted."),
|
|
433
|
+
page: z.number().int().positive().optional(),
|
|
434
|
+
pageSize: z.number().int().positive().max(200).optional()
|
|
542
435
|
}
|
|
543
436
|
},
|
|
544
437
|
async ({ ticker: ticker2, month, page, pageSize }) => run(() => client.stocks.mfHoldings(ticker2, { month, page, pageSize }))
|
|
@@ -549,10 +442,10 @@ function buildServer(options = {}) {
|
|
|
549
442
|
title: "List / search mutual-fund portfolios",
|
|
550
443
|
description: "List/search the mutual-fund schemes we hold monthly PORTFOLIO data for (scheme discovery, to populate a picker). This is the INVERSE of get_mutual_fund_holdings: that answers 'which funds own stock X', this answers 'which schemes do we have portfolios for'. Each row is keyed by the holdings scheme-NAME string (NOT the AMFI scheme_code used by the scheme/NAV tools) and has no Direct/Regular distinction, since AMFI publishes monthly portfolios at the scheme level. Use the returned scheme_name with get_mutual_fund_portfolio_holdings.",
|
|
551
444
|
inputSchema: {
|
|
552
|
-
q:
|
|
553
|
-
fundHouse:
|
|
554
|
-
limit:
|
|
555
|
-
offset:
|
|
445
|
+
q: z.string().optional().describe("Substring match on scheme name."),
|
|
446
|
+
fundHouse: z.string().optional().describe("Filter by fund house (substring)."),
|
|
447
|
+
limit: z.number().int().positive().max(200).optional(),
|
|
448
|
+
offset: z.number().int().nonnegative().optional()
|
|
556
449
|
}
|
|
557
450
|
},
|
|
558
451
|
async ({ q, fundHouse, limit, offset }) => run(() => client.mutualFunds.listPortfolios({ q, fundHouse, limit, offset }))
|
|
@@ -563,10 +456,10 @@ function buildServer(options = {}) {
|
|
|
563
456
|
title: "Get a mutual-fund scheme's portfolio holdings",
|
|
564
457
|
description: "One scheme's equity PORTFOLIO for a month (the stocks it holds) with month-over-month quantity change. The INVERSE of get_mutual_fund_holdings (which funds own a stock). schemeName must match the stored holdings name exactly, as returned by list_mutual_fund_portfolios \u2014 it is the scheme-NAME string, NOT the AMFI scheme_code. month is 'YYYY-MM' (latest if omitted). An unknown scheme name returns an empty portfolio, not an error.",
|
|
565
458
|
inputSchema: {
|
|
566
|
-
schemeName:
|
|
567
|
-
month:
|
|
568
|
-
page:
|
|
569
|
-
pageSize:
|
|
459
|
+
schemeName: z.string().describe("Holdings scheme name, exactly as returned by list_mutual_fund_portfolios."),
|
|
460
|
+
month: z.string().optional().describe("Portfolio month YYYY-MM. Latest if omitted."),
|
|
461
|
+
page: z.number().int().positive().optional(),
|
|
462
|
+
pageSize: z.number().int().positive().max(100).optional()
|
|
570
463
|
}
|
|
571
464
|
},
|
|
572
465
|
async ({ schemeName, month, page, pageSize }) => run(() => client.mutualFunds.getPortfolioHoldings(schemeName, { month, page, pageSize }))
|
|
@@ -580,10 +473,6 @@ function buildServer(options = {}) {
|
|
|
580
473
|
},
|
|
581
474
|
async () => run(() => client.status())
|
|
582
475
|
);
|
|
583
|
-
if (adminSecret) {
|
|
584
|
-
const baseUrl = sdkOptions.baseUrl ?? "https://bharatstockapi.com";
|
|
585
|
-
registerOperatorTools(server, { baseUrl, adminSecret, fetchImpl: operatorFetchImpl });
|
|
586
|
-
}
|
|
587
476
|
return server;
|
|
588
477
|
}
|
|
589
478
|
|
package/dist/http.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,34 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { BharatStockOptions } from 'bharatstock';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* OPERATOR mode for the BharatStock MCP server.
|
|
6
|
-
*
|
|
7
|
-
* These tools are for the operator ONLY — they manage support tickets and are
|
|
8
|
-
* NOT part of the consumer surface. They are registered only when a
|
|
9
|
-
* `BHARATSTOCK_ADMIN_SECRET` is configured (see index.ts), and they do NOT go
|
|
10
|
-
* through the consumer `bharatstock` SDK (which is X-API-Key scoped and knows
|
|
11
|
-
* nothing about tickets). Instead each tool makes a JSON-RPC `tools/call` to
|
|
12
|
-
* the hosted MCP endpoint `POST {baseUrl}/v1/mcp` with an `X-Admin-Secret`
|
|
13
|
-
* header — the server exposes the operator tool catalogue only to a request
|
|
14
|
-
* that carries a valid admin secret (see app/api/v1/mcp.py).
|
|
15
|
-
*
|
|
16
|
-
* So the wall is end-to-end: a normal (API-key) MCP session never sees these
|
|
17
|
-
* tools (they aren't registered), and even if a tool name were guessed, the
|
|
18
|
-
* hosted server rejects an operator tool without the admin secret.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
type FetchLike = (input: string, init?: {
|
|
22
|
-
method?: string;
|
|
23
|
-
headers?: Record<string, string>;
|
|
24
|
-
body?: string;
|
|
25
|
-
}) => Promise<{
|
|
26
|
-
ok: boolean;
|
|
27
|
-
status: number;
|
|
28
|
-
text: () => Promise<string>;
|
|
29
|
-
json: () => Promise<unknown>;
|
|
30
|
-
}>;
|
|
31
|
-
|
|
32
4
|
/**
|
|
33
5
|
* Builds the BharatStock MCP server: a thin layer that exposes the
|
|
34
6
|
* BharatStock REST API's consumer-facing (X-API-Key gated) endpoints as
|
|
@@ -42,25 +14,14 @@ type FetchLike = (input: string, init?: {
|
|
|
42
14
|
*/
|
|
43
15
|
|
|
44
16
|
declare const SERVER_NAME = "bharatstock";
|
|
45
|
-
declare const SERVER_VERSION = "0.2.
|
|
46
|
-
/** Extra, non-consumer options for buildServer. When `adminSecret` is set the
|
|
47
|
-
* OPERATOR ticket tools are additionally registered (see operator.ts); they
|
|
48
|
-
* are absent otherwise, so a normal consumer session never sees them. */
|
|
49
|
-
interface BuildServerExtras {
|
|
50
|
-
/** Operator admin secret. When present, register the support-ticket tools. */
|
|
51
|
-
adminSecret?: string;
|
|
52
|
-
/** Injectable fetch for the operator tools' JSON-RPC calls (tests). Named
|
|
53
|
-
* distinctly from the SDK's own `fetchImpl` so the two don't collide when
|
|
54
|
-
* the options objects are intersected. */
|
|
55
|
-
operatorFetchImpl?: FetchLike;
|
|
56
|
-
}
|
|
17
|
+
declare const SERVER_VERSION = "0.2.1";
|
|
57
18
|
/**
|
|
58
19
|
* Construct the MCP server and register all tools against a BharatStock
|
|
59
20
|
* client. Exposed separately from the stdio wiring so tests can build a
|
|
60
21
|
* server with an injected `fetchImpl` and drive it over an in-memory
|
|
61
22
|
* transport (no network, no real key).
|
|
62
23
|
*/
|
|
63
|
-
declare function buildServer(options?: BharatStockOptions
|
|
24
|
+
declare function buildServer(options?: BharatStockOptions): McpServer;
|
|
64
25
|
|
|
65
26
|
/**
|
|
66
27
|
* Shared plan-gate check for both the stdio and HTTP transports.
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
SERVER_VERSION,
|
|
5
5
|
buildServer,
|
|
6
6
|
checkAuthorization
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-AQVPTO4U.js";
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
10
10
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -25,11 +25,7 @@ async function main() {
|
|
|
25
25
|
`);
|
|
26
26
|
process.exit(1);
|
|
27
27
|
}
|
|
28
|
-
const
|
|
29
|
-
if (adminSecret) {
|
|
30
|
-
process.stderr.write("bharatstock-mcp: operator mode ON (support-ticket tools registered).\n");
|
|
31
|
-
}
|
|
32
|
-
const server = buildServer({ apiKey, baseUrl, adminSecret });
|
|
28
|
+
const server = buildServer({ apiKey, baseUrl });
|
|
33
29
|
const transport = new StdioServerTransport();
|
|
34
30
|
await server.connect(transport);
|
|
35
31
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bharatstock-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"mcpName": "com.bharatstockapi/bharatstock-mcp",
|
|
5
5
|
"description": "Official Model Context Protocol (MCP) server for the BharatStock API - Indian stock market data (NSE/BSE) as AI-agent tools: real-time quotes, EOD prices, financial statements, ratios, shareholding patterns (FII/DII/promoter), corporate actions, NIFTY/SENSEX indices, mutual fund holdings and NAVs, FII/DII activity, bulk/block deals, insider trades, and a fundamentals stock screener for Indian equities.",
|
|
6
6
|
"license": "MIT",
|