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 +53 -14
- package/README_KO.md +53 -14
- package/bin/mcp-aws-manager.js +253 -17
- package/package.json +1 -1
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
|
|
151
|
-
mcp-aws-manager setup --clients
|
|
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
|
|
151
|
-
mcp-aws-manager setup --clients
|
|
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
|
+
|
package/bin/mcp-aws-manager.js
CHANGED
|
@@ -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
|
-
" (
|
|
549
|
-
` --clients <${clientList}> (default:
|
|
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: [
|
|
879
|
-
clients: options.clients ? parseClients(options.clients) :
|
|
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
|
-
: [
|
|
2270
|
-
const
|
|
2271
|
-
|
|
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,
|
|
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: ${
|
|
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
|
-
: [
|
|
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,
|
|
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
|
|
2367
|
-
const
|
|
2368
|
-
if (!exists) {
|
|
2369
|
-
|
|
2370
|
-
|
|
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