finhay-mcp-server 1.0.8 → 1.1.0

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/README.en.md CHANGED
@@ -88,32 +88,35 @@ Open Claude and ask:
88
88
 
89
89
  ## Tools
90
90
 
91
- ### Market data (16 tools)
91
+ ### Market data (20 tools)
92
92
 
93
93
  | Tool | Description |
94
94
  |------|-------------|
95
95
  | `get_stock_realtime` | Realtime stock prices (single, multiple, or by exchange) |
96
- | `get_price_history_chart` | OHLCV price history |
96
+ | `get_news` | Stock corporate events (dividends, rights issues, AGM...) |
97
+ | `get_price_history_chart` | OHLCV price history (1D/5/15/30/1H/4H) |
97
98
  | `get_recommendation_reports` | Analyst recommendation reports |
98
- | `get_funds` | List of investment funds |
99
- | `get_fund_portfolio` | Fund portfolio composition |
100
- | `get_fund_months` | Available months for fund data |
101
99
  | `get_gold_prices` | Gold prices (SJC, DOJI, PNJ, BTMC) |
102
100
  | `get_gold_chart` | Gold price chart |
103
101
  | `get_gold_providers` | Gold prices by provider |
104
102
  | `get_silver_prices` | Silver prices |
105
103
  | `get_silver_chart` | Silver price chart |
104
+ | `get_metal_providers` | Gold + silver prices by provider |
106
105
  | `get_all_financial_data` | All-in-one: gold, silver, crypto, rates, FX |
107
106
  | `get_bank_interest_rates` | Bank savings interest rates |
108
107
  | `get_crypto_top_trending` | Top trending cryptocurrencies |
108
+ | `get_market_data` | Global indices (SP500, Nikkei...), big-tech stocks, commodities, forex |
109
+ | `get_economic_calendar_events` | Upcoming economic events (CPI, Fed meetings, PMI...) |
110
+ | `get_company_financial_overview` | Company financial overview (PE, PB, ROE, EPS...) |
111
+ | `get_company_financial_analysis` | Financial analysis by year/quarter |
112
+ | `get_financial_statement` | Financial statements (income, balance sheet, cash flow) |
109
113
  | `get_macro_data` | Macro indicators (CPI, PMI, IIP, FED rate...) |
110
114
  | `get_market_session` | Trading session status |
111
115
 
112
- ### Account (10 tools)
116
+ ### Account (8 tools)
113
117
 
114
118
  | Tool | Description |
115
119
  |------|-------------|
116
- | `get_owner_info` | Account owner info |
117
120
  | `get_account_summary` | Balance: cash, securities, margin |
118
121
  | `get_asset_summary` | Total assets |
119
122
  | `get_portfolio` | Stock portfolio with P/L |
@@ -122,7 +125,6 @@ Open Claude and ask:
122
125
  | `get_order_book` | Today's order book |
123
126
  | `get_order_detail` | Single order detail |
124
127
  | `get_user_rights` | Shareholder rights: dividends, rights issues... |
125
- | `get_trade_info` | Buying power / sellable quantity |
126
128
 
127
129
 
128
130
  ## Requirements
package/README.md CHANGED
@@ -88,32 +88,35 @@ Mở Claude, hỏi:
88
88
 
89
89
  ## Tools
90
90
 
91
- ### Thị trường (16 tools)
91
+ ### Thị trường (20 tools)
92
92
 
93
93
  | Tool | Mô tả |
94
94
  |------|-------|
95
95
  | `get_stock_realtime` | Giá cổ phiếu realtime (1 mã, nhiều mã, hoặc theo sàn) |
96
- | `get_price_history_chart` | Lịch sử giá OHLCV |
96
+ | `get_news` | Sự kiện doanh nghiệp (cổ tức, quyền mua, ĐHĐCĐ...) |
97
+ | `get_price_history_chart` | Lịch sử giá OHLCV (1D/5/15/30/1H/4H) |
97
98
  | `get_recommendation_reports` | Báo cáo phân tích từ chuyên gia |
98
- | `get_funds` | Danh sách quỹ đầu tư |
99
- | `get_fund_portfolio` | Danh mục của quỹ |
100
- | `get_fund_months` | Các tháng có dữ liệu quỹ |
101
99
  | `get_gold_prices` | Giá vàng (SJC, DOJI, PNJ, BTMC) |
102
100
  | `get_gold_chart` | Biểu đồ giá vàng |
103
101
  | `get_gold_providers` | Giá vàng theo nhà cung cấp |
104
102
  | `get_silver_prices` | Giá bạc |
105
103
  | `get_silver_chart` | Biểu đồ giá bạc |
104
+ | `get_metal_providers` | Giá vàng + bạc theo nhà cung cấp |
106
105
  | `get_all_financial_data` | Tổng hợp: vàng, bạc, crypto, lãi suất, tỷ giá |
107
106
  | `get_bank_interest_rates` | Lãi suất tiết kiệm ngân hàng |
108
107
  | `get_crypto_top_trending` | Crypto xu hướng |
108
+ | `get_market_data` | Dữ liệu chỉ số global (SP500, Nikkei...), big-tech, hàng hoá, forex |
109
+ | `get_economic_calendar_events` | Sự kiện kinh tế sắp tới (CPI, họp Fed, PMI...) |
110
+ | `get_company_financial_overview` | Tổng quan tài chính doanh nghiệp (PE, PB, ROE, EPS...) |
111
+ | `get_company_financial_analysis` | Phân tích tài chính theo năm/quý |
112
+ | `get_financial_statement` | Báo cáo tài chính (BCTC, CDKT, LCTT) |
109
113
  | `get_macro_data` | Chỉ số vĩ mô (CPI, PMI, IIP, FED rate...) |
110
114
  | `get_market_session` | Trạng thái phiên giao dịch |
111
115
 
112
- ### Tài khoản (10 tools)
116
+ ### Tài khoản (8 tools)
113
117
 
114
118
  | Tool | Mô tả |
115
119
  |------|-------|
116
- | `get_owner_info` | Thông tin chủ tài khoản |
117
120
  | `get_account_summary` | Số dư: tiền mặt, chứng khoán, ký quỹ |
118
121
  | `get_asset_summary` | Tổng tài sản |
119
122
  | `get_portfolio` | Danh mục cổ phiếu với lãi/lỗ |
@@ -122,7 +125,6 @@ Mở Claude, hỏi:
122
125
  | `get_order_book` | Sổ lệnh trong ngày |
123
126
  | `get_order_detail` | Chi tiết 1 lệnh |
124
127
  | `get_user_rights` | Quyền cổ đông: cổ tức, quyền mua... |
125
- | `get_trade_info` | Sức mua / số lượng bán được |
126
128
 
127
129
 
128
130
  ## Yêu cầu
@@ -1,11 +1,15 @@
1
1
  import { FinhayClient } from './FinhayClient.js';
2
+ export interface SubAccount {
3
+ id: string;
4
+ type: string;
5
+ subAccountExt: string;
6
+ }
2
7
  export interface AccountInfo {
3
8
  userId: string;
4
- defaultSubAccountId: string;
5
- subAccountIds: string[];
9
+ subAccounts: SubAccount[];
6
10
  }
7
11
  /**
8
- * Fetches and caches account info (userId, subAccountIds) on startup.
12
+ * Fetches and caches account info (userId, subAccounts) on startup.
9
13
  * Tools use this as default when user does not provide subAccountId.
10
14
  */
11
15
  export declare class AccountContext {
@@ -14,6 +18,7 @@ export declare class AccountContext {
14
18
  constructor(client: FinhayClient);
15
19
  init(): Promise<void>;
16
20
  getUserId(): string | undefined;
21
+ getSubAccountByType(type: string): SubAccount | undefined;
17
22
  getDefaultSubAccountId(): string | undefined;
18
23
  getSubAccountIds(): string[];
19
24
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Fetches and caches account info (userId, subAccountIds) on startup.
2
+ * Fetches and caches account info (userId, subAccounts) on startup.
3
3
  * Tools use this as default when user does not provide subAccountId.
4
4
  */
5
5
  export class AccountContext {
@@ -10,24 +10,27 @@ export class AccountContext {
10
10
  }
11
11
  async init() {
12
12
  try {
13
- // Step 1: get userId
13
+ // Step 1: get userId from .data.user_id
14
14
  const meData = await this.client.get('/users/v1/users/me');
15
- const meResult = meData.result ?? meData.data;
16
- const userId = meResult?.user_id ?? meResult?.userId ?? meResult?.id ?? '';
15
+ const userId = meData.data?.user_id ?? '';
17
16
  if (!userId) {
18
- throw new Error('Could not extract userId from /users/v1/users/me');
17
+ throw new Error('Could not extract user_id from /users/v1/users/me');
19
18
  }
20
- // Step 2: get sub-accounts
19
+ // Step 2: get sub-accounts with type and sub_account_ext
21
20
  const subData = await this.client.get(`/users/v1/users/${userId}/sub-accounts`);
22
- const subResult = subData.result ?? subData.data ?? [];
23
- const subAccounts = Array.isArray(subResult) ? subResult : subResult?.subAccounts ?? subResult?.sub_accounts ?? [];
24
- const subAccountIds = subAccounts.map((sa) => sa.subAccountId ?? sa.sub_account_id ?? sa.id ?? '').filter(Boolean);
25
- this.info = {
26
- userId: String(userId),
27
- defaultSubAccountId: subAccountIds[0] ?? '',
28
- subAccountIds,
29
- };
30
- console.error(`[finhay-mcp] Account loaded: userId=${this.info.userId}, subAccounts=[${subAccountIds.join(', ')}]`);
21
+ const subResult = Array.isArray(subData.result) ? subData.result
22
+ : Array.isArray(subData.data) ? subData.data
23
+ : [];
24
+ const subAccounts = subResult
25
+ .map((sa) => ({
26
+ id: sa.id ?? '',
27
+ type: (sa.type ?? '').toUpperCase(),
28
+ subAccountExt: sa.sub_account_ext ?? '',
29
+ }))
30
+ .filter((sa) => sa.id);
31
+ this.info = { userId: String(userId), subAccounts };
32
+ const summary = subAccounts.map((sa) => `${sa.type}=${sa.id}`).join(', ');
33
+ console.error(`[finhay-mcp] Account loaded: userId=${this.info.userId}, subAccounts=[${summary}]`);
31
34
  }
32
35
  catch (err) {
33
36
  console.error(`[finhay-mcp] Warning: could not fetch account info: ${err.message}`);
@@ -37,18 +40,23 @@ export class AccountContext {
37
40
  getUserId() {
38
41
  return this.info?.userId || undefined;
39
42
  }
43
+ getSubAccountByType(type) {
44
+ return this.info?.subAccounts.find((sa) => sa.type === type.toUpperCase());
45
+ }
40
46
  getDefaultSubAccountId() {
41
- return this.info?.defaultSubAccountId || undefined;
47
+ // Prefer NORMAL, fallback to first available
48
+ const normal = this.getSubAccountByType('NORMAL');
49
+ return normal?.id || this.info?.subAccounts[0]?.id || undefined;
42
50
  }
43
51
  getSubAccountIds() {
44
- return this.info?.subAccountIds ?? [];
52
+ return this.info?.subAccounts.map((sa) => sa.id) ?? [];
45
53
  }
46
54
  /**
47
55
  * Returns the given subAccountId if provided, otherwise falls back to default.
48
56
  * Throws if neither is available.
49
57
  */
50
58
  resolveSubAccountId(subAccountId) {
51
- const resolved = subAccountId || this.info?.defaultSubAccountId;
59
+ const resolved = subAccountId || this.getDefaultSubAccountId();
52
60
  if (!resolved) {
53
61
  throw new Error('subAccountId is required. Could not auto-detect — provide it explicitly or check your API credentials.');
54
62
  }
@@ -11,6 +11,9 @@ export class FinhayClient {
11
11
  this.http = axios.create({
12
12
  baseURL: baseUrl,
13
13
  timeout: 15000,
14
+ headers: {
15
+ 'User-Agent': 'finhay-openapi (Skill)',
16
+ },
14
17
  });
15
18
  }
16
19
  /**
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ const account = new AccountContext(client);
17
17
  await account.init();
18
18
  const server = new McpServer({
19
19
  name: 'finhay-mcp-server',
20
- version: '1.0.0',
20
+ version: '1.1.0',
21
21
  });
22
22
  registerAllTools(server, client, account);
23
23
  const transport = new StdioServerTransport();
@@ -1,6 +1,6 @@
1
1
  import { registerMarketTools } from './market.js';
2
2
  import { registerPortfolioTools } from './portfolio.js';
3
3
  export function registerAllTools(server, client, account) {
4
- registerMarketTools(server, client); // 16 tools: stock, funds, gold, silver, crypto, macro, etc.
5
- registerPortfolioTools(server, client, account); // 10 tools: account, portfolio, orders, pnl, rights, etc.
4
+ registerMarketTools(server, client); // 20 tools: stock, funds, gold, silver, metals, crypto, macro, company financials, etc.
5
+ registerPortfolioTools(server, client, account); // 8 tools: account, portfolio, orders, pnl, rights, etc.
6
6
  }
@@ -17,10 +17,29 @@ export function registerMarketTools(server, client) {
17
17
  const data = await client.get('/market/stock-realtime', query);
18
18
  return JSON.stringify(data.result, null, 2);
19
19
  }));
20
+ // --- News (corporate events) ---
21
+ server.tool('get_news', 'Get stock corporate events (rights issues, dividends, AGM dates, etc.) filtered by symbol(s) and/or date range. Dates must be DD/MM/YYYY format.', {
22
+ stock: z.string().optional().describe('Single stock symbol (e.g., VNM)'),
23
+ stocks: z.string().optional().describe('Comma-separated symbols (e.g., VNM,VIC,HPG)'),
24
+ from_date: z.string().optional().describe('Start date in DD/MM/YYYY (defaults to 1 year ago)'),
25
+ to_date: z.string().optional().describe('End date in DD/MM/YYYY (only applied when both dates provided)'),
26
+ }, safeHandler(async ({ stock, stocks, from_date, to_date }) => {
27
+ const query = {};
28
+ if (stock)
29
+ query.stock = stock.toUpperCase();
30
+ if (stocks)
31
+ query.stocks = stocks.toUpperCase();
32
+ if (from_date)
33
+ query.from_date = from_date;
34
+ if (to_date)
35
+ query.to_date = to_date;
36
+ const data = await client.get('/market/news', query);
37
+ return JSON.stringify(data.result, null, 2);
38
+ }));
20
39
  // --- Price history chart ---
21
40
  server.tool('get_price_history_chart', 'Get OHLCV price history chart for a stock. Timestamps must be Unix seconds (not milliseconds).', {
22
41
  symbol: z.string().describe('Stock symbol (e.g., FPT)'),
23
- resolution: z.string().optional().describe('Resolution, only "1D" supported').default('1D'),
42
+ resolution: z.enum(['1D', '5', '15', '30', '1H', '4H']).optional().describe('Chart resolution: 1D (daily), 5/15/30 (minutes), 1H, 4H. Default 1D.').default('1D'),
24
43
  from: z.number().describe('Start timestamp in Unix SECONDS'),
25
44
  to: z.number().describe('End timestamp in Unix SECONDS'),
26
45
  }, safeHandler(async ({ symbol, resolution, from, to }) => {
@@ -37,25 +56,6 @@ export function registerMarketTools(server, client) {
37
56
  const data = await client.get(`/market/recommendation-reports/${symbol.toUpperCase()}`);
38
57
  return JSON.stringify(data.data, null, 2);
39
58
  }));
40
- // --- Funds ---
41
- server.tool('get_funds', 'Get list of all available investment funds with performance data', {}, safeHandler(async () => {
42
- const data = await client.get('/market/funds');
43
- return JSON.stringify(data.data, null, 2);
44
- }));
45
- server.tool('get_fund_portfolio', 'Get portfolio composition of a specific fund', {
46
- fund: z.string().describe('Fund code (e.g., DCDS)'),
47
- month: z.string().optional().describe('Month in YYYY-MM format (defaults to latest)'),
48
- }, safeHandler(async ({ fund, month }) => {
49
- const query = {};
50
- if (month)
51
- query.month = month;
52
- const data = await client.get(`/market/funds/${fund}/portfolio`, query);
53
- return JSON.stringify(data.data, null, 2);
54
- }));
55
- server.tool('get_fund_months', 'Get available months for a fund portfolio', { fund: z.string().describe('Fund code (e.g., DCDS)') }, safeHandler(async ({ fund }) => {
56
- const data = await client.get(`/market/funds/${fund}/months`);
57
- return JSON.stringify(data.data, null, 2);
58
- }));
59
59
  // --- Gold ---
60
60
  server.tool('get_gold_prices', 'Get current gold prices from Vietnamese providers (SJC, DOJI, PNJ, BTMC)', {}, safeHandler(async () => {
61
61
  const data = await client.get('/market/financial-data/gold');
@@ -78,6 +78,11 @@ export function registerMarketTools(server, client) {
78
78
  const data = await client.get('/market/financial-data/silver-chart', { days: String(days) });
79
79
  return JSON.stringify(data.data, null, 2);
80
80
  }));
81
+ // --- Metal providers ---
82
+ server.tool('get_metal_providers', 'Get gold and silver prices grouped by provider (superset of gold-providers and silver data)', {}, safeHandler(async () => {
83
+ const data = await client.get('/market/financial-data/metal-providers');
84
+ return JSON.stringify(data.data, null, 2);
85
+ }));
81
86
  // --- Financial data ---
82
87
  server.tool('get_all_financial_data', 'Get all financial data: gold, silver, crypto, bank rates, USD exchange rate', {}, safeHandler(async () => {
83
88
  const data = await client.get('/market/financial-data');
@@ -91,14 +96,82 @@ export function registerMarketTools(server, client) {
91
96
  const data = await client.get('/market/financial-data/cryptos/top-trending');
92
97
  return JSON.stringify(data.data, null, 2);
93
98
  }));
99
+ // --- Market data (global indices, big-tech, commodities, forex) ---
100
+ server.tool('get_market_data', 'Get historical data points for a global market index, big-tech stock, commodity, or forex pair. Results ordered descending by date.', {
101
+ type: z.enum([
102
+ 'SP500', 'DOW_JONES', 'NASDAQ', 'RUSSELL2000', 'VIX', 'DXY',
103
+ 'KOSPI', 'HANGSENG', 'SHANGHAI', 'NIKKEI',
104
+ 'APPLE', 'MICROSOFT', 'ALPHABET', 'AMAZON', 'META', 'NVIDIA', 'TESLA',
105
+ 'GOLD', 'SILVER', 'COPPER', 'CRUDE_OIL', 'BRENT_OIL', 'NATURAL_GAS',
106
+ 'EURUSD', 'USDJPY', 'GBPUSD',
107
+ ]).describe('Market data type (US/Asian indices, big-tech stocks, commodities, forex)'),
108
+ limit: z.number().min(1).max(500).optional().describe('Number of data points (default 50, max 500)'),
109
+ }, safeHandler(async ({ type, limit }) => {
110
+ const query = { type };
111
+ if (limit)
112
+ query.limit = String(limit);
113
+ const data = await client.get('/market/financial-data/market', query);
114
+ return JSON.stringify(data.data, null, 2);
115
+ }));
116
+ // --- Economic calendar events ---
117
+ server.tool('get_economic_calendar_events', 'Get upcoming global economic events (CPI releases, Fed meetings, PMI announcements, etc.)', {
118
+ weeks: z.number().optional().describe('Number of weeks ahead to fetch (default 1)'),
119
+ country: z.enum([
120
+ 'China', 'Euro Area', 'Japan', 'United States', 'United Kingdom', 'Vietnam',
121
+ ]).optional().describe('Filter by country (omit for all countries)'),
122
+ }, safeHandler(async ({ weeks, country }) => {
123
+ const query = {};
124
+ if (weeks)
125
+ query.weeks = String(weeks);
126
+ if (country)
127
+ query.country = country;
128
+ const data = await client.get('/market/financial-data/economic-calendar-events', query);
129
+ return JSON.stringify(data.data, null, 2);
130
+ }));
131
+ // --- Company financials ---
132
+ server.tool('get_company_financial_overview', 'Get financial overview for a company: PE, PB, EV/EBITDA, gross margin, ROE, EPS, dividend yield, NIM, ROA, and industry averages', {
133
+ symbol: z.string().describe('Stock symbol (e.g., VNM)'),
134
+ }, safeHandler(async ({ symbol }) => {
135
+ const data = await client.get('/market/company-financial/overview', {
136
+ symbol: symbol.toUpperCase(),
137
+ });
138
+ return JSON.stringify(data.data, null, 2);
139
+ }));
140
+ server.tool('get_company_financial_analysis', 'Get financial analysis entries by year or quarter for a company', {
141
+ symbol: z.string().describe('Stock symbol (e.g., VNM)'),
142
+ period: z.enum(['annual', 'quarterly']).optional().describe('Period type (default: annual)'),
143
+ }, safeHandler(async ({ symbol, period }) => {
144
+ const query = { symbol: symbol.toUpperCase() };
145
+ if (period)
146
+ query.period = period;
147
+ const data = await client.get('/market/company-financial/analysis', query);
148
+ return JSON.stringify(data.data, null, 2);
149
+ }));
150
+ server.tool('get_financial_statement', 'Get financial statements (income statement, balance sheet, or cash flow) for a company', {
151
+ symbol: z.string().describe('Stock symbol (e.g., VNM)'),
152
+ type: z.enum(['income-statement', 'balance-sheet', 'cash-flow']).describe('Statement type'),
153
+ period: z.enum(['annual', 'quarterly']).optional().describe('Period type'),
154
+ limit: z.number().min(1).max(5).optional().describe('Number of periods to return (1-5, default: 5)'),
155
+ }, safeHandler(async ({ symbol, type, period, limit }) => {
156
+ const query = {
157
+ symbol: symbol.toUpperCase(),
158
+ type,
159
+ };
160
+ if (period)
161
+ query.period = period;
162
+ if (limit)
163
+ query.limit = String(limit);
164
+ const data = await client.get('/market/v2/financial-statement/statement', query);
165
+ return JSON.stringify(data.data, null, 2);
166
+ }));
94
167
  // --- Macro ---
95
- server.tool('get_macro_data', 'Get macroeconomic indicators for Vietnam or US (CPI, PMI, IIP, FED rate, etc.)', {
168
+ server.tool('get_macro_data', 'Get macroeconomic indicators for Vietnam or US (CPI, PMI, IIP, FED rate, etc.). JP and DE are only valid when type=GOVERNMENT_10Y_BOND_YIELD.', {
96
169
  type: z.enum([
97
170
  'IIP', 'CPI', 'PMI', 'PCE', 'CORE_PCE', 'NFP', 'GOODS_RETAIL', 'SERVICE_RETAIL',
98
171
  'TOTAL_EXPORT', 'FDI_EXPORT', 'DOMESTIC_EXPORT', 'FED_FUNDS_RATE', 'INTERBANK_RATE',
99
172
  'GOVERNMENT_10Y_BOND_YIELD', 'UNEMPLOYMENT_RATE',
100
173
  ]).describe('Macro indicator type'),
101
- country: z.enum(['VN', 'US']).describe('Country code'),
174
+ country: z.enum(['VN', 'US', 'JP', 'DE']).describe('Country code (JP/DE only for GOVERNMENT_10Y_BOND_YIELD)'),
102
175
  period: z.enum(['ONE_MONTH', 'ONE_YEAR', 'YTD']).optional().describe('Time period filter'),
103
176
  }, safeHandler(async ({ type, country, period }) => {
104
177
  const query = { type, country };
@@ -1,11 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { safeHandler } from '../utils/safeTool.js';
3
3
  export function registerPortfolioTools(server, client, account) {
4
- // --- Owner ---
5
- server.tool('get_owner_info', 'Get owner identity info (name, accounts, sub-account IDs, etc.)', {}, safeHandler(async () => {
6
- const data = await client.get('/users/v1/users/me');
7
- return JSON.stringify(data.result, null, 2);
8
- }));
9
4
  // --- Account summary ---
10
5
  server.tool('get_account_summary', 'Get account balance summary: cash, securities value, margin, net asset value', {
11
6
  subAccountId: z.string().optional().describe('Sub-account ID (auto-detected if omitted)'),
@@ -16,10 +11,10 @@ export function registerPortfolioTools(server, client, account) {
16
11
  }));
17
12
  // --- Asset summary ---
18
13
  server.tool('get_asset_summary', 'Get asset summary with total valuation', {
19
- subAccountId: z.string().optional().describe('Sub-account ID (auto-detected if omitted)'),
20
- }, safeHandler(async ({ subAccountId }) => {
21
- const id = account.resolveSubAccountId(subAccountId);
22
- const data = await client.get(`/trading/sub-accounts/${id}/asset-summary`);
14
+ userId: z.string().optional().describe('User ID (auto-detected if omitted)'),
15
+ }, safeHandler(async ({ userId }) => {
16
+ const uid = account.resolveUserId(userId);
17
+ const data = await client.get(`/users/v3/users/${uid}/assets/summary`);
23
18
  return JSON.stringify(data.data, null, 2);
24
19
  }));
25
20
  // --- Portfolio ---
@@ -85,9 +80,10 @@ export function registerPortfolioTools(server, client, account) {
85
80
  fromDate: z.string().optional().describe('Start date (YYYY-MM-DD)'),
86
81
  toDate: z.string().optional().describe('End date (YYYY-MM-DD)'),
87
82
  catType: z.string().optional().describe('Category type, comma-separated or ALL (default: ALL)'),
83
+ isCom: z.string().optional().describe('Commission filter (default: ALL)'),
88
84
  symbol: z.string().optional().describe('Filter by symbol (default: ALL)'),
89
85
  status: z.string().optional().describe('Status filter, comma-separated or ALL (default: ALL)'),
90
- }, safeHandler(async ({ subAccountId, fromDate, toDate, catType, symbol, status }) => {
86
+ }, safeHandler(async ({ subAccountId, fromDate, toDate, catType, isCom, symbol, status }) => {
91
87
  const id = account.resolveSubAccountId(subAccountId);
92
88
  const query = {};
93
89
  if (fromDate)
@@ -96,6 +92,8 @@ export function registerPortfolioTools(server, client, account) {
96
92
  query.toDate = toDate;
97
93
  if (catType)
98
94
  query.catType = catType;
95
+ if (isCom)
96
+ query.isCom = isCom;
99
97
  if (symbol)
100
98
  query.symbol = symbol.toUpperCase();
101
99
  if (status)
@@ -103,19 +101,4 @@ export function registerPortfolioTools(server, client, account) {
103
101
  const data = await client.get(`/trading/v5/account/${id}/user-rights`, query);
104
102
  return JSON.stringify(data.result, null, 2);
105
103
  }));
106
- // --- Trade info (pre-order check) ---
107
- server.tool('get_trade_info', 'Get buying power (BUY) or available quantity (SELL) before placing an order', {
108
- subAccountId: z.string().optional().describe('Sub-account ID (auto-detected if omitted)'),
109
- symbol: z.string().describe('Stock symbol'),
110
- side: z.enum(['BUY', 'SELL']).describe('Order side'),
111
- quotePrice: z.number().describe('Quote price in VND'),
112
- }, safeHandler(async ({ subAccountId, symbol, side, quotePrice }) => {
113
- const id = account.resolveSubAccountId(subAccountId);
114
- const data = await client.get(`/trading/sub-accounts/${id}/trade-info`, {
115
- symbol: symbol.toUpperCase(),
116
- side,
117
- quote_price: String(quotePrice),
118
- });
119
- return JSON.stringify(data.result, null, 2);
120
- }));
121
104
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "finhay-mcp-server",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "mcpName": "io.github.finhay/mcp-server",
5
5
  "description": "Finhay MCP Server — xem gia co phieu, danh muc dau tu qua Claude AI",
6
6
  "type": "module",