@mseep/korean-dart-mcp 0.9.3

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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +660 -0
  3. package/build/cli.d.ts +2 -0
  4. package/build/cli.js +22 -0
  5. package/build/index.d.ts +2 -0
  6. package/build/index.js +36 -0
  7. package/build/lib/corp-code.d.ts +53 -0
  8. package/build/lib/corp-code.js +235 -0
  9. package/build/lib/dart-client.d.ts +25 -0
  10. package/build/lib/dart-client.js +71 -0
  11. package/build/lib/dart-xml.d.ts +26 -0
  12. package/build/lib/dart-xml.js +187 -0
  13. package/build/lib/xbrl-parser.d.ts +145 -0
  14. package/build/lib/xbrl-parser.js +673 -0
  15. package/build/server/mcp-server.d.ts +7 -0
  16. package/build/server/mcp-server.js +40 -0
  17. package/build/setup.d.ts +8 -0
  18. package/build/setup.js +264 -0
  19. package/build/tools/_helpers.d.ts +28 -0
  20. package/build/tools/_helpers.js +35 -0
  21. package/build/tools/buffett-quality-snapshot.d.ts +10 -0
  22. package/build/tools/buffett-quality-snapshot.js +261 -0
  23. package/build/tools/disclosure-anomaly.d.ts +14 -0
  24. package/build/tools/disclosure-anomaly.js +231 -0
  25. package/build/tools/download-document.d.ts +14 -0
  26. package/build/tools/download-document.js +89 -0
  27. package/build/tools/get-attachments.d.ts +15 -0
  28. package/build/tools/get-attachments.js +339 -0
  29. package/build/tools/get-company.d.ts +7 -0
  30. package/build/tools/get-company.js +32 -0
  31. package/build/tools/get-corporate-event.d.ts +14 -0
  32. package/build/tools/get-corporate-event.js +180 -0
  33. package/build/tools/get-executive-compensation.d.ts +13 -0
  34. package/build/tools/get-executive-compensation.js +89 -0
  35. package/build/tools/get-financials.d.ts +15 -0
  36. package/build/tools/get-financials.js +127 -0
  37. package/build/tools/get-major-holdings.d.ts +10 -0
  38. package/build/tools/get-major-holdings.js +117 -0
  39. package/build/tools/get-periodic-report.d.ts +7 -0
  40. package/build/tools/get-periodic-report.js +100 -0
  41. package/build/tools/get-shareholders.d.ts +13 -0
  42. package/build/tools/get-shareholders.js +87 -0
  43. package/build/tools/get-xbrl.d.ts +12 -0
  44. package/build/tools/get-xbrl.js +96 -0
  45. package/build/tools/index.d.ts +38 -0
  46. package/build/tools/index.js +66 -0
  47. package/build/tools/insider-signal.d.ts +15 -0
  48. package/build/tools/insider-signal.js +208 -0
  49. package/build/tools/resolve-corp-code.d.ts +7 -0
  50. package/build/tools/resolve-corp-code.js +40 -0
  51. package/build/tools/search-disclosures.d.ts +14 -0
  52. package/build/tools/search-disclosures.js +300 -0
  53. package/build/utils/safe-zip.d.ts +47 -0
  54. package/build/utils/safe-zip.js +215 -0
  55. package/build/version.d.ts +2 -0
  56. package/build/version.js +2 -0
  57. package/package.json +67 -0
@@ -0,0 +1,15 @@
1
+ /**
2
+ * get_financials — 재무정보 통합 (주요계정 summary + 전체 재무제표 full)
3
+ *
4
+ * scope="summary" (기본): fnlttSinglAcnt / fnlttMultiAcnt 주요계정
5
+ * - corps 1개 → 단일사 API
6
+ * - corps 2+ → 다중사 비교 API
7
+ * scope="full": fnlttSinglAcntAll 전체 재무제표 (수백~천여 행)
8
+ * - corps 1개 전용 (여러 개면 에러)
9
+ * - fs 로 연결(CFS) / 별도(OFS) 선택
10
+ *
11
+ * 참고:
12
+ * summary: https://opendart.fss.or.kr/guide/detail.do?apiGrpCd=DS003&apiId=2019016
13
+ * full: https://opendart.fss.or.kr/guide/detail.do?apiGrpCd=DS003&apiId=2019020
14
+ */
15
+ export declare const getFinancialsTool: import("./_helpers.js").ToolDef;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * get_financials — 재무정보 통합 (주요계정 summary + 전체 재무제표 full)
3
+ *
4
+ * scope="summary" (기본): fnlttSinglAcnt / fnlttMultiAcnt 주요계정
5
+ * - corps 1개 → 단일사 API
6
+ * - corps 2+ → 다중사 비교 API
7
+ * scope="full": fnlttSinglAcntAll 전체 재무제표 (수백~천여 행)
8
+ * - corps 1개 전용 (여러 개면 에러)
9
+ * - fs 로 연결(CFS) / 별도(OFS) 선택
10
+ *
11
+ * 참고:
12
+ * summary: https://opendart.fss.or.kr/guide/detail.do?apiGrpCd=DS003&apiId=2019016
13
+ * full: https://opendart.fss.or.kr/guide/detail.do?apiGrpCd=DS003&apiId=2019020
14
+ */
15
+ import { z } from "zod";
16
+ import { defineTool, resolveCorp } from "./_helpers.js";
17
+ const REPORT_CODE = {
18
+ q1: "11013",
19
+ half: "11012",
20
+ q3: "11014",
21
+ annual: "11011",
22
+ };
23
+ const FS_DIV = {
24
+ consolidated: "CFS",
25
+ separate: "OFS",
26
+ };
27
+ const Input = z
28
+ .object({
29
+ corps: z
30
+ .array(z.string().min(1))
31
+ .min(1)
32
+ .describe("회사 배열. scope=summary 면 1개(단일) 또는 2+(다중사 비교). full 은 1개만."),
33
+ year: z.number().int().min(2015),
34
+ report: z
35
+ .enum(["q1", "half", "q3", "annual"])
36
+ .default("annual")
37
+ .describe("q1/half/q3/annual"),
38
+ scope: z
39
+ .enum(["summary", "full"])
40
+ .default("summary")
41
+ .describe("summary: 주요계정 8~10행 (빠름). full: 전체 재무제표(BS/IS/CF/CIS/SCE) 수백~천여 행"),
42
+ fs: z
43
+ .enum(["consolidated", "separate"])
44
+ .default("consolidated")
45
+ .describe("scope=full 시 연결(consolidated)/별도(separate) 선택"),
46
+ sj_div: z
47
+ .array(z.enum(["BS", "IS", "CF", "CIS", "SCE"]))
48
+ .optional()
49
+ .describe("scope=full 시 재무제표 종류 필터 (미지정 시 BS+IS — 기본 응답 사이즈 ~70% 절감). " +
50
+ "BS=재무상태표, IS=손익계산서, CF=현금흐름표, CIS=포괄손익계산서, SCE=자본변동표. " +
51
+ "전체 받으려면 [\"BS\",\"IS\",\"CF\",\"CIS\",\"SCE\"] 명시."),
52
+ })
53
+ .refine((v) => v.scope !== "full" || v.corps.length === 1, {
54
+ message: "scope=full 은 corps 1개만 지원 (다중사 전체 재무제표 API 없음)",
55
+ });
56
+ export const getFinancialsTool = defineTool({
57
+ name: "get_financials",
58
+ description: "재무정보 조회 — scope=summary(주요계정, 단일/다중사 자동) 또는 full(전체 재무제표, 단일사). " +
59
+ "summary 는 매출·영업이익·당기순이익·자산/부채/자본 핵심만, full 은 BS/IS/CF 전체 수백 행.",
60
+ input: Input,
61
+ handler: async (ctx, args) => {
62
+ const records = args.corps.map((c) => resolveCorp(ctx.resolver, c));
63
+ const reprt_code = REPORT_CODE[args.report];
64
+ const bsns_year = String(args.year);
65
+ if (args.scope === "full") {
66
+ const raw = await ctx.client.getJson("fnlttSinglAcntAll.json", {
67
+ corp_code: records[0].corp_code,
68
+ bsns_year,
69
+ reprt_code,
70
+ fs_div: FS_DIV[args.fs],
71
+ });
72
+ if (raw.status !== "000") {
73
+ throw new Error(`DART 응답 오류 [${raw.status}]: ${raw.message}`);
74
+ }
75
+ const allItems = raw.list ?? [];
76
+ const filterDivs = args.sj_div ?? ["BS", "IS"];
77
+ const filterSet = new Set(filterDivs);
78
+ const items = allItems.filter((it) => filterSet.has(String(it.sj_div)));
79
+ return {
80
+ mode: "full",
81
+ resolved: records,
82
+ year: args.year,
83
+ report: args.report,
84
+ fs: args.fs,
85
+ sj_div_filter: filterDivs,
86
+ total_count: allItems.length,
87
+ count: items.length,
88
+ items,
89
+ };
90
+ }
91
+ // summary 모드
92
+ if (records.length === 1) {
93
+ const raw = await ctx.client.getJson("fnlttSinglAcnt.json", {
94
+ corp_code: records[0].corp_code,
95
+ bsns_year,
96
+ reprt_code,
97
+ });
98
+ if (raw.status !== "000") {
99
+ throw new Error(`DART 응답 오류 [${raw.status}]: ${raw.message}`);
100
+ }
101
+ return {
102
+ mode: "summary_single",
103
+ resolved: records,
104
+ year: args.year,
105
+ report: args.report,
106
+ items: raw.list ?? [],
107
+ };
108
+ }
109
+ // 다중사 — corp_code 콤마 연결
110
+ const corp_code = records.map((r) => r.corp_code).join(",");
111
+ const raw = await ctx.client.getJson("fnlttMultiAcnt.json", {
112
+ corp_code,
113
+ bsns_year,
114
+ reprt_code,
115
+ });
116
+ if (raw.status !== "000") {
117
+ throw new Error(`DART 응답 오류 [${raw.status}]: ${raw.message}`);
118
+ }
119
+ return {
120
+ mode: "summary_multi",
121
+ resolved: records,
122
+ year: args.year,
123
+ report: args.report,
124
+ items: raw.list ?? [],
125
+ };
126
+ },
127
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * get_major_holdings — DS004 지분공시 2개 엔드포인트 합성
3
+ *
4
+ * - majorstock.json : 대량보유 상황보고 (5% 룰)
5
+ * - elestock.json : 임원·주요주주 소유보고
6
+ *
7
+ * 두 테이블 모두 특정 사업연도·보고서 코드 파라미터 없이 corp_code 만으로 동작.
8
+ * 두 관점(외부 5%+ 주주 vs 임원 본인 보유)을 한 번에 묶어 지분 구조 전체 뷰 제공.
9
+ */
10
+ export declare const getMajorHoldingsTool: import("./_helpers.js").ToolDef;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * get_major_holdings — DS004 지분공시 2개 엔드포인트 합성
3
+ *
4
+ * - majorstock.json : 대량보유 상황보고 (5% 룰)
5
+ * - elestock.json : 임원·주요주주 소유보고
6
+ *
7
+ * 두 테이블 모두 특정 사업연도·보고서 코드 파라미터 없이 corp_code 만으로 동작.
8
+ * 두 관점(외부 5%+ 주주 vs 임원 본인 보유)을 한 번에 묶어 지분 구조 전체 뷰 제공.
9
+ */
10
+ import { z } from "zod";
11
+ import { defineTool, normalizeDate, resolveCorp } from "./_helpers.js";
12
+ const Input = z.object({
13
+ corp: z.string().min(1).describe("회사명/종목코드/corp_code"),
14
+ include: z
15
+ .array(z.enum(["majorstock", "elestock"]))
16
+ .optional()
17
+ .describe("조회 대상 (미지정 시 둘 다). majorstock=대량보유 5%룰, elestock=임원·주요주주 본인 보유"),
18
+ start: z
19
+ .string()
20
+ .optional()
21
+ .describe("기간 시작 (YYYY-MM-DD / YYYYMMDD). 미지정 시 최근 3년."),
22
+ end: z
23
+ .string()
24
+ .optional()
25
+ .describe("기간 종료 (미지정 시 오늘)"),
26
+ limit: z
27
+ .number()
28
+ .int()
29
+ .min(1)
30
+ .max(2000)
31
+ .default(200)
32
+ .describe("최대 반환 행 수 (각 kind 별). 대형 상장사는 누적 수만 건 → 디폴트 200."),
33
+ });
34
+ function normalizeRcept(s) {
35
+ if (!s)
36
+ return null;
37
+ const digits = s.replace(/\D/g, "");
38
+ return /^\d{8}$/.test(digits) ? digits : null;
39
+ }
40
+ function defaultStart() {
41
+ const d = new Date();
42
+ d.setFullYear(d.getFullYear() - 3);
43
+ const y = d.getFullYear();
44
+ const m = String(d.getMonth() + 1).padStart(2, "0");
45
+ const day = String(d.getDate()).padStart(2, "0");
46
+ return `${y}${m}${day}`;
47
+ }
48
+ function todayYmd() {
49
+ const d = new Date();
50
+ const y = d.getFullYear();
51
+ const m = String(d.getMonth() + 1).padStart(2, "0");
52
+ const day = String(d.getDate()).padStart(2, "0");
53
+ return `${y}${m}${day}`;
54
+ }
55
+ export const getMajorHoldingsTool = defineTool({
56
+ name: "get_major_holdings",
57
+ description: "지분공시 2종 합성 조회: 대량보유(5%룰) + 임원·주요주주 본인 소유. " +
58
+ "내부자·외부 대주주 지분 이력을 한 번에 스냅샷.",
59
+ input: Input,
60
+ handler: async (ctx, args) => {
61
+ const record = resolveCorp(ctx.resolver, args.corp);
62
+ const targets = args.include ?? ["majorstock", "elestock"];
63
+ const startYmd = args.start ? normalizeDate(args.start) : defaultStart();
64
+ const endYmd = args.end ? normalizeDate(args.end) : todayYmd();
65
+ const results = await Promise.all(targets.map(async (kind) => {
66
+ try {
67
+ const raw = await ctx.client.getJson(`${kind}.json`, {
68
+ corp_code: record.corp_code,
69
+ });
70
+ if (raw.status !== "000") {
71
+ return {
72
+ kind,
73
+ status: raw.status,
74
+ message: raw.message,
75
+ total_count: 0,
76
+ count: 0,
77
+ items: [],
78
+ };
79
+ }
80
+ const all = raw.list ?? [];
81
+ const filtered = all.filter((it) => {
82
+ const ymd = normalizeRcept(it.rcept_dt);
83
+ if (!ymd)
84
+ return false;
85
+ return ymd >= startYmd && ymd <= endYmd;
86
+ });
87
+ // 최신 순으로 잘라 limit 적용
88
+ filtered.sort((a, b) => String(b.rcept_dt ?? "").localeCompare(String(a.rcept_dt ?? "")));
89
+ const items = filtered.slice(0, args.limit);
90
+ return {
91
+ kind,
92
+ status: raw.status,
93
+ total_count: all.length,
94
+ filtered_count: filtered.length,
95
+ count: items.length,
96
+ truncated: filtered.length > args.limit,
97
+ items,
98
+ };
99
+ }
100
+ catch (e) {
101
+ return {
102
+ kind,
103
+ error: e instanceof Error ? e.message : String(e),
104
+ total_count: 0,
105
+ count: 0,
106
+ items: [],
107
+ };
108
+ }
109
+ }));
110
+ return {
111
+ resolved: record,
112
+ period: { start: startYmd, end: endYmd },
113
+ limit: args.limit,
114
+ results,
115
+ };
116
+ },
117
+ });
@@ -0,0 +1,7 @@
1
+ /**
2
+ * get_periodic_report — 정기보고서 주요정보 (DS002, 29개 엔드포인트 enum 압축)
3
+ *
4
+ * MCP 도구 폭발 방지를 위해 정기보고서 내 29개 섹션 조회를 `report_type` 파라미터로 단일화.
5
+ * 매핑 출처: FinanceData/OpenDartReader (https://github.com/FinanceData/OpenDartReader/blob/master/dart_report.py)
6
+ */
7
+ export declare const getPeriodicReportTool: import("./_helpers.js").ToolDef;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * get_periodic_report — 정기보고서 주요정보 (DS002, 29개 엔드포인트 enum 압축)
3
+ *
4
+ * MCP 도구 폭발 방지를 위해 정기보고서 내 29개 섹션 조회를 `report_type` 파라미터로 단일화.
5
+ * 매핑 출처: FinanceData/OpenDartReader (https://github.com/FinanceData/OpenDartReader/blob/master/dart_report.py)
6
+ */
7
+ import { z } from "zod";
8
+ import { defineTool, resolveCorp } from "./_helpers.js";
9
+ const REPORT_CODE = {
10
+ q1: "11013",
11
+ half: "11012",
12
+ q3: "11014",
13
+ annual: "11011",
14
+ };
15
+ // report_type → OpenDART 엔드포인트 파일 (확장자 .json 은 클라이언트에서 붙임)
16
+ const ENDPOINT = {
17
+ // 주주/지분
18
+ largest_shareholder: "hyslrSttus", // 최대주주 현황
19
+ largest_shareholder_changes: "hyslrChgSttus", // 최대주주 변동
20
+ minority_shareholders: "mrhlSttus", // 소액주주 현황
21
+ total_stocks: "stockTotqySttus", // 주식의 총수 현황
22
+ // 임직원
23
+ executives: "exctvSttus", // 임원 현황
24
+ employees: "empSttus", // 직원 현황
25
+ outside_director_changes: "outcmpnyDrctrNdChangeSttus", // 사외이사·변동
26
+ // 보수
27
+ executive_compensation_total: "hmvAuditAllSttus", // 임원·감사 전체 보수
28
+ executive_compensation_individual: "hmvAuditIndvdlBySttus", // 개인별 5억 이상
29
+ individual_pay_top5: "indvdlByPay", // 상위 5명 개인별 보수
30
+ unregistered_executive_comp: "unrstExctvMendngSttus", // 미등기임원 보수
31
+ director_total_comp_approval: "drctrAdtAllMendngSttusGmtsckConfmAmount", // 주총 승인금액
32
+ director_total_comp_by_type: "drctrAdtAllMendngSttusMendngPymntamtTyCl", // 유형별 지급금액
33
+ // 회계감사
34
+ auditor_opinion: "accnutAdtorNmNdAdtOpinion", // 회계감사인 및 의견
35
+ audit_service_contract: "adtServcCnclsSttus", // 감사용역 체결현황
36
+ non_audit_service_contract: "accnutAdtorNonAdtServcCnclsSttus", // 비감사용역 체결
37
+ // 자본/주식
38
+ capital_increase_decrease: "irdsSttus", // 증자(감자) 현황
39
+ dividends: "alotMatter", // 배당
40
+ treasury_stock: "tesstkAcqsDspsSttus", // 자기주식 취득·처분
41
+ // 자금사용
42
+ private_placement_fund_use: "prvsrpCptalUseDtls", // 사모자금 사용내역
43
+ public_offering_fund_use: "pssrpCptalUseDtls", // 공모자금 사용내역
44
+ // 타법인 출자
45
+ other_company_investment: "otrCprInvstmntSttus", // 타법인 출자현황
46
+ // 채무증권 발행/잔액
47
+ debt_securities_issuance: "detScritsIsuAcmslt", // 채무증권 발행실적
48
+ cp_unredeemed: "entrprsBilScritsNrdmpBlce", // 기업어음 미상환
49
+ short_term_bond_unredeemed: "srtpdPsndbtNrdmpBlce", // 단기사채 미상환
50
+ corp_bond_unredeemed: "cprndNrdmpBlce", // 회사채 미상환
51
+ hybrid_capital_unredeemed: "newCaplScritsNrdmpBlce", // 신종자본증권 미상환
52
+ contingent_capital_unredeemed: "cndlCaplScritsNrdmpBlce", // 조건부자본증권 미상환
53
+ };
54
+ const REPORT_TYPES = Object.keys(ENDPOINT);
55
+ const Input = z.object({
56
+ corp: z.string().min(1).describe("회사명/종목코드/corp_code"),
57
+ year: z.number().int().min(2015),
58
+ report: z.enum(["q1", "half", "q3", "annual"]).default("annual"),
59
+ report_type: z
60
+ .enum(REPORT_TYPES)
61
+ .describe("섹션 29종: 주주(largest_shareholder·largest_shareholder_changes·minority_shareholders·total_stocks) / " +
62
+ "임직원(executives·employees·outside_director_changes) / " +
63
+ "보수(executive_compensation_total·executive_compensation_individual·individual_pay_top5·unregistered_executive_comp·director_total_comp_approval·director_total_comp_by_type) / " +
64
+ "회계감사(auditor_opinion·audit_service_contract·non_audit_service_contract) / " +
65
+ "자본(capital_increase_decrease·dividends·treasury_stock) / " +
66
+ "자금사용(private_placement_fund_use·public_offering_fund_use) / " +
67
+ "타법인출자(other_company_investment) / " +
68
+ "채무증권(debt_securities_issuance·cp_unredeemed·short_term_bond_unredeemed·corp_bond_unredeemed·hybrid_capital_unredeemed·contingent_capital_unredeemed)"),
69
+ });
70
+ export const getPeriodicReportTool = defineTool({
71
+ name: "get_periodic_report",
72
+ description: "사업보고서 내 29개 세부 섹션을 report_type enum 으로 단일 도구 호출. " +
73
+ "예: 최대주주 현황·임원 보수·감사인·배당·자기주식·회사채 미상환 등. " +
74
+ "(정기보고서 전체 원문이 필요하면 download_document.)",
75
+ input: Input,
76
+ handler: async (ctx, args) => {
77
+ const endpoint = ENDPOINT[args.report_type];
78
+ if (!endpoint) {
79
+ throw new Error(`알 수 없는 report_type: ${args.report_type}`);
80
+ }
81
+ const record = resolveCorp(ctx.resolver, args.corp);
82
+ const raw = await ctx.client.getJson(`${endpoint}.json`, {
83
+ corp_code: record.corp_code,
84
+ bsns_year: String(args.year),
85
+ reprt_code: REPORT_CODE[args.report],
86
+ });
87
+ if (raw.status !== "000") {
88
+ throw new Error(`DART 응답 오류 [${raw.status}]: ${raw.message}`);
89
+ }
90
+ return {
91
+ resolved: record,
92
+ year: args.year,
93
+ report: args.report,
94
+ report_type: args.report_type,
95
+ endpoint,
96
+ count: raw.list?.length ?? 0,
97
+ items: raw.list ?? [],
98
+ };
99
+ },
100
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * get_shareholders — 지배구조 4개 섹션을 병렬로 합성해 단일 뷰로 반환
3
+ *
4
+ * 정기보고서 내 지배구조 관련 4개 섹션을 한 번의 도구 호출로 수집.
5
+ * - 최대주주 현황 (hyslrSttus)
6
+ * - 최대주주 변동 (hyslrChgSttus)
7
+ * - 소액주주 현황 (mrhlSttus)
8
+ * - 주식의 총수 (stockTotqySttus)
9
+ *
10
+ * 같은 기간을 get_periodic_report 로 4번 호출하는 것 대비 1/4 왕복.
11
+ * 섹션 단일 조회가 필요하면 get_periodic_report.
12
+ */
13
+ export declare const getShareholdersTool: import("./_helpers.js").ToolDef;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * get_shareholders — 지배구조 4개 섹션을 병렬로 합성해 단일 뷰로 반환
3
+ *
4
+ * 정기보고서 내 지배구조 관련 4개 섹션을 한 번의 도구 호출로 수집.
5
+ * - 최대주주 현황 (hyslrSttus)
6
+ * - 최대주주 변동 (hyslrChgSttus)
7
+ * - 소액주주 현황 (mrhlSttus)
8
+ * - 주식의 총수 (stockTotqySttus)
9
+ *
10
+ * 같은 기간을 get_periodic_report 로 4번 호출하는 것 대비 1/4 왕복.
11
+ * 섹션 단일 조회가 필요하면 get_periodic_report.
12
+ */
13
+ import { z } from "zod";
14
+ import { defineTool, resolveCorp } from "./_helpers.js";
15
+ const REPORT_CODE = {
16
+ q1: "11013",
17
+ half: "11012",
18
+ q3: "11014",
19
+ annual: "11011",
20
+ };
21
+ const SECTIONS = {
22
+ largest_shareholder: "hyslrSttus",
23
+ largest_shareholder_changes: "hyslrChgSttus",
24
+ minority_shareholders: "mrhlSttus",
25
+ total_stocks: "stockTotqySttus",
26
+ };
27
+ const Input = z.object({
28
+ corp: z.string().min(1).describe("회사명/종목코드/corp_code"),
29
+ year: z.number().int().min(2015),
30
+ report: z.enum(["q1", "half", "q3", "annual"]).default("annual"),
31
+ sections: z
32
+ .array(z.enum(Object.keys(SECTIONS)))
33
+ .optional()
34
+ .describe("조회할 섹션 (미지정 시 4개 모두)"),
35
+ });
36
+ export const getShareholdersTool = defineTool({
37
+ name: "get_shareholders",
38
+ description: "지배구조 스냅샷: 최대주주·변동·소액주주·주식총수 4개 섹션을 한 번에 합성 조회. " +
39
+ "(특정 섹션만 필요하면 get_periodic_report 로 단일 조회 가능.)",
40
+ input: Input,
41
+ handler: async (ctx, args) => {
42
+ const record = resolveCorp(ctx.resolver, args.corp);
43
+ const reprt_code = REPORT_CODE[args.report];
44
+ const bsns_year = String(args.year);
45
+ const targets = (args.sections ?? Object.keys(SECTIONS));
46
+ const results = await Promise.all(targets.map(async (key) => {
47
+ const endpoint = SECTIONS[key];
48
+ try {
49
+ const raw = await ctx.client.getJson(`${endpoint}.json`, {
50
+ corp_code: record.corp_code,
51
+ bsns_year,
52
+ reprt_code,
53
+ });
54
+ if (raw.status !== "000") {
55
+ return {
56
+ section: key,
57
+ endpoint,
58
+ status: raw.status,
59
+ message: raw.message,
60
+ items: [],
61
+ };
62
+ }
63
+ return {
64
+ section: key,
65
+ endpoint,
66
+ status: raw.status,
67
+ count: raw.list?.length ?? 0,
68
+ items: raw.list ?? [],
69
+ };
70
+ }
71
+ catch (e) {
72
+ return {
73
+ section: key,
74
+ endpoint,
75
+ error: e instanceof Error ? e.message : String(e),
76
+ items: [],
77
+ };
78
+ }
79
+ }));
80
+ return {
81
+ resolved: record,
82
+ year: args.year,
83
+ report: args.report,
84
+ sections: results,
85
+ };
86
+ },
87
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * get_xbrl — 재무제표 XBRL 원본 또는 마크다운 변환
3
+ *
4
+ * format:
5
+ * - "raw" (기본): ZIP 을 해제해 파일시스템에 저장.
6
+ * - "markdown" (v0.8.0+): whitelist 기반 BS/IS/CF 3개 표.
7
+ * - "markdown_full" (v0.9.0+): presentation linkbase 기반 전체 계정 + 계산 검증.
8
+ * 택소노미에서 직접 추출하므로 업종별(금융/보험 등) 고유 계정에도 자동 대응.
9
+ *
10
+ * 참고: https://opendart.fss.or.kr/guide/detail.do?apiGrpCd=DS003&apiId=2019019
11
+ */
12
+ export declare const getXbrlTool: import("./_helpers.js").ToolDef;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * get_xbrl — 재무제표 XBRL 원본 또는 마크다운 변환
3
+ *
4
+ * format:
5
+ * - "raw" (기본): ZIP 을 해제해 파일시스템에 저장.
6
+ * - "markdown" (v0.8.0+): whitelist 기반 BS/IS/CF 3개 표.
7
+ * - "markdown_full" (v0.9.0+): presentation linkbase 기반 전체 계정 + 계산 검증.
8
+ * 택소노미에서 직접 추출하므로 업종별(금융/보험 등) 고유 계정에도 자동 대응.
9
+ *
10
+ * 참고: https://opendart.fss.or.kr/guide/detail.do?apiGrpCd=DS003&apiId=2019019
11
+ */
12
+ import { z } from "zod";
13
+ import { homedir } from "node:os";
14
+ import { defineTool } from "./_helpers.js";
15
+ import { parseXbrlZip, buildStatements, buildStatementsFull, renderMarkdown, } from "../lib/xbrl-parser.js";
16
+ import { safeUnzipToDisk, defaultXbrlOutDir } from "../utils/safe-zip.js";
17
+ const REPORT_CODE = {
18
+ q1: "11013",
19
+ half: "11012",
20
+ q3: "11014",
21
+ annual: "11011",
22
+ };
23
+ const Input = z.object({
24
+ rcept_no: z.string().regex(/^\d{14}$/).describe("접수번호 14자리"),
25
+ report: z
26
+ .enum(["q1", "half", "q3", "annual"])
27
+ .default("annual")
28
+ .describe("보고서 종류"),
29
+ format: z
30
+ .enum(["raw", "markdown", "markdown_full"])
31
+ .default("raw")
32
+ .describe('raw: ZIP 파일시스템 저장. markdown: whitelist 50태그 마크다운. markdown_full(v0.9.0+): taxonomy 전체 계정 + 계산 검증.'),
33
+ fs_div: z
34
+ .enum(["consolidated", "separate"])
35
+ .default("consolidated")
36
+ .describe('markdown/markdown_full 전용: 연결/별도 기준'),
37
+ sections: z
38
+ .array(z.enum(["BS", "IS", "CF"]))
39
+ .default(["BS", "IS", "CF"])
40
+ .describe('markdown/markdown_full 전용: 생성할 재무제표 종류'),
41
+ out_dir: z
42
+ .string()
43
+ .optional()
44
+ .describe('format="raw" 전용: 저장 디렉터리 (미지정 시 ~/.korean-dart-mcp/xbrl/{rcept_no}_{report}/)'),
45
+ });
46
+ export const getXbrlTool = defineTool({
47
+ name: "get_xbrl",
48
+ description: "재무제표 XBRL 조회 — format 선택: " +
49
+ "raw=원본 ZIP 파일시스템 해제, " +
50
+ "markdown=whitelist 50태그 3년 3열 (~8KB), " +
51
+ "markdown_full=taxonomy 전체 계정 + 계산 검증 (업종별 자동 대응, ~30-60KB).",
52
+ input: Input,
53
+ handler: async (ctx, args) => {
54
+ const reprt_code = REPORT_CODE[args.report];
55
+ const buf = await ctx.client.getZip("fnlttXbrl.xml", {
56
+ rcept_no: args.rcept_no,
57
+ reprt_code,
58
+ });
59
+ if (args.format === "markdown" || args.format === "markdown_full") {
60
+ const full = args.format === "markdown_full";
61
+ const data = await parseXbrlZip(buf, { loadTaxonomy: full });
62
+ const st = full
63
+ ? buildStatementsFull(data, { fs_div: args.fs_div, sections: args.sections })
64
+ : buildStatements(data, { fs_div: args.fs_div, sections: args.sections });
65
+ const md = renderMarkdown(st);
66
+ return {
67
+ rcept_no: args.rcept_no,
68
+ report: args.report,
69
+ format: args.format,
70
+ fs_div: args.fs_div,
71
+ entity_id: data.entityId,
72
+ periods: st.periods,
73
+ statements: st.statements,
74
+ validations: st.validations,
75
+ markdown: md,
76
+ meta: {
77
+ total_facts: data.facts.length,
78
+ total_contexts: data.contexts.size,
79
+ labels_loaded: data.labels.size,
80
+ presentation_roles: data.taxonomy?.presentations.length,
81
+ calculation_roles: data.taxonomy?.calculations.length,
82
+ },
83
+ };
84
+ }
85
+ // raw 모드 — 기존 동작 (zip slip · zip bomb 방어 포함)
86
+ const outDir = args.out_dir ?? defaultXbrlOutDir(homedir(), args.rcept_no, reprt_code);
87
+ const files = await safeUnzipToDisk(buf, outDir);
88
+ return {
89
+ rcept_no: args.rcept_no,
90
+ report: args.report,
91
+ format: "raw",
92
+ dir: outDir,
93
+ files,
94
+ };
95
+ },
96
+ });
@@ -0,0 +1,38 @@
1
+ /**
2
+ * DART MCP 도구 레지스트리 — 15개
3
+ *
4
+ * v0.7.0 통폐합:
5
+ * - get_financials 가 get_full_financials 흡수 (scope: summary/full)
6
+ * - buffett_quality_snapshot 가 quality_compare 흡수 (corps 배열)
7
+ * - search_disclosures 가 list_recent_filings 흡수 (preset + 페이지 병렬화)
8
+ */
9
+ import type { ToolCtx, ToolDef } from "./_helpers.js";
10
+ export type ToolContext = ToolCtx;
11
+ export type ToolDefinition = ToolDef;
12
+ /**
13
+ * 15개 도구.
14
+ *
15
+ * 기본 조회 (7):
16
+ * [x] 1. resolve_corp_code
17
+ * [x] 2. search_disclosures (preset + all_pages 병렬 통합)
18
+ * [x] 3. get_company
19
+ * [x] 4. get_financials (scope: summary/full 통합)
20
+ * [x] 5. download_document (format: markdown/raw/text)
21
+ * [x] 6. get_xbrl
22
+ * [x] 7. get_periodic_report (29 섹션 enum)
23
+ *
24
+ * 합성 래퍼 (4):
25
+ * [x] 8. get_shareholders
26
+ * [x] 9. get_executive_compensation
27
+ * [x] 10. get_major_holdings
28
+ * [x] 11. get_corporate_event (36 enum + timeline mode)
29
+ *
30
+ * 애널리스트 프레임 (3 · 킬러):
31
+ * [x] 12. insider_signal
32
+ * [x] 13. disclosure_anomaly
33
+ * [x] 14. buffett_quality_snapshot (corps 배열 → 1개=snapshot / 2+=compare)
34
+ *
35
+ * 원문 분석 (1):
36
+ * [x] 15. get_attachments (kordoc + ZIP 재귀)
37
+ */
38
+ export declare const TOOL_REGISTRY: ToolDef[];