@2oolkit/kiwoom-cli 0.1.0 → 0.1.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/README.md +19 -1
- package/dist/index.js +66 -34
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +25 -7
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +14 -8
- package/skill/references/market-data.md +20 -13
package/dist/mcp.js
CHANGED
|
@@ -84,6 +84,14 @@ var ORDER_TYPES = {
|
|
|
84
84
|
"81": "\uC7A5\uB9C8\uAC10\uD6C4\uC2DC\uAC04\uC678"
|
|
85
85
|
};
|
|
86
86
|
var MARKET_ORDER_TYPES = /* @__PURE__ */ new Set(["3", "13", "23"]);
|
|
87
|
+
var CHART_PER_PAGE_CAP = {
|
|
88
|
+
tick: 900,
|
|
89
|
+
minute: 900,
|
|
90
|
+
day: 600,
|
|
91
|
+
week: 300,
|
|
92
|
+
month: 240,
|
|
93
|
+
year: 30
|
|
94
|
+
};
|
|
87
95
|
|
|
88
96
|
// src/config/store.ts
|
|
89
97
|
var fs = __toESM(require("fs"));
|
|
@@ -374,7 +382,7 @@ var KiwoomClient = class {
|
|
|
374
382
|
*/
|
|
375
383
|
async callEndpoint(def, body = {}, opts = {}) {
|
|
376
384
|
if (opts.paginate && def.listKey) {
|
|
377
|
-
const data = await this.requestAll(def.apiId, def.path, body, def.listKey);
|
|
385
|
+
const data = await this.requestAll(def.apiId, def.path, body, def.listKey, opts.maxPages);
|
|
378
386
|
return { data, contYn: false, nextKey: "" };
|
|
379
387
|
}
|
|
380
388
|
return this.request(def.apiId, def.path, body, {
|
|
@@ -384,9 +392,11 @@ var KiwoomClient = class {
|
|
|
384
392
|
}
|
|
385
393
|
/**
|
|
386
394
|
* Fetch all pages of a TR, concatenating the array under `listKey`.
|
|
387
|
-
* Caps at `maxPages` to avoid runaway loops
|
|
395
|
+
* Caps at `maxPages` to avoid runaway loops (default 100 — high enough for
|
|
396
|
+
* large chart pulls; callers pass a tighter bound when they know how many
|
|
397
|
+
* pages a target row count needs).
|
|
388
398
|
*/
|
|
389
|
-
async requestAll(apiId, path2, body, listKey, maxPages =
|
|
399
|
+
async requestAll(apiId, path2, body, listKey, maxPages = 100) {
|
|
390
400
|
let page = await this.request(apiId, path2, body);
|
|
391
401
|
const acc = Array.isArray(page.data[listKey]) ? [...page.data[listKey]] : [];
|
|
392
402
|
let pages = 1;
|
|
@@ -687,25 +697,30 @@ var PERIOD_EP = {
|
|
|
687
697
|
month: ENDPOINTS.monthlyChart,
|
|
688
698
|
year: ENDPOINTS.yearlyChart
|
|
689
699
|
};
|
|
700
|
+
var DEFAULT_COUNT = 50;
|
|
701
|
+
var MAX_COUNT = 1e5;
|
|
690
702
|
function registerChartTools(server2) {
|
|
691
703
|
tool(
|
|
692
704
|
server2,
|
|
693
705
|
"get_chart",
|
|
694
706
|
{
|
|
695
|
-
description: "Get OHLC chart data for a stock. Period charts (day/week/month/year) end at base date; tick/minute use an aggregation scope. Returns latest-first; use count to cap rows.",
|
|
707
|
+
description: "Get OHLC chart data for a stock. Period charts (day/week/month/year) end at base date; tick/minute use an aggregation scope. Returns latest-first; use count to cap rows. Per-request caps: tick/minute 900, day 600, week 300, month 240, year 30. When count exceeds the cap the tool auto-paginates (cont-yn/next-key) and returns up to count rows.",
|
|
696
708
|
inputSchema: {
|
|
697
709
|
code: import_zod2.z.string().describe("6-digit stock code"),
|
|
698
710
|
timeframe: import_zod2.z.enum(["tick", "minute", "day", "week", "month", "year"]).describe("Chart timeframe"),
|
|
699
711
|
scope: import_zod2.z.string().optional().describe("Tick units (1/3/5/10/30) or minute interval (1/3/5/10/15/30/45/60); default 1"),
|
|
700
712
|
date: import_zod2.z.string().optional().describe("Base date YYYYMMDD for period charts (default today)"),
|
|
701
713
|
adjusted: import_zod2.z.boolean().optional().describe("Adjust for splits/rights (default true)"),
|
|
702
|
-
count: import_zod2.z.number().min(1).max(
|
|
714
|
+
count: import_zod2.z.number().min(1).max(MAX_COUNT).optional().describe(
|
|
715
|
+
"Max rows to return (default 50). Exceeding the per-request cap (tick/min 900, day 600, week 300, month 240, year 30) auto-paginates."
|
|
716
|
+
)
|
|
703
717
|
}
|
|
704
718
|
},
|
|
705
719
|
async ({ code, timeframe, scope, date, adjusted, count }) => withErrorHandling(async () => {
|
|
706
720
|
const client = clientOrThrow();
|
|
707
721
|
const stk = normalizeStockCode(code);
|
|
708
722
|
const upd = adjusted === false ? "0" : "1";
|
|
723
|
+
const want = count ?? DEFAULT_COUNT;
|
|
709
724
|
let def;
|
|
710
725
|
let body;
|
|
711
726
|
if (timeframe === "tick") {
|
|
@@ -718,12 +733,15 @@ function registerChartTools(server2) {
|
|
|
718
733
|
def = PERIOD_EP[timeframe];
|
|
719
734
|
body = { stk_cd: stk, base_dt: date ?? todayKst(), upd_stkpc_tp: upd };
|
|
720
735
|
}
|
|
721
|
-
const
|
|
736
|
+
const cap = CHART_PER_PAGE_CAP[timeframe];
|
|
737
|
+
const paginate = want > cap;
|
|
738
|
+
const maxPages = paginate ? Math.max(1, Math.ceil(want / cap)) : 1;
|
|
739
|
+
const { data } = await client.callEndpoint(def, body, { paginate, maxPages });
|
|
722
740
|
const rows = Array.isArray(data[def.listKey]) ? data[def.listKey] : [];
|
|
723
741
|
return mcpJson({
|
|
724
742
|
code: stk,
|
|
725
743
|
timeframe,
|
|
726
|
-
rows: rows.slice(0,
|
|
744
|
+
rows: rows.slice(0, want)
|
|
727
745
|
});
|
|
728
746
|
})
|
|
729
747
|
);
|
package/dist/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp.ts","../src/mcp/tools/market.ts","../src/config/constants.ts","../src/config/store.ts","../src/output/error.ts","../src/utils/helpers.ts","../src/client/api-client.ts","../src/mcp/helpers.ts","../src/client/endpoints.ts","../src/utils/format.ts","../src/utils/orderbook.ts","../src/mcp/tools/chart.ts","../src/mcp/tools/account.ts","../src/mcp/tools/order.ts","../src/mcp/tools/ranking.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { registerMarketTools } from './mcp/tools/market';\nimport { registerChartTools } from './mcp/tools/chart';\nimport { registerAccountTools } from './mcp/tools/account';\nimport { registerOrderTools } from './mcp/tools/order';\nimport { registerRankingTools } from './mcp/tools/ranking';\n\nconst server = new McpServer({\n name: 'kiwoom-mcp',\n version: '0.1.0',\n});\n\nregisterMarketTools(server);\nregisterChartTools(server);\nregisterAccountTools(server);\nregisterOrderTools(server);\nregisterRankingTools(server);\n\nconst transport = new StdioServerTransport();\nserver.connect(transport).catch((err) => {\n console.error('MCP server error:', err);\n process.exit(1);\n});\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { clientOrThrow, mcpJson, withErrorHandling, tool } from '../helpers';\nimport { ENDPOINTS } from '../../client/endpoints';\nimport { normalizeStockCode, todayKst } from '../../utils/helpers';\nimport { parseOrderbook } from '../../utils/orderbook';\n\nexport function registerMarketTools(server: McpServer): void {\n tool(\n server,\n 'get_stock_info',\n {\n description:\n 'Get Kiwoom stock fundamentals + current price for a Korean stock (name, price, change, PER/EPS/ROE/PBR/BPS, OHLC, limits). ka10001.',\n inputSchema: { code: z.string().describe('6-digit stock code, e.g. 005930 (삼성전자)') },\n },\n async ({ code }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.stockInfo, {\n stk_cd: normalizeStockCode(code),\n });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_price',\n {\n description: 'Get a rich current-price snapshot incl. top-of-book for a stock. ka10007.',\n inputSchema: { code: z.string().describe('6-digit stock code') },\n },\n async ({ code }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.priceTableInfo, {\n stk_cd: normalizeStockCode(code),\n });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_orderbook',\n {\n description: 'Get the 10-level bid/ask order book for a stock, parsed into ordered levels. ka10004.',\n inputSchema: { code: z.string().describe('6-digit stock code') },\n },\n async ({ code }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const stk = normalizeStockCode(code);\n const { data } = await client.callEndpoint(ENDPOINTS.orderbook, { stk_cd: stk });\n return mcpJson({ code: stk, ...parseOrderbook(data) });\n }),\n );\n\n tool(\n server,\n 'get_daily_price',\n {\n description: 'Get daily price history ending at a base date. ka10086.',\n inputSchema: {\n code: z.string().describe('6-digit stock code'),\n date: z.string().optional().describe('Base date YYYYMMDD (default today)'),\n },\n },\n async ({ code, date }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.dailyPrice, {\n stk_cd: normalizeStockCode(code),\n qry_dt: date ?? todayKst(),\n indc_tp: '0',\n });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_recent_trades',\n {\n description: 'Get recent tick-by-tick executions for a stock. ka10003.',\n inputSchema: { code: z.string().describe('6-digit stock code') },\n },\n async ({ code }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.stockTrades, {\n stk_cd: normalizeStockCode(code),\n });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'search_stocks',\n {\n description:\n 'Search the master stock list by name keyword or code prefix. Returns matching code/name/market. ka10099.',\n inputSchema: {\n keyword: z.string().describe('Name substring or code prefix to match'),\n market: z.enum(['0', '10']).optional().describe('0=KOSPI (default), 10=KOSDAQ'),\n limit: z.number().min(1).max(200).optional().describe('Max rows (default 30)'),\n },\n },\n async ({ keyword, market, limit }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(\n ENDPOINTS.stockList,\n { mrkt_tp: market ?? '0' },\n { paginate: true },\n );\n const rows: Array<Record<string, any>> = Array.isArray(data.list) ? data.list : [];\n const kw = keyword.toLowerCase();\n const matches = rows\n .filter(\n (r) =>\n String(r.name ?? '').toLowerCase().includes(kw) ||\n String(r.code ?? '').startsWith(keyword),\n )\n .slice(0, limit ?? 30)\n .map((r) => ({ code: r.code, name: r.name, market: r.marketName, sector: r.upName }));\n return mcpJson(matches);\n }),\n );\n}\n","export type Environment = 'real' | 'mock';\n\n/** API host per environment. */\nexport const BASE_URLS: Record<Environment, string> = {\n real: 'https://api.kiwoom.com',\n mock: 'https://mockapi.kiwoom.com',\n};\n\n/** Accepted aliases for the environment, including Korean labels. */\nexport const ENV_ALIASES: Record<string, Environment> = {\n real: 'real',\n prod: 'real',\n production: 'real',\n live: 'real',\n 실전: 'real',\n 실전투자: 'real',\n mock: 'mock',\n test: 'mock',\n paper: 'mock',\n demo: 'mock',\n 모의: 'mock',\n 모의투자: 'mock',\n};\n\n/** OAuth2 endpoints (relative to the environment base URL). */\nexport const TOKEN_PATH = '/oauth2/token';\nexport const REVOKE_PATH = '/oauth2/revoke';\n\n/** Config + token-cache storage. */\nexport const CONFIG_DIR_NAME = '.kiwoom-cli';\nexport const CONFIG_FILE_NAME = 'config.json';\nexport const TOKEN_FILE_NAME = 'token.json';\n\n/** Environment variables used as a fallback when the config file is unset. */\nexport const ENV_VARS = {\n appkey: 'KIWOOM_APPKEY',\n secretkey: 'KIWOOM_SECRETKEY',\n env: 'KIWOOM_ENV',\n} as const;\n\n/** Re-issue a cached token if it expires within this many milliseconds. */\nexport const TOKEN_REFRESH_BUFFER_MS = 60_000;\n\n/** HTTP request timeout. */\nexport const REQUEST_TIMEOUT_MS = 30_000;\n\n/**\n * `return_code` values that signal \"no matching data\" rather than a real error.\n * The API returns 20 ([2000] 관련자료가없습니다) for empty result sets; callers\n * should receive the (empty) payload, not an exception.\n */\nexport const SOFT_EMPTY_RETURN_CODES = new Set<number>([20]);\n\n/** Exchange routing for ORDER TRs (dmst_stex_tp): best-execution SOR allowed. */\nexport const ORDER_EXCHANGE_TYPES = ['KRX', 'NXT', 'SOR'] as const;\nexport type OrderExchangeType = (typeof ORDER_EXCHANGE_TYPES)[number];\n\n/** Exchange filter for ACCOUNT query TRs (dmst_stex_tp): % = all exchanges. */\nexport const ACCOUNT_EXCHANGE_TYPES = ['KRX', 'NXT', '%'] as const;\nexport type AccountExchangeType = (typeof ACCOUNT_EXCHANGE_TYPES)[number];\n\n/**\n * trde_tp (매매구분 / order-type) codes for buy/sell orders, from the Kiwoom\n * spec. NOTE: 7 = 최우선지정가 (the assignment's \"8\" is wrong). Market types\n * (3/13/23) must be sent with an empty ord_uv.\n */\nexport const ORDER_TYPES: Record<string, string> = {\n '0': '보통(지정가/limit)',\n '3': '시장가(market)',\n '5': '조건부지정가',\n '6': '최유리지정가',\n '7': '최우선지정가',\n '10': '보통(IOC)',\n '13': '시장가(IOC)',\n '16': '최유리(IOC)',\n '20': '보통(FOK)',\n '23': '시장가(FOK)',\n '26': '최유리(FOK)',\n '28': '스톱지정가(stop-limit)',\n '29': '중간가',\n '30': '중간가(IOC)',\n '31': '중간가(FOK)',\n '61': '장시작전시간외',\n '62': '시간외단일가',\n '81': '장마감후시간외',\n};\n\n/** trde_tp codes that are market-style (price must be empty). */\nexport const MARKET_ORDER_TYPES = new Set(['3', '13', '23']);\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport {\n CONFIG_DIR_NAME,\n CONFIG_FILE_NAME,\n TOKEN_FILE_NAME,\n ENV_VARS,\n ENV_ALIASES,\n Environment,\n} from './constants';\n\nexport interface CliConfig {\n env: Environment;\n appkey?: string;\n secretkey?: string;\n}\n\nexport interface CachedToken {\n token: string;\n /** Kiwoom expiry stamp, \"YYYYMMDDHHmmss\" in KST. */\n expiresDt: string;\n /** First 6 chars of the appkey the token was issued for (cache-invalidation guard). */\n appkeyHint: string;\n}\n\n/** token.json holds one cached token per environment. */\ntype TokenCache = Partial<Record<Environment, CachedToken>>;\n\nconst DEFAULT_CONFIG: CliConfig = { env: 'real' };\n\nfunction getConfigDir(): string {\n return path.join(os.homedir(), CONFIG_DIR_NAME);\n}\n\nfunction getConfigPath(): string {\n return path.join(getConfigDir(), CONFIG_FILE_NAME);\n}\n\nfunction getTokenPath(): string {\n return path.join(getConfigDir(), TOKEN_FILE_NAME);\n}\n\nfunction ensureConfigDir(): void {\n const dir = getConfigDir();\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { mode: 0o700, recursive: true });\n } else {\n // Tighten perms even if the dir pre-existed with looser permissions.\n try {\n fs.chmodSync(dir, 0o700);\n } catch {\n /* best effort */\n }\n }\n}\n\n/**\n * Write a secrets file with 0600 perms. `writeFileSync`'s mode only applies when\n * the file is newly created, so chmod afterward to tighten a pre-existing file\n * that may have looser permissions.\n */\nfunction writeSecure(filePath: string, data: string): void {\n fs.writeFileSync(filePath, data, { mode: 0o600 });\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n /* best effort */\n }\n}\n\n// ─── Config ───────────────────────────────────────────────────────────────\n\nexport function loadConfig(): CliConfig {\n try {\n const raw = fs.readFileSync(getConfigPath(), 'utf-8');\n return { ...DEFAULT_CONFIG, ...JSON.parse(raw) };\n } catch {\n return { ...DEFAULT_CONFIG };\n }\n}\n\nexport function saveConfig(partial: Partial<CliConfig>): void {\n ensureConfigDir();\n const current = loadConfig();\n const merged = { ...current, ...partial };\n writeSecure(getConfigPath(), JSON.stringify(merged, null, 2));\n}\n\n/**\n * Resolve the effective config: config file first, environment variables as a\n * fallback for any unset secret.\n */\nexport function getEffectiveConfig(): CliConfig {\n const disk = loadConfig();\n const envVal = process.env[ENV_VARS.env];\n const env: Environment =\n envVal && ENV_ALIASES[envVal.toLowerCase()]\n ? ENV_ALIASES[envVal.toLowerCase()]\n : disk.env;\n return {\n env,\n appkey: disk.appkey ?? process.env[ENV_VARS.appkey],\n secretkey: disk.secretkey ?? process.env[ENV_VARS.secretkey],\n };\n}\n\nexport function maskSecret(value: string | undefined): string {\n if (!value) return '(not set)';\n if (value.length <= 10) return '****';\n return value.slice(0, 6) + '...' + value.slice(-4);\n}\n\n// ─── Token cache ────────────────────────────────────────────────────────────\n\nfunction loadTokenCache(): TokenCache {\n try {\n return JSON.parse(fs.readFileSync(getTokenPath(), 'utf-8'));\n } catch {\n return {};\n }\n}\n\nexport function getCachedToken(env: Environment): CachedToken | undefined {\n return loadTokenCache()[env];\n}\n\nexport function saveCachedToken(env: Environment, token: CachedToken): void {\n ensureConfigDir();\n const cache = loadTokenCache();\n cache[env] = token;\n writeSecure(getTokenPath(), JSON.stringify(cache, null, 2));\n}\n\nexport function clearCachedToken(env?: Environment): void {\n if (!env) {\n try {\n fs.rmSync(getTokenPath());\n } catch {\n /* nothing to clear */\n }\n return;\n }\n const cache = loadTokenCache();\n delete cache[env];\n try {\n ensureConfigDir();\n writeSecure(getTokenPath(), JSON.stringify(cache, null, 2));\n } catch {\n /* ignore */\n }\n}\n\n/** Expose paths for diagnostics/tests. */\nexport const _paths = { getConfigDir, getConfigPath, getTokenPath };\n","/**\n * An error that carries a suggested recovery command, surfaced to the user as\n * a \"Try: ...\" hint.\n */\nexport class ActionableError extends Error {\n suggestedCommand?: string;\n\n constructor(message: string, suggestedCommand?: string) {\n super(message);\n this.name = 'ActionableError';\n this.suggestedCommand = suggestedCommand;\n }\n}\n\n/**\n * An error raised when the Kiwoom API returns a non-zero return_code.\n */\nexport class KiwoomApiError extends Error {\n returnCode: number;\n returnMsg: string;\n apiId?: string;\n\n constructor(returnCode: number, returnMsg: string, apiId?: string) {\n super(`[${returnCode}] ${returnMsg}`);\n this.name = 'KiwoomApiError';\n this.returnCode = returnCode;\n this.returnMsg = returnMsg;\n this.apiId = apiId;\n }\n}\n\nexport function handleError(err: unknown): never {\n if (err instanceof ActionableError) {\n console.error(`\\nError: ${err.message}`);\n if (err.suggestedCommand) {\n console.error(`\\nTry: ${err.suggestedCommand}`);\n }\n process.exit(1);\n }\n\n if (err instanceof KiwoomApiError) {\n console.error(`\\nError: ${err.returnMsg} (code ${err.returnCode})`);\n if (isAuthError(err.returnMsg) || err.returnCode === 3) {\n console.error(`\\nTry: kiwoom-cli config init`);\n }\n process.exit(1);\n }\n\n if (err instanceof Error) {\n let message = err.message;\n if (message.length > 500) {\n message = message.slice(0, 500) + '...';\n }\n if (isAuthError(message)) {\n console.error(`\\nError: ${message}`);\n console.error(`\\nTry: kiwoom-cli config init`);\n process.exit(1);\n }\n console.error(`\\nError: ${message}`);\n process.exit(1);\n }\n\n console.error(`\\nUnknown error:`, err);\n process.exit(1);\n}\n\nfunction isAuthError(message: string): boolean {\n return /unauthorized|forbidden|not authenticated|토큰|인증|appkey|access token|만료/i.test(\n message,\n );\n}\n\nexport function requireConfig(\n value: string | undefined,\n name: string,\n setCommand: string,\n): asserts value is string {\n if (!value) {\n throw new ActionableError(\n `${name} is not configured.`,\n `kiwoom-cli config set ${setCommand}`,\n );\n }\n}\n","import { ActionableError } from '../output/error';\n\n/** Parse an integer strictly, throwing on failure. */\nexport function parseIntStrict(value: string, name: string): number {\n const n = parseInt(value, 10);\n if (Number.isNaN(n)) {\n throw new Error(`Invalid ${name}: \"${value}\" is not a valid integer`);\n }\n return n;\n}\n\n/** Parse a float strictly, throwing on failure. */\nexport function parseFloatStrict(value: string, name: string): number {\n const n = parseFloat(value);\n if (Number.isNaN(n)) {\n throw new Error(`Invalid ${name}: \"${value}\" is not a valid number`);\n }\n return n;\n}\n\n/**\n * Normalize a Korean stock code to the 6-digit form the API expects.\n * Accepts \"005930\", \"A005930\" (response form), or \"005930_AL\" style suffixes.\n */\nexport function normalizeStockCode(code: string): string {\n const trimmed = code.trim().toUpperCase();\n const stripped = trimmed.replace(/^A(?=\\d)/, '');\n const base = stripped.split('_')[0];\n if (!/^\\d{6}$/.test(base)) {\n throw new ActionableError(\n `Invalid stock code \"${code}\". Expected a 6-digit code like 005930 (삼성전자).`,\n );\n }\n return base;\n}\n\n/**\n * Parse a Kiwoom expiry stamp (\"YYYYMMDDHHmmss\", Korea Standard Time) into\n * epoch milliseconds.\n */\nexport function parseKiwoomExpiry(expiresDt: string): number {\n const m = expiresDt.match(/^(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})$/);\n if (!m) return NaN;\n const [, y, mo, d, h, mi, s] = m;\n // KST is UTC+9, no DST.\n return Date.parse(`${y}-${mo}-${d}T${h}:${mi}:${s}+09:00`);\n}\n\n/** True if the token is expired or will expire within `bufferMs`. */\nexport function isTokenExpired(expiresDt: string, bufferMs = 0): boolean {\n const expiry = parseKiwoomExpiry(expiresDt);\n if (Number.isNaN(expiry)) return true;\n return Date.now() + bufferMs >= expiry;\n}\n\n/** Today's date in KST as \"YYYYMMDD\". */\nexport function todayKst(): string {\n const now = new Date(Date.now() + 9 * 3600 * 1000);\n return now.toISOString().slice(0, 10).replace(/-/g, '');\n}\n","import {\n BASE_URLS,\n TOKEN_PATH,\n REVOKE_PATH,\n TOKEN_REFRESH_BUFFER_MS,\n REQUEST_TIMEOUT_MS,\n SOFT_EMPTY_RETURN_CODES,\n Environment,\n} from '../config/constants';\nimport {\n getCachedToken,\n saveCachedToken,\n clearCachedToken,\n} from '../config/store';\nimport { isTokenExpired } from '../utils/helpers';\nimport { KiwoomApiError, ActionableError } from '../output/error';\nimport { EndpointDef } from './endpoints';\n\n/** Result of a TR request, with pagination metadata from the response headers. */\nexport interface TrResponse<T = Record<string, any>> {\n data: T;\n /** True when more pages are available (response header `cont-yn` === 'Y'). */\n contYn: boolean;\n /** Cursor to pass back as `next-key` to fetch the next page. */\n nextKey: string;\n}\n\nexport interface RequestOptions {\n /** Pass 'Y' (with nextKey) to continue a paginated query. */\n contYn?: string;\n nextKey?: string;\n /** Override the api-id header (defaults to the one in the registry call). */\n apiId?: string;\n}\n\nexport interface ClientOptions {\n env: Environment;\n appkey?: string;\n secretkey?: string;\n /** Explicit token; bypasses issuance/caching when provided. */\n token?: string;\n}\n\nfunction fetchWithTimeout(url: string, init: RequestInit): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n return fetch(url, { ...init, signal: controller.signal }).finally(() =>\n clearTimeout(timeoutId),\n );\n}\n\n/**\n * Kiwoom Securities REST API client.\n *\n * Handles OAuth2 token issuance + caching and routes every TR through a single\n * POST with the api-id header. Tokens are cached per environment on disk so\n * separate CLI invocations reuse them until expiry.\n */\nexport class KiwoomClient {\n private env: Environment;\n private baseUrl: string;\n private appkey?: string;\n private secretkey?: string;\n private tokenOverride?: string;\n private memoToken?: string;\n\n constructor(opts: ClientOptions) {\n this.env = opts.env;\n this.baseUrl = BASE_URLS[opts.env];\n if (!this.baseUrl) {\n throw new Error(`Invalid environment: ${opts.env}. Use: real, mock`);\n }\n this.appkey = opts.appkey;\n this.secretkey = opts.secretkey;\n this.tokenOverride = opts.token;\n }\n\n getEnv(): Environment {\n return this.env;\n }\n\n /** Ensure a valid token exists (issuing + caching as needed) and return it. */\n async authenticate(): Promise<string> {\n return this.getToken();\n }\n\n // ─── OAuth2 ───────────────────────────────────────────────────────────────\n\n /** Issue a fresh access token from the app key + secret key. */\n async issueToken(): Promise<{ token: string; expiresDt: string }> {\n if (!this.appkey || !this.secretkey) {\n throw new ActionableError(\n 'App key and secret key are required to issue a token.',\n 'kiwoom-cli config init',\n );\n }\n const res = await fetchWithTimeout(`${this.baseUrl}${TOKEN_PATH}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json;charset=UTF-8' },\n body: JSON.stringify({\n grant_type: 'client_credentials',\n appkey: this.appkey,\n secretkey: this.secretkey,\n }),\n });\n const body = (await res.json().catch(() => ({}))) as Record<string, any>;\n if (!res.ok || body.return_code !== 0 || !body.token) {\n throw new KiwoomApiError(\n body.return_code ?? res.status,\n body.return_msg ?? `Token request failed (HTTP ${res.status})`,\n );\n }\n return { token: body.token, expiresDt: body.expires_dt };\n }\n\n /** Revoke an access token (defaults to the cached/override token). */\n async revokeToken(token?: string): Promise<Record<string, any>> {\n if (!this.appkey || !this.secretkey) {\n throw new ActionableError(\n 'App key and secret key are required to revoke a token.',\n 'kiwoom-cli config init',\n );\n }\n const target = token ?? this.tokenOverride ?? getCachedToken(this.env)?.token;\n if (!target) {\n throw new ActionableError('No token to revoke.');\n }\n const res = await fetchWithTimeout(`${this.baseUrl}${REVOKE_PATH}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json;charset=UTF-8' },\n body: JSON.stringify({\n appkey: this.appkey,\n secretkey: this.secretkey,\n token: target,\n }),\n });\n const body = (await res.json().catch(() => ({}))) as Record<string, any>;\n if (!res.ok || body.return_code !== 0) {\n throw new KiwoomApiError(\n body.return_code ?? res.status,\n body.return_msg ?? `Token revoke failed (HTTP ${res.status})`,\n );\n }\n clearCachedToken(this.env);\n return body;\n }\n\n /**\n * Return a valid token, reusing the cache when possible and issuing+caching a\n * new one when missing or near expiry.\n */\n private async getToken(): Promise<string> {\n if (this.tokenOverride) return this.tokenOverride;\n if (this.memoToken) return this.memoToken;\n\n const cached = getCachedToken(this.env);\n const hint = this.appkey?.slice(0, 6);\n if (\n cached &&\n cached.appkeyHint === hint &&\n !isTokenExpired(cached.expiresDt, TOKEN_REFRESH_BUFFER_MS)\n ) {\n this.memoToken = cached.token;\n return cached.token;\n }\n\n const { token, expiresDt } = await this.issueToken();\n this.memoToken = token;\n if (hint) {\n saveCachedToken(this.env, { token, expiresDt, appkeyHint: hint });\n }\n return token;\n }\n\n // ─── Generic TR request ──────────────────────────────────────────────────\n\n /**\n * Execute a TR. `apiId` is sent in the api-id header; `path` is the category\n * route (e.g. /api/dostk/stkinfo). Returns the parsed body plus pagination.\n */\n async request<T = Record<string, any>>(\n apiId: string,\n path: string,\n body: Record<string, unknown> = {},\n options: RequestOptions = {},\n ): Promise<TrResponse<T>> {\n const send = async (token: string): Promise<Response> =>\n fetchWithTimeout(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json;charset=UTF-8',\n authorization: `Bearer ${token}`,\n 'api-id': options.apiId ?? apiId,\n 'cont-yn': options.contYn ?? 'N',\n 'next-key': options.nextKey ?? '',\n },\n body: JSON.stringify(body),\n });\n\n let res = await send(await this.getToken());\n\n // Token might have been revoked/expired server-side: clear and retry once.\n if (res.status === 401 && !this.tokenOverride) {\n clearCachedToken(this.env);\n this.memoToken = undefined;\n res = await send(await this.getToken());\n }\n\n const payload = (await res.json().catch(() => ({}))) as Record<string, any>;\n if (!res.ok) {\n throw new KiwoomApiError(\n payload.return_code ?? res.status,\n payload.return_msg ?? `Request failed (HTTP ${res.status})`,\n apiId,\n );\n }\n if (\n typeof payload.return_code === 'number' &&\n payload.return_code !== 0 &&\n !SOFT_EMPTY_RETURN_CODES.has(payload.return_code)\n ) {\n throw new KiwoomApiError(payload.return_code, payload.return_msg ?? 'Error', apiId);\n }\n\n return {\n data: payload as T,\n contYn: res.headers.get('cont-yn') === 'Y',\n nextKey: res.headers.get('next-key') ?? '',\n };\n }\n\n /**\n * Execute an endpoint from the registry. When `paginate` is set and the\n * endpoint declares a listKey, all pages are fetched and concatenated.\n */\n async callEndpoint<T = Record<string, any>>(\n def: EndpointDef,\n body: Record<string, unknown> = {},\n opts: { paginate?: boolean; contYn?: string; nextKey?: string } = {},\n ): Promise<TrResponse<T>> {\n if (opts.paginate && def.listKey) {\n const data = await this.requestAll<T>(def.apiId, def.path, body, def.listKey);\n return { data, contYn: false, nextKey: '' };\n }\n return this.request<T>(def.apiId, def.path, body, {\n contYn: opts.contYn,\n nextKey: opts.nextKey,\n });\n }\n\n /**\n * Fetch all pages of a TR, concatenating the array under `listKey`.\n * Caps at `maxPages` to avoid runaway loops.\n */\n async requestAll<T = Record<string, any>>(\n apiId: string,\n path: string,\n body: Record<string, unknown>,\n listKey: string,\n maxPages = 20,\n ): Promise<T> {\n let page = await this.request<Record<string, any>>(apiId, path, body);\n const acc = Array.isArray(page.data[listKey]) ? [...page.data[listKey]] : [];\n let pages = 1;\n while (page.contYn && page.nextKey && pages < maxPages) {\n page = await this.request<Record<string, any>>(apiId, path, body, {\n contYn: 'Y',\n nextKey: page.nextKey,\n });\n if (Array.isArray(page.data[listKey])) acc.push(...page.data[listKey]);\n pages += 1;\n }\n return { ...page.data, [listKey]: acc } as T;\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { ZodRawShape } from 'zod';\nimport { KiwoomClient } from '../client/api-client';\nimport { getEffectiveConfig } from '../config/store';\n\nexport function mcpText(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nexport function mcpJson(data: unknown) {\n return mcpText(JSON.stringify(data, null, 2));\n}\n\nexport function mcpError(message: string) {\n return {\n content: [{ type: 'text' as const, text: `ERROR: ${message}` }],\n isError: true,\n };\n}\n\nexport function createClient():\n | { client: KiwoomClient }\n | { error: ReturnType<typeof mcpError> } {\n const config = getEffectiveConfig();\n if (!config.appkey || !config.secretkey) {\n return {\n error: mcpError(\n 'App key / secret key not configured. Run: kiwoom-cli config init',\n ),\n };\n }\n return {\n client: new KiwoomClient({\n env: config.env,\n appkey: config.appkey,\n secretkey: config.secretkey,\n }),\n };\n}\n\n/** Build a client or throw — convenient inside withErrorHandling(). */\nexport function clientOrThrow(): KiwoomClient {\n const config = getEffectiveConfig();\n if (!config.appkey || !config.secretkey) {\n throw new Error('App key / secret key not configured. Run: kiwoom-cli config init');\n }\n return new KiwoomClient({\n env: config.env,\n appkey: config.appkey,\n secretkey: config.secretkey,\n });\n}\n\n/** Current environment ('real' | 'mock'), for order safety checks. */\nexport function currentEnv(): string {\n return getEffectiveConfig().env;\n}\n\n/**\n * Register an MCP tool. Thin wrapper over server.registerTool that fixes the\n * SDK's deep generic inference (zod inputSchema → TS2589) by erasing the\n * shape generic at the call boundary. Runtime validation is unaffected.\n */\nexport function tool(\n server: McpServer,\n name: string,\n config: { description: string; inputSchema?: ZodRawShape },\n handler: (args: any) => Promise<ReturnType<typeof mcpText> | ReturnType<typeof mcpError>>,\n): void {\n (server.registerTool as any)(name, config, handler);\n}\n\nexport async function withErrorHandling(\n fn: () => Promise<ReturnType<typeof mcpText>>,\n): Promise<ReturnType<typeof mcpText> | ReturnType<typeof mcpError>> {\n try {\n return await fn();\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return mcpError(message.slice(0, 500));\n }\n}\n","/**\n * Registry of every Kiwoom TR this CLI exposes — the single source of truth for\n * api-id, route, response list key, and whether the call places/modifies orders.\n *\n * All specs were live-verified against https://api.kiwoom.com (read TRs) or\n * taken from the official spec + typed community wrappers (order TRs, which are\n * never called live for safety).\n */\n\nexport const PATHS = {\n stkinfo: '/api/dostk/stkinfo',\n mrkcond: '/api/dostk/mrkcond',\n chart: '/api/dostk/chart',\n acnt: '/api/dostk/acnt',\n ordr: '/api/dostk/ordr',\n crdordr: '/api/dostk/crdordr',\n rkinfo: '/api/dostk/rkinfo',\n sect: '/api/dostk/sect',\n} as const;\n\nexport interface EndpointDef {\n /** api-id header value, e.g. \"ka10001\". */\n apiId: string;\n /** Category route. */\n path: string;\n /** Korean TR name. */\n korean: string;\n /** Array field in the response payload, if the TR returns a list. */\n listKey?: string;\n /** True for order place/modify/cancel TRs (real-money writes). */\n isWrite?: boolean;\n}\n\nexport const ENDPOINTS = {\n // ── Stock info (종목정보) ──────────────────────────────────────────────────\n stockInfo: { apiId: 'ka10001', path: PATHS.stkinfo, korean: '주식기본정보요청' },\n tradingMembers: { apiId: 'ka10002', path: PATHS.stkinfo, korean: '주식거래원요청' },\n stockTrades: { apiId: 'ka10003', path: PATHS.stkinfo, korean: '체결정보요청', listKey: 'cntr_infr' },\n watchlist: { apiId: 'ka10095', path: PATHS.stkinfo, korean: '관심종목정보요청', listKey: 'atn_stk_infr' },\n stockList: { apiId: 'ka10099', path: PATHS.stkinfo, korean: '종목정보 리스트', listKey: 'list' },\n stockInfoSingle: { apiId: 'ka10100', path: PATHS.stkinfo, korean: '종목정보 조회' },\n sectorCodeList: { apiId: 'ka10101', path: PATHS.stkinfo, korean: '업종코드 리스트', listKey: 'list' },\n creditTrend: { apiId: 'ka10013', path: PATHS.stkinfo, korean: '신용매매동향요청', listKey: 'crd_trde_trend' },\n\n // ── Market quote (시세) ────────────────────────────────────────────────────\n orderbook: { apiId: 'ka10004', path: PATHS.mrkcond, korean: '주식호가요청' },\n quoteSnapshot: { apiId: 'ka10006', path: PATHS.mrkcond, korean: '주식시분요청' },\n priceTableInfo: { apiId: 'ka10007', path: PATHS.mrkcond, korean: '시세표성정보요청' },\n dailyPrice: { apiId: 'ka10086', path: PATHS.mrkcond, korean: '일별주가요청', listKey: 'daly_stkpc' },\n instTradedStocks: { apiId: 'ka10044', path: PATHS.mrkcond, korean: '일별기관매매종목요청', listKey: 'daly_orgn_trde_stk' },\n instForeignTrend: { apiId: 'ka10045', path: PATHS.mrkcond, korean: '종목별기관매매추이요청', listKey: 'stk_orgn_trde_trnsn' },\n strengthByTime: { apiId: 'ka10046', path: PATHS.mrkcond, korean: '체결강도시간별요청', listKey: 'cntr_str_tm' },\n strengthByDay: { apiId: 'ka10047', path: PATHS.mrkcond, korean: '체결강도일별요청', listKey: 'cntr_str_daly' },\n afterHoursOrderbook: { apiId: 'ka10087', path: PATHS.mrkcond, korean: '시간외단일가요청' },\n\n // ── Chart (차트) ───────────────────────────────────────────────────────────\n tickChart: { apiId: 'ka10079', path: PATHS.chart, korean: '주식틱차트조회', listKey: 'stk_tic_chart_qry' },\n minuteChart: { apiId: 'ka10080', path: PATHS.chart, korean: '주식분봉차트조회', listKey: 'stk_min_pole_chart_qry' },\n dailyChart: { apiId: 'ka10081', path: PATHS.chart, korean: '주식일봉차트조회', listKey: 'stk_dt_pole_chart_qry' },\n // NOTE: weekly list key is the doubled \"stk_stk_...\" — verified live, not a typo.\n weeklyChart: { apiId: 'ka10082', path: PATHS.chart, korean: '주식주봉차트조회', listKey: 'stk_stk_pole_chart_qry' },\n monthlyChart: { apiId: 'ka10083', path: PATHS.chart, korean: '주식월봉차트조회', listKey: 'stk_mth_pole_chart_qry' },\n yearlyChart: { apiId: 'ka10094', path: PATHS.chart, korean: '주식년봉차트조회', listKey: 'stk_yr_pole_chart_qry' },\n\n // ── Account (계좌) ─────────────────────────────────────────────────────────\n balance: { apiId: 'kt00018', path: PATHS.acnt, korean: '계좌평가잔고내역요청', listKey: 'acnt_evlt_remn_indv_tot' },\n deposit: { apiId: 'kt00001', path: PATHS.acnt, korean: '예수금상세현황요청', listKey: 'stk_entr_prst' },\n evalStatus: { apiId: 'kt00004', path: PATHS.acnt, korean: '계좌평가현황요청', listKey: 'stk_acnt_evlt_prst' },\n settledBalance: { apiId: 'kt00005', path: PATHS.acnt, korean: '체결잔고요청', listKey: 'stk_cntr_remn' },\n orderDetail: { apiId: 'kt00007', path: PATHS.acnt, korean: '계좌별주문체결내역상세요청', listKey: 'acnt_ord_cntr_prps_dtl' },\n orderStatus: { apiId: 'kt00009', path: PATHS.acnt, korean: '계좌별주문체결현황요청', listKey: 'acnt_ord_cntr_prst' },\n openOrders: { apiId: 'ka10075', path: PATHS.acnt, korean: '미체결요청', listKey: 'oso' },\n executions: { apiId: 'ka10076', path: PATHS.acnt, korean: '체결요청', listKey: 'cntr' },\n realizedPlByDate: { apiId: 'ka10072', path: PATHS.acnt, korean: '일자별종목별실현손익요청_일자', listKey: 'dt_stk_div_rlzt_pl' },\n realizedPlByPeriod: { apiId: 'ka10073', path: PATHS.acnt, korean: '일자별종목별실현손익요청_기간', listKey: 'dt_stk_rlzt_pl' },\n tradeJournal: { apiId: 'ka10170', path: PATHS.acnt, korean: '당일매매일지요청', listKey: 'tdy_trde_diary' },\n dailyReturn: { apiId: 'kt00016', path: PATHS.acnt, korean: '일별계좌수익률상세현황요청' },\n\n // ── Order (주문) — WRITE, real money ──────────────────────────────────────\n buy: { apiId: 'kt10000', path: PATHS.ordr, korean: '주식 매수주문', isWrite: true },\n sell: { apiId: 'kt10001', path: PATHS.ordr, korean: '주식 매도주문', isWrite: true },\n modify: { apiId: 'kt10002', path: PATHS.ordr, korean: '주식 정정주문', isWrite: true },\n cancel: { apiId: 'kt10003', path: PATHS.ordr, korean: '주식 취소주문', isWrite: true },\n creditBuy: { apiId: 'kt10006', path: PATHS.crdordr, korean: '신용 매수주문', isWrite: true },\n creditSell: { apiId: 'kt10007', path: PATHS.crdordr, korean: '신용 매도주문', isWrite: true },\n creditModify: { apiId: 'kt10008', path: PATHS.crdordr, korean: '신용 정정주문', isWrite: true },\n creditCancel: { apiId: 'kt10009', path: PATHS.crdordr, korean: '신용 취소주문', isWrite: true },\n\n // ── Ranking (순위정보) ─────────────────────────────────────────────────────\n rankFluctuation: { apiId: 'ka10027', path: PATHS.rkinfo, korean: '전일대비등락률순위요청', listKey: 'pred_pre_flu_rt_upper' },\n rankVolume: { apiId: 'ka10030', path: PATHS.rkinfo, korean: '당일거래량상위요청', listKey: 'tdy_trde_qty_upper' },\n rankTradeAmount: { apiId: 'ka10032', path: PATHS.rkinfo, korean: '거래대금상위요청', listKey: 'trde_prica_upper' },\n rankVolumeSurge: { apiId: 'ka10023', path: PATHS.rkinfo, korean: '거래량급증요청', listKey: 'trde_qty_sdnin' },\n rankPrevVolume: { apiId: 'ka10031', path: PATHS.rkinfo, korean: '전일거래량상위요청', listKey: 'pred_trde_qty_upper' },\n\n // ── Sector / industry (업종) ───────────────────────────────────────────────\n sectorPrice: { apiId: 'ka20001', path: PATHS.sect, korean: '업종현재가요청', listKey: 'inds_cur_prc_tm' },\n sectorStocks: { apiId: 'ka20002', path: PATHS.sect, korean: '업종별주가요청', listKey: 'inds_stkpc' },\n sectorAllIndex: { apiId: 'ka20003', path: PATHS.sect, korean: '전업종지수요청', listKey: 'all_inds_idex' },\n sectorDaily: { apiId: 'ka20009', path: PATHS.sect, korean: '업종현재가 일별요청', listKey: 'inds_cur_prc_daly_rept' },\n} satisfies Record<string, EndpointDef>;\n\nexport type EndpointKey = keyof typeof ENDPOINTS;\n","/**\n * Kiwoom value normalization.\n *\n * The REST API returns numbers as strings: integers are often zero-padded to a\n * fixed width (\"000000019471143\"), and price/percent fields carry a leading\n * sign that encodes direction (\"+0.39\", \"-350000\"). These helpers turn those\n * wire values into something readable without losing the sign.\n */\n\n// Kiwoom occasionally double-signs delta fields, e.g. \"--3405260\" (negative\n// delta) or \"++100\"; treat a leading run of signs as one effective sign where\n// any '-' makes it negative.\nconst NUMERIC_RE = /^([+-]*)0*(\\d+)(\\.\\d+)?$/;\n\n/**\n * Strip zero-padding while preserving sign and decimals.\n * \"000000019471143\" -> \"19471143\", \"-00000001485445\" -> \"-1485445\",\n * \"+0.39\" -> \"+0.39\", \"--3405260\" -> \"-3405260\", \"000000000000000\" -> \"0\".\n * Non-numeric strings (codes, dates-as-text, \"KRX\") are returned unchanged.\n */\nexport function unpad(value: string | number | null | undefined): string {\n if (value === null || value === undefined) return '';\n const str = String(value).trim();\n const m = str.match(NUMERIC_RE);\n if (!m) return str;\n const sign = m[1].includes('-') ? '-' : m[1].includes('+') ? '+' : '';\n const intPart = m[2].replace(/^0+(?=\\d)/, '');\n return `${sign}${intPart}${m[3] ?? ''}`;\n}\n\n/** Parse a Kiwoom numeric string into a JS number (sign + padding aware). */\nexport function toNumber(value: string | number | null | undefined): number {\n if (value === null || value === undefined || value === '') return NaN;\n const cleaned = unpad(value).replace(/^\\+/, '');\n // Guard sign-only/empty results so \"+\" doesn't coerce to 0.\n if (cleaned === '' || cleaned === '-' || cleaned === '+') return NaN;\n const n = Number(cleaned);\n return Number.isNaN(n) ? NaN : n;\n}\n\n/** Group an integer/decimal string with thousands separators, keeping sign. */\nexport function withCommas(value: string): string {\n const m = value.match(/^([+-]?)(\\d+)(\\.\\d+)?$/);\n if (!m) return value;\n const grouped = m[2].replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n return `${m[1]}${grouped}${m[3] ?? ''}`;\n}\n\n/** Format a (possibly zero-padded) won amount as \"19,471,143\". */\nexport function won(value: string | number | null | undefined): string {\n return withCommas(unpad(value));\n}\n\n/**\n * Format a market price. Kiwoom prefixes quote/chart prices with a direction\n * sign (e.g. \"-353750\" = price 353,750, trading below prior close); that sign\n * is NOT part of the value, so it is dropped and the magnitude is grouped.\n */\nexport function price(value: string | number | null | undefined): string {\n return withCommas(unpad(value).replace(/^[+-]/, ''));\n}\n\n/**\n * Format a Kiwoom date/time stamp for display.\n * \"20260622135400\" -> \"2026-06-22 13:54:00\"\n * \"20260622\" -> \"2026-06-22\"\n * \"135400\" -> \"13:54:00\"\n */\nexport function formatStamp(value: string | null | undefined): string {\n if (!value) return '';\n const s = String(value).trim();\n if (/^\\d{14}$/.test(s)) {\n return `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)} ${s.slice(8, 10)}:${s.slice(10, 12)}:${s.slice(12, 14)}`;\n }\n if (/^\\d{8}$/.test(s)) {\n return `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}`;\n }\n if (/^\\d{6}$/.test(s)) {\n return `${s.slice(0, 2)}:${s.slice(2, 4)}:${s.slice(4, 6)}`;\n }\n return s;\n}\n\n/**\n * Apply a set of formatters to selected keys of an object (shallow).\n * Used by table commands to pretty-print known numeric fields.\n */\nexport function formatFields<T extends Record<string, unknown>>(\n row: T,\n formatters: Partial<Record<keyof T | string, (v: string) => string>>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = { ...row };\n for (const [key, fn] of Object.entries(formatters)) {\n if (fn && out[key] !== undefined && out[key] !== null) {\n out[key] = fn(String(out[key]));\n }\n }\n return out;\n}\n","import { unpad, formatStamp } from './format';\n\nexport interface BookLevel {\n level: number;\n price: string;\n qty: string;\n}\n\nexport interface ParsedBook {\n baseTime: string;\n asks: BookLevel[]; // highest ask first (level 10 → 1)\n bids: BookLevel[]; // best bid first (level 1 → 10)\n totalAskQty: string;\n totalBidQty: string;\n}\n\n/**\n * Parse a ka10004 (주식호가요청) payload into ordered bid/ask levels.\n *\n * The field naming is irregular: level-1 quotes use the `*_fpr_*` (최우선)\n * keys, while levels 2–10 use `*_{N}th_pre_*`.\n */\nexport function parseOrderbook(d: Record<string, any>): ParsedBook {\n const asks: BookLevel[] = [];\n for (let n = 10; n >= 2; n--) {\n asks.push({\n level: n,\n price: unpad(d[`sel_${n}th_pre_bid`]),\n qty: unpad(d[`sel_${n}th_pre_req`]),\n });\n }\n asks.push({ level: 1, price: unpad(d.sel_fpr_bid), qty: unpad(d.sel_fpr_req) });\n\n const bids: BookLevel[] = [];\n bids.push({ level: 1, price: unpad(d.buy_fpr_bid), qty: unpad(d.buy_fpr_req) });\n for (let n = 2; n <= 10; n++) {\n bids.push({\n level: n,\n price: unpad(d[`buy_${n}th_pre_bid`]),\n qty: unpad(d[`buy_${n}th_pre_req`]),\n });\n }\n\n return {\n baseTime: formatStamp(d.bid_req_base_tm),\n asks,\n bids,\n totalAskQty: unpad(d.tot_sel_req),\n totalBidQty: unpad(d.tot_buy_req),\n };\n}\n\n/** Render a parsed book as a compact two-sided ladder for the terminal. */\nexport function renderOrderbook(book: ParsedBook, title: string): string {\n const lines: string[] = [];\n lines.push(`\\n Order Book: ${title} (${book.baseTime})\\n`);\n lines.push(' ── Asks ──');\n for (const a of book.asks) {\n lines.push(` ${a.price.padStart(12)} ${a.qty.padStart(12)}`);\n }\n lines.push(' ─────────');\n for (const b of book.bids) {\n lines.push(` ${b.price.padStart(12)} ${b.qty.padStart(12)}`);\n }\n lines.push(' ── Bids ──');\n lines.push(`\\n Total ask qty: ${book.totalAskQty} Total bid qty: ${book.totalBidQty}\\n`);\n return lines.join('\\n');\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { clientOrThrow, mcpJson, withErrorHandling, tool } from '../helpers';\nimport { ENDPOINTS, EndpointDef } from '../../client/endpoints';\nimport { normalizeStockCode, todayKst } from '../../utils/helpers';\n\nconst PERIOD_EP: Record<string, EndpointDef> = {\n day: ENDPOINTS.dailyChart,\n week: ENDPOINTS.weeklyChart,\n month: ENDPOINTS.monthlyChart,\n year: ENDPOINTS.yearlyChart,\n};\n\nexport function registerChartTools(server: McpServer): void {\n tool(\n server,\n 'get_chart',\n {\n description:\n 'Get OHLC chart data for a stock. Period charts (day/week/month/year) end at base date; tick/minute use an aggregation scope. Returns latest-first; use count to cap rows.',\n inputSchema: {\n code: z.string().describe('6-digit stock code'),\n timeframe: z\n .enum(['tick', 'minute', 'day', 'week', 'month', 'year'])\n .describe('Chart timeframe'),\n scope: z\n .string()\n .optional()\n .describe('Tick units (1/3/5/10/30) or minute interval (1/3/5/10/15/30/45/60); default 1'),\n date: z.string().optional().describe('Base date YYYYMMDD for period charts (default today)'),\n adjusted: z.boolean().optional().describe('Adjust for splits/rights (default true)'),\n count: z.number().min(1).max(900).optional().describe('Max rows to return (default 50)'),\n },\n },\n async ({ code, timeframe, scope, date, adjusted, count }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const stk = normalizeStockCode(code);\n const upd = adjusted === false ? '0' : '1';\n\n let def: EndpointDef;\n let body: Record<string, unknown>;\n if (timeframe === 'tick') {\n def = ENDPOINTS.tickChart;\n body = { stk_cd: stk, tic_scope: scope ?? '1', upd_stkpc_tp: upd };\n } else if (timeframe === 'minute') {\n def = ENDPOINTS.minuteChart;\n body = { stk_cd: stk, tic_scope: scope ?? '1', upd_stkpc_tp: upd };\n } else {\n def = PERIOD_EP[timeframe];\n body = { stk_cd: stk, base_dt: date ?? todayKst(), upd_stkpc_tp: upd };\n }\n\n const { data } = await client.callEndpoint(def, body);\n const rows: unknown[] = Array.isArray(data[def.listKey!]) ? data[def.listKey!] : [];\n return mcpJson({\n code: stk,\n timeframe,\n rows: rows.slice(0, count ?? 50),\n });\n }),\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { clientOrThrow, mcpJson, withErrorHandling, tool } from '../helpers';\nimport { ENDPOINTS } from '../../client/endpoints';\nimport { normalizeStockCode, todayKst } from '../../utils/helpers';\n\nexport function registerAccountTools(server: McpServer): void {\n tool(\n server,\n 'get_balance',\n {\n description:\n 'Get account evaluation balance: totals (purchase/eval/PnL/profit-rate) and per-holding detail. kt00018.',\n inputSchema: {\n exchange: z.enum(['KRX', 'NXT', '%']).optional().describe('Exchange filter (default KRX)'),\n },\n },\n async ({ exchange }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.balance, {\n qry_tp: '1',\n dmst_stex_tp: exchange ?? 'KRX',\n });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_deposit',\n {\n description: 'Get cash deposit detail: cash, orderable/withdrawable, D+2 settlement. kt00001.',\n },\n async () =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.deposit, { qry_tp: '3' });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_open_orders',\n {\n description: 'Get open / unfilled orders (optionally for one stock). ka10075.',\n inputSchema: { code: z.string().optional().describe('6-digit stock code to filter') },\n },\n async ({ code }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const body = code\n ? { all_stk_tp: '0', trde_tp: '0', stk_cd: normalizeStockCode(code), stex_tp: '0' }\n : { all_stk_tp: '1', trde_tp: '0', stk_cd: '', stex_tp: '0' };\n const { data } = await client.callEndpoint(ENDPOINTS.openOrders, body);\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_executions',\n {\n description: 'Get filled executions (optionally for one stock). ka10076.',\n inputSchema: { code: z.string().optional().describe('6-digit stock code to filter') },\n },\n async ({ code }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.executions, {\n stk_cd: code ? normalizeStockCode(code) : '',\n qry_tp: code ? '1' : '0',\n sell_tp: '0',\n ord_no: '',\n stex_tp: '0',\n });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_realized_pnl',\n {\n description: 'Get realized profit/loss for a stock over a date (or period, max 3 months). ka10072 / ka10073.',\n inputSchema: {\n code: z.string().describe('6-digit stock code'),\n start: z.string().optional().describe('Start date YYYYMMDD (default today)'),\n end: z.string().optional().describe('End date YYYYMMDD; if set, queries the period'),\n },\n },\n async ({ code, start, end }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const stk = normalizeStockCode(code);\n const { data } = end\n ? await client.callEndpoint(ENDPOINTS.realizedPlByPeriod, {\n stk_cd: stk,\n strt_dt: start ?? end,\n end_dt: end,\n })\n : await client.callEndpoint(ENDPOINTS.realizedPlByDate, {\n stk_cd: stk,\n strt_dt: start ?? todayKst(),\n });\n return mcpJson(data);\n }),\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { clientOrThrow, mcpJson, mcpText, withErrorHandling, currentEnv, tool } from '../helpers';\nimport { ENDPOINTS, EndpointDef } from '../../client/endpoints';\nimport { ORDER_TYPES, MARKET_ORDER_TYPES, ORDER_EXCHANGE_TYPES } from '../../config/constants';\nimport { normalizeStockCode } from '../../utils/helpers';\n\nfunction resolveType(type: string | undefined, price: string | undefined) {\n const trde_tp = type ?? (price ? '0' : '3');\n if (!ORDER_TYPES[trde_tp]) {\n throw new Error(`Invalid order type ${trde_tp}. Known: ${Object.keys(ORDER_TYPES).join(', ')}`);\n }\n if (MARKET_ORDER_TYPES.has(trde_tp)) {\n if (price) throw new Error('Market orders must not include a price.');\n return { trde_tp, ord_uv: '' };\n }\n if (!price) throw new Error(`Order type ${trde_tp} requires a price.`);\n return { trde_tp, ord_uv: price };\n}\n\nexport function registerOrderTools(server: McpServer): void {\n tool(\n server,\n 'place_order',\n {\n description:\n 'Place a buy or sell order. SAFETY: nothing is sent unless confirm=true; otherwise a preview is returned. On the \"real\" environment this trades REAL money. kt10000/kt10001 (or credit kt10006/kt10007).',\n inputSchema: {\n side: z.enum(['buy', 'sell']).describe('buy or sell'),\n code: z.string().describe('6-digit stock code'),\n qty: z.string().describe('Order quantity'),\n price: z.string().optional().describe('Limit price (won); omit for a market order'),\n type: z.string().optional().describe('Order type code trde_tp (default 0=limit / 3=market)'),\n condPrice: z.string().optional().describe('Trigger price for stop/conditional types (e.g. 28)'),\n exchange: z.enum(['KRX', 'NXT', 'SOR']).optional().describe('Exchange routing (default KRX)'),\n credit: z.boolean().optional().describe('Use a credit (margin) order'),\n creditDeal: z.enum(['33', '99']).optional().describe('Credit deal type (sell): 33=융자, 99=융자합'),\n loanDate: z.string().optional().describe('Credit loan date YYYYMMDD (some credit sells)'),\n confirm: z.boolean().optional().describe('Must be true to actually submit'),\n },\n },\n async ({ side, code, qty, price, type, condPrice, exchange, credit, creditDeal, loanDate, confirm }) =>\n withErrorHandling(async () => {\n const stk = normalizeStockCode(code);\n const { trde_tp, ord_uv } = resolveType(type, price);\n if (trde_tp === '28' && !condPrice) {\n throw new Error('Stop-limit orders (type 28) require condPrice.');\n }\n const ex = exchange ?? 'KRX';\n if (!ORDER_EXCHANGE_TYPES.includes(ex)) throw new Error(`Invalid exchange ${ex}`);\n const def: EndpointDef = credit\n ? side === 'buy'\n ? ENDPOINTS.creditBuy\n : ENDPOINTS.creditSell\n : side === 'buy'\n ? ENDPOINTS.buy\n : ENDPOINTS.sell;\n const body: Record<string, unknown> = {\n dmst_stex_tp: ex,\n stk_cd: stk,\n ord_qty: qty,\n ord_uv,\n trde_tp,\n cond_uv: condPrice ?? '',\n };\n if (credit && side === 'sell') {\n body.crd_deal_tp = creditDeal ?? '33';\n if (loanDate) body.crd_loan_dt = loanDate;\n }\n\n const preview = {\n willSubmit: confirm === true,\n env: currentEnv(),\n apiId: def.apiId,\n side,\n credit: Boolean(credit),\n stock: stk,\n qty,\n price: MARKET_ORDER_TYPES.has(trde_tp) ? 'MARKET' : ord_uv,\n orderType: `${trde_tp} (${ORDER_TYPES[trde_tp]})`,\n exchange: ex,\n };\n if (confirm !== true) {\n return mcpText(\n `PREVIEW ONLY — set confirm=true to submit.\\n${JSON.stringify(preview, null, 2)}`,\n );\n }\n const { data } = await clientOrThrow().callEndpoint(def, body);\n return mcpJson({ submitted: true, ...preview, result: data });\n }),\n );\n\n tool(\n server,\n 'modify_order',\n {\n description: 'Modify a resting order (new qty/price). Nothing is sent unless confirm=true. kt10002/kt10008.',\n inputSchema: {\n orderNo: z.string().describe('Original order number'),\n code: z.string().describe('6-digit stock code'),\n qty: z.string().describe('New quantity'),\n price: z.string().describe('New price'),\n condPrice: z.string().optional().describe('New trigger price for conditional/stop orders'),\n exchange: z.enum(['KRX', 'NXT', 'SOR']).optional().describe('Exchange (default KRX)'),\n credit: z.boolean().optional(),\n confirm: z.boolean().optional().describe('Must be true to actually submit'),\n },\n },\n async ({ orderNo, code, qty, price, condPrice, exchange, credit, confirm }) =>\n withErrorHandling(async () => {\n const stk = normalizeStockCode(code);\n const ex = exchange ?? 'KRX';\n const def = credit ? ENDPOINTS.creditModify : ENDPOINTS.modify;\n const body = {\n dmst_stex_tp: ex,\n orig_ord_no: orderNo,\n stk_cd: stk,\n mdfy_qty: qty,\n mdfy_uv: price,\n mdfy_cond_uv: condPrice ?? '',\n };\n const preview = { willSubmit: confirm === true, env: currentEnv(), apiId: def.apiId, orderNo, stock: stk, qty, price, exchange: ex };\n if (confirm !== true) {\n return mcpText(`PREVIEW ONLY — set confirm=true to submit.\\n${JSON.stringify(preview, null, 2)}`);\n }\n const { data } = await clientOrThrow().callEndpoint(def, body);\n return mcpJson({ submitted: true, ...preview, result: data });\n }),\n );\n\n tool(\n server,\n 'cancel_order',\n {\n description: 'Cancel a resting order (qty 0 = all). Nothing is sent unless confirm=true. kt10003/kt10009.',\n inputSchema: {\n orderNo: z.string().describe('Original order number'),\n code: z.string().describe('6-digit stock code'),\n qty: z.string().optional().describe('Quantity to cancel; 0 = all remaining (default 0)'),\n exchange: z.enum(['KRX', 'NXT', 'SOR']).optional().describe('Exchange (default KRX)'),\n credit: z.boolean().optional(),\n confirm: z.boolean().optional().describe('Must be true to actually submit'),\n },\n },\n async ({ orderNo, code, qty, exchange, credit, confirm }) =>\n withErrorHandling(async () => {\n const stk = normalizeStockCode(code);\n const ex = exchange ?? 'KRX';\n const def = credit ? ENDPOINTS.creditCancel : ENDPOINTS.cancel;\n const body = { dmst_stex_tp: ex, orig_ord_no: orderNo, stk_cd: stk, cncl_qty: qty ?? '0' };\n const preview = { willSubmit: confirm === true, env: currentEnv(), apiId: def.apiId, orderNo, stock: stk, qty: qty ?? '0 (all)', exchange: ex };\n if (confirm !== true) {\n return mcpText(`PREVIEW ONLY — set confirm=true to submit.\\n${JSON.stringify(preview, null, 2)}`);\n }\n const { data } = await clientOrThrow().callEndpoint(def, body);\n return mcpJson({ submitted: true, ...preview, result: data });\n }),\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { clientOrThrow, mcpJson, withErrorHandling, tool } from '../helpers';\nimport { ENDPOINTS, EndpointDef } from '../../client/endpoints';\n\nexport function registerRankingTools(server: McpServer): void {\n tool(\n server,\n 'get_ranking',\n {\n description:\n 'Get a market ranking list: gainers/losers (fluctuation), today volume, trade value (amount), volume surge, or previous-day volume.',\n inputSchema: {\n kind: z\n .enum(['fluctuation', 'volume', 'amount', 'surge', 'prev-volume'])\n .describe('Ranking type'),\n market: z.enum(['000', '001', '101']).optional().describe('000=all, 001=KOSPI, 101=KOSDAQ'),\n sort: z.string().optional().describe('Sort code (meaning varies per kind; default 1)'),\n exchange: z.enum(['1', '2', '3']).optional().describe('1=KRX, 2=NXT, 3=unified (default 3)'),\n },\n },\n async ({ kind, market, sort, exchange }) =>\n withErrorHandling(async () => {\n const mrkt_tp = market ?? '000';\n const stex_tp = exchange ?? '3';\n let def: EndpointDef;\n let body: Record<string, unknown>;\n switch (kind) {\n case 'fluctuation':\n def = ENDPOINTS.rankFluctuation;\n body = {\n mrkt_tp,\n sort_tp: sort ?? '1',\n trde_qty_cnd: '0000',\n stk_cnd: '0',\n crd_cnd: '0',\n updown_incls: '1',\n pric_cnd: '0',\n trde_prica_cnd: '0',\n stex_tp,\n };\n break;\n case 'volume':\n def = ENDPOINTS.rankVolume;\n body = {\n mrkt_tp,\n sort_tp: sort ?? '1',\n mang_stk_incls: '0',\n crd_tp: '0',\n trde_qty_tp: '0',\n pric_tp: '0',\n trde_prica_tp: '0',\n mrkt_open_tp: '0',\n stex_tp,\n };\n break;\n case 'amount':\n def = ENDPOINTS.rankTradeAmount;\n body = { mrkt_tp, mang_stk_incls: '1', stex_tp };\n break;\n case 'surge':\n def = ENDPOINTS.rankVolumeSurge;\n body = {\n mrkt_tp,\n sort_tp: sort ?? '1',\n tm_tp: '2',\n trde_qty_tp: '5',\n tm: '',\n stk_cnd: '0',\n pric_tp: '0',\n stex_tp,\n };\n break;\n default: // prev-volume\n def = ENDPOINTS.rankPrevVolume;\n body = { mrkt_tp, qry_tp: '1', rank_strt: '0', rank_end: '100', stex_tp };\n }\n const { data } = await clientOrThrow().callEndpoint(def, body);\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_sector',\n {\n description:\n 'Get sector/industry data: index price, constituent stock prices, all sector indices, or daily index history.',\n inputSchema: {\n kind: z.enum(['price', 'stocks', 'all', 'daily']).describe('Sector query type'),\n market: z.enum(['0', '1', '2']).optional().describe('0=KOSPI, 1=KOSDAQ, 2=KOSPI200'),\n code: z.string().optional().describe('Industry code inds_cd (default 001=종합KOSPI)'),\n },\n },\n async ({ kind, market, code }) =>\n withErrorHandling(async () => {\n const mrkt_tp = market ?? '0';\n const inds_cd = code ?? '001';\n let def: EndpointDef;\n let body: Record<string, unknown>;\n switch (kind) {\n case 'stocks':\n def = ENDPOINTS.sectorStocks;\n body = { mrkt_tp, inds_cd, stex_tp: '1' };\n break;\n case 'all':\n def = ENDPOINTS.sectorAllIndex;\n body = { inds_cd };\n break;\n case 'daily':\n def = ENDPOINTS.sectorDaily;\n body = { mrkt_tp, inds_cd };\n break;\n default: // price\n def = ENDPOINTS.sectorPrice;\n body = { mrkt_tp, inds_cd };\n }\n const { data } = await clientOrThrow().callEndpoint(def, body);\n return mcpJson(data);\n }),\n );\n}\n"],"mappings":"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","names":["path","server","server","import_zod","server","import_zod","server","import_zod","server","import_zod","server"]}
|
|
1
|
+
{"version":3,"sources":["../src/mcp.ts","../src/mcp/tools/market.ts","../src/config/constants.ts","../src/config/store.ts","../src/output/error.ts","../src/utils/helpers.ts","../src/client/api-client.ts","../src/mcp/helpers.ts","../src/client/endpoints.ts","../src/utils/format.ts","../src/utils/orderbook.ts","../src/mcp/tools/chart.ts","../src/mcp/tools/account.ts","../src/mcp/tools/order.ts","../src/mcp/tools/ranking.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { registerMarketTools } from './mcp/tools/market';\nimport { registerChartTools } from './mcp/tools/chart';\nimport { registerAccountTools } from './mcp/tools/account';\nimport { registerOrderTools } from './mcp/tools/order';\nimport { registerRankingTools } from './mcp/tools/ranking';\n\nconst server = new McpServer({\n name: 'kiwoom-mcp',\n version: '0.1.0',\n});\n\nregisterMarketTools(server);\nregisterChartTools(server);\nregisterAccountTools(server);\nregisterOrderTools(server);\nregisterRankingTools(server);\n\nconst transport = new StdioServerTransport();\nserver.connect(transport).catch((err) => {\n console.error('MCP server error:', err);\n process.exit(1);\n});\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { clientOrThrow, mcpJson, withErrorHandling, tool } from '../helpers';\nimport { ENDPOINTS } from '../../client/endpoints';\nimport { normalizeStockCode, todayKst } from '../../utils/helpers';\nimport { parseOrderbook } from '../../utils/orderbook';\n\nexport function registerMarketTools(server: McpServer): void {\n tool(\n server,\n 'get_stock_info',\n {\n description:\n 'Get Kiwoom stock fundamentals + current price for a Korean stock (name, price, change, PER/EPS/ROE/PBR/BPS, OHLC, limits). ka10001.',\n inputSchema: { code: z.string().describe('6-digit stock code, e.g. 005930 (삼성전자)') },\n },\n async ({ code }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.stockInfo, {\n stk_cd: normalizeStockCode(code),\n });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_price',\n {\n description: 'Get a rich current-price snapshot incl. top-of-book for a stock. ka10007.',\n inputSchema: { code: z.string().describe('6-digit stock code') },\n },\n async ({ code }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.priceTableInfo, {\n stk_cd: normalizeStockCode(code),\n });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_orderbook',\n {\n description: 'Get the 10-level bid/ask order book for a stock, parsed into ordered levels. ka10004.',\n inputSchema: { code: z.string().describe('6-digit stock code') },\n },\n async ({ code }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const stk = normalizeStockCode(code);\n const { data } = await client.callEndpoint(ENDPOINTS.orderbook, { stk_cd: stk });\n return mcpJson({ code: stk, ...parseOrderbook(data) });\n }),\n );\n\n tool(\n server,\n 'get_daily_price',\n {\n description: 'Get daily price history ending at a base date. ka10086.',\n inputSchema: {\n code: z.string().describe('6-digit stock code'),\n date: z.string().optional().describe('Base date YYYYMMDD (default today)'),\n },\n },\n async ({ code, date }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.dailyPrice, {\n stk_cd: normalizeStockCode(code),\n qry_dt: date ?? todayKst(),\n indc_tp: '0',\n });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_recent_trades',\n {\n description: 'Get recent tick-by-tick executions for a stock. ka10003.',\n inputSchema: { code: z.string().describe('6-digit stock code') },\n },\n async ({ code }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.stockTrades, {\n stk_cd: normalizeStockCode(code),\n });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'search_stocks',\n {\n description:\n 'Search the master stock list by name keyword or code prefix. Returns matching code/name/market. ka10099.',\n inputSchema: {\n keyword: z.string().describe('Name substring or code prefix to match'),\n market: z.enum(['0', '10']).optional().describe('0=KOSPI (default), 10=KOSDAQ'),\n limit: z.number().min(1).max(200).optional().describe('Max rows (default 30)'),\n },\n },\n async ({ keyword, market, limit }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(\n ENDPOINTS.stockList,\n { mrkt_tp: market ?? '0' },\n { paginate: true },\n );\n const rows: Array<Record<string, any>> = Array.isArray(data.list) ? data.list : [];\n const kw = keyword.toLowerCase();\n const matches = rows\n .filter(\n (r) =>\n String(r.name ?? '').toLowerCase().includes(kw) ||\n String(r.code ?? '').startsWith(keyword),\n )\n .slice(0, limit ?? 30)\n .map((r) => ({ code: r.code, name: r.name, market: r.marketName, sector: r.upName }));\n return mcpJson(matches);\n }),\n );\n}\n","export type Environment = 'real' | 'mock';\n\n/** API host per environment. */\nexport const BASE_URLS: Record<Environment, string> = {\n real: 'https://api.kiwoom.com',\n mock: 'https://mockapi.kiwoom.com',\n};\n\n/** Accepted aliases for the environment, including Korean labels. */\nexport const ENV_ALIASES: Record<string, Environment> = {\n real: 'real',\n prod: 'real',\n production: 'real',\n live: 'real',\n 실전: 'real',\n 실전투자: 'real',\n mock: 'mock',\n test: 'mock',\n paper: 'mock',\n demo: 'mock',\n 모의: 'mock',\n 모의투자: 'mock',\n};\n\n/** OAuth2 endpoints (relative to the environment base URL). */\nexport const TOKEN_PATH = '/oauth2/token';\nexport const REVOKE_PATH = '/oauth2/revoke';\n\n/** Config + token-cache storage. */\nexport const CONFIG_DIR_NAME = '.kiwoom-cli';\nexport const CONFIG_FILE_NAME = 'config.json';\nexport const TOKEN_FILE_NAME = 'token.json';\n\n/** Environment variables used as a fallback when the config file is unset. */\nexport const ENV_VARS = {\n appkey: 'KIWOOM_APPKEY',\n secretkey: 'KIWOOM_SECRETKEY',\n env: 'KIWOOM_ENV',\n} as const;\n\n/** Re-issue a cached token if it expires within this many milliseconds. */\nexport const TOKEN_REFRESH_BUFFER_MS = 60_000;\n\n/** HTTP request timeout. */\nexport const REQUEST_TIMEOUT_MS = 30_000;\n\n/**\n * `return_code` values that signal \"no matching data\" rather than a real error.\n * The API returns 20 ([2000] 관련자료가없습니다) for empty result sets; callers\n * should receive the (empty) payload, not an exception.\n */\nexport const SOFT_EMPTY_RETURN_CODES = new Set<number>([20]);\n\n/** Exchange routing for ORDER TRs (dmst_stex_tp): best-execution SOR allowed. */\nexport const ORDER_EXCHANGE_TYPES = ['KRX', 'NXT', 'SOR'] as const;\nexport type OrderExchangeType = (typeof ORDER_EXCHANGE_TYPES)[number];\n\n/** Exchange filter for ACCOUNT query TRs (dmst_stex_tp): % = all exchanges. */\nexport const ACCOUNT_EXCHANGE_TYPES = ['KRX', 'NXT', '%'] as const;\nexport type AccountExchangeType = (typeof ACCOUNT_EXCHANGE_TYPES)[number];\n\n/**\n * trde_tp (매매구분 / order-type) codes for buy/sell orders, from the Kiwoom\n * spec. NOTE: 7 = 최우선지정가 (the assignment's \"8\" is wrong). Market types\n * (3/13/23) must be sent with an empty ord_uv.\n */\nexport const ORDER_TYPES: Record<string, string> = {\n '0': '보통(지정가/limit)',\n '3': '시장가(market)',\n '5': '조건부지정가',\n '6': '최유리지정가',\n '7': '최우선지정가',\n '10': '보통(IOC)',\n '13': '시장가(IOC)',\n '16': '최유리(IOC)',\n '20': '보통(FOK)',\n '23': '시장가(FOK)',\n '26': '최유리(FOK)',\n '28': '스톱지정가(stop-limit)',\n '29': '중간가',\n '30': '중간가(IOC)',\n '31': '중간가(FOK)',\n '61': '장시작전시간외',\n '62': '시간외단일가',\n '81': '장마감후시간외',\n};\n\n/** trde_tp codes that are market-style (price must be empty). */\nexport const MARKET_ORDER_TYPES = new Set(['3', '13', '23']);\n\n/**\n * Maximum candles a single chart TR returns (one page). Larger `--count` /\n * `count` values are satisfied by paging on the response `cont-yn` / `next-key`\n * headers (handled by KiwoomClient.requestAll). Verified against the official\n * spec: tick/minute 900, day 600, week 300, month 240, year 30.\n */\nexport const CHART_PER_PAGE_CAP = {\n tick: 900,\n minute: 900,\n day: 600,\n week: 300,\n month: 240,\n year: 30,\n} as const;\n\nexport type ChartType = keyof typeof CHART_PER_PAGE_CAP;\n\n/**\n * Upper bound on the candle count a single command/tool invocation may request,\n * so a typo can't trigger an unbounded paginate loop. Counts above this are\n * clamped (CLI) or rejected by the schema (MCP).\n */\nexport const CHART_MAX_COUNT = 100000;\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport {\n CONFIG_DIR_NAME,\n CONFIG_FILE_NAME,\n TOKEN_FILE_NAME,\n ENV_VARS,\n ENV_ALIASES,\n Environment,\n} from './constants';\n\nexport interface CliConfig {\n env: Environment;\n appkey?: string;\n secretkey?: string;\n}\n\nexport interface CachedToken {\n token: string;\n /** Kiwoom expiry stamp, \"YYYYMMDDHHmmss\" in KST. */\n expiresDt: string;\n /** First 6 chars of the appkey the token was issued for (cache-invalidation guard). */\n appkeyHint: string;\n}\n\n/** token.json holds one cached token per environment. */\ntype TokenCache = Partial<Record<Environment, CachedToken>>;\n\nconst DEFAULT_CONFIG: CliConfig = { env: 'real' };\n\nfunction getConfigDir(): string {\n return path.join(os.homedir(), CONFIG_DIR_NAME);\n}\n\nfunction getConfigPath(): string {\n return path.join(getConfigDir(), CONFIG_FILE_NAME);\n}\n\nfunction getTokenPath(): string {\n return path.join(getConfigDir(), TOKEN_FILE_NAME);\n}\n\nfunction ensureConfigDir(): void {\n const dir = getConfigDir();\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { mode: 0o700, recursive: true });\n } else {\n // Tighten perms even if the dir pre-existed with looser permissions.\n try {\n fs.chmodSync(dir, 0o700);\n } catch {\n /* best effort */\n }\n }\n}\n\n/**\n * Write a secrets file with 0600 perms. `writeFileSync`'s mode only applies when\n * the file is newly created, so chmod afterward to tighten a pre-existing file\n * that may have looser permissions.\n */\nfunction writeSecure(filePath: string, data: string): void {\n fs.writeFileSync(filePath, data, { mode: 0o600 });\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n /* best effort */\n }\n}\n\n// ─── Config ───────────────────────────────────────────────────────────────\n\nexport function loadConfig(): CliConfig {\n try {\n const raw = fs.readFileSync(getConfigPath(), 'utf-8');\n return { ...DEFAULT_CONFIG, ...JSON.parse(raw) };\n } catch {\n return { ...DEFAULT_CONFIG };\n }\n}\n\nexport function saveConfig(partial: Partial<CliConfig>): void {\n ensureConfigDir();\n const current = loadConfig();\n const merged = { ...current, ...partial };\n writeSecure(getConfigPath(), JSON.stringify(merged, null, 2));\n}\n\n/**\n * Resolve the effective config: config file first, environment variables as a\n * fallback for any unset secret.\n */\nexport function getEffectiveConfig(): CliConfig {\n const disk = loadConfig();\n const envVal = process.env[ENV_VARS.env];\n const env: Environment =\n envVal && ENV_ALIASES[envVal.toLowerCase()]\n ? ENV_ALIASES[envVal.toLowerCase()]\n : disk.env;\n return {\n env,\n appkey: disk.appkey ?? process.env[ENV_VARS.appkey],\n secretkey: disk.secretkey ?? process.env[ENV_VARS.secretkey],\n };\n}\n\nexport function maskSecret(value: string | undefined): string {\n if (!value) return '(not set)';\n if (value.length <= 10) return '****';\n return value.slice(0, 6) + '...' + value.slice(-4);\n}\n\n// ─── Token cache ────────────────────────────────────────────────────────────\n\nfunction loadTokenCache(): TokenCache {\n try {\n return JSON.parse(fs.readFileSync(getTokenPath(), 'utf-8'));\n } catch {\n return {};\n }\n}\n\nexport function getCachedToken(env: Environment): CachedToken | undefined {\n return loadTokenCache()[env];\n}\n\nexport function saveCachedToken(env: Environment, token: CachedToken): void {\n ensureConfigDir();\n const cache = loadTokenCache();\n cache[env] = token;\n writeSecure(getTokenPath(), JSON.stringify(cache, null, 2));\n}\n\nexport function clearCachedToken(env?: Environment): void {\n if (!env) {\n try {\n fs.rmSync(getTokenPath());\n } catch {\n /* nothing to clear */\n }\n return;\n }\n const cache = loadTokenCache();\n delete cache[env];\n try {\n ensureConfigDir();\n writeSecure(getTokenPath(), JSON.stringify(cache, null, 2));\n } catch {\n /* ignore */\n }\n}\n\n/** Expose paths for diagnostics/tests. */\nexport const _paths = { getConfigDir, getConfigPath, getTokenPath };\n","/**\n * An error that carries a suggested recovery command, surfaced to the user as\n * a \"Try: ...\" hint.\n */\nexport class ActionableError extends Error {\n suggestedCommand?: string;\n\n constructor(message: string, suggestedCommand?: string) {\n super(message);\n this.name = 'ActionableError';\n this.suggestedCommand = suggestedCommand;\n }\n}\n\n/**\n * An error raised when the Kiwoom API returns a non-zero return_code.\n */\nexport class KiwoomApiError extends Error {\n returnCode: number;\n returnMsg: string;\n apiId?: string;\n\n constructor(returnCode: number, returnMsg: string, apiId?: string) {\n super(`[${returnCode}] ${returnMsg}`);\n this.name = 'KiwoomApiError';\n this.returnCode = returnCode;\n this.returnMsg = returnMsg;\n this.apiId = apiId;\n }\n}\n\nexport function handleError(err: unknown): never {\n if (err instanceof ActionableError) {\n console.error(`\\nError: ${err.message}`);\n if (err.suggestedCommand) {\n console.error(`\\nTry: ${err.suggestedCommand}`);\n }\n process.exit(1);\n }\n\n if (err instanceof KiwoomApiError) {\n console.error(`\\nError: ${err.returnMsg} (code ${err.returnCode})`);\n if (isAuthError(err.returnMsg) || err.returnCode === 3) {\n console.error(`\\nTry: kiwoom-cli config init`);\n }\n process.exit(1);\n }\n\n if (err instanceof Error) {\n let message = err.message;\n if (message.length > 500) {\n message = message.slice(0, 500) + '...';\n }\n if (isAuthError(message)) {\n console.error(`\\nError: ${message}`);\n console.error(`\\nTry: kiwoom-cli config init`);\n process.exit(1);\n }\n console.error(`\\nError: ${message}`);\n process.exit(1);\n }\n\n console.error(`\\nUnknown error:`, err);\n process.exit(1);\n}\n\nfunction isAuthError(message: string): boolean {\n return /unauthorized|forbidden|not authenticated|토큰|인증|appkey|access token|만료/i.test(\n message,\n );\n}\n\nexport function requireConfig(\n value: string | undefined,\n name: string,\n setCommand: string,\n): asserts value is string {\n if (!value) {\n throw new ActionableError(\n `${name} is not configured.`,\n `kiwoom-cli config set ${setCommand}`,\n );\n }\n}\n","import { ActionableError } from '../output/error';\n\n/** Parse an integer strictly, throwing on failure. */\nexport function parseIntStrict(value: string, name: string): number {\n const n = parseInt(value, 10);\n if (Number.isNaN(n)) {\n throw new Error(`Invalid ${name}: \"${value}\" is not a valid integer`);\n }\n return n;\n}\n\n/** Parse a float strictly, throwing on failure. */\nexport function parseFloatStrict(value: string, name: string): number {\n const n = parseFloat(value);\n if (Number.isNaN(n)) {\n throw new Error(`Invalid ${name}: \"${value}\" is not a valid number`);\n }\n return n;\n}\n\n/**\n * Normalize a Korean stock code to the 6-digit form the API expects.\n * Accepts \"005930\", \"A005930\" (response form), or \"005930_AL\" style suffixes.\n */\nexport function normalizeStockCode(code: string): string {\n const trimmed = code.trim().toUpperCase();\n const stripped = trimmed.replace(/^A(?=\\d)/, '');\n const base = stripped.split('_')[0];\n if (!/^\\d{6}$/.test(base)) {\n throw new ActionableError(\n `Invalid stock code \"${code}\". Expected a 6-digit code like 005930 (삼성전자).`,\n );\n }\n return base;\n}\n\n/**\n * Parse a Kiwoom expiry stamp (\"YYYYMMDDHHmmss\", Korea Standard Time) into\n * epoch milliseconds.\n */\nexport function parseKiwoomExpiry(expiresDt: string): number {\n const m = expiresDt.match(/^(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})$/);\n if (!m) return NaN;\n const [, y, mo, d, h, mi, s] = m;\n // KST is UTC+9, no DST.\n return Date.parse(`${y}-${mo}-${d}T${h}:${mi}:${s}+09:00`);\n}\n\n/** True if the token is expired or will expire within `bufferMs`. */\nexport function isTokenExpired(expiresDt: string, bufferMs = 0): boolean {\n const expiry = parseKiwoomExpiry(expiresDt);\n if (Number.isNaN(expiry)) return true;\n return Date.now() + bufferMs >= expiry;\n}\n\n/** Today's date in KST as \"YYYYMMDD\". */\nexport function todayKst(): string {\n const now = new Date(Date.now() + 9 * 3600 * 1000);\n return now.toISOString().slice(0, 10).replace(/-/g, '');\n}\n","import {\n BASE_URLS,\n TOKEN_PATH,\n REVOKE_PATH,\n TOKEN_REFRESH_BUFFER_MS,\n REQUEST_TIMEOUT_MS,\n SOFT_EMPTY_RETURN_CODES,\n Environment,\n} from '../config/constants';\nimport {\n getCachedToken,\n saveCachedToken,\n clearCachedToken,\n} from '../config/store';\nimport { isTokenExpired } from '../utils/helpers';\nimport { KiwoomApiError, ActionableError } from '../output/error';\nimport { EndpointDef } from './endpoints';\n\n/** Result of a TR request, with pagination metadata from the response headers. */\nexport interface TrResponse<T = Record<string, any>> {\n data: T;\n /** True when more pages are available (response header `cont-yn` === 'Y'). */\n contYn: boolean;\n /** Cursor to pass back as `next-key` to fetch the next page. */\n nextKey: string;\n}\n\nexport interface RequestOptions {\n /** Pass 'Y' (with nextKey) to continue a paginated query. */\n contYn?: string;\n nextKey?: string;\n /** Override the api-id header (defaults to the one in the registry call). */\n apiId?: string;\n}\n\nexport interface ClientOptions {\n env: Environment;\n appkey?: string;\n secretkey?: string;\n /** Explicit token; bypasses issuance/caching when provided. */\n token?: string;\n}\n\nfunction fetchWithTimeout(url: string, init: RequestInit): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n return fetch(url, { ...init, signal: controller.signal }).finally(() =>\n clearTimeout(timeoutId),\n );\n}\n\n/**\n * Kiwoom Securities REST API client.\n *\n * Handles OAuth2 token issuance + caching and routes every TR through a single\n * POST with the api-id header. Tokens are cached per environment on disk so\n * separate CLI invocations reuse them until expiry.\n */\nexport class KiwoomClient {\n private env: Environment;\n private baseUrl: string;\n private appkey?: string;\n private secretkey?: string;\n private tokenOverride?: string;\n private memoToken?: string;\n\n constructor(opts: ClientOptions) {\n this.env = opts.env;\n this.baseUrl = BASE_URLS[opts.env];\n if (!this.baseUrl) {\n throw new Error(`Invalid environment: ${opts.env}. Use: real, mock`);\n }\n this.appkey = opts.appkey;\n this.secretkey = opts.secretkey;\n this.tokenOverride = opts.token;\n }\n\n getEnv(): Environment {\n return this.env;\n }\n\n /** Ensure a valid token exists (issuing + caching as needed) and return it. */\n async authenticate(): Promise<string> {\n return this.getToken();\n }\n\n // ─── OAuth2 ───────────────────────────────────────────────────────────────\n\n /** Issue a fresh access token from the app key + secret key. */\n async issueToken(): Promise<{ token: string; expiresDt: string }> {\n if (!this.appkey || !this.secretkey) {\n throw new ActionableError(\n 'App key and secret key are required to issue a token.',\n 'kiwoom-cli config init',\n );\n }\n const res = await fetchWithTimeout(`${this.baseUrl}${TOKEN_PATH}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json;charset=UTF-8' },\n body: JSON.stringify({\n grant_type: 'client_credentials',\n appkey: this.appkey,\n secretkey: this.secretkey,\n }),\n });\n const body = (await res.json().catch(() => ({}))) as Record<string, any>;\n if (!res.ok || body.return_code !== 0 || !body.token) {\n throw new KiwoomApiError(\n body.return_code ?? res.status,\n body.return_msg ?? `Token request failed (HTTP ${res.status})`,\n );\n }\n return { token: body.token, expiresDt: body.expires_dt };\n }\n\n /** Revoke an access token (defaults to the cached/override token). */\n async revokeToken(token?: string): Promise<Record<string, any>> {\n if (!this.appkey || !this.secretkey) {\n throw new ActionableError(\n 'App key and secret key are required to revoke a token.',\n 'kiwoom-cli config init',\n );\n }\n const target = token ?? this.tokenOverride ?? getCachedToken(this.env)?.token;\n if (!target) {\n throw new ActionableError('No token to revoke.');\n }\n const res = await fetchWithTimeout(`${this.baseUrl}${REVOKE_PATH}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json;charset=UTF-8' },\n body: JSON.stringify({\n appkey: this.appkey,\n secretkey: this.secretkey,\n token: target,\n }),\n });\n const body = (await res.json().catch(() => ({}))) as Record<string, any>;\n if (!res.ok || body.return_code !== 0) {\n throw new KiwoomApiError(\n body.return_code ?? res.status,\n body.return_msg ?? `Token revoke failed (HTTP ${res.status})`,\n );\n }\n clearCachedToken(this.env);\n return body;\n }\n\n /**\n * Return a valid token, reusing the cache when possible and issuing+caching a\n * new one when missing or near expiry.\n */\n private async getToken(): Promise<string> {\n if (this.tokenOverride) return this.tokenOverride;\n if (this.memoToken) return this.memoToken;\n\n const cached = getCachedToken(this.env);\n const hint = this.appkey?.slice(0, 6);\n if (\n cached &&\n cached.appkeyHint === hint &&\n !isTokenExpired(cached.expiresDt, TOKEN_REFRESH_BUFFER_MS)\n ) {\n this.memoToken = cached.token;\n return cached.token;\n }\n\n const { token, expiresDt } = await this.issueToken();\n this.memoToken = token;\n if (hint) {\n saveCachedToken(this.env, { token, expiresDt, appkeyHint: hint });\n }\n return token;\n }\n\n // ─── Generic TR request ──────────────────────────────────────────────────\n\n /**\n * Execute a TR. `apiId` is sent in the api-id header; `path` is the category\n * route (e.g. /api/dostk/stkinfo). Returns the parsed body plus pagination.\n */\n async request<T = Record<string, any>>(\n apiId: string,\n path: string,\n body: Record<string, unknown> = {},\n options: RequestOptions = {},\n ): Promise<TrResponse<T>> {\n const send = async (token: string): Promise<Response> =>\n fetchWithTimeout(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json;charset=UTF-8',\n authorization: `Bearer ${token}`,\n 'api-id': options.apiId ?? apiId,\n 'cont-yn': options.contYn ?? 'N',\n 'next-key': options.nextKey ?? '',\n },\n body: JSON.stringify(body),\n });\n\n let res = await send(await this.getToken());\n\n // Token might have been revoked/expired server-side: clear and retry once.\n if (res.status === 401 && !this.tokenOverride) {\n clearCachedToken(this.env);\n this.memoToken = undefined;\n res = await send(await this.getToken());\n }\n\n const payload = (await res.json().catch(() => ({}))) as Record<string, any>;\n if (!res.ok) {\n throw new KiwoomApiError(\n payload.return_code ?? res.status,\n payload.return_msg ?? `Request failed (HTTP ${res.status})`,\n apiId,\n );\n }\n if (\n typeof payload.return_code === 'number' &&\n payload.return_code !== 0 &&\n !SOFT_EMPTY_RETURN_CODES.has(payload.return_code)\n ) {\n throw new KiwoomApiError(payload.return_code, payload.return_msg ?? 'Error', apiId);\n }\n\n return {\n data: payload as T,\n contYn: res.headers.get('cont-yn') === 'Y',\n nextKey: res.headers.get('next-key') ?? '',\n };\n }\n\n /**\n * Execute an endpoint from the registry. When `paginate` is set and the\n * endpoint declares a listKey, all pages are fetched and concatenated.\n */\n async callEndpoint<T = Record<string, any>>(\n def: EndpointDef,\n body: Record<string, unknown> = {},\n opts: { paginate?: boolean; contYn?: string; nextKey?: string; maxPages?: number } = {},\n ): Promise<TrResponse<T>> {\n if (opts.paginate && def.listKey) {\n const data = await this.requestAll<T>(def.apiId, def.path, body, def.listKey, opts.maxPages);\n return { data, contYn: false, nextKey: '' };\n }\n return this.request<T>(def.apiId, def.path, body, {\n contYn: opts.contYn,\n nextKey: opts.nextKey,\n });\n }\n\n /**\n * Fetch all pages of a TR, concatenating the array under `listKey`.\n * Caps at `maxPages` to avoid runaway loops (default 100 — high enough for\n * large chart pulls; callers pass a tighter bound when they know how many\n * pages a target row count needs).\n */\n async requestAll<T = Record<string, any>>(\n apiId: string,\n path: string,\n body: Record<string, unknown>,\n listKey: string,\n maxPages = 100,\n ): Promise<T> {\n let page = await this.request<Record<string, any>>(apiId, path, body);\n const acc = Array.isArray(page.data[listKey]) ? [...page.data[listKey]] : [];\n let pages = 1;\n while (page.contYn && page.nextKey && pages < maxPages) {\n page = await this.request<Record<string, any>>(apiId, path, body, {\n contYn: 'Y',\n nextKey: page.nextKey,\n });\n if (Array.isArray(page.data[listKey])) acc.push(...page.data[listKey]);\n pages += 1;\n }\n return { ...page.data, [listKey]: acc } as T;\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { ZodRawShape } from 'zod';\nimport { KiwoomClient } from '../client/api-client';\nimport { getEffectiveConfig } from '../config/store';\n\nexport function mcpText(text: string) {\n return { content: [{ type: 'text' as const, text }] };\n}\n\nexport function mcpJson(data: unknown) {\n return mcpText(JSON.stringify(data, null, 2));\n}\n\nexport function mcpError(message: string) {\n return {\n content: [{ type: 'text' as const, text: `ERROR: ${message}` }],\n isError: true,\n };\n}\n\nexport function createClient():\n | { client: KiwoomClient }\n | { error: ReturnType<typeof mcpError> } {\n const config = getEffectiveConfig();\n if (!config.appkey || !config.secretkey) {\n return {\n error: mcpError(\n 'App key / secret key not configured. Run: kiwoom-cli config init',\n ),\n };\n }\n return {\n client: new KiwoomClient({\n env: config.env,\n appkey: config.appkey,\n secretkey: config.secretkey,\n }),\n };\n}\n\n/** Build a client or throw — convenient inside withErrorHandling(). */\nexport function clientOrThrow(): KiwoomClient {\n const config = getEffectiveConfig();\n if (!config.appkey || !config.secretkey) {\n throw new Error('App key / secret key not configured. Run: kiwoom-cli config init');\n }\n return new KiwoomClient({\n env: config.env,\n appkey: config.appkey,\n secretkey: config.secretkey,\n });\n}\n\n/** Current environment ('real' | 'mock'), for order safety checks. */\nexport function currentEnv(): string {\n return getEffectiveConfig().env;\n}\n\n/**\n * Register an MCP tool. Thin wrapper over server.registerTool that fixes the\n * SDK's deep generic inference (zod inputSchema → TS2589) by erasing the\n * shape generic at the call boundary. Runtime validation is unaffected.\n */\nexport function tool(\n server: McpServer,\n name: string,\n config: { description: string; inputSchema?: ZodRawShape },\n handler: (args: any) => Promise<ReturnType<typeof mcpText> | ReturnType<typeof mcpError>>,\n): void {\n (server.registerTool as any)(name, config, handler);\n}\n\nexport async function withErrorHandling(\n fn: () => Promise<ReturnType<typeof mcpText>>,\n): Promise<ReturnType<typeof mcpText> | ReturnType<typeof mcpError>> {\n try {\n return await fn();\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return mcpError(message.slice(0, 500));\n }\n}\n","/**\n * Registry of every Kiwoom TR this CLI exposes — the single source of truth for\n * api-id, route, response list key, and whether the call places/modifies orders.\n *\n * All specs were live-verified against https://api.kiwoom.com (read TRs) or\n * taken from the official spec + typed community wrappers (order TRs, which are\n * never called live for safety).\n */\n\nexport const PATHS = {\n stkinfo: '/api/dostk/stkinfo',\n mrkcond: '/api/dostk/mrkcond',\n chart: '/api/dostk/chart',\n acnt: '/api/dostk/acnt',\n ordr: '/api/dostk/ordr',\n crdordr: '/api/dostk/crdordr',\n rkinfo: '/api/dostk/rkinfo',\n sect: '/api/dostk/sect',\n} as const;\n\nexport interface EndpointDef {\n /** api-id header value, e.g. \"ka10001\". */\n apiId: string;\n /** Category route. */\n path: string;\n /** Korean TR name. */\n korean: string;\n /** Array field in the response payload, if the TR returns a list. */\n listKey?: string;\n /** True for order place/modify/cancel TRs (real-money writes). */\n isWrite?: boolean;\n}\n\nexport const ENDPOINTS = {\n // ── Stock info (종목정보) ──────────────────────────────────────────────────\n stockInfo: { apiId: 'ka10001', path: PATHS.stkinfo, korean: '주식기본정보요청' },\n tradingMembers: { apiId: 'ka10002', path: PATHS.stkinfo, korean: '주식거래원요청' },\n stockTrades: { apiId: 'ka10003', path: PATHS.stkinfo, korean: '체결정보요청', listKey: 'cntr_infr' },\n watchlist: { apiId: 'ka10095', path: PATHS.stkinfo, korean: '관심종목정보요청', listKey: 'atn_stk_infr' },\n stockList: { apiId: 'ka10099', path: PATHS.stkinfo, korean: '종목정보 리스트', listKey: 'list' },\n stockInfoSingle: { apiId: 'ka10100', path: PATHS.stkinfo, korean: '종목정보 조회' },\n sectorCodeList: { apiId: 'ka10101', path: PATHS.stkinfo, korean: '업종코드 리스트', listKey: 'list' },\n creditTrend: { apiId: 'ka10013', path: PATHS.stkinfo, korean: '신용매매동향요청', listKey: 'crd_trde_trend' },\n\n // ── Market quote (시세) ────────────────────────────────────────────────────\n orderbook: { apiId: 'ka10004', path: PATHS.mrkcond, korean: '주식호가요청' },\n quoteSnapshot: { apiId: 'ka10006', path: PATHS.mrkcond, korean: '주식시분요청' },\n priceTableInfo: { apiId: 'ka10007', path: PATHS.mrkcond, korean: '시세표성정보요청' },\n dailyPrice: { apiId: 'ka10086', path: PATHS.mrkcond, korean: '일별주가요청', listKey: 'daly_stkpc' },\n instTradedStocks: { apiId: 'ka10044', path: PATHS.mrkcond, korean: '일별기관매매종목요청', listKey: 'daly_orgn_trde_stk' },\n instForeignTrend: { apiId: 'ka10045', path: PATHS.mrkcond, korean: '종목별기관매매추이요청', listKey: 'stk_orgn_trde_trnsn' },\n strengthByTime: { apiId: 'ka10046', path: PATHS.mrkcond, korean: '체결강도시간별요청', listKey: 'cntr_str_tm' },\n strengthByDay: { apiId: 'ka10047', path: PATHS.mrkcond, korean: '체결강도일별요청', listKey: 'cntr_str_daly' },\n afterHoursOrderbook: { apiId: 'ka10087', path: PATHS.mrkcond, korean: '시간외단일가요청' },\n\n // ── Chart (차트) ───────────────────────────────────────────────────────────\n tickChart: { apiId: 'ka10079', path: PATHS.chart, korean: '주식틱차트조회', listKey: 'stk_tic_chart_qry' },\n minuteChart: { apiId: 'ka10080', path: PATHS.chart, korean: '주식분봉차트조회', listKey: 'stk_min_pole_chart_qry' },\n dailyChart: { apiId: 'ka10081', path: PATHS.chart, korean: '주식일봉차트조회', listKey: 'stk_dt_pole_chart_qry' },\n // NOTE: weekly list key is the doubled \"stk_stk_...\" — verified live, not a typo.\n weeklyChart: { apiId: 'ka10082', path: PATHS.chart, korean: '주식주봉차트조회', listKey: 'stk_stk_pole_chart_qry' },\n monthlyChart: { apiId: 'ka10083', path: PATHS.chart, korean: '주식월봉차트조회', listKey: 'stk_mth_pole_chart_qry' },\n yearlyChart: { apiId: 'ka10094', path: PATHS.chart, korean: '주식년봉차트조회', listKey: 'stk_yr_pole_chart_qry' },\n\n // ── Account (계좌) ─────────────────────────────────────────────────────────\n balance: { apiId: 'kt00018', path: PATHS.acnt, korean: '계좌평가잔고내역요청', listKey: 'acnt_evlt_remn_indv_tot' },\n deposit: { apiId: 'kt00001', path: PATHS.acnt, korean: '예수금상세현황요청', listKey: 'stk_entr_prst' },\n evalStatus: { apiId: 'kt00004', path: PATHS.acnt, korean: '계좌평가현황요청', listKey: 'stk_acnt_evlt_prst' },\n settledBalance: { apiId: 'kt00005', path: PATHS.acnt, korean: '체결잔고요청', listKey: 'stk_cntr_remn' },\n orderDetail: { apiId: 'kt00007', path: PATHS.acnt, korean: '계좌별주문체결내역상세요청', listKey: 'acnt_ord_cntr_prps_dtl' },\n orderStatus: { apiId: 'kt00009', path: PATHS.acnt, korean: '계좌별주문체결현황요청', listKey: 'acnt_ord_cntr_prst' },\n openOrders: { apiId: 'ka10075', path: PATHS.acnt, korean: '미체결요청', listKey: 'oso' },\n executions: { apiId: 'ka10076', path: PATHS.acnt, korean: '체결요청', listKey: 'cntr' },\n realizedPlByDate: { apiId: 'ka10072', path: PATHS.acnt, korean: '일자별종목별실현손익요청_일자', listKey: 'dt_stk_div_rlzt_pl' },\n realizedPlByPeriod: { apiId: 'ka10073', path: PATHS.acnt, korean: '일자별종목별실현손익요청_기간', listKey: 'dt_stk_rlzt_pl' },\n tradeJournal: { apiId: 'ka10170', path: PATHS.acnt, korean: '당일매매일지요청', listKey: 'tdy_trde_diary' },\n dailyReturn: { apiId: 'kt00016', path: PATHS.acnt, korean: '일별계좌수익률상세현황요청' },\n\n // ── Order (주문) — WRITE, real money ──────────────────────────────────────\n buy: { apiId: 'kt10000', path: PATHS.ordr, korean: '주식 매수주문', isWrite: true },\n sell: { apiId: 'kt10001', path: PATHS.ordr, korean: '주식 매도주문', isWrite: true },\n modify: { apiId: 'kt10002', path: PATHS.ordr, korean: '주식 정정주문', isWrite: true },\n cancel: { apiId: 'kt10003', path: PATHS.ordr, korean: '주식 취소주문', isWrite: true },\n creditBuy: { apiId: 'kt10006', path: PATHS.crdordr, korean: '신용 매수주문', isWrite: true },\n creditSell: { apiId: 'kt10007', path: PATHS.crdordr, korean: '신용 매도주문', isWrite: true },\n creditModify: { apiId: 'kt10008', path: PATHS.crdordr, korean: '신용 정정주문', isWrite: true },\n creditCancel: { apiId: 'kt10009', path: PATHS.crdordr, korean: '신용 취소주문', isWrite: true },\n\n // ── Ranking (순위정보) ─────────────────────────────────────────────────────\n rankFluctuation: { apiId: 'ka10027', path: PATHS.rkinfo, korean: '전일대비등락률순위요청', listKey: 'pred_pre_flu_rt_upper' },\n rankVolume: { apiId: 'ka10030', path: PATHS.rkinfo, korean: '당일거래량상위요청', listKey: 'tdy_trde_qty_upper' },\n rankTradeAmount: { apiId: 'ka10032', path: PATHS.rkinfo, korean: '거래대금상위요청', listKey: 'trde_prica_upper' },\n rankVolumeSurge: { apiId: 'ka10023', path: PATHS.rkinfo, korean: '거래량급증요청', listKey: 'trde_qty_sdnin' },\n rankPrevVolume: { apiId: 'ka10031', path: PATHS.rkinfo, korean: '전일거래량상위요청', listKey: 'pred_trde_qty_upper' },\n\n // ── Sector / industry (업종) ───────────────────────────────────────────────\n sectorPrice: { apiId: 'ka20001', path: PATHS.sect, korean: '업종현재가요청', listKey: 'inds_cur_prc_tm' },\n sectorStocks: { apiId: 'ka20002', path: PATHS.sect, korean: '업종별주가요청', listKey: 'inds_stkpc' },\n sectorAllIndex: { apiId: 'ka20003', path: PATHS.sect, korean: '전업종지수요청', listKey: 'all_inds_idex' },\n sectorDaily: { apiId: 'ka20009', path: PATHS.sect, korean: '업종현재가 일별요청', listKey: 'inds_cur_prc_daly_rept' },\n} satisfies Record<string, EndpointDef>;\n\nexport type EndpointKey = keyof typeof ENDPOINTS;\n","/**\n * Kiwoom value normalization.\n *\n * The REST API returns numbers as strings: integers are often zero-padded to a\n * fixed width (\"000000019471143\"), and price/percent fields carry a leading\n * sign that encodes direction (\"+0.39\", \"-350000\"). These helpers turn those\n * wire values into something readable without losing the sign.\n */\n\n// Kiwoom occasionally double-signs delta fields, e.g. \"--3405260\" (negative\n// delta) or \"++100\"; treat a leading run of signs as one effective sign where\n// any '-' makes it negative.\nconst NUMERIC_RE = /^([+-]*)0*(\\d+)(\\.\\d+)?$/;\n\n/**\n * Strip zero-padding while preserving sign and decimals.\n * \"000000019471143\" -> \"19471143\", \"-00000001485445\" -> \"-1485445\",\n * \"+0.39\" -> \"+0.39\", \"--3405260\" -> \"-3405260\", \"000000000000000\" -> \"0\".\n * Non-numeric strings (codes, dates-as-text, \"KRX\") are returned unchanged.\n */\nexport function unpad(value: string | number | null | undefined): string {\n if (value === null || value === undefined) return '';\n const str = String(value).trim();\n const m = str.match(NUMERIC_RE);\n if (!m) return str;\n const sign = m[1].includes('-') ? '-' : m[1].includes('+') ? '+' : '';\n const intPart = m[2].replace(/^0+(?=\\d)/, '');\n return `${sign}${intPart}${m[3] ?? ''}`;\n}\n\n/** Parse a Kiwoom numeric string into a JS number (sign + padding aware). */\nexport function toNumber(value: string | number | null | undefined): number {\n if (value === null || value === undefined || value === '') return NaN;\n const cleaned = unpad(value).replace(/^\\+/, '');\n // Guard sign-only/empty results so \"+\" doesn't coerce to 0.\n if (cleaned === '' || cleaned === '-' || cleaned === '+') return NaN;\n const n = Number(cleaned);\n return Number.isNaN(n) ? NaN : n;\n}\n\n/** Group an integer/decimal string with thousands separators, keeping sign. */\nexport function withCommas(value: string): string {\n const m = value.match(/^([+-]?)(\\d+)(\\.\\d+)?$/);\n if (!m) return value;\n const grouped = m[2].replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n return `${m[1]}${grouped}${m[3] ?? ''}`;\n}\n\n/** Format a (possibly zero-padded) won amount as \"19,471,143\". */\nexport function won(value: string | number | null | undefined): string {\n return withCommas(unpad(value));\n}\n\n/**\n * Format a market price. Kiwoom prefixes quote/chart prices with a direction\n * sign (e.g. \"-353750\" = price 353,750, trading below prior close); that sign\n * is NOT part of the value, so it is dropped and the magnitude is grouped.\n */\nexport function price(value: string | number | null | undefined): string {\n return withCommas(unpad(value).replace(/^[+-]/, ''));\n}\n\n/**\n * Format a Kiwoom date/time stamp for display.\n * \"20260622135400\" -> \"2026-06-22 13:54:00\"\n * \"20260622\" -> \"2026-06-22\"\n * \"135400\" -> \"13:54:00\"\n */\nexport function formatStamp(value: string | null | undefined): string {\n if (!value) return '';\n const s = String(value).trim();\n if (/^\\d{14}$/.test(s)) {\n return `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)} ${s.slice(8, 10)}:${s.slice(10, 12)}:${s.slice(12, 14)}`;\n }\n if (/^\\d{8}$/.test(s)) {\n return `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}`;\n }\n if (/^\\d{6}$/.test(s)) {\n return `${s.slice(0, 2)}:${s.slice(2, 4)}:${s.slice(4, 6)}`;\n }\n return s;\n}\n\n/**\n * Apply a set of formatters to selected keys of an object (shallow).\n * Used by table commands to pretty-print known numeric fields.\n */\nexport function formatFields<T extends Record<string, unknown>>(\n row: T,\n formatters: Partial<Record<keyof T | string, (v: string) => string>>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = { ...row };\n for (const [key, fn] of Object.entries(formatters)) {\n if (fn && out[key] !== undefined && out[key] !== null) {\n out[key] = fn(String(out[key]));\n }\n }\n return out;\n}\n","import { unpad, formatStamp } from './format';\n\nexport interface BookLevel {\n level: number;\n price: string;\n qty: string;\n}\n\nexport interface ParsedBook {\n baseTime: string;\n asks: BookLevel[]; // highest ask first (level 10 → 1)\n bids: BookLevel[]; // best bid first (level 1 → 10)\n totalAskQty: string;\n totalBidQty: string;\n}\n\n/**\n * Parse a ka10004 (주식호가요청) payload into ordered bid/ask levels.\n *\n * The field naming is irregular: level-1 quotes use the `*_fpr_*` (최우선)\n * keys, while levels 2–10 use `*_{N}th_pre_*`.\n */\nexport function parseOrderbook(d: Record<string, any>): ParsedBook {\n const asks: BookLevel[] = [];\n for (let n = 10; n >= 2; n--) {\n asks.push({\n level: n,\n price: unpad(d[`sel_${n}th_pre_bid`]),\n qty: unpad(d[`sel_${n}th_pre_req`]),\n });\n }\n asks.push({ level: 1, price: unpad(d.sel_fpr_bid), qty: unpad(d.sel_fpr_req) });\n\n const bids: BookLevel[] = [];\n bids.push({ level: 1, price: unpad(d.buy_fpr_bid), qty: unpad(d.buy_fpr_req) });\n for (let n = 2; n <= 10; n++) {\n bids.push({\n level: n,\n price: unpad(d[`buy_${n}th_pre_bid`]),\n qty: unpad(d[`buy_${n}th_pre_req`]),\n });\n }\n\n return {\n baseTime: formatStamp(d.bid_req_base_tm),\n asks,\n bids,\n totalAskQty: unpad(d.tot_sel_req),\n totalBidQty: unpad(d.tot_buy_req),\n };\n}\n\n/** Render a parsed book as a compact two-sided ladder for the terminal. */\nexport function renderOrderbook(book: ParsedBook, title: string): string {\n const lines: string[] = [];\n lines.push(`\\n Order Book: ${title} (${book.baseTime})\\n`);\n lines.push(' ── Asks ──');\n for (const a of book.asks) {\n lines.push(` ${a.price.padStart(12)} ${a.qty.padStart(12)}`);\n }\n lines.push(' ─────────');\n for (const b of book.bids) {\n lines.push(` ${b.price.padStart(12)} ${b.qty.padStart(12)}`);\n }\n lines.push(' ── Bids ──');\n lines.push(`\\n Total ask qty: ${book.totalAskQty} Total bid qty: ${book.totalBidQty}\\n`);\n return lines.join('\\n');\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { clientOrThrow, mcpJson, withErrorHandling, tool } from '../helpers';\nimport { ENDPOINTS, EndpointDef } from '../../client/endpoints';\nimport { normalizeStockCode, todayKst } from '../../utils/helpers';\nimport { CHART_PER_PAGE_CAP, ChartType } from '../../config/constants';\n\nconst PERIOD_EP: Record<string, EndpointDef> = {\n day: ENDPOINTS.dailyChart,\n week: ENDPOINTS.weeklyChart,\n month: ENDPOINTS.monthlyChart,\n year: ENDPOINTS.yearlyChart,\n};\n\nconst DEFAULT_COUNT = 50;\nconst MAX_COUNT = 100000;\n\nexport function registerChartTools(server: McpServer): void {\n tool(\n server,\n 'get_chart',\n {\n description:\n 'Get OHLC chart data for a stock. Period charts (day/week/month/year) end at base date; tick/minute use an aggregation scope. Returns latest-first; use count to cap rows. ' +\n 'Per-request caps: tick/minute 900, day 600, week 300, month 240, year 30. When count exceeds the cap the tool auto-paginates (cont-yn/next-key) and returns up to count rows.',\n inputSchema: {\n code: z.string().describe('6-digit stock code'),\n timeframe: z\n .enum(['tick', 'minute', 'day', 'week', 'month', 'year'])\n .describe('Chart timeframe'),\n scope: z\n .string()\n .optional()\n .describe('Tick units (1/3/5/10/30) or minute interval (1/3/5/10/15/30/45/60); default 1'),\n date: z.string().optional().describe('Base date YYYYMMDD for period charts (default today)'),\n adjusted: z.boolean().optional().describe('Adjust for splits/rights (default true)'),\n count: z\n .number()\n .min(1)\n .max(MAX_COUNT)\n .optional()\n .describe(\n 'Max rows to return (default 50). Exceeding the per-request cap (tick/min 900, day 600, week 300, month 240, year 30) auto-paginates.',\n ),\n },\n },\n async ({ code, timeframe, scope, date, adjusted, count }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const stk = normalizeStockCode(code);\n const upd = adjusted === false ? '0' : '1';\n const want = count ?? DEFAULT_COUNT;\n\n let def: EndpointDef;\n let body: Record<string, unknown>;\n if (timeframe === 'tick') {\n def = ENDPOINTS.tickChart;\n body = { stk_cd: stk, tic_scope: scope ?? '1', upd_stkpc_tp: upd };\n } else if (timeframe === 'minute') {\n def = ENDPOINTS.minuteChart;\n body = { stk_cd: stk, tic_scope: scope ?? '1', upd_stkpc_tp: upd };\n } else {\n def = PERIOD_EP[timeframe];\n body = { stk_cd: stk, base_dt: date ?? todayKst(), upd_stkpc_tp: upd };\n }\n\n const cap = CHART_PER_PAGE_CAP[timeframe as ChartType];\n const paginate = want > cap;\n const maxPages = paginate ? Math.max(1, Math.ceil(want / cap)) : 1;\n\n const { data } = await client.callEndpoint(def, body, { paginate, maxPages });\n const rows: unknown[] = Array.isArray(data[def.listKey!]) ? data[def.listKey!] : [];\n return mcpJson({\n code: stk,\n timeframe,\n rows: rows.slice(0, want),\n });\n }),\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { clientOrThrow, mcpJson, withErrorHandling, tool } from '../helpers';\nimport { ENDPOINTS } from '../../client/endpoints';\nimport { normalizeStockCode, todayKst } from '../../utils/helpers';\n\nexport function registerAccountTools(server: McpServer): void {\n tool(\n server,\n 'get_balance',\n {\n description:\n 'Get account evaluation balance: totals (purchase/eval/PnL/profit-rate) and per-holding detail. kt00018.',\n inputSchema: {\n exchange: z.enum(['KRX', 'NXT', '%']).optional().describe('Exchange filter (default KRX)'),\n },\n },\n async ({ exchange }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.balance, {\n qry_tp: '1',\n dmst_stex_tp: exchange ?? 'KRX',\n });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_deposit',\n {\n description: 'Get cash deposit detail: cash, orderable/withdrawable, D+2 settlement. kt00001.',\n },\n async () =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.deposit, { qry_tp: '3' });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_open_orders',\n {\n description: 'Get open / unfilled orders (optionally for one stock). ka10075.',\n inputSchema: { code: z.string().optional().describe('6-digit stock code to filter') },\n },\n async ({ code }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const body = code\n ? { all_stk_tp: '0', trde_tp: '0', stk_cd: normalizeStockCode(code), stex_tp: '0' }\n : { all_stk_tp: '1', trde_tp: '0', stk_cd: '', stex_tp: '0' };\n const { data } = await client.callEndpoint(ENDPOINTS.openOrders, body);\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_executions',\n {\n description: 'Get filled executions (optionally for one stock). ka10076.',\n inputSchema: { code: z.string().optional().describe('6-digit stock code to filter') },\n },\n async ({ code }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const { data } = await client.callEndpoint(ENDPOINTS.executions, {\n stk_cd: code ? normalizeStockCode(code) : '',\n qry_tp: code ? '1' : '0',\n sell_tp: '0',\n ord_no: '',\n stex_tp: '0',\n });\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_realized_pnl',\n {\n description: 'Get realized profit/loss for a stock over a date (or period, max 3 months). ka10072 / ka10073.',\n inputSchema: {\n code: z.string().describe('6-digit stock code'),\n start: z.string().optional().describe('Start date YYYYMMDD (default today)'),\n end: z.string().optional().describe('End date YYYYMMDD; if set, queries the period'),\n },\n },\n async ({ code, start, end }) =>\n withErrorHandling(async () => {\n const client = clientOrThrow();\n const stk = normalizeStockCode(code);\n const { data } = end\n ? await client.callEndpoint(ENDPOINTS.realizedPlByPeriod, {\n stk_cd: stk,\n strt_dt: start ?? end,\n end_dt: end,\n })\n : await client.callEndpoint(ENDPOINTS.realizedPlByDate, {\n stk_cd: stk,\n strt_dt: start ?? todayKst(),\n });\n return mcpJson(data);\n }),\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { clientOrThrow, mcpJson, mcpText, withErrorHandling, currentEnv, tool } from '../helpers';\nimport { ENDPOINTS, EndpointDef } from '../../client/endpoints';\nimport { ORDER_TYPES, MARKET_ORDER_TYPES, ORDER_EXCHANGE_TYPES } from '../../config/constants';\nimport { normalizeStockCode } from '../../utils/helpers';\n\nfunction resolveType(type: string | undefined, price: string | undefined) {\n const trde_tp = type ?? (price ? '0' : '3');\n if (!ORDER_TYPES[trde_tp]) {\n throw new Error(`Invalid order type ${trde_tp}. Known: ${Object.keys(ORDER_TYPES).join(', ')}`);\n }\n if (MARKET_ORDER_TYPES.has(trde_tp)) {\n if (price) throw new Error('Market orders must not include a price.');\n return { trde_tp, ord_uv: '' };\n }\n if (!price) throw new Error(`Order type ${trde_tp} requires a price.`);\n return { trde_tp, ord_uv: price };\n}\n\nexport function registerOrderTools(server: McpServer): void {\n tool(\n server,\n 'place_order',\n {\n description:\n 'Place a buy or sell order. SAFETY: nothing is sent unless confirm=true; otherwise a preview is returned. On the \"real\" environment this trades REAL money. kt10000/kt10001 (or credit kt10006/kt10007).',\n inputSchema: {\n side: z.enum(['buy', 'sell']).describe('buy or sell'),\n code: z.string().describe('6-digit stock code'),\n qty: z.string().describe('Order quantity'),\n price: z.string().optional().describe('Limit price (won); omit for a market order'),\n type: z.string().optional().describe('Order type code trde_tp (default 0=limit / 3=market)'),\n condPrice: z.string().optional().describe('Trigger price for stop/conditional types (e.g. 28)'),\n exchange: z.enum(['KRX', 'NXT', 'SOR']).optional().describe('Exchange routing (default KRX)'),\n credit: z.boolean().optional().describe('Use a credit (margin) order'),\n creditDeal: z.enum(['33', '99']).optional().describe('Credit deal type (sell): 33=융자, 99=융자합'),\n loanDate: z.string().optional().describe('Credit loan date YYYYMMDD (some credit sells)'),\n confirm: z.boolean().optional().describe('Must be true to actually submit'),\n },\n },\n async ({ side, code, qty, price, type, condPrice, exchange, credit, creditDeal, loanDate, confirm }) =>\n withErrorHandling(async () => {\n const stk = normalizeStockCode(code);\n const { trde_tp, ord_uv } = resolveType(type, price);\n if (trde_tp === '28' && !condPrice) {\n throw new Error('Stop-limit orders (type 28) require condPrice.');\n }\n const ex = exchange ?? 'KRX';\n if (!ORDER_EXCHANGE_TYPES.includes(ex)) throw new Error(`Invalid exchange ${ex}`);\n const def: EndpointDef = credit\n ? side === 'buy'\n ? ENDPOINTS.creditBuy\n : ENDPOINTS.creditSell\n : side === 'buy'\n ? ENDPOINTS.buy\n : ENDPOINTS.sell;\n const body: Record<string, unknown> = {\n dmst_stex_tp: ex,\n stk_cd: stk,\n ord_qty: qty,\n ord_uv,\n trde_tp,\n cond_uv: condPrice ?? '',\n };\n if (credit && side === 'sell') {\n body.crd_deal_tp = creditDeal ?? '33';\n if (loanDate) body.crd_loan_dt = loanDate;\n }\n\n const preview = {\n willSubmit: confirm === true,\n env: currentEnv(),\n apiId: def.apiId,\n side,\n credit: Boolean(credit),\n stock: stk,\n qty,\n price: MARKET_ORDER_TYPES.has(trde_tp) ? 'MARKET' : ord_uv,\n orderType: `${trde_tp} (${ORDER_TYPES[trde_tp]})`,\n exchange: ex,\n };\n if (confirm !== true) {\n return mcpText(\n `PREVIEW ONLY — set confirm=true to submit.\\n${JSON.stringify(preview, null, 2)}`,\n );\n }\n const { data } = await clientOrThrow().callEndpoint(def, body);\n return mcpJson({ submitted: true, ...preview, result: data });\n }),\n );\n\n tool(\n server,\n 'modify_order',\n {\n description: 'Modify a resting order (new qty/price). Nothing is sent unless confirm=true. kt10002/kt10008.',\n inputSchema: {\n orderNo: z.string().describe('Original order number'),\n code: z.string().describe('6-digit stock code'),\n qty: z.string().describe('New quantity'),\n price: z.string().describe('New price'),\n condPrice: z.string().optional().describe('New trigger price for conditional/stop orders'),\n exchange: z.enum(['KRX', 'NXT', 'SOR']).optional().describe('Exchange (default KRX)'),\n credit: z.boolean().optional(),\n confirm: z.boolean().optional().describe('Must be true to actually submit'),\n },\n },\n async ({ orderNo, code, qty, price, condPrice, exchange, credit, confirm }) =>\n withErrorHandling(async () => {\n const stk = normalizeStockCode(code);\n const ex = exchange ?? 'KRX';\n const def = credit ? ENDPOINTS.creditModify : ENDPOINTS.modify;\n const body = {\n dmst_stex_tp: ex,\n orig_ord_no: orderNo,\n stk_cd: stk,\n mdfy_qty: qty,\n mdfy_uv: price,\n mdfy_cond_uv: condPrice ?? '',\n };\n const preview = { willSubmit: confirm === true, env: currentEnv(), apiId: def.apiId, orderNo, stock: stk, qty, price, exchange: ex };\n if (confirm !== true) {\n return mcpText(`PREVIEW ONLY — set confirm=true to submit.\\n${JSON.stringify(preview, null, 2)}`);\n }\n const { data } = await clientOrThrow().callEndpoint(def, body);\n return mcpJson({ submitted: true, ...preview, result: data });\n }),\n );\n\n tool(\n server,\n 'cancel_order',\n {\n description: 'Cancel a resting order (qty 0 = all). Nothing is sent unless confirm=true. kt10003/kt10009.',\n inputSchema: {\n orderNo: z.string().describe('Original order number'),\n code: z.string().describe('6-digit stock code'),\n qty: z.string().optional().describe('Quantity to cancel; 0 = all remaining (default 0)'),\n exchange: z.enum(['KRX', 'NXT', 'SOR']).optional().describe('Exchange (default KRX)'),\n credit: z.boolean().optional(),\n confirm: z.boolean().optional().describe('Must be true to actually submit'),\n },\n },\n async ({ orderNo, code, qty, exchange, credit, confirm }) =>\n withErrorHandling(async () => {\n const stk = normalizeStockCode(code);\n const ex = exchange ?? 'KRX';\n const def = credit ? ENDPOINTS.creditCancel : ENDPOINTS.cancel;\n const body = { dmst_stex_tp: ex, orig_ord_no: orderNo, stk_cd: stk, cncl_qty: qty ?? '0' };\n const preview = { willSubmit: confirm === true, env: currentEnv(), apiId: def.apiId, orderNo, stock: stk, qty: qty ?? '0 (all)', exchange: ex };\n if (confirm !== true) {\n return mcpText(`PREVIEW ONLY — set confirm=true to submit.\\n${JSON.stringify(preview, null, 2)}`);\n }\n const { data } = await clientOrThrow().callEndpoint(def, body);\n return mcpJson({ submitted: true, ...preview, result: data });\n }),\n );\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { clientOrThrow, mcpJson, withErrorHandling, tool } from '../helpers';\nimport { ENDPOINTS, EndpointDef } from '../../client/endpoints';\n\nexport function registerRankingTools(server: McpServer): void {\n tool(\n server,\n 'get_ranking',\n {\n description:\n 'Get a market ranking list: gainers/losers (fluctuation), today volume, trade value (amount), volume surge, or previous-day volume.',\n inputSchema: {\n kind: z\n .enum(['fluctuation', 'volume', 'amount', 'surge', 'prev-volume'])\n .describe('Ranking type'),\n market: z.enum(['000', '001', '101']).optional().describe('000=all, 001=KOSPI, 101=KOSDAQ'),\n sort: z.string().optional().describe('Sort code (meaning varies per kind; default 1)'),\n exchange: z.enum(['1', '2', '3']).optional().describe('1=KRX, 2=NXT, 3=unified (default 3)'),\n },\n },\n async ({ kind, market, sort, exchange }) =>\n withErrorHandling(async () => {\n const mrkt_tp = market ?? '000';\n const stex_tp = exchange ?? '3';\n let def: EndpointDef;\n let body: Record<string, unknown>;\n switch (kind) {\n case 'fluctuation':\n def = ENDPOINTS.rankFluctuation;\n body = {\n mrkt_tp,\n sort_tp: sort ?? '1',\n trde_qty_cnd: '0000',\n stk_cnd: '0',\n crd_cnd: '0',\n updown_incls: '1',\n pric_cnd: '0',\n trde_prica_cnd: '0',\n stex_tp,\n };\n break;\n case 'volume':\n def = ENDPOINTS.rankVolume;\n body = {\n mrkt_tp,\n sort_tp: sort ?? '1',\n mang_stk_incls: '0',\n crd_tp: '0',\n trde_qty_tp: '0',\n pric_tp: '0',\n trde_prica_tp: '0',\n mrkt_open_tp: '0',\n stex_tp,\n };\n break;\n case 'amount':\n def = ENDPOINTS.rankTradeAmount;\n body = { mrkt_tp, mang_stk_incls: '1', stex_tp };\n break;\n case 'surge':\n def = ENDPOINTS.rankVolumeSurge;\n body = {\n mrkt_tp,\n sort_tp: sort ?? '1',\n tm_tp: '2',\n trde_qty_tp: '5',\n tm: '',\n stk_cnd: '0',\n pric_tp: '0',\n stex_tp,\n };\n break;\n default: // prev-volume\n def = ENDPOINTS.rankPrevVolume;\n body = { mrkt_tp, qry_tp: '1', rank_strt: '0', rank_end: '100', stex_tp };\n }\n const { data } = await clientOrThrow().callEndpoint(def, body);\n return mcpJson(data);\n }),\n );\n\n tool(\n server,\n 'get_sector',\n {\n description:\n 'Get sector/industry data: index price, constituent stock prices, all sector indices, or daily index history.',\n inputSchema: {\n kind: z.enum(['price', 'stocks', 'all', 'daily']).describe('Sector query type'),\n market: z.enum(['0', '1', '2']).optional().describe('0=KOSPI, 1=KOSDAQ, 2=KOSPI200'),\n code: z.string().optional().describe('Industry code inds_cd (default 001=종합KOSPI)'),\n },\n },\n async ({ kind, market, code }) =>\n withErrorHandling(async () => {\n const mrkt_tp = market ?? '0';\n const inds_cd = code ?? '001';\n let def: EndpointDef;\n let body: Record<string, unknown>;\n switch (kind) {\n case 'stocks':\n def = ENDPOINTS.sectorStocks;\n body = { mrkt_tp, inds_cd, stex_tp: '1' };\n break;\n case 'all':\n def = ENDPOINTS.sectorAllIndex;\n body = { inds_cd };\n break;\n case 'daily':\n def = ENDPOINTS.sectorDaily;\n body = { mrkt_tp, inds_cd };\n break;\n default: // price\n def = ENDPOINTS.sectorPrice;\n body = { mrkt_tp, inds_cd };\n }\n const { data } = await clientOrThrow().callEndpoint(def, body);\n return mcpJson(data);\n }),\n );\n}\n"],"mappings":"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","names":["path","server","server","import_zod","server","import_zod","server","import_zod","server","import_zod","server"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@2oolkit/kiwoom-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "CLI & MCP server for Kiwoom Securities (키움증권) REST API — quote stocks, query charts, manage your account, and place orders from the terminal",
|
|
5
5
|
"author": "haeminmoon",
|
|
6
6
|
"license": "MIT",
|
package/skill/SKILL.md
CHANGED
|
@@ -127,14 +127,20 @@ kiwoom-cli stock search 삼성 -o json
|
|
|
127
127
|
| `kiwoom-cli market inst-foreign <code> -s <date> -e <date>` | Institution/foreigner trend |
|
|
128
128
|
|
|
129
129
|
### Charts
|
|
130
|
-
| Command | Description |
|
|
131
|
-
|
|
132
|
-
| `kiwoom-cli chart tick <code> [-s 1\|3\|5\|10\|30]` | Tick chart |
|
|
133
|
-
| `kiwoom-cli chart min <code> [-i 1\|3\|5\|10\|15\|30\|45\|60]` | Minute chart |
|
|
134
|
-
| `kiwoom-cli chart day <code> [-d YYYYMMDD]` | Daily |
|
|
135
|
-
| `kiwoom-cli chart week
|
|
136
|
-
|
|
137
|
-
|
|
130
|
+
| Command | Description | Per-request max |
|
|
131
|
+
|---------|-------------|-----------------|
|
|
132
|
+
| `kiwoom-cli chart tick <code> [-s 1\|3\|5\|10\|30]` | Tick chart | 900 |
|
|
133
|
+
| `kiwoom-cli chart min <code> [-i 1\|3\|5\|10\|15\|30\|45\|60]` | Minute chart | 900 |
|
|
134
|
+
| `kiwoom-cli chart day <code> [-d YYYYMMDD]` | Daily | 600 |
|
|
135
|
+
| `kiwoom-cli chart week <code>` | Weekly | 300 |
|
|
136
|
+
| `kiwoom-cli chart month <code>` | Monthly | 240 |
|
|
137
|
+
| `kiwoom-cli chart year <code>` | Yearly | 30 |
|
|
138
|
+
|
|
139
|
+
All charts: `-n <count>` (candles, default 50), `--raw` (unadjusted prices), `-p/--paginate`
|
|
140
|
+
(force multi-page). A single request returns up to the per-request max above; a larger
|
|
141
|
+
`-n/--count` **auto-paginates** via the API's `cont-yn`/`next-key` headers and returns ~that many
|
|
142
|
+
bars (clamped to 100,000). e.g. `chart day 005930 -n 2000` pulls ~2000 daily bars over multiple
|
|
143
|
+
pages.
|
|
138
144
|
|
|
139
145
|
### Account
|
|
140
146
|
| Command | Description |
|