@bimatrix-aud-platform/aud_mcp_server 1.1.0 → 1.1.2

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.
@@ -0,0 +1,17 @@
1
+ export interface ControlEntry {
2
+ Name: string;
3
+ Type: string;
4
+ InGroup: string | null;
5
+ }
6
+ export interface ControlInfoResult {
7
+ success: boolean;
8
+ controls: ControlEntry[];
9
+ imports: string[];
10
+ errors: string[];
11
+ }
12
+ /**
13
+ * MTSD 파일에서 컨트롤 Name → Type 매핑을 추출합니다.
14
+ * @param filePath MTSD 파일 경로
15
+ * @param filterName 특정 컨트롤만 조회 (생략 시 전체)
16
+ */
17
+ export declare function getControlInfo(filePath: string, filterName?: string): ControlInfoResult;
@@ -0,0 +1,89 @@
1
+ import { readFileSync } from "fs";
2
+ // MTSD Type → TypeScript import 매핑 (예외 케이스만 정의)
3
+ const TYPE_TO_IMPORT = {
4
+ MXGrid: { importName: "iGrid", importPath: "@AUD_CLIENT/control/iGrid" },
5
+ };
6
+ /**
7
+ * MTSD 파일에서 컨트롤 Name → Type 매핑을 추출합니다.
8
+ * @param filePath MTSD 파일 경로
9
+ * @param filterName 특정 컨트롤만 조회 (생략 시 전체)
10
+ */
11
+ export function getControlInfo(filePath, filterName) {
12
+ const errors = [];
13
+ // 1. 파일 읽기
14
+ let raw;
15
+ try {
16
+ raw = readFileSync(filePath, "utf-8");
17
+ }
18
+ catch (e) {
19
+ return {
20
+ success: false,
21
+ controls: [],
22
+ imports: [],
23
+ errors: [
24
+ `파일을 읽을 수 없습니다: ${e instanceof Error ? e.message : String(e)}`,
25
+ ],
26
+ };
27
+ }
28
+ let doc;
29
+ try {
30
+ doc = JSON.parse(raw);
31
+ }
32
+ catch (e) {
33
+ return {
34
+ success: false,
35
+ controls: [],
36
+ imports: [],
37
+ errors: ["JSON 파싱 오류: 유효한 JSON 형식이 아닙니다."],
38
+ };
39
+ }
40
+ // 2. Elements 트리 순회하여 컨트롤 수집
41
+ const controls = [];
42
+ const typeSet = new Set();
43
+ function walkElements(elements, parentGroup) {
44
+ for (const el of elements) {
45
+ const name = el.Name || "";
46
+ const type = el.Type || "";
47
+ const inGroup = el.InGroup || parentGroup;
48
+ if (name && type) {
49
+ // filterName이 지정된 경우 해당 이름만 수집
50
+ if (!filterName || name === filterName) {
51
+ controls.push({ Name: name, Type: type, InGroup: inGroup });
52
+ typeSet.add(type);
53
+ }
54
+ }
55
+ // ChildElements 재귀 순회
56
+ if (el.ChildElements && Array.isArray(el.ChildElements)) {
57
+ walkElements(el.ChildElements, type === "Group" ? name : parentGroup);
58
+ }
59
+ }
60
+ }
61
+ // Forms > Elements 순회
62
+ const forms = doc.Forms || [];
63
+ for (const form of forms) {
64
+ const elements = form.Elements || [];
65
+ walkElements(elements, null);
66
+ }
67
+ // 최상위 Elements (Forms 없는 구조)
68
+ if (doc.Elements && Array.isArray(doc.Elements)) {
69
+ walkElements(doc.Elements, null);
70
+ }
71
+ // 3. import 문 생성
72
+ const imports = [];
73
+ const sortedTypes = Array.from(typeSet).sort();
74
+ for (const type of sortedTypes) {
75
+ const mapping = TYPE_TO_IMPORT[type];
76
+ if (mapping) {
77
+ imports.push(`import { ${mapping.importName} } from "${mapping.importPath}";`);
78
+ }
79
+ else {
80
+ imports.push(`import { ${type} } from "@AUD_CLIENT/control/${type}";`);
81
+ }
82
+ }
83
+ return {
84
+ success: true,
85
+ controls,
86
+ imports,
87
+ errors,
88
+ };
89
+ }
@@ -43,13 +43,15 @@ export function fixMtsd(filePath) {
43
43
  // 3. 보정 규칙 적용
44
44
  // Rule 1: DataSource Name→Id 참조 보정
45
45
  fixDataSourceReferences(doc, dsNameToId, dsIdSet, fixes);
46
- // Rule 2: DataSource Params에 Value 누락 보정
46
+ // Rule 2: OlapGrid DataSource 기반 Fields 자동 생성
47
+ fixOlapGridFields(doc, datas, fixes);
48
+ // Rule 3: DataSource Params에 Value 누락 보정
47
49
  //fixParamsMissingValue(datas, fixes);
48
- // Rule 3: DataSource Columns에 Type 누락 보정
50
+ // Rule 4: DataSource Columns에 Type 누락 보정
49
51
  //fixColumnsMissingType(datas, fixes);
50
- // Rule 4: Element Position.Docking 필수 속성 보정
52
+ // Rule 5: Element Position.Docking 필수 속성 보정
51
53
  //fixDockingDefaults(doc, fixes);
52
- // Rule 5: Element Style.Border 필수 속성 보정
54
+ // Rule 6: Element Style.Border 필수 속성 보정
53
55
  //fixBorderDefaults(doc, fixes);
54
56
  // 4. 파일 저장
55
57
  if (fixes.length > 0) {
@@ -89,7 +91,166 @@ function fixDataSourceReferences(doc, dsNameToId, dsIdSet, fixes) {
89
91
  });
90
92
  }
91
93
  }
92
- // ---- Rule 2: Params Value 누락 보정 ----
94
+ // ---- Rule 2: OlapGrid DataSource 기반 Fields 자동 생성 ----
95
+ // DataSource Column.Type이 "Numeric"이면 숫자형으로 판단
96
+ const NUMERIC_COLUMN_TYPES = new Set([
97
+ "Numeric",
98
+ // SQL 타입 호환
99
+ "INT", "INTEGER", "BIGINT", "SMALLINT", "TINYINT",
100
+ "NUMERIC", "DECIMAL", "FLOAT", "DOUBLE", "REAL", "NUMBER", "MONEY",
101
+ ]);
102
+ function isNumericColumnType(type) {
103
+ return NUMERIC_COLUMN_TYPES.has(type) || NUMERIC_COLUMN_TYPES.has(type.toUpperCase());
104
+ }
105
+ /** OlapField 기본값 생성 (OlapField constructor + Serialize 기준) */
106
+ function createDefaultOlapField(key, caption) {
107
+ return {
108
+ Key: key,
109
+ Caption: caption,
110
+ ToolTipField: "",
111
+ ToolTipText: "",
112
+ Category: 1, // enCategory.Dimension
113
+ Area: 0, // enArea.Hidden
114
+ SummaryType: 0, // enSummaryType.None
115
+ TotalSummaryType: 0,
116
+ SummaryVariation: 0,
117
+ GroupByType: 0, // enGroupByType.Auto
118
+ Format: "",
119
+ Formula: "",
120
+ Formula2: "",
121
+ RefFormula: "",
122
+ Width: 100,
123
+ Unit: 1,
124
+ CreateType: 0, // enOlapFieldCreateType.Default
125
+ SortType: 0, // enSortType.None
126
+ MeasureSortField: "",
127
+ SortBaseField: "",
128
+ MoveAble: true,
129
+ SortAble: true,
130
+ FilterAble: true,
131
+ AllowFilter: true,
132
+ AllowRow: true,
133
+ AllowColumn: true,
134
+ AllowData: true,
135
+ KeyType: 0, // enKeyType.None
136
+ DataType: 0, // enDataType.Numeric
137
+ SaveMode: 0, // enSaveMode.All
138
+ LanguageCode: "",
139
+ UseChartSource: true,
140
+ VisibleSubTotal: true,
141
+ SummaryBaseFieldKey: "",
142
+ TextAlignment: 1, // enHorizonAlign.Right
143
+ HeaderAlignment: 0, // enHorizonAlign.Left
144
+ InDimensions: "",
145
+ Visible: true,
146
+ Expanded: true,
147
+ MetaItemCode: "",
148
+ MetaItemName: "",
149
+ MetaRollupType: 0,
150
+ MetaCalculatorField: false,
151
+ MetaSummaryTypeIsDistinct: false,
152
+ FilterInfo: {
153
+ FilterType: 0, // enOlapFilterType.In
154
+ FilterKind: 0, // enOlapFilterKind.Dimension
155
+ HasMeasureFilter: false,
156
+ MeasureFilterTypeA: 0, // enOlapMeasureFilterType.Equals
157
+ MeasureFilterTypeB: 0,
158
+ MeasureAndOrOperator: 2, // enAndOrOperator.And
159
+ },
160
+ };
161
+ }
162
+ function fixOlapGridFields(doc, datas, fixes) {
163
+ // DataSource Id → DataSource 객체 매핑
164
+ const dsById = new Map();
165
+ for (const ds of datas) {
166
+ if (ds.Id)
167
+ dsById.set(ds.Id, ds);
168
+ }
169
+ const forms = doc.Forms || [];
170
+ for (const form of forms) {
171
+ const elements = form.Elements || [];
172
+ walkElements(elements, (el, path) => {
173
+ // OlapGrid 타입만 대상
174
+ if (el.Type !== "OlapGrid")
175
+ return;
176
+ // DataSource가 설정되어 있어야 함
177
+ if (!el.DataSource)
178
+ return;
179
+ // iOLAPView.Fields가 이미 존재하고 비어있지 않으면 스킵
180
+ const fields = el.iOLAPView?.Fields;
181
+ if (fields && Array.isArray(fields) && fields.length > 0)
182
+ return;
183
+ // 해당 DataSource 찾기
184
+ const ds = dsById.get(el.DataSource);
185
+ if (!ds)
186
+ return;
187
+ const columns = ds.Columns || [];
188
+ if (columns.length === 0)
189
+ return;
190
+ // Fields 생성
191
+ const newFields = [];
192
+ for (const col of columns) {
193
+ const colName = col.Name || "";
194
+ const caption = col.Caption || colName;
195
+ const field = createDefaultOlapField(colName, caption);
196
+ if (isNumericColumnType(col.Type || "")) {
197
+ // Measure 필드
198
+ field.Category = 2; // enCategory.Measure
199
+ field.Format = "{0:N0}";
200
+ field.DataType = 0; // enDataType.Numeric
201
+ field.SummaryType = 1; // enSummaryType.Sum
202
+ field.TextAlignment = 1; // enHorizonAlign.Right
203
+ }
204
+ else {
205
+ // Dimension 필드
206
+ field.Category = 1; // enCategory.Dimension
207
+ field.DataType = 1; // enDataType.String
208
+ field.SortType = 1; // enSortType.Asc
209
+ field.SummaryType = 5; // enSummaryType.Count
210
+ field.TextAlignment = 1; // enHorizonAlign.Right
211
+ }
212
+ newFields.push(field);
213
+ }
214
+ // Area 자동 배치 (setDataSource 로직 준용)
215
+ let rowCnt = 0;
216
+ let colCnt = 0;
217
+ let dataCnt = 0;
218
+ for (const field of newFields) {
219
+ if (field.DataType === 0) {
220
+ // Numeric → Data 영역 (최대 3개), 나머지 Filter
221
+ if (dataCnt < 3) {
222
+ field.Area = 4; // enArea.Data
223
+ dataCnt++;
224
+ }
225
+ else {
226
+ field.Area = 3; // enArea.Filter
227
+ }
228
+ }
229
+ else {
230
+ // String → Row(최대 2개) → Column(최대 2개) → Filter
231
+ if (rowCnt < 2) {
232
+ field.Area = 1; // enArea.Row
233
+ rowCnt++;
234
+ }
235
+ else if (colCnt < 2) {
236
+ field.Area = 2; // enArea.Column
237
+ colCnt++;
238
+ }
239
+ else {
240
+ field.Area = 3; // enArea.Filter
241
+ }
242
+ }
243
+ }
244
+ // iOLAPView 구조 생성/보정
245
+ if (!el.iOLAPView) {
246
+ el.iOLAPView = {};
247
+ }
248
+ el.iOLAPView.Fields = newFields;
249
+ fixes.push(`[Rule2] ${path}: DataSource "${ds.Name || ds.Id}" 컬럼 기준 OlapGrid Fields ${newFields.length}개 자동 생성`);
250
+ });
251
+ }
252
+ }
253
+ // ---- Rule 3: Params Value 누락 보정 ----
93
254
  // function fixParamsMissingValue(datas: any[], fixes: string[]) {
94
255
  // for (const ds of datas) {
95
256
  // const params: any[] = ds.Params || [];
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ import { generateElement } from "./generators/element.js";
11
11
  import { generateGridColumns } from "./generators/grid-column.js";
12
12
  import { generateDataSource } from "./generators/datasource.js";
13
13
  import { fixMtsd } from "./generators/fixer.js";
14
+ import { getControlInfo } from "./generators/control-info.js";
14
15
  const __filename = fileURLToPath(import.meta.url);
15
16
  const __dirname = dirname(__filename);
16
17
  // Load MTSD schema
@@ -409,6 +410,24 @@ const tools = [
409
410
  required: ["path"],
410
411
  },
411
412
  },
413
+ {
414
+ name: "get_control_info",
415
+ description: "MTSD 파일에서 컨트롤의 Name과 Type 매핑 정보를 추출합니다. TypeScript 스크립트 작성 시 컨트롤 변수의 타입을 확인하거나, import 문을 생성할 때 사용합니다.",
416
+ inputSchema: {
417
+ type: "object",
418
+ properties: {
419
+ path: {
420
+ type: "string",
421
+ description: "MTSD 파일의 전체 경로 (예: C:/reports/sample/sample.mtsd)",
422
+ },
423
+ name: {
424
+ type: "string",
425
+ description: "특정 컨트롤 이름 (생략 시 전체 컨트롤 목록 반환)",
426
+ },
427
+ },
428
+ required: ["path"],
429
+ },
430
+ },
412
431
  ];
413
432
  // Create MCP server
414
433
  const server = new Server({
@@ -714,6 +733,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
714
733
  ],
715
734
  };
716
735
  }
736
+ case "get_control_info": {
737
+ const filePath = args?.path;
738
+ if (!filePath) {
739
+ return {
740
+ content: [
741
+ {
742
+ type: "text",
743
+ text: JSON.stringify({ error: "path 파라미터가 필요합니다." }),
744
+ },
745
+ ],
746
+ };
747
+ }
748
+ const filterName = args?.name;
749
+ const result = getControlInfo(filePath, filterName);
750
+ return {
751
+ content: [
752
+ {
753
+ type: "text",
754
+ text: JSON.stringify(result, null, 2),
755
+ },
756
+ ],
757
+ };
758
+ }
717
759
  default:
718
760
  return {
719
761
  content: [
@@ -742,7 +784,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
742
784
  async function main() {
743
785
  const transport = new StdioServerTransport();
744
786
  await server.connect(transport);
745
- console.error("MCP MTSD Validator server started");
787
+ console.error("i-AUD MCP server started");
746
788
  }
747
789
  main().catch((error) => {
748
790
  console.error("Server error:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bimatrix-aud-platform/aud_mcp_server",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "MCP Server for i-AUD MTSD document validation and generation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",