mcp-aws-manager 0.4.0 → 0.4.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.
package/README.md CHANGED
@@ -8,6 +8,7 @@ This package orchestrates AWS operations (inventory/runtime/remediation) with a
8
8
 
9
9
  ```bash
10
10
  npm install -g mcp-aws-manager
11
+ mcp-aws-manager --version
11
12
  mcp-aws-manager
12
13
  mcp-aws-manager doctor
13
14
  mcp-aws-manager discover --profiles default --no-progress
@@ -39,7 +40,7 @@ Use `mcp-aws-manager` when you need an operations workflow MCP, not just generic
39
40
 
40
41
  See detailed comparison and product boundaries in:
41
42
 
42
- - `MCP_DIFFERENTIATION.md`
43
+ - `docs/MCP_DIFFERENTIATION.md`
43
44
 
44
45
  ## Quick Comparison
45
46
 
@@ -51,7 +52,7 @@ See detailed comparison and product boundaries in:
51
52
  | Response contract | Normalized (`ok/summary/records/requiredActions/meta`) | Varies by implementation |
52
53
  | Best fit | Reproducible ops loops with operator guidance | Exploratory or wide API probing |
53
54
 
54
- For full rationale and boundaries, see `MCP_DIFFERENTIATION.md` and `MCP_DIFFERENTIATION_KO.md`.
55
+ For full rationale and boundaries, see `docs/MCP_DIFFERENTIATION.md` and `docs/MCP_DIFFERENTIATION_KO.md`.
55
56
 
56
57
  ## API Coverage Snapshot
57
58
 
@@ -142,21 +143,43 @@ mcp-aws-manager
142
143
  Bootstrap registers the default single MCP server for detected clients:
143
144
 
144
145
  - `mcp-aws-manager` (single-entry, `--surface all`)
146
+ - Runtime command is auto-resolved for host stability.
147
+ - Windows priority: absolute `mcp-aws-manager-mcp.cmd`
148
+ - macOS/Linux priority: absolute `mcp-aws-manager-mcp` binary
149
+ - Fallback: `node <installed-package>/bin/mcp-aws-manager-mcp.js`
150
+ - Last fallback (ephemeral npx context): pinned `npx -y -p mcp-aws-manager@<version> mcp-aws-manager-mcp`
145
151
 
146
152
  Optional explicit registration:
147
153
 
148
154
  ```bash
149
155
  mcp-aws-manager setup
150
- mcp-aws-manager setup --clients codex,claude
151
- mcp-aws-manager setup --clients cursor,windsurf,antigravity
156
+ mcp-aws-manager setup --clients cursor
157
+ mcp-aws-manager setup --clients codex
158
+ mcp-aws-manager setup --clients claude
152
159
  ```
153
160
 
161
+ Default behavior (`setup`/`bootstrap` without `--clients`) auto-detects installed clients and registers only detected CLIs.
162
+
154
163
  2. Health check:
155
164
 
156
165
  ```bash
157
166
  mcp-aws-manager doctor
158
167
  ```
159
168
 
169
+ Default behavior (`doctor` without `--clients`) auto-detects installed clients and skips non-installed CLIs.
170
+
171
+ ### Agent Instruction Snippet (Any Client)
172
+
173
+ When asking an agent to check AWS status, use this exact sequence first:
174
+
175
+ ```bash
176
+ mcp-aws-manager setup --force
177
+ mcp-aws-manager doctor
178
+ mcp-aws-manager discover --profiles default --no-progress
179
+ ```
180
+
181
+ Require the agent to include `runId`/`started_at` (live run evidence) in the response. If missing, treat it as file-only/cached summary.
182
+
160
183
  3. Configure AWS auth (SSO recommended):
161
184
 
162
185
  ```bash
@@ -231,7 +254,22 @@ Use this only when automatic `bootstrap/setup` registration is unavailable.
231
254
  }
232
255
  ```
233
256
 
234
- 2) Global npm install:
257
+ 2) Global npm install (manual fallback):
258
+
259
+ Windows (recommended):
260
+
261
+ ```json
262
+ {
263
+ "mcpServers": {
264
+ "mcp-aws-manager": {
265
+ "command": "C:\\Users\\<user>\\AppData\\Roaming\\npm\\mcp-aws-manager-mcp.cmd",
266
+ "args": ["--surface", "all"]
267
+ }
268
+ }
269
+ }
270
+ ```
271
+
272
+ macOS/Linux:
235
273
 
236
274
  ```json
237
275
  {
@@ -489,7 +527,7 @@ Common `ACTION_REQUIRED` codes:
489
527
  - `rawNormalized` (always included for re-processing stability)
490
528
  - `schema` / `schemaVersion`
491
529
  - Contract schema: `schemas/mcp-tool-response.schema.json`
492
- - Compatibility policy: `RESPONSE_COMPATIBILITY_POLICY.md`
530
+ - Compatibility policy: `docs/RESPONSE_COMPATIBILITY_POLICY.md`
493
531
 
494
532
  <details>
495
533
  <summary>Detailed AWS Auth Setup (SSO vs Access Key)</summary>
@@ -942,15 +980,16 @@ Manual fallback mode:
942
980
  ## Related Docs
943
981
 
944
982
  Document status:
945
- - Canonical (keep synchronized with implementation): `README.md`, `RESPONSE_COMPATIBILITY_POLICY.md`
946
- - Reference (detail/positioning): `IMPLEMENTATION_INTEGRATIONS.md`, `MCP_DIFFERENTIATION.md`, `MCP_DIFFERENTIATION_KO.md`, `AGENT_WORKING_CONTEXT_KO.md`, `RECORDS_FIELD_REFERENCE_KO.md`
983
+ - Canonical (keep synchronized with implementation): `README.md`, `docs/RESPONSE_COMPATIBILITY_POLICY.md`
984
+ - Reference (detail/positioning): `docs/IMPLEMENTATION_INTEGRATIONS.md`, `docs/MCP_DIFFERENTIATION.md`, `docs/MCP_DIFFERENTIATION_KO.md`, `workflow/AGENT_WORKING_CONTEXT_KO.md`, `docs/RECORDS_FIELD_REFERENCE_KO.md`
947
985
 
948
986
  - `README_KO.md`: Korean overview and quick start
949
- - `IMPLEMENTATION_INTEGRATIONS.md`: API/CLI integration inventory
950
- - `MCP_DIFFERENTIATION.md`: differentiation from existing AWS MCP servers
951
- - `MCP_DIFFERENTIATION_KO.md`: Korean differentiation guide and selection criteria
952
- - `AGENT_WORKING_CONTEXT_KO.md`: agent-focused implementation invariants, gateway loop, and operation catalog quick reference
953
- - `RECORDS_FIELD_REFERENCE_KO.md`: full `records[]` field reference (292 fields)
954
- - `RESPONSE_COMPATIBILITY_POLICY.md`: response schema/version compatibility rules
987
+ - `docs/IMPLEMENTATION_INTEGRATIONS.md`: API/CLI integration inventory
988
+ - `docs/MCP_DIFFERENTIATION.md`: differentiation from existing AWS MCP servers
989
+ - `docs/MCP_DIFFERENTIATION_KO.md`: Korean differentiation guide and selection criteria
990
+ - `workflow/AGENT_WORKING_CONTEXT_KO.md`: agent-focused implementation invariants, gateway loop, and operation catalog quick reference
991
+ - `docs/RECORDS_FIELD_REFERENCE_KO.md`: full `records[]` field reference (292 fields)
992
+ - `docs/RESPONSE_COMPATIBILITY_POLICY.md`: response schema/version compatibility rules
955
993
  - `schemas/mcp-tool-response.schema.json`: canonical tool response JSON schema
956
994
 
995
+
package/README_KO.md CHANGED
@@ -8,6 +8,7 @@ SSM 우선(SSM-first) 방식의 AWS 운영 CLI + MCP stdio 서버입니다.
8
8
 
9
9
  ```bash
10
10
  npm install -g mcp-aws-manager
11
+ mcp-aws-manager --version
11
12
  mcp-aws-manager
12
13
  mcp-aws-manager doctor
13
14
  mcp-aws-manager discover --profiles default --no-progress
@@ -39,7 +40,7 @@ mcp-aws-manager discover --profiles default --no-progress
39
40
 
40
41
  비교/경계는 아래 문서를 참고하세요.
41
42
 
42
- - `MCP_DIFFERENTIATION.md`
43
+ - `docs/MCP_DIFFERENTIATION.md`
43
44
 
44
45
  ## 빠른 비교 요약
45
46
 
@@ -51,7 +52,7 @@ mcp-aws-manager discover --profiles default --no-progress
51
52
  | 응답 계약 | 정규화(`ok/summary/records/requiredActions/meta`) | 구현마다 상이 |
52
53
  | 적합한 상황 | 반복 가능한 운영 루프 + 운영자 가이드 필요 | 탐색/실험 위주의 광범위 API 접근 |
53
54
 
54
- 상세 근거와 경계는 `MCP_DIFFERENTIATION.md`, `MCP_DIFFERENTIATION_KO.md`를 참고하세요.
55
+ 상세 근거와 경계는 `docs/MCP_DIFFERENTIATION.md`, `docs/MCP_DIFFERENTIATION_KO.md`를 참고하세요.
55
56
 
56
57
  ## API 커버리지 스냅샷
57
58
 
@@ -142,21 +143,43 @@ mcp-aws-manager
142
143
  bootstrap/setup 기본 동작은 단일 서버 1개를 등록합니다.
143
144
 
144
145
  - `mcp-aws-manager` (single-entry, `--surface all`)
146
+ - 런타임 커맨드는 호스트 안정성을 위해 자동으로 해석됩니다.
147
+ - Windows 우선순위: 절대경로 `mcp-aws-manager-mcp.cmd`
148
+ - macOS/Linux 우선순위: 절대경로 `mcp-aws-manager-mcp` 바이너리
149
+ - 폴백: `node <installed-package>/bin/mcp-aws-manager-mcp.js`
150
+ - 최종 폴백(npx 임시 실행 컨텍스트): 버전 고정 `npx -y -p mcp-aws-manager@<version> mcp-aws-manager-mcp`
145
151
 
146
152
  명시적 등록 예시:
147
153
 
148
154
  ```bash
149
155
  mcp-aws-manager setup
150
- mcp-aws-manager setup --clients codex,claude
151
- mcp-aws-manager setup --clients cursor,windsurf,antigravity
156
+ mcp-aws-manager setup --clients cursor
157
+ mcp-aws-manager setup --clients codex
158
+ mcp-aws-manager setup --clients claude
152
159
  ```
153
160
 
161
+ 기본 동작(`--clients` 없이 `setup`/`bootstrap`)은 설치된 클라이언트를 자동 감지해, 감지된 CLI만 등록합니다.
162
+
154
163
  2. 상태 점검:
155
164
 
156
165
  ```bash
157
166
  mcp-aws-manager doctor
158
167
  ```
159
168
 
169
+ 기본 동작(`--clients` 없이 `doctor`)은 설치된 클라이언트를 자동 감지하고, 미설치 CLI는 skip 처리합니다.
170
+
171
+ ### 에이전트 지시용 공통 스니펫
172
+
173
+ 에이전트에게 AWS 상태 점검을 지시할 때는 먼저 아래 순서를 그대로 실행하도록 요청하세요.
174
+
175
+ ```bash
176
+ mcp-aws-manager setup --force
177
+ mcp-aws-manager doctor
178
+ mcp-aws-manager discover --profiles default --no-progress
179
+ ```
180
+
181
+ 응답에는 반드시 `runId`/`started_at`(라이브 실행 증거)를 포함하도록 요구하세요. 없으면 파일 기반/캐시 요약으로 간주하세요.
182
+
160
183
  3. AWS 인증 설정(권장: SSO):
161
184
 
162
185
  ```bash
@@ -232,7 +255,22 @@ mcp-aws-manager discover --profiles default --html-out ./inventory.html --open-h
232
255
  }
233
256
  ```
234
257
 
235
- 2) 전역 npm 설치:
258
+ 2) 전역 npm 설치(수동 대체 경로):
259
+
260
+ Windows(권장):
261
+
262
+ ```json
263
+ {
264
+ "mcpServers": {
265
+ "mcp-aws-manager": {
266
+ "command": "C:\\Users\\<user>\\AppData\\Roaming\\npm\\mcp-aws-manager-mcp.cmd",
267
+ "args": ["--surface", "all"]
268
+ }
269
+ }
270
+ }
271
+ ```
272
+
273
+ macOS/Linux:
236
274
 
237
275
  ```json
238
276
  {
@@ -490,7 +528,7 @@ Discover 연산 참고:
490
528
  - `rawNormalized` (재처리 안정성을 위해 항상 포함)
491
529
  - `schema` / `schemaVersion`
492
530
  - 계약 스키마: `schemas/mcp-tool-response.schema.json`
493
- - 호환성 정책: `RESPONSE_COMPATIBILITY_POLICY.md`
531
+ - 호환성 정책: `docs/RESPONSE_COMPATIBILITY_POLICY.md`
494
532
 
495
533
  <details>
496
534
  <summary>상세 AWS 인증 설정 (SSO vs Access Key)</summary>
@@ -936,16 +974,17 @@ E2E 러너 검증 항목:
936
974
  ## 관련 문서
937
975
 
938
976
  문서 상태:
939
- - Canonical(구현과 동기화 필수): `README.md`, `RESPONSE_COMPATIBILITY_POLICY.md`
940
- - Reference(상세/비교 참고): `IMPLEMENTATION_INTEGRATIONS.md`, `MCP_DIFFERENTIATION.md`, `MCP_DIFFERENTIATION_KO.md`, `AGENT_WORKING_CONTEXT_KO.md`, `RECORDS_FIELD_REFERENCE_KO.md`
977
+ - Canonical(구현과 동기화 필수): `README.md`, `docs/RESPONSE_COMPATIBILITY_POLICY.md`
978
+ - Reference(상세/비교 참고): `docs/IMPLEMENTATION_INTEGRATIONS.md`, `docs/MCP_DIFFERENTIATION.md`, `docs/MCP_DIFFERENTIATION_KO.md`, `workflow/AGENT_WORKING_CONTEXT_KO.md`, `docs/RECORDS_FIELD_REFERENCE_KO.md`
941
979
 
942
980
  - `README_KO.md`: 한국어 개요/빠른 시작
943
- - `IMPLEMENTATION_INTEGRATIONS.md`: API/CLI 연동 인벤토리
944
- - `MCP_DIFFERENTIATION.md`: AWS MCP 대안 대비 차별점
945
- - `MCP_DIFFERENTIATION_KO.md`: 한국어 차별화/선택 가이드
946
- - `AGENT_WORKING_CONTEXT_KO.md`: 에이전트 협업 시 불변 원칙/작업 루프/operationId 빠른 참조
947
- - `RECORDS_FIELD_REFERENCE_KO.md`: `records[]` 전수 필드(292개) 레퍼런스
948
- - `RESPONSE_COMPATIBILITY_POLICY.md`: 응답 스키마/버전 호환 정책
981
+ - `docs/IMPLEMENTATION_INTEGRATIONS.md`: API/CLI 연동 인벤토리
982
+ - `docs/MCP_DIFFERENTIATION.md`: AWS MCP 대안 대비 차별점
983
+ - `docs/MCP_DIFFERENTIATION_KO.md`: 한국어 차별화/선택 가이드
984
+ - `workflow/AGENT_WORKING_CONTEXT_KO.md`: 에이전트 협업 시 불변 원칙/작업 루프/operationId 빠른 참조
985
+ - `docs/RECORDS_FIELD_REFERENCE_KO.md`: `records[]` 전수 필드(292개) 레퍼런스
986
+ - `docs/RESPONSE_COMPATIBILITY_POLICY.md`: 응답 스키마/버전 호환 정책
949
987
  - `schemas/mcp-tool-response.schema.json`: 표준 도구 응답 JSON 스키마
950
988
 
951
989
 
990
+
@@ -52,6 +52,8 @@ const APP_ANALYSIS_MEM_HIGH_PCT = 90;
52
52
  const ACM_EXPIRING_SOON_DAYS = 30;
53
53
  const DEFAULT_SERVER_NAME = "mcp-aws-manager";
54
54
  const DEFAULT_MCP_COMMAND = "mcp-aws-manager-mcp";
55
+ const DEFAULT_MCP_COMMAND_ENTRY = path.resolve(__dirname, "mcp-aws-manager-mcp.js");
56
+ const PACKAGE_JSON_PATH = path.resolve(__dirname, "..", "package.json");
55
57
  const DEFAULT_HTML_REPORT_NAME = "aws-inventory.html";
56
58
  const DEFAULT_TOPOLOGY_REPORT_NAME = "aws-topology.json";
57
59
  const DEFAULT_RELATIONSHIPS_REPORT_NAME = "aws-relationships.json";
@@ -154,6 +156,7 @@ const REQUIRED_ACTION_DEFAULTS = Object.freeze({
154
156
  const POLICY_PACK_NAMES = Object.keys(POLICY_PACKS);
155
157
  const ASSUMED_PROFILE_PREFIX = "assume-role://";
156
158
  const assumeRoleProviderCache = new Map();
159
+ let packageVersionCache = null;
157
160
 
158
161
  function eprint(msg) {
159
162
  process.stderr.write(String(msg) + "\n");
@@ -187,6 +190,200 @@ function parseCsv(raw) {
187
190
  return list.length ? list : null;
188
191
  }
189
192
 
193
+ function pathCompareKey(value) {
194
+ const resolved = path.resolve(String(value || ""));
195
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
196
+ }
197
+
198
+ function uniquePaths(values) {
199
+ const out = [];
200
+ const seen = new Set();
201
+ for (const raw of Array.isArray(values) ? values : []) {
202
+ const text = String(raw || "").trim();
203
+ if (!text) continue;
204
+ const key = pathCompareKey(text);
205
+ if (seen.has(key)) continue;
206
+ seen.add(key);
207
+ out.push(path.resolve(text));
208
+ }
209
+ return out;
210
+ }
211
+
212
+ function readPathEnvDirs() {
213
+ const raw = process.env.PATH || process.env.Path || process.env.path || "";
214
+ if (!raw) return [];
215
+ return String(raw)
216
+ .split(path.delimiter)
217
+ .map((item) => String(item || "").trim())
218
+ .filter(Boolean);
219
+ }
220
+
221
+ function fileExists(filePath) {
222
+ try {
223
+ return fs.statSync(filePath).isFile();
224
+ } catch (_error) {
225
+ return false;
226
+ }
227
+ }
228
+
229
+ function readCurrentPackageVersion() {
230
+ if (packageVersionCache) return packageVersionCache;
231
+ try {
232
+ const parsed = JSON.parse(fs.readFileSync(PACKAGE_JSON_PATH, "utf8"));
233
+ const value = String(parsed && parsed.version ? parsed.version : "").trim();
234
+ packageVersionCache = value || "latest";
235
+ } catch (_error) {
236
+ packageVersionCache = "latest";
237
+ }
238
+ return packageVersionCache;
239
+ }
240
+
241
+ function isLikelyNpxCachePath(filePath) {
242
+ const normalized = String(filePath || "")
243
+ .replace(/\\/g, "/")
244
+ .toLowerCase();
245
+ return normalized.includes("/_npx/");
246
+ }
247
+
248
+ function lookupCommandInPath(commandName) {
249
+ const bin = String(commandName || "").trim();
250
+ if (!bin) return null;
251
+ const lookupTool = process.platform === "win32" ? "where.exe" : "which";
252
+ try {
253
+ const run = spawnSync(lookupTool, [bin], {
254
+ stdio: "pipe",
255
+ encoding: "utf8",
256
+ shell: false,
257
+ timeout: 2500,
258
+ windowsHide: true
259
+ });
260
+ if (!run || run.status !== 0) return null;
261
+ const lines = String(run.stdout || "")
262
+ .split(/\r?\n/)
263
+ .map((line) => String(line || "").trim())
264
+ .filter(Boolean);
265
+ for (const line of lines) {
266
+ const resolved = path.resolve(line);
267
+ if (fileExists(resolved)) return resolved;
268
+ }
269
+ } catch (_error) {
270
+ return null;
271
+ }
272
+ return null;
273
+ }
274
+
275
+ function resolveNpxCommandBinary() {
276
+ if (process.platform === "win32") {
277
+ const nodeDir = path.dirname(path.resolve(String(process.execPath || "")));
278
+ const bundled = path.join(nodeDir, "npx.cmd");
279
+ if (fileExists(bundled)) return bundled;
280
+ return lookupCommandInPath("npx.cmd") || lookupCommandInPath("npx") || "npx";
281
+ }
282
+ return lookupCommandInPath("npx") || "npx";
283
+ }
284
+
285
+ function resolveWindowsMcpCommand() {
286
+ if (process.platform !== "win32") return null;
287
+
288
+ const fromPathCmd = lookupCommandInPath(`${DEFAULT_MCP_COMMAND}.cmd`);
289
+ if (fromPathCmd) {
290
+ return { mcpCommand: fromPathCmd, mcpArgs: [], resolution: "windows-cmd-path" };
291
+ }
292
+
293
+ const packageRoot = path.resolve(__dirname, "..");
294
+ const nodeModulesDir = path.dirname(packageRoot);
295
+ const npmRootCandidate = path.dirname(nodeModulesDir);
296
+ const npmPrefix = envText("npm_config_prefix");
297
+ const appData = envText("APPDATA");
298
+ const candidateDirs = uniquePaths([
299
+ path.join(nodeModulesDir, ".bin"),
300
+ npmRootCandidate,
301
+ npmPrefix ? path.resolve(expandHome(npmPrefix)) : null,
302
+ appData ? path.join(path.resolve(appData), "npm") : null,
303
+ ...readPathEnvDirs()
304
+ ]);
305
+
306
+ for (const dirPath of candidateDirs) {
307
+ const candidate = path.join(dirPath, `${DEFAULT_MCP_COMMAND}.cmd`);
308
+ if (!fileExists(candidate)) continue;
309
+ return { mcpCommand: candidate, mcpArgs: [], resolution: "windows-cmd-absolute" };
310
+ }
311
+
312
+ const fromPathGeneric = lookupCommandInPath(DEFAULT_MCP_COMMAND);
313
+ if (fromPathGeneric && fromPathGeneric.toLowerCase().endsWith(".cmd")) {
314
+ return { mcpCommand: fromPathGeneric, mcpArgs: [], resolution: "windows-cmd-generic" };
315
+ }
316
+ return null;
317
+ }
318
+
319
+ function resolvePosixMcpCommand() {
320
+ if (process.platform === "win32") return null;
321
+
322
+ const fromPath = lookupCommandInPath(DEFAULT_MCP_COMMAND);
323
+ if (fromPath) {
324
+ return { mcpCommand: fromPath, mcpArgs: [], resolution: "posix-path-absolute" };
325
+ }
326
+
327
+ const packageRoot = path.resolve(__dirname, "..");
328
+ const localBin = path.join(path.dirname(packageRoot), ".bin", DEFAULT_MCP_COMMAND);
329
+ if (fileExists(localBin)) {
330
+ return { mcpCommand: localBin, mcpArgs: [], resolution: "posix-local-bin" };
331
+ }
332
+ return null;
333
+ }
334
+
335
+ function resolveNodeScriptMcpTarget() {
336
+ if (!fileExists(DEFAULT_MCP_COMMAND_ENTRY)) return null;
337
+ const nodeBin = path.resolve(String(process.execPath || ""));
338
+ if (!fileExists(nodeBin)) return null;
339
+ const ephemeral = isLikelyNpxCachePath(DEFAULT_MCP_COMMAND_ENTRY);
340
+ return {
341
+ mcpCommand: nodeBin,
342
+ mcpArgs: [DEFAULT_MCP_COMMAND_ENTRY],
343
+ resolution: ephemeral ? "node-script-ephemeral" : "node-script-absolute",
344
+ ephemeral
345
+ };
346
+ }
347
+
348
+ function resolveNpxMcpTarget() {
349
+ const npxCommand = resolveNpxCommandBinary();
350
+ if (!npxCommand) return null;
351
+ const version = readCurrentPackageVersion();
352
+ return {
353
+ mcpCommand: npxCommand,
354
+ mcpArgs: ["-y", "-p", `mcp-aws-manager@${version}`, DEFAULT_MCP_COMMAND],
355
+ resolution: "npx-pinned"
356
+ };
357
+ }
358
+
359
+ function resolveDefaultRegistrationTarget() {
360
+ const serverName = DEFAULT_SERVER_NAME;
361
+ const windowsCmdTarget = resolveWindowsMcpCommand();
362
+ const posixTarget = resolvePosixMcpCommand();
363
+ const nodeScriptTarget = resolveNodeScriptMcpTarget();
364
+ const npxTarget = resolveNpxMcpTarget();
365
+
366
+ if (process.platform === "win32") {
367
+ if (windowsCmdTarget) return { serverName, ...windowsCmdTarget };
368
+ if (nodeScriptTarget && !nodeScriptTarget.ephemeral) return { serverName, ...nodeScriptTarget };
369
+ if (npxTarget) return { serverName, ...npxTarget };
370
+ if (nodeScriptTarget) return { serverName, ...nodeScriptTarget };
371
+ return { serverName, mcpCommand: DEFAULT_MCP_COMMAND, mcpArgs: [], resolution: "path-default" };
372
+ }
373
+
374
+ if (posixTarget) return { serverName, ...posixTarget };
375
+ if (nodeScriptTarget && !nodeScriptTarget.ephemeral) return { serverName, ...nodeScriptTarget };
376
+ if (npxTarget) return { serverName, ...npxTarget };
377
+ if (nodeScriptTarget) return { serverName, ...nodeScriptTarget };
378
+ return { serverName, mcpCommand: DEFAULT_MCP_COMMAND, mcpArgs: [], resolution: "path-default" };
379
+ }
380
+
381
+ function registrationTargetHelpArgs(target) {
382
+ const args = Array.isArray(target && target.mcpArgs) ? target.mcpArgs.slice() : [];
383
+ args.push("--help");
384
+ return args;
385
+ }
386
+
190
387
  function parseShardSpec(raw, label) {
191
388
  if (raw == null || String(raw).trim() === "") return null;
192
389
  const text = String(raw).trim();
@@ -526,6 +723,8 @@ function usageText() {
526
723
  return [
527
724
  "Usage:",
528
725
  " mcp-aws-manager",
726
+ " mcp-aws-manager version",
727
+ " mcp-aws-manager --version",
529
728
  " mcp-aws-manager bootstrap [options]",
530
729
  " mcp-aws-manager setup [options]",
531
730
  " mcp-aws-manager doctor [options]",
@@ -537,6 +736,7 @@ function usageText() {
537
736
  "SSM-first AWS inventory/runtime collector (EC2/Lambda/ALB/ASG/RDS/ElastiCache/Route53 + VPC/ECS/S3/IAM/KMS/CloudWatch/CloudTrail/Config/ECR/DynamoDB/SNS/SQS/Budgets/CostAnomaly + EBS/EFS/EKS/APIGateway/CloudFront/WAF/Shield/StepFunctions/CloudWatchLogs/X-Ray/Inspector2/Redshift/OpenSearch/Organizations/ControlTower + ACM/Kinesis/MSK) plus MCP client setup helper.",
538
737
  "",
539
738
  "Commands:",
739
+ " version Print CLI package version",
540
740
  " bootstrap Ensure mcp-aws-manager MCP server is registered (default command)",
541
741
  " setup Register/re-register MCP server for supported clients",
542
742
  " doctor Check install and registration health",
@@ -545,8 +745,8 @@ function usageText() {
545
745
  " discover Run multi-service inventory workflow (EC2/Lambda/ALB/ASG/RDS/ElastiCache/Route53 + VPC/ECS/S3/IAM/KMS/CloudWatch + extended platform/security/analytics services)",
546
746
  "",
547
747
  "Setup/Bootstrap/Doctor options:",
548
- " (fixed target: mcp-aws-manager => mcp-aws-manager-mcp)",
549
- ` --clients <${clientList}> (default: codex,claude)`,
748
+ " (target: mcp-aws-manager => auto-resolved runtime command for mcp-aws-manager-mcp)",
749
+ ` --clients <${clientList}> (default: auto-detect installed clients)`,
550
750
  " --force (setup/bootstrap only; always remove then add)",
551
751
  " -h, --help",
552
752
  "",
@@ -798,6 +998,9 @@ function parseCommand(argv) {
798
998
  }
799
999
 
800
1000
  const first = String(args[0] || "");
1001
+ if (first === "-v" || first === "--version" || first === "version") {
1002
+ return { command: "version", args: [] };
1003
+ }
801
1004
  if (first === "-h" || first === "--help") {
802
1005
  return { command: "help", args: [] };
803
1006
  }
@@ -873,10 +1076,13 @@ function parseRegistrationArgs(argv, opts = {}) {
873
1076
  return { help: true };
874
1077
  }
875
1078
 
1079
+ const defaultTarget = resolveDefaultRegistrationTarget();
1080
+ const clientsExplicit = Boolean(options.clients && String(options.clients).trim());
876
1081
  return {
877
1082
  help: false,
878
- targets: [{ serverName: DEFAULT_SERVER_NAME, mcpCommand: DEFAULT_MCP_COMMAND, mcpArgs: [] }],
879
- clients: options.clients ? parseClients(options.clients) : ["codex", "claude"],
1083
+ targets: [defaultTarget],
1084
+ clients: options.clients ? parseClients(options.clients) : Array.from(SUPPORTED_CLIENTS),
1085
+ clientsExplicit,
880
1086
  force: options.force
881
1087
  };
882
1088
  }
@@ -2262,23 +2468,39 @@ function targetCommandLabel(target) {
2262
2468
  return [command, ...args].join(" ");
2263
2469
  }
2264
2470
 
2471
+ function detectClientAvailability(clients) {
2472
+ const candidateClients = Array.isArray(clients) && clients.length
2473
+ ? clients
2474
+ : Array.from(SUPPORTED_CLIENTS);
2475
+ return candidateClients.map((cliBin) => {
2476
+ const exists = clientHelpAttempts(cliBin).some((args) => commandExists(cliBin, args));
2477
+ return { cliBin, exists };
2478
+ });
2479
+ }
2480
+
2265
2481
  function runSetupInternal(config, options = {}) {
2266
2482
  const ensureOnly = options.ensureOnly === true;
2483
+ const defaultTarget = resolveDefaultRegistrationTarget();
2267
2484
  const targets = Array.isArray(config.targets) && config.targets.length
2268
2485
  ? config.targets
2269
- : [{ serverName: DEFAULT_SERVER_NAME, mcpCommand: DEFAULT_MCP_COMMAND, mcpArgs: [] }];
2270
- const clients = config.clients.filter((cli) =>
2271
- clientHelpAttempts(cli).some((args) => commandExists(cli, args))
2272
- );
2486
+ : [defaultTarget];
2487
+ const clientsExplicit = config && config.clientsExplicit === true;
2488
+ const availability = detectClientAvailability(config.clients);
2489
+ const clients = availability.filter((row) => row.exists).map((row) => row.cliBin);
2490
+ const missingClients = availability.filter((row) => !row.exists).map((row) => row.cliBin);
2273
2491
  const results = [];
2274
2492
  const commandChecks = targets.map((target) => ({
2275
2493
  ...target,
2276
- ok: commandExists(target.mcpCommand, ["--help"])
2494
+ ok: commandExists(target.mcpCommand, registrationTargetHelpArgs(target))
2277
2495
  }));
2278
2496
 
2279
2497
  process.stdout.write(ensureOnly ? "Bootstrap start.\n" : "Setup start.\n");
2280
2498
  process.stdout.write(`Targets: ${targets.map((t) => `${t.serverName}=>${targetCommandLabel(t)}`).join(", ")}\n`);
2281
- process.stdout.write(`Clients: ${config.clients.join(",")}\n`);
2499
+ process.stdout.write(`Clients(requested): ${availability.map((row) => row.cliBin).join(",")}\n`);
2500
+ process.stdout.write(`Clients(detected): ${clients.length ? clients.join(",") : "none"}\n`);
2501
+ if (!clientsExplicit && missingClients.length) {
2502
+ process.stdout.write(`Clients(skipped): ${missingClients.join(",")} (not installed or not available in PATH)\n`);
2503
+ }
2282
2504
 
2283
2505
  if (!clients.length) {
2284
2506
  process.stdout.write(`No supported client CLI found. Supported: ${Array.from(SUPPORTED_CLIENTS).join(", ")}\n`);
@@ -2343,16 +2565,20 @@ function runSetupInternal(config, options = {}) {
2343
2565
  }
2344
2566
 
2345
2567
  function runDoctor(config) {
2568
+ const defaultTarget = resolveDefaultRegistrationTarget();
2346
2569
  const targets = Array.isArray(config.targets) && config.targets.length
2347
2570
  ? config.targets
2348
- : [{ serverName: DEFAULT_SERVER_NAME, mcpCommand: DEFAULT_MCP_COMMAND, mcpArgs: [] }];
2571
+ : [defaultTarget];
2572
+ const clientsExplicit = config && config.clientsExplicit === true;
2573
+ const availability = detectClientAvailability(config.clients);
2349
2574
  process.stdout.write("Doctor start.\n");
2350
2575
  process.stdout.write(`Targets: ${targets.map((t) => `${t.serverName}=>${targetCommandLabel(t)}`).join(", ")}\n`);
2576
+ process.stdout.write(`Clients(requested): ${availability.map((row) => row.cliBin).join(",")}\n`);
2351
2577
  let hasIssue = false;
2352
2578
  let foundClient = false;
2353
2579
 
2354
2580
  for (const target of targets) {
2355
- const mcpCommandRun = runCLICommand(target.mcpCommand, ["--help"], { stdio: "pipe" });
2581
+ const mcpCommandRun = runCLICommand(target.mcpCommand, registrationTargetHelpArgs(target), { stdio: "pipe" });
2356
2582
  if (mcpCommandRun && mcpCommandRun.status === 0) {
2357
2583
  process.stdout.write(`mcp-command: ok (${target.serverName}=>${targetCommandLabel(target)})\n`);
2358
2584
  } else {
@@ -2363,11 +2589,15 @@ function runDoctor(config) {
2363
2589
  }
2364
2590
  }
2365
2591
 
2366
- for (const cliBin of config.clients) {
2367
- const exists = clientHelpAttempts(cliBin).some((args) => commandExists(cliBin, args));
2368
- if (!exists) {
2369
- hasIssue = true;
2370
- process.stdout.write(`${cliBin}: not installed or not available in PATH\n`);
2592
+ for (const row of availability) {
2593
+ const cliBin = row.cliBin;
2594
+ if (!row.exists) {
2595
+ if (clientsExplicit) {
2596
+ hasIssue = true;
2597
+ process.stdout.write(`${cliBin}: not installed or not available in PATH\n`);
2598
+ } else {
2599
+ process.stdout.write(`${cliBin}: skipped (not installed or not available in PATH)\n`);
2600
+ }
2371
2601
  continue;
2372
2602
  }
2373
2603
 
@@ -2386,6 +2616,7 @@ function runDoctor(config) {
2386
2616
  }
2387
2617
 
2388
2618
  if (!foundClient) {
2619
+ hasIssue = true;
2389
2620
  process.stdout.write("No requested clients detected. Install at least one supported client first.\n");
2390
2621
  }
2391
2622
 
@@ -11865,6 +12096,11 @@ async function runWorkflow(config) {
11865
12096
  async function main() {
11866
12097
  try {
11867
12098
  const parsed = parseCommand(process.argv.slice(2));
12099
+ if (parsed.command === "version") {
12100
+ process.stdout.write(`${readCurrentPackageVersion()}\n`);
12101
+ process.exitCode = 0;
12102
+ return;
12103
+ }
11868
12104
  if (parsed.command === "help") {
11869
12105
  process.stdout.write(usageText());
11870
12106
  process.exitCode = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-aws-manager",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "AWS operations CLI and MCP server (SSM-only) for EC2/Lambda inventory, remediation, and runtime snapshots",
5
5
  "license": "MIT",
6
6
  "publishConfig": {