@airmcp-dev/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +17 -0
- package/dist/commands/add.d.ts +3 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +192 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/check.d.ts +3 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +218 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/connect.d.ts +3 -0
- package/dist/commands/connect.d.ts.map +1 -0
- package/dist/commands/connect.js +135 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/create.d.ts +3 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +150 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dev.d.ts +3 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +184 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/disconnect.d.ts +3 -0
- package/dist/commands/disconnect.d.ts.map +1 -0
- package/dist/commands/disconnect.js +69 -0
- package/dist/commands/disconnect.js.map +1 -0
- package/dist/commands/index.d.ts +13 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +16 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/inspect.d.ts +3 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +159 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/license.d.ts +3 -0
- package/dist/commands/license.d.ts.map +1 -0
- package/dist/commands/license.js +113 -0
- package/dist/commands/license.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +101 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/start.d.ts +3 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +130 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +83 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stop.d.ts +3 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +56 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/agent/.ai/context.md +36 -0
- package/dist/templates/agent/.vscode/air.code-snippets +178 -0
- package/dist/templates/agent/package.json +17 -0
- package/dist/templates/agent/src/index.ts +98 -0
- package/dist/templates/agent/tsconfig.json +14 -0
- package/dist/templates/agent-ko/.ai/context.md +36 -0
- package/dist/templates/agent-ko/.vscode/air.code-snippets +178 -0
- package/dist/templates/agent-ko/package.json +17 -0
- package/dist/templates/agent-ko/src/index.ts +92 -0
- package/dist/templates/agent-ko/tsconfig.json +14 -0
- package/dist/templates/api/.ai/context.md +36 -0
- package/dist/templates/api/.vscode/air.code-snippets +178 -0
- package/dist/templates/api/package.json +17 -0
- package/dist/templates/api/src/index.ts +76 -0
- package/dist/templates/api/tsconfig.json +14 -0
- package/dist/templates/api-ko/.ai/context.md +36 -0
- package/dist/templates/api-ko/.vscode/air.code-snippets +178 -0
- package/dist/templates/api-ko/package.json +17 -0
- package/dist/templates/api-ko/src/index.ts +70 -0
- package/dist/templates/api-ko/tsconfig.json +14 -0
- package/dist/templates/basic/.ai/context.md +36 -0
- package/dist/templates/basic/.vscode/air.code-snippets +178 -0
- package/dist/templates/basic/package.json +17 -0
- package/dist/templates/basic/src/index.ts +39 -0
- package/dist/templates/basic/tsconfig.json +15 -0
- package/dist/templates/basic-ko/.ai/context.md +36 -0
- package/dist/templates/basic-ko/.vscode/air.code-snippets +178 -0
- package/dist/templates/basic-ko/package.json +17 -0
- package/dist/templates/basic-ko/src/index.ts +39 -0
- package/dist/templates/basic-ko/tsconfig.json +14 -0
- package/dist/templates/crud/.ai/context.md +36 -0
- package/dist/templates/crud/.vscode/air.code-snippets +178 -0
- package/dist/templates/crud/package.json +17 -0
- package/dist/templates/crud/src/index.ts +82 -0
- package/dist/templates/crud/tsconfig.json +14 -0
- package/dist/templates/crud-ko/.ai/context.md +36 -0
- package/dist/templates/crud-ko/.vscode/air.code-snippets +178 -0
- package/dist/templates/crud-ko/package.json +17 -0
- package/dist/templates/crud-ko/src/index.ts +81 -0
- package/dist/templates/crud-ko/tsconfig.json +14 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/json-editor.d.ts +45 -0
- package/dist/utils/json-editor.d.ts.map +1 -0
- package/dist/utils/json-editor.js +122 -0
- package/dist/utils/json-editor.js.map +1 -0
- package/dist/utils/path-resolver.d.ts +30 -0
- package/dist/utils/path-resolver.d.ts.map +1 -0
- package/dist/utils/path-resolver.js +121 -0
- package/dist/utils/path-resolver.js.map +1 -0
- package/dist/utils/printer.d.ts +26 -0
- package/dist/utils/printer.d.ts.map +1 -0
- package/dist/utils/printer.js +65 -0
- package/dist/utils/printer.js.map +1 -0
- package/dist/utils/process-manager.d.ts +36 -0
- package/dist/utils/process-manager.d.ts.map +1 -0
- package/dist/utils/process-manager.js +143 -0
- package/dist/utils/process-manager.js.map +1 -0
- package/dist/utils/test-console.d.ts +8 -0
- package/dist/utils/test-console.d.ts.map +1 -0
- package/dist/utils/test-console.js +198 -0
- package/dist/utils/test-console.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// Copyright 2026 CodePedia Labs. Licensed under Apache-2.0.
|
|
2
|
+
// air CLI — commands/create.ts
|
|
3
|
+
//
|
|
4
|
+
// air create <name> [--template basic|crud|api|agent] [--lang en|ko]
|
|
5
|
+
//
|
|
6
|
+
// 새 MCP 서버 프로젝트를 스캐폴딩한다.
|
|
7
|
+
// 언어를 선택하면 해당 언어의 템플릿(주석/설명)이 적용된다.
|
|
8
|
+
//
|
|
9
|
+
// @example
|
|
10
|
+
// npx air create my-tool
|
|
11
|
+
// npx air create my-tool --lang ko
|
|
12
|
+
// npx air create db-server --template crud --lang en
|
|
13
|
+
import { Command } from 'commander';
|
|
14
|
+
import { cp, readFile, writeFile, access } from 'node:fs/promises';
|
|
15
|
+
import { join, resolve } from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { dirname } from 'node:path';
|
|
18
|
+
import { createInterface } from 'node:readline';
|
|
19
|
+
import { printer } from '../utils/printer.js';
|
|
20
|
+
const TEMPLATES = ['basic', 'crud', 'api', 'agent'];
|
|
21
|
+
/** 인터랙티브 언어 선택 */
|
|
22
|
+
async function askLanguage() {
|
|
23
|
+
const rl = createInterface({
|
|
24
|
+
input: process.stdin,
|
|
25
|
+
output: process.stdout,
|
|
26
|
+
});
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
console.log();
|
|
29
|
+
console.log(' Select language / 언어를 선택하세요:');
|
|
30
|
+
console.log();
|
|
31
|
+
console.log(' 1) English');
|
|
32
|
+
console.log(' 2) 한국어');
|
|
33
|
+
console.log();
|
|
34
|
+
rl.question(' > ', (answer) => {
|
|
35
|
+
rl.close();
|
|
36
|
+
const trimmed = answer.trim();
|
|
37
|
+
if (trimmed === '2' || trimmed.toLowerCase() === 'ko' || trimmed === '한국어') {
|
|
38
|
+
resolve('ko');
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
resolve('en');
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/** 언어별 메시지 */
|
|
47
|
+
const MESSAGES = {
|
|
48
|
+
en: {
|
|
49
|
+
creating: (name) => `Creating MCP server: ${name}`,
|
|
50
|
+
copying: 'Copying template files...',
|
|
51
|
+
configuring: 'Configuring project...',
|
|
52
|
+
done: 'Done!',
|
|
53
|
+
created: (name, template) => `Project "${name}" created with "${template}" template.`,
|
|
54
|
+
nextSteps: 'Next steps:',
|
|
55
|
+
},
|
|
56
|
+
ko: {
|
|
57
|
+
creating: (name) => `MCP 서버 생성: ${name}`,
|
|
58
|
+
copying: '템플릿 파일 복사 중...',
|
|
59
|
+
configuring: '프로젝트 설정 중...',
|
|
60
|
+
done: '완료!',
|
|
61
|
+
created: (name, template) => `"${name}" 프로젝트가 "${template}" 템플릿으로 생성되었습니다.`,
|
|
62
|
+
nextSteps: '다음 단계:',
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
/** 템플릿 디렉토리 절대 경로 (빌드 후 dist/ 기준) */
|
|
66
|
+
function templatesRoot() {
|
|
67
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
68
|
+
const __dirname = dirname(__filename);
|
|
69
|
+
return resolve(__dirname, '..', 'templates');
|
|
70
|
+
}
|
|
71
|
+
export const createCommand = new Command('create')
|
|
72
|
+
.description('Create a new MCP server project')
|
|
73
|
+
.argument('<name>', 'Project name (directory name)')
|
|
74
|
+
.option('-t, --template <template>', 'Template to use', 'basic')
|
|
75
|
+
.option('-l, --lang <lang>', 'Language: en | ko')
|
|
76
|
+
.action(async (name, opts) => {
|
|
77
|
+
const template = opts.template;
|
|
78
|
+
// ── 1. 템플릿 유효성 검사 ──
|
|
79
|
+
if (!TEMPLATES.includes(template)) {
|
|
80
|
+
printer.error(`Unknown template "${template}". Available: ${TEMPLATES.join(', ')}`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
// ── 2. 언어 선택 ──
|
|
84
|
+
let lang;
|
|
85
|
+
if (opts.lang === 'ko' || opts.lang === 'en') {
|
|
86
|
+
lang = opts.lang;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
lang = await askLanguage();
|
|
90
|
+
}
|
|
91
|
+
const msg = MESSAGES[lang];
|
|
92
|
+
// ── 3. 대상 디렉토리 확인 ──
|
|
93
|
+
const targetDir = resolve(process.cwd(), name);
|
|
94
|
+
try {
|
|
95
|
+
await access(targetDir);
|
|
96
|
+
printer.error(`Directory "${name}" already exists.`);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// 존재하지 않으면 정상
|
|
101
|
+
}
|
|
102
|
+
const totalSteps = 3;
|
|
103
|
+
printer.blank();
|
|
104
|
+
printer.heading(msg.creating(name));
|
|
105
|
+
// ── 4. 언어별 템플릿 디렉토리 결정 ──
|
|
106
|
+
// ko면 basic-ko, en이면 basic
|
|
107
|
+
const templateDirName = lang === 'ko' ? `${template}-ko` : template;
|
|
108
|
+
let templateDir = join(templatesRoot(), templateDirName);
|
|
109
|
+
// 한글 템플릿이 없으면 영어로 폴백
|
|
110
|
+
try {
|
|
111
|
+
await access(templateDir);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
templateDir = join(templatesRoot(), template);
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
await access(templateDir);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
printer.error(`Template "${template}" not found at ${templateDir}`);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
// ── 5. 템플릿 복사 ──
|
|
124
|
+
printer.step(1, totalSteps, msg.copying);
|
|
125
|
+
await cp(templateDir, targetDir, { recursive: true });
|
|
126
|
+
// ── 6. package.json name 교체 ──
|
|
127
|
+
printer.step(2, totalSteps, msg.configuring);
|
|
128
|
+
const pkgPath = join(targetDir, 'package.json');
|
|
129
|
+
try {
|
|
130
|
+
const raw = await readFile(pkgPath, 'utf-8');
|
|
131
|
+
const pkg = JSON.parse(raw);
|
|
132
|
+
pkg.name = name;
|
|
133
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// package.json이 없는 템플릿이면 스킵
|
|
137
|
+
}
|
|
138
|
+
// ── 7. 완료 안내 ──
|
|
139
|
+
printer.step(3, totalSteps, msg.done);
|
|
140
|
+
printer.blank();
|
|
141
|
+
printer.success(msg.created(name, template));
|
|
142
|
+
printer.blank();
|
|
143
|
+
printer.info(msg.nextSteps);
|
|
144
|
+
printer.blank();
|
|
145
|
+
console.log(` cd ${name}`);
|
|
146
|
+
console.log(' npm install');
|
|
147
|
+
console.log(' air dev');
|
|
148
|
+
printer.blank();
|
|
149
|
+
});
|
|
150
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,+BAA+B;AAC/B,EAAE;AACF,qEAAqE;AACrE,EAAE;AACF,yBAAyB;AACzB,oCAAoC;AACpC,EAAE;AACF,WAAW;AACX,2BAA2B;AAC3B,qCAAqC;AACrC,uDAAuD;AAEvD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAU,CAAC;AAK7D,kBAAkB;AAClB,KAAK,UAAU,WAAW;IACxB,MAAM,EAAE,GAAG,eAAe,CAAC;QACzB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IAEH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC1B,OAAO,CAAC,GAAG,EAAE,CAAC;QAEd,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;YAC7B,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,CAAC,WAAW,EAAE,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;gBAC3E,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,cAAc;AACd,MAAM,QAAQ,GAAG;IACf,EAAE,EAAE;QACF,QAAQ,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,wBAAwB,IAAI,EAAE;QAC1D,OAAO,EAAE,2BAA2B;QACpC,WAAW,EAAE,wBAAwB;QACrC,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,CAAC,IAAY,EAAE,QAAgB,EAAE,EAAE,CAC1C,YAAY,IAAI,mBAAmB,QAAQ,aAAa;QAC1D,SAAS,EAAE,aAAa;KACzB;IACD,EAAE,EAAE;QACF,QAAQ,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,cAAc,IAAI,EAAE;QAChD,OAAO,EAAE,gBAAgB;QACzB,WAAW,EAAE,cAAc;QAC3B,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,CAAC,IAAY,EAAE,QAAgB,EAAE,EAAE,CAC1C,IAAI,IAAI,YAAY,QAAQ,kBAAkB;QAChD,SAAS,EAAE,QAAQ;KACpB;CACF,CAAC;AAEF,qCAAqC;AACrC,SAAS,aAAa;IACpB,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtC,OAAO,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,iCAAiC,CAAC;KAC9C,QAAQ,CAAC,QAAQ,EAAE,+BAA+B,CAAC;KACnD,MAAM,CAAC,2BAA2B,EAAE,iBAAiB,EAAE,OAAO,CAAC;KAC/D,MAAM,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;KAChD,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,IAAyC,EAAE,EAAE;IACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAwB,CAAC;IAE/C,sBAAsB;IACtB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,qBAAqB,QAAQ,iBAAiB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,iBAAiB;IACjB,IAAI,IAAU,CAAC;IACf,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAC7C,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,MAAM,WAAW,EAAE,CAAC;IAC7B,CAAC;IAED,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE3B,sBAAsB;IACtB,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QACxB,OAAO,CAAC,KAAK,CAAC,cAAc,IAAI,mBAAmB,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,cAAc;IAChB,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,CAAC;IACrB,OAAO,CAAC,KAAK,EAAE,CAAC;IAChB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IAEpC,2BAA2B;IAC3B,2BAA2B;IAC3B,MAAM,eAAe,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;IACpE,IAAI,WAAW,GAAG,IAAI,CAAC,aAAa,EAAE,EAAE,eAAe,CAAC,CAAC;IAEzD,qBAAqB;IACrB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,WAAW,GAAG,IAAI,CAAC,aAAa,EAAE,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,aAAa,QAAQ,kBAAkB,WAAW,EAAE,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,kBAAkB;IAClB,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEtD,gCAAgC;IAChC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5B,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAChB,MAAM,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,4BAA4B;IAC9B,CAAC;IAED,iBAAiB;IACjB,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,CAAC,KAAK,EAAE,CAAC;IAChB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,KAAK,EAAE,CAAC;IAChB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC5B,OAAO,CAAC,KAAK,EAAE,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC5B,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC7B,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACzB,OAAO,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+CpC,eAAO,MAAM,UAAU,SAwInB,CAAC"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// Copyright 2026 CodePedia Labs. Licensed under Apache-2.0.
|
|
2
|
+
// air CLI — commands/dev.ts
|
|
3
|
+
//
|
|
4
|
+
// air dev [--port <port>] [--transport stdio|http|sse]
|
|
5
|
+
//
|
|
6
|
+
// 개발 모드로 MCP 서버를 실행한다.
|
|
7
|
+
// - 소스 변경 감지 → 자동 재시작 (fs.watch)
|
|
8
|
+
// - 빌드 없이 tsx/ts-node로 직접 실행
|
|
9
|
+
// - 종료 시 graceful shutdown
|
|
10
|
+
//
|
|
11
|
+
// @example
|
|
12
|
+
// air dev
|
|
13
|
+
// air dev --port 3100
|
|
14
|
+
// air dev --transport http
|
|
15
|
+
import { Command } from 'commander';
|
|
16
|
+
import { spawn } from 'node:child_process';
|
|
17
|
+
import { watch } from 'node:fs';
|
|
18
|
+
import { resolve, extname } from 'node:path';
|
|
19
|
+
import { access } from 'node:fs/promises';
|
|
20
|
+
import { printer } from '../utils/printer.js';
|
|
21
|
+
/** 감시 대상 확장자 */
|
|
22
|
+
const WATCH_EXTENSIONS = new Set(['.ts', '.js', '.json']);
|
|
23
|
+
/** 재시작 디바운스 (ms) */
|
|
24
|
+
const DEBOUNCE_MS = 300;
|
|
25
|
+
/**
|
|
26
|
+
* 엔트리 파일을 찾는다.
|
|
27
|
+
* air.config.ts → src/index.ts → index.ts 순서.
|
|
28
|
+
*/
|
|
29
|
+
async function findEntry(cwd) {
|
|
30
|
+
const candidates = ['src/index.ts', 'src/index.js', 'index.ts', 'index.js'];
|
|
31
|
+
for (const candidate of candidates) {
|
|
32
|
+
const full = resolve(cwd, candidate);
|
|
33
|
+
try {
|
|
34
|
+
await access(full);
|
|
35
|
+
return full;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
throw new Error('No entry file found. Expected src/index.ts or index.ts');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* tsx로 서버 프로세스를 실행한다.
|
|
45
|
+
* tsx가 없으면 ts-node/esm, 그것도 없으면 node 사용.
|
|
46
|
+
*/
|
|
47
|
+
function spawnServer(entry, env, cwd) {
|
|
48
|
+
const child = spawn('npx', ['tsx', entry], {
|
|
49
|
+
cwd,
|
|
50
|
+
stdio: 'inherit',
|
|
51
|
+
env: { ...process.env, ...env },
|
|
52
|
+
shell: true,
|
|
53
|
+
});
|
|
54
|
+
return child;
|
|
55
|
+
}
|
|
56
|
+
export const devCommand = new Command('dev')
|
|
57
|
+
.description('Start MCP server in development mode (hot reload)')
|
|
58
|
+
.option('-p, --port <port>', 'HTTP/SSE port', '3000')
|
|
59
|
+
.option('-t, --transport <type>', 'Transport: stdio | http | sse', 'stdio')
|
|
60
|
+
.option('-c, --console', 'Open interactive test console (requires SSE transport)')
|
|
61
|
+
.action(async (opts) => {
|
|
62
|
+
const cwd = process.cwd();
|
|
63
|
+
// 콘솔 모드는 SSE transport가 필요
|
|
64
|
+
if (opts.console && opts.transport === 'stdio') {
|
|
65
|
+
opts.transport = 'sse';
|
|
66
|
+
printer.info('Console mode requires SSE transport — switching to SSE.');
|
|
67
|
+
}
|
|
68
|
+
// ── 1. 엔트리 파일 찾기 ──
|
|
69
|
+
let entry;
|
|
70
|
+
try {
|
|
71
|
+
entry = await findEntry(cwd);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
printer.error(err.message);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
const env = {
|
|
78
|
+
NODE_ENV: 'development',
|
|
79
|
+
AIR_TRANSPORT: opts.transport,
|
|
80
|
+
AIR_PORT: opts.port,
|
|
81
|
+
};
|
|
82
|
+
printer.blank();
|
|
83
|
+
printer.banner('dev');
|
|
84
|
+
printer.kv('entry', entry);
|
|
85
|
+
printer.kv('transport', opts.transport);
|
|
86
|
+
if (opts.transport !== 'stdio') {
|
|
87
|
+
printer.kv('port', opts.port);
|
|
88
|
+
}
|
|
89
|
+
if (opts.console) {
|
|
90
|
+
printer.kv('console', 'enabled');
|
|
91
|
+
}
|
|
92
|
+
printer.blank();
|
|
93
|
+
// ── 2. 서버 시작 ──
|
|
94
|
+
let child = null;
|
|
95
|
+
let restarting = false;
|
|
96
|
+
function startServer() {
|
|
97
|
+
printer.info('Starting server...');
|
|
98
|
+
child = spawnServer(entry, env, cwd);
|
|
99
|
+
child.on('exit', (code, signal) => {
|
|
100
|
+
if (!restarting) {
|
|
101
|
+
if (signal === 'SIGTERM' || signal === 'SIGINT') {
|
|
102
|
+
printer.info('Server stopped.');
|
|
103
|
+
}
|
|
104
|
+
else if (code !== 0) {
|
|
105
|
+
printer.error(`Server exited with code ${code}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
child = null;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function stopServer() {
|
|
112
|
+
return new Promise((resolve) => {
|
|
113
|
+
if (!child) {
|
|
114
|
+
resolve();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
restarting = true;
|
|
118
|
+
child.once('exit', () => {
|
|
119
|
+
restarting = false;
|
|
120
|
+
resolve();
|
|
121
|
+
});
|
|
122
|
+
child.kill('SIGTERM');
|
|
123
|
+
// 강제 종료 타이머
|
|
124
|
+
setTimeout(() => {
|
|
125
|
+
if (child) {
|
|
126
|
+
child.kill('SIGKILL');
|
|
127
|
+
}
|
|
128
|
+
}, 3000);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async function restart() {
|
|
132
|
+
printer.blank();
|
|
133
|
+
printer.info('Change detected, restarting...');
|
|
134
|
+
await stopServer();
|
|
135
|
+
startServer();
|
|
136
|
+
}
|
|
137
|
+
startServer();
|
|
138
|
+
// ── 3. 파일 감시 (디바운스) ──
|
|
139
|
+
let debounceTimer = null;
|
|
140
|
+
let watcher = null;
|
|
141
|
+
try {
|
|
142
|
+
const srcDir = resolve(cwd, 'src');
|
|
143
|
+
watcher = watch(srcDir, { recursive: true }, (_event, filename) => {
|
|
144
|
+
if (!filename)
|
|
145
|
+
return;
|
|
146
|
+
const ext = extname(filename);
|
|
147
|
+
if (!WATCH_EXTENSIONS.has(ext))
|
|
148
|
+
return;
|
|
149
|
+
if (debounceTimer)
|
|
150
|
+
clearTimeout(debounceTimer);
|
|
151
|
+
debounceTimer = setTimeout(() => restart(), DEBOUNCE_MS);
|
|
152
|
+
});
|
|
153
|
+
printer.success('Watching src/ for changes...');
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
printer.warn('Could not watch src/ — hot reload disabled.');
|
|
157
|
+
}
|
|
158
|
+
// ── 4. 테스트 콘솔 (--console) ──
|
|
159
|
+
if (opts.console) {
|
|
160
|
+
// 서버가 뜰 시간 대기
|
|
161
|
+
const { startTestConsole } = await import('../utils/test-console.js');
|
|
162
|
+
await startTestConsole({
|
|
163
|
+
serverUrl: `http://localhost:${opts.port}`,
|
|
164
|
+
onQuit: async () => {
|
|
165
|
+
await shutdown();
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
// ── 5. Graceful shutdown ──
|
|
170
|
+
async function shutdown() {
|
|
171
|
+
printer.blank();
|
|
172
|
+
printer.info('Shutting down...');
|
|
173
|
+
if (watcher)
|
|
174
|
+
watcher.close();
|
|
175
|
+
if (debounceTimer)
|
|
176
|
+
clearTimeout(debounceTimer);
|
|
177
|
+
await stopServer();
|
|
178
|
+
process.exit(0);
|
|
179
|
+
}
|
|
180
|
+
process.on('SIGTERM', shutdown);
|
|
181
|
+
process.on('SIGINT', shutdown);
|
|
182
|
+
});
|
|
183
|
+
;
|
|
184
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disconnect.d.ts","sourceRoot":"","sources":["../../src/commands/disconnect.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBpC,eAAO,MAAM,iBAAiB,SA8C1B,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Copyright 2026 CodePedia Labs. Licensed under Apache-2.0.
|
|
2
|
+
// air CLI — commands/disconnect.ts
|
|
3
|
+
//
|
|
4
|
+
// air disconnect <client> [--name <n>]
|
|
5
|
+
//
|
|
6
|
+
// MCP 클라이언트의 설정 파일에서 서버 등록을 제거한다.
|
|
7
|
+
//
|
|
8
|
+
// @example
|
|
9
|
+
// air disconnect claude-desktop
|
|
10
|
+
// air disconnect cursor --name my-db-tool
|
|
11
|
+
import { Command } from 'commander';
|
|
12
|
+
import { resolve } from 'node:path';
|
|
13
|
+
import { readFile } from 'node:fs/promises';
|
|
14
|
+
import { printer } from '../utils/printer.js';
|
|
15
|
+
import { resolveClientConfig, listClients } from '../utils/path-resolver.js';
|
|
16
|
+
import { JsonEditor } from '../utils/json-editor.js';
|
|
17
|
+
/** package.json에서 서버 이름을 읽는다 */
|
|
18
|
+
async function readServerName(cwd) {
|
|
19
|
+
try {
|
|
20
|
+
const raw = await readFile(resolve(cwd, 'package.json'), 'utf-8');
|
|
21
|
+
const pkg = JSON.parse(raw);
|
|
22
|
+
return pkg.name || 'air-server';
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return 'air-server';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export const disconnectCommand = new Command('disconnect')
|
|
29
|
+
.description('Remove this MCP server from a client')
|
|
30
|
+
.argument('<client>', 'Client to disconnect: claude-desktop, cursor, vscode, ...')
|
|
31
|
+
.option('-n, --name <n>', 'Server name to remove (default: from package.json)')
|
|
32
|
+
.action(async (client, opts) => {
|
|
33
|
+
const cwd = process.cwd();
|
|
34
|
+
// ── 1. 클라이언트 경로 해석 ──
|
|
35
|
+
let clientInfo;
|
|
36
|
+
try {
|
|
37
|
+
clientInfo = resolveClientConfig(client);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
printer.error(err.message);
|
|
41
|
+
printer.blank();
|
|
42
|
+
printer.info('Available clients:');
|
|
43
|
+
printer.blank();
|
|
44
|
+
printer.list(listClients().map((c) => ({ name: c.id, description: c.name })));
|
|
45
|
+
printer.blank();
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
// ── 2. 서버 이름 결정 ──
|
|
49
|
+
const serverName = opts.name || (await readServerName(cwd));
|
|
50
|
+
// ── 3. 설정 파일 로드 ──
|
|
51
|
+
printer.blank();
|
|
52
|
+
printer.step(1, 2, `Loading ${clientInfo.displayName} config...`);
|
|
53
|
+
const editor = await JsonEditor.load(clientInfo.configPath);
|
|
54
|
+
const entryPath = `${clientInfo.mcpKey}.${serverName}`;
|
|
55
|
+
// ── 4. 등록 제거 ──
|
|
56
|
+
if (!editor.has(entryPath)) {
|
|
57
|
+
printer.warn(`"${serverName}" is not registered in ${clientInfo.displayName}.`);
|
|
58
|
+
printer.blank();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
printer.step(2, 2, `Removing "${serverName}"...`);
|
|
62
|
+
editor.delete(entryPath);
|
|
63
|
+
await editor.save();
|
|
64
|
+
printer.blank();
|
|
65
|
+
printer.success(`Disconnected "${serverName}" from ${clientInfo.displayName}`);
|
|
66
|
+
printer.kv('config', clientInfo.configPath);
|
|
67
|
+
printer.blank();
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=disconnect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disconnect.js","sourceRoot":"","sources":["../../src/commands/disconnect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { createCommand } from './create.js';
|
|
2
|
+
export { addCommand } from './add.js';
|
|
3
|
+
export { devCommand } from './dev.js';
|
|
4
|
+
export { startCommand } from './start.js';
|
|
5
|
+
export { stopCommand } from './stop.js';
|
|
6
|
+
export { statusCommand } from './status.js';
|
|
7
|
+
export { listCommand } from './list.js';
|
|
8
|
+
export { inspectCommand } from './inspect.js';
|
|
9
|
+
export { connectCommand } from './connect.js';
|
|
10
|
+
export { disconnectCommand } from './disconnect.js';
|
|
11
|
+
export { checkCommand } from './check.js';
|
|
12
|
+
export { licenseCommand } from './license.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Copyright 2026 CodePedia Labs. Licensed under Apache-2.0.
|
|
2
|
+
// air CLI — commands/index.ts
|
|
3
|
+
// re-export only. 로직 없음.
|
|
4
|
+
export { createCommand } from './create.js';
|
|
5
|
+
export { addCommand } from './add.js';
|
|
6
|
+
export { devCommand } from './dev.js';
|
|
7
|
+
export { startCommand } from './start.js';
|
|
8
|
+
export { stopCommand } from './stop.js';
|
|
9
|
+
export { statusCommand } from './status.js';
|
|
10
|
+
export { listCommand } from './list.js';
|
|
11
|
+
export { inspectCommand } from './inspect.js';
|
|
12
|
+
export { connectCommand } from './connect.js';
|
|
13
|
+
export { disconnectCommand } from './disconnect.js';
|
|
14
|
+
export { checkCommand } from './check.js';
|
|
15
|
+
export { licenseCommand } from './license.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,8BAA8B;AAC9B,yBAAyB;AAEzB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../src/commands/inspect.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwEpC,eAAO,MAAM,cAAc,SA2FvB,CAAC"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// Copyright 2026 CodePedia Labs. Licensed under Apache-2.0.
|
|
2
|
+
// air CLI — commands/inspect.ts
|
|
3
|
+
//
|
|
4
|
+
// air inspect <tool>
|
|
5
|
+
//
|
|
6
|
+
// 특정 도구의 상세 스키마를 출력한다.
|
|
7
|
+
// - 파라미터 이름, 타입, 설명, 필수 여부
|
|
8
|
+
// - JSON 스키마 형태로도 출력 가능 (--json)
|
|
9
|
+
//
|
|
10
|
+
// @example
|
|
11
|
+
// air inspect hello
|
|
12
|
+
// air inspect search --json
|
|
13
|
+
import { Command } from 'commander';
|
|
14
|
+
import { resolve } from 'node:path';
|
|
15
|
+
import { access } from 'node:fs/promises';
|
|
16
|
+
import { printer } from '../utils/printer.js';
|
|
17
|
+
/** 엔트리 파일 탐색 */
|
|
18
|
+
async function findEntry(cwd) {
|
|
19
|
+
const candidates = ['src/index.ts', 'src/index.js', 'dist/index.js', 'index.ts', 'index.js'];
|
|
20
|
+
for (const candidate of candidates) {
|
|
21
|
+
const full = resolve(cwd, candidate);
|
|
22
|
+
try {
|
|
23
|
+
await access(full);
|
|
24
|
+
return full;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw new Error('No entry file found. Expected src/index.ts or index.ts');
|
|
31
|
+
}
|
|
32
|
+
/** zod 스키마에서 파라미터 정보를 추출한다 */
|
|
33
|
+
function extractParams(schema) {
|
|
34
|
+
const params = [];
|
|
35
|
+
if (!schema || !schema.shape)
|
|
36
|
+
return params;
|
|
37
|
+
const shape = schema.shape;
|
|
38
|
+
for (const [key, field] of Object.entries(shape)) {
|
|
39
|
+
let type = 'unknown';
|
|
40
|
+
let required = true;
|
|
41
|
+
let description;
|
|
42
|
+
// zod 타입 추출
|
|
43
|
+
if (field?._def) {
|
|
44
|
+
const typeName = field._def.typeName;
|
|
45
|
+
if (typeName === 'ZodString')
|
|
46
|
+
type = 'string';
|
|
47
|
+
else if (typeName === 'ZodNumber')
|
|
48
|
+
type = 'number';
|
|
49
|
+
else if (typeName === 'ZodBoolean')
|
|
50
|
+
type = 'boolean';
|
|
51
|
+
else if (typeName === 'ZodArray')
|
|
52
|
+
type = 'array';
|
|
53
|
+
else if (typeName === 'ZodObject')
|
|
54
|
+
type = 'object';
|
|
55
|
+
else if (typeName === 'ZodEnum')
|
|
56
|
+
type = `enum(${field._def.values?.join('|')})`;
|
|
57
|
+
else if (typeName === 'ZodOptional') {
|
|
58
|
+
required = false;
|
|
59
|
+
// 내부 타입 확인
|
|
60
|
+
const inner = field._def.innerType?._def?.typeName;
|
|
61
|
+
if (inner === 'ZodString')
|
|
62
|
+
type = 'string';
|
|
63
|
+
else if (inner === 'ZodNumber')
|
|
64
|
+
type = 'number';
|
|
65
|
+
else if (inner === 'ZodBoolean')
|
|
66
|
+
type = 'boolean';
|
|
67
|
+
else
|
|
68
|
+
type = inner?.replace('Zod', '').toLowerCase() || 'any';
|
|
69
|
+
}
|
|
70
|
+
description = field._def.description || field.description;
|
|
71
|
+
}
|
|
72
|
+
params.push({ name: key, type, required, description });
|
|
73
|
+
}
|
|
74
|
+
return params;
|
|
75
|
+
}
|
|
76
|
+
export const inspectCommand = new Command('inspect')
|
|
77
|
+
.description("Inspect a tool's schema and parameters")
|
|
78
|
+
.argument('<tool>', 'Tool name to inspect')
|
|
79
|
+
.option('--json', 'Output as JSON schema', false)
|
|
80
|
+
.action(async (toolName, opts) => {
|
|
81
|
+
const cwd = process.cwd();
|
|
82
|
+
// ── 1. 엔트리 로드 ──
|
|
83
|
+
let entry;
|
|
84
|
+
try {
|
|
85
|
+
entry = await findEntry(cwd);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
printer.error(err.message);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
let server;
|
|
92
|
+
try {
|
|
93
|
+
const mod = await import(entry);
|
|
94
|
+
server = mod.default || mod;
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
printer.error(`Failed to load server: ${err.message}`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
// ── 2. 도구 찾기 ──
|
|
101
|
+
if (typeof server.tools !== 'function') {
|
|
102
|
+
printer.error('Server does not expose tools().');
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
const tools = server.tools();
|
|
106
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
107
|
+
if (!tool) {
|
|
108
|
+
printer.error(`Tool "${toolName}" not found.`);
|
|
109
|
+
printer.blank();
|
|
110
|
+
printer.info('Available tools:');
|
|
111
|
+
printer.blank();
|
|
112
|
+
printer.list(tools.map((t) => ({ name: t.name, description: t.description })));
|
|
113
|
+
printer.blank();
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
// ── 3. JSON 출력 ──
|
|
117
|
+
if (opts.json) {
|
|
118
|
+
const output = {
|
|
119
|
+
name: tool.name,
|
|
120
|
+
description: tool.description,
|
|
121
|
+
};
|
|
122
|
+
// JSON Schema 변환 시도
|
|
123
|
+
if (tool.schema && typeof tool.schema.toJsonSchema === 'function') {
|
|
124
|
+
output.inputSchema = tool.schema.toJsonSchema();
|
|
125
|
+
}
|
|
126
|
+
else if (tool.jsonSchema) {
|
|
127
|
+
output.inputSchema = tool.jsonSchema;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
output.inputSchema = null;
|
|
131
|
+
}
|
|
132
|
+
console.log(JSON.stringify(output, null, 2));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// ── 4. 사람용 출력 ──
|
|
136
|
+
printer.blank();
|
|
137
|
+
printer.heading(tool.name);
|
|
138
|
+
if (tool.description) {
|
|
139
|
+
printer.kv('description', tool.description);
|
|
140
|
+
}
|
|
141
|
+
// 파라미터 추출
|
|
142
|
+
const params = extractParams(tool.schema);
|
|
143
|
+
if (params.length > 0) {
|
|
144
|
+
printer.blank();
|
|
145
|
+
printer.info(`Parameters (${params.length}):`);
|
|
146
|
+
printer.blank();
|
|
147
|
+
for (const p of params) {
|
|
148
|
+
const req = p.required ? ' *' : '';
|
|
149
|
+
const desc = p.description ? ` ${p.description}` : '';
|
|
150
|
+
console.log(` ${p.name}${req} (${p.type})${desc}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
printer.blank();
|
|
155
|
+
printer.info('No parameters.');
|
|
156
|
+
}
|
|
157
|
+
printer.blank();
|
|
158
|
+
});
|
|
159
|
+
//# sourceMappingURL=inspect.js.map
|