@pecot/tools-mcp 1.0.2 → 1.1.1
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/.claude-plugin/plugin.json +1 -1
- package/README.md +30 -3
- package/cli/dist/cli.d.ts +1 -2
- package/cli/dist/cli.js +9 -13
- package/cli/dist/diagnostics.d.ts +38 -0
- package/cli/dist/diagnostics.js +46 -0
- package/cli/dist/domain/arguments.d.ts +2 -15
- package/cli/dist/domain/arguments.js +38 -99
- package/cli/dist/domain/athena-query.d.ts +2 -8
- package/cli/dist/domain/athena-query.js +10 -35
- package/cli/dist/domain/s3-input.d.ts +2 -9
- package/cli/dist/domain/s3-input.js +8 -21
- package/cli/dist/infra/awscli.d.ts +8 -36
- package/cli/dist/infra/awscli.js +15 -55
- package/cli/dist/infra/sso/login-registry.d.ts +4 -21
- package/cli/dist/infra/sso/login-registry.js +13 -56
- package/cli/dist/mcp/serve.d.ts +6 -28
- package/cli/dist/mcp/serve.js +20 -64
- package/cli/dist/mcp/server.d.ts +4 -20
- package/cli/dist/mcp/server.js +9 -35
- package/cli/dist/mcp/tools/_shared.d.ts +6 -27
- package/cli/dist/mcp/tools/_shared.js +36 -65
- package/cli/dist/mcp/tools/athena.js +17 -27
- package/cli/dist/mcp/tools/cloudwatch.js +13 -15
- package/cli/dist/mcp/tools/ec2.js +7 -10
- package/cli/dist/mcp/tools/ecr.js +10 -16
- package/cli/dist/mcp/tools/index.js +1 -5
- package/cli/dist/mcp/tools/lambda.js +10 -10
- package/cli/dist/mcp/tools/logs.js +22 -22
- package/cli/dist/mcp/tools/rds.js +15 -18
- package/cli/dist/mcp/tools/s3.js +19 -29
- package/cli/dist/mcp/tools/scheduler.js +10 -13
- package/cli/dist/mcp/tools/sqs.js +8 -8
- package/cli/dist/mcp/tools/sso.d.ts +1 -5
- package/cli/dist/mcp/tools/sso.js +33 -59
- package/cli/dist/mcp/tools/sts.js +2 -2
- package/cli/dist/node-runtime.d.ts +6 -1
- package/cli/dist/node-runtime.js +16 -8
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
// Athena に投げるクエリが参照専用かの判定(FR-008 / research R6)。
|
|
2
|
-
//
|
|
3
|
-
// 完全な SQL パーサは持たない。目的は「変更系と複文を awscli 起動前に落とす」ことなので、
|
|
4
|
-
// 判定できない入力(未終端の引用符・コメント)は許可側ではなく拒否側へ倒す。
|
|
5
|
-
// 判定は Trino 構文を前提にする。Athena では `WITH` で始まる文は必ず問い合わせで、
|
|
6
|
-
// `INSERT INTO … WITH` / `CREATE TABLE … AS WITH` の `WITH` は先頭に来ない。
|
|
7
|
-
// STG1 での確認(2026-09-07): `WITH … INSERT` は StartQueryExecution 自体が失敗する。
|
|
8
|
-
// 失敗の内容は InvalidRequestException / MALFORMED_QUERY で、実行に至らない。
|
|
9
|
-
// 例外は内側の文を実際に実行する `EXPLAIN ANALYZE` だけで、これは `EXPLAIN` を剥がす反復で塞ぐ。
|
|
2
|
+
// 判定できない入力は拒否側へ倒す。`EXPLAIN ANALYZE` は内側の文を実行するので、`EXPLAIN` を剥がす反復で塞ぐ。
|
|
10
3
|
/**
|
|
11
4
|
参照専用として受け付ける先頭キーワード。判定は大小文字を区別しない。
|
|
12
5
|
*/
|
|
@@ -20,10 +13,10 @@ const EXPLAIN_MODIFIERS = new Set(['ANALYZE', 'VERBOSE']);
|
|
|
20
13
|
*/
|
|
21
14
|
const LEADING_WORD = /^\s*[A-Z]+/i;
|
|
22
15
|
const REJECTION_MESSAGES = {
|
|
23
|
-
'empty': 'query_string
|
|
24
|
-
'multi-statement': '
|
|
25
|
-
'not-read-only': '
|
|
26
|
-
'unterminated': '
|
|
16
|
+
'empty': 'query_string is empty. Specify a read-only query',
|
|
17
|
+
'multi-statement': 'Multiple statements are not allowed. Specify a single statement with nothing after the ;',
|
|
18
|
+
'not-read-only': 'Only read-only queries (SELECT / WITH / SHOW / DESCRIBE / EXPLAIN) can be executed',
|
|
19
|
+
'unterminated': 'Cannot confirm the query is read-only because a quote or comment is not closed',
|
|
27
20
|
};
|
|
28
21
|
function reject(reason, message) {
|
|
29
22
|
return { allowed: false, reason, message };
|
|
@@ -43,11 +36,7 @@ function skipBlockComment(query, start) {
|
|
|
43
36
|
return end === -1 ? undefined : end + 2;
|
|
44
37
|
}
|
|
45
38
|
/**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
`'…'` 文字列リテラルと `"…"` 引用識別子のエスケープは、同じ引用符の 2 重化だけ。
|
|
49
|
-
Trino では `\` は文字列リテラル中でも普通の文字なので、`'\'` は「閉じていない」ではなく
|
|
50
|
-
「バックスラッシュ 1 文字のリテラル」になる。
|
|
39
|
+
引用の終端(閉じ引用符の次)。エスケープは引用符の 2 重化だけで、Trino では `\` は普通の文字。
|
|
51
40
|
*/
|
|
52
41
|
function skipQuoted(query, start, quote) {
|
|
53
42
|
let index = start;
|
|
@@ -65,11 +54,7 @@ function skipQuoted(query, start, quote) {
|
|
|
65
54
|
return undefined;
|
|
66
55
|
}
|
|
67
56
|
/**
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
中身を捨てるのは、後段が見るのは「リテラル外の `;`」と「先頭キーワード」だけだから。
|
|
71
|
-
リテラルの内容は判定材料にならない。均しておけばリテラル内の `;` や `--` が
|
|
72
|
-
後段の単純な文字列走査に引っ掛からない。
|
|
57
|
+
コメントを空白へ、引用の中身を空の引用へ均す。リテラル内の `;` や `--` を後段の走査に拾わせないため。
|
|
73
58
|
*/
|
|
74
59
|
function scan(query) {
|
|
75
60
|
let code = '';
|
|
@@ -126,10 +111,7 @@ function afterLeadingWord(text) {
|
|
|
126
111
|
return matched === undefined ? text : text.slice(matched[0].length);
|
|
127
112
|
}
|
|
128
113
|
/**
|
|
129
|
-
先頭が `(`
|
|
130
|
-
|
|
131
|
-
対応する `)` が無い場合は剥がさずに返す。剥がせないと、後段の語判定は左括弧を見て
|
|
132
|
-
語を取れず、拒否側へ倒れる。
|
|
114
|
+
先頭が `(` ならその対応括弧までを剥がす。対応する `)` が無ければ剥がさず、拒否側へ倒す。
|
|
133
115
|
*/
|
|
134
116
|
function afterOptionGroup(text) {
|
|
135
117
|
const trimmed = text.trimStart();
|
|
@@ -149,11 +131,7 @@ function afterOptionGroup(text) {
|
|
|
149
131
|
return trimmed;
|
|
150
132
|
}
|
|
151
133
|
/**
|
|
152
|
-
1
|
|
153
|
-
|
|
154
|
-
`EXPLAIN` は修飾語とオプション群を剥がして内側を見直す。1 周ごとに必ず `EXPLAIN` の 1 語を
|
|
155
|
-
消費するので停止する。再帰ではなくループにするのは、`EXPLAIN` を数万段重ねた入力で
|
|
156
|
-
コールスタックを溢れさせないため(溢れると失敗種別の外の例外になる)。
|
|
134
|
+
1 文が参照専用か。再帰でなくループなのは、`EXPLAIN` を重ねた入力でコールスタックを溢れさせないため。
|
|
157
135
|
*/
|
|
158
136
|
function isReadOnlyStatement(statement) {
|
|
159
137
|
let current = statement;
|
|
@@ -169,10 +147,7 @@ function isReadOnlyStatement(statement) {
|
|
|
169
147
|
return READ_ONLY_KEYWORDS.has(word);
|
|
170
148
|
}
|
|
171
149
|
/**
|
|
172
|
-
クエリを参照専用 /
|
|
173
|
-
|
|
174
|
-
判定順は 未終端 → 複文 → 空 → 先頭キーワード。複文を空より先に見るのは、
|
|
175
|
-
`; SELECT 1` のように 1 文目が空の入力を「空」ではなく「複文」と伝えるため。
|
|
150
|
+
クエリを参照専用 / 拒否に分類する。複文を空より先に見るのは、`; SELECT 1` を「複文」と伝えるため。
|
|
176
151
|
*/
|
|
177
152
|
export function classifyAthenaQuery(query) {
|
|
178
153
|
const scanned = scan(query);
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
`s3://<bucket>/<key>`
|
|
3
|
-
|
|
4
|
-
空白を含む値は拒否しない。引数は shell を介さず、1 要素 1 値で awscli へ渡る。
|
|
5
|
-
そのため `s3://b/k --recursive` のような値は「そういう名前のキー」として扱われ、
|
|
6
|
-
別オプションには化けない(research R3)。
|
|
2
|
+
`s3://<bucket>/<key>` 形式か。空白を含む値は、shell を介さず 1 値で渡るので拒否しない(research R3)。
|
|
7
3
|
*/
|
|
8
4
|
export declare function validateS3Uri(value: string): string | undefined;
|
|
9
5
|
/**
|
|
@@ -11,10 +7,7 @@ export declare function validateS3Uri(value: string): string | undefined;
|
|
|
11
7
|
*/
|
|
12
8
|
export declare function validateS3ListUri(value: string): string | undefined;
|
|
13
9
|
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
`s3://…` を先に見るのは、絶対パスの検証だけでも弾けるが「相対パスだ」という案内になり、
|
|
17
|
-
取得元と書き出し先を取り違えた利用者に誤った直し方を促すため。
|
|
10
|
+
書き出し先のローカルパスとして妥当か。`s3://…` を先に弾くのは、取得元と取り違えた利用者に誤案内しないため。
|
|
18
11
|
*/
|
|
19
12
|
export declare function validateLocalPath(value: string): string | undefined;
|
|
20
13
|
//# sourceMappingURL=s3-input.d.ts.map
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
// S3 の入力値(オブジェクト URI・一覧 URI
|
|
2
|
-
//
|
|
3
|
-
// research R7 の方向制約は「入力の形で表現できない」ことで担保しているが、値そのものが
|
|
4
|
-
// 期待どおりの形かはここで見る。書式は contracts/mcp-tools.md の正規表現を逐語で写す。
|
|
5
|
-
//
|
|
6
|
-
// 戻り値は「入力名に続く述部」で、入力名と値の連結は arguments.ts が行う(`<name> <述部>: <value>`)。
|
|
7
|
-
// 述部に助詞まで含めるのは、`s3://…` のような欧文始まりの語で助詞との間に空白が要るかどうかが
|
|
8
|
-
// 語ごとに変わるため。文言の組み立てを 1 箇所に寄せると、規則を足したときに書式が割れない。
|
|
1
|
+
// S3 の入力値(オブジェクト URI・一覧 URI・ローカル出力先)の書式検証。書式は contracts/mcp-tools.md の正規表現を写す。
|
|
2
|
+
// 戻り値は入力名に続く述部で、助詞の前の空白の要否を 1 箇所に寄せるため、連結は arguments.ts が行う。
|
|
9
3
|
import path from 'node:path';
|
|
10
4
|
/**
|
|
11
5
|
`aws_s3_cp` の取得元。バケットとキーの双方を要求する(契約表 aws_s3_cp の s3-uri)。
|
|
@@ -21,30 +15,23 @@ const S3_LIST_URI = /^s3:\/\/[^/]*(?:\/.*)?$/;
|
|
|
21
15
|
*/
|
|
22
16
|
const S3_SCHEME = /^s3:\/\//i;
|
|
23
17
|
/**
|
|
24
|
-
`s3://<bucket>/<key>`
|
|
25
|
-
|
|
26
|
-
空白を含む値は拒否しない。引数は shell を介さず、1 要素 1 値で awscli へ渡る。
|
|
27
|
-
そのため `s3://b/k --recursive` のような値は「そういう名前のキー」として扱われ、
|
|
28
|
-
別オプションには化けない(research R3)。
|
|
18
|
+
`s3://<bucket>/<key>` 形式か。空白を含む値は、shell を介さず 1 値で渡るので拒否しない(research R3)。
|
|
29
19
|
*/
|
|
30
20
|
export function validateS3Uri(value) {
|
|
31
|
-
return S3_OBJECT_URI.test(value) ? undefined : '
|
|
21
|
+
return S3_OBJECT_URI.test(value) ? undefined : 'must be in the form s3://<bucket>/<key>';
|
|
32
22
|
}
|
|
33
23
|
/**
|
|
34
24
|
`s3 ls` の位置引数として妥当か(バケット一覧・prefix 指定の双方を許す)。
|
|
35
25
|
*/
|
|
36
26
|
export function validateS3ListUri(value) {
|
|
37
|
-
return S3_LIST_URI.test(value) ? undefined : '
|
|
27
|
+
return S3_LIST_URI.test(value) ? undefined : 'must be in the form s3://<bucket>[/<prefix>]';
|
|
38
28
|
}
|
|
39
29
|
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
`s3://…` を先に見るのは、絶対パスの検証だけでも弾けるが「相対パスだ」という案内になり、
|
|
43
|
-
取得元と書き出し先を取り違えた利用者に誤った直し方を促すため。
|
|
30
|
+
書き出し先のローカルパスとして妥当か。`s3://…` を先に弾くのは、取得元と取り違えた利用者に誤案内しないため。
|
|
44
31
|
*/
|
|
45
32
|
export function validateLocalPath(value) {
|
|
46
33
|
if (S3_SCHEME.test(value))
|
|
47
|
-
return '
|
|
48
|
-
return path.isAbsolute(value) ? undefined : '
|
|
34
|
+
return 'must be a local absolute path (s3:// cannot be a destination)';
|
|
35
|
+
return path.isAbsolute(value) ? undefined : 'must be an absolute path';
|
|
49
36
|
}
|
|
50
37
|
//# sourceMappingURL=s3-input.js.map
|
|
@@ -1,30 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
起動するコマンド。PATH
|
|
3
|
-
|
|
4
|
-
SSO のログインレジストリ(infra/sso/login-registry.ts)も同じコマンドで spawn するため
|
|
5
|
-
export する。片方だけ絶対パス化するような差が入らないようにしておく。
|
|
2
|
+
起動するコマンド。PATH 解決に委ねる。SSO のログインレジストリも同じコマンドで spawn するので export する。
|
|
6
3
|
*/
|
|
7
4
|
export declare const AWSCLI_COMMAND = "aws";
|
|
8
5
|
/**
|
|
9
|
-
awscli 出力の上限(バイト、research R8
|
|
10
|
-
|
|
11
|
-
応答へ載せる際の切り詰め(mcp/result.ts の limitOutput)と、本モジュールの捕捉上限が
|
|
12
|
-
同じ値を基準にする。依存方向が infra → mcp を許さないため、定義は infra に置く。
|
|
6
|
+
awscli 出力の上限(バイト、research R8)。依存方向が infra → mcp を許さないため、定義は infra に置く。
|
|
13
7
|
*/
|
|
14
8
|
export declare const OUTPUT_LIMIT_BYTES: number;
|
|
15
9
|
/**
|
|
16
|
-
子プロセスの stdout / stderr
|
|
17
|
-
|
|
18
|
-
research R8 の `filter-log-events` は既定 10,000 件で数 MiB になり得る。全件を溜めると
|
|
19
|
-
同時実行数(4)と掛かってサーバの常駐メモリに直結するため、応答上限の 4 倍で打ち切る
|
|
20
|
-
(切り詰め後の応答に必要な分は必ず残る)。
|
|
10
|
+
子プロセスの stdout / stderr を捕捉する上限(バイト)。超過分は捨てる。常駐メモリを抑えるため、応答上限の 4 倍で打ち切る。
|
|
21
11
|
*/
|
|
22
12
|
export declare const CAPTURE_LIMIT_BYTES: number;
|
|
23
13
|
/**
|
|
24
|
-
生存中の awscli 子プロセス全てへ SIGTERM
|
|
25
|
-
|
|
26
|
-
終了処理(mcp/serve.ts の onShutdown)から呼ぶ。既に終了した子への kill は no-op で、
|
|
27
|
-
finalize 側が Set から外すため二重送出にはならない。
|
|
14
|
+
生存中の awscli 子プロセス全てへ SIGTERM を送る。終了済みの子への kill は no-op なので二重送出にならない。
|
|
28
15
|
*/
|
|
29
16
|
export declare function killRunningAwscli(): void;
|
|
30
17
|
/**
|
|
@@ -44,10 +31,7 @@ export interface SpawnResult {
|
|
|
44
31
|
export interface RunAwscliOptions {
|
|
45
32
|
readonly timeoutMs: number;
|
|
46
33
|
/**
|
|
47
|
-
呼び出し元がキャンセルされたら子プロセスへ SIGTERM
|
|
48
|
-
|
|
49
|
-
MCP の tools/call は ToolCallContext.signal でキャンセルされる。配線しないと、クライアントが
|
|
50
|
-
諦めたあとも awscli が孤児として動き続ける。
|
|
34
|
+
呼び出し元がキャンセルされたら子プロセスへ SIGTERM を送る。配線しないと awscli が孤児として動き続ける。
|
|
51
35
|
*/
|
|
52
36
|
readonly signal?: AbortSignal | undefined;
|
|
53
37
|
}
|
|
@@ -63,18 +47,11 @@ export interface OutputCapture {
|
|
|
63
47
|
text(): string;
|
|
64
48
|
}
|
|
65
49
|
/**
|
|
66
|
-
CAPTURE_LIMIT_BYTES まで溜め、超過分は捨てる capture を作る。
|
|
67
|
-
|
|
68
|
-
SSO のログインレジストリ(infra/sso/login-registry.ts)も同じ上限で子プロセスの出力を
|
|
69
|
-
溜めるため export する。上限が呼び出し元ごとに割れると、常駐メモリの見積もりが立たなくなる。
|
|
50
|
+
CAPTURE_LIMIT_BYTES まで溜め、超過分は捨てる capture を作る。SSO のログインレジストリも同じ上限を使うので export する。
|
|
70
51
|
*/
|
|
71
52
|
export declare function createCapture(): OutputCapture;
|
|
72
53
|
/**
|
|
73
|
-
`aws <args>` を起動し、
|
|
74
|
-
|
|
75
|
-
timeout 到達・abort では SIGTERM を送り、`timedOut` / signal を結果へ載せる。
|
|
76
|
-
起動 error は reject せず `exitCode: null` + `spawnErrorCode` に詰める。
|
|
77
|
-
そのため呼び出し側は、失敗種別の判定を 1 か所(classifyFailure)に集約できる。
|
|
54
|
+
`aws <args>` を起動し、capture した stdout / stderr を返す。起動 error は reject せず spawnErrorCode に詰める。
|
|
78
55
|
*/
|
|
79
56
|
export declare function runAwscli(arguments_: readonly string[], options: Readonly<RunAwscliOptions>): Promise<SpawnResult>;
|
|
80
57
|
/**
|
|
@@ -89,12 +66,7 @@ export type AwscliProbe = {
|
|
|
89
66
|
readonly message: string;
|
|
90
67
|
};
|
|
91
68
|
/**
|
|
92
|
-
awscli
|
|
93
|
-
|
|
94
|
-
毎回 `aws --version` を起こすとツール 1 件あたりの子プロセスが 2 本になるため、
|
|
95
|
-
Promise 自体を memo する(並行呼び出しでも実行は 1 回)。
|
|
96
|
-
ただし失敗(未導入・版不足)を memo すると、利用者が awscli を導入してもサーバ再起動まで全ツールが awscli-missing を返し続ける。
|
|
97
|
-
そのため失敗時は memo を捨てて、次の呼び出しで再判定する。
|
|
69
|
+
awscli の存在と版を確認し、成功結果だけを memo する。失敗を memo すると導入後も再起動まで awscli-missing が続くため捨てる。
|
|
98
70
|
*/
|
|
99
71
|
export declare function ensureAwscli(): Promise<AwscliProbe>;
|
|
100
72
|
/**
|
package/cli/dist/infra/awscli.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
// awscli の子プロセス実行。
|
|
2
|
-
//
|
|
3
|
-
// shell:false で起動するため、引数配列の要素はそのまま 1 値として awscli に渡る
|
|
4
|
-
// (連結や特殊文字による別コマンド委譲が構造的に起きない)。timeout / AbortSignal は
|
|
5
|
-
// SIGTERM で子を落とし、起動失敗(ENOENT 等)は reject せず spawnErrorCode に詰める。
|
|
1
|
+
// awscli の子プロセス実行。shell:false で起動し、起動失敗は reject せず spawnErrorCode に詰める。
|
|
6
2
|
import { spawn } from 'node:child_process';
|
|
7
3
|
/**
|
|
8
|
-
起動するコマンド。PATH
|
|
9
|
-
|
|
10
|
-
SSO のログインレジストリ(infra/sso/login-registry.ts)も同じコマンドで spawn するため
|
|
11
|
-
export する。片方だけ絶対パス化するような差が入らないようにしておく。
|
|
4
|
+
起動するコマンド。PATH 解決に委ねる。SSO のログインレジストリも同じコマンドで spawn するので export する。
|
|
12
5
|
*/
|
|
13
6
|
export const AWSCLI_COMMAND = 'aws';
|
|
14
7
|
/**
|
|
@@ -25,34 +18,19 @@ message に載せる最低版の表記。
|
|
|
25
18
|
const MINIMUM_VERSION_TEXT = MINIMUM_VERSION.join('.');
|
|
26
19
|
const VERSION_PATTERN = /aws-cli\/(\d+)\.(\d+)\.(\d+)/;
|
|
27
20
|
/**
|
|
28
|
-
awscli 出力の上限(バイト、research R8
|
|
29
|
-
|
|
30
|
-
応答へ載せる際の切り詰め(mcp/result.ts の limitOutput)と、本モジュールの捕捉上限が
|
|
31
|
-
同じ値を基準にする。依存方向が infra → mcp を許さないため、定義は infra に置く。
|
|
21
|
+
awscli 出力の上限(バイト、research R8)。依存方向が infra → mcp を許さないため、定義は infra に置く。
|
|
32
22
|
*/
|
|
33
23
|
export const OUTPUT_LIMIT_BYTES = 256 * 1024;
|
|
34
24
|
/**
|
|
35
|
-
子プロセスの stdout / stderr
|
|
36
|
-
|
|
37
|
-
research R8 の `filter-log-events` は既定 10,000 件で数 MiB になり得る。全件を溜めると
|
|
38
|
-
同時実行数(4)と掛かってサーバの常駐メモリに直結するため、応答上限の 4 倍で打ち切る
|
|
39
|
-
(切り詰め後の応答に必要な分は必ず残る)。
|
|
25
|
+
子プロセスの stdout / stderr を捕捉する上限(バイト)。超過分は捨てる。常駐メモリを抑えるため、応答上限の 4 倍で打ち切る。
|
|
40
26
|
*/
|
|
41
27
|
export const CAPTURE_LIMIT_BYTES = OUTPUT_LIMIT_BYTES * 4;
|
|
42
28
|
/**
|
|
43
|
-
生存中の awscli
|
|
44
|
-
|
|
45
|
-
runAwscli が spawn 直後に足し、finalize(close / error のどちらか先)で外す。
|
|
46
|
-
サーバが SIGTERM で畳まれるときは親が先に消える。
|
|
47
|
-
そのため、ここを辿って子へも SIGTERM を送らないと、子が PPID 1 へ再親付けされて動き続ける。
|
|
48
|
-
timeout を張っていた親が居なくなるので、打ち切りも働かない。
|
|
29
|
+
生存中の awscli 子プロセス。サーバが SIGTERM で畳まれるとき、ここを辿って子へも送らないと PPID 1 で動き続ける。
|
|
49
30
|
*/
|
|
50
31
|
const runningChildren = new Set();
|
|
51
32
|
/**
|
|
52
|
-
生存中の awscli 子プロセス全てへ SIGTERM
|
|
53
|
-
|
|
54
|
-
終了処理(mcp/serve.ts の onShutdown)から呼ぶ。既に終了した子への kill は no-op で、
|
|
55
|
-
finalize 側が Set から外すため二重送出にはならない。
|
|
33
|
+
生存中の awscli 子プロセス全てへ SIGTERM を送る。終了済みの子への kill は no-op なので二重送出にならない。
|
|
56
34
|
*/
|
|
57
35
|
export function killRunningAwscli() {
|
|
58
36
|
for (const child of runningChildren)
|
|
@@ -72,10 +50,7 @@ export function mergeStderr(captured, extra) {
|
|
|
72
50
|
return `${captured}\n${extra}`;
|
|
73
51
|
}
|
|
74
52
|
/**
|
|
75
|
-
CAPTURE_LIMIT_BYTES まで溜め、超過分は捨てる capture を作る。
|
|
76
|
-
|
|
77
|
-
SSO のログインレジストリ(infra/sso/login-registry.ts)も同じ上限で子プロセスの出力を
|
|
78
|
-
溜めるため export する。上限が呼び出し元ごとに割れると、常駐メモリの見積もりが立たなくなる。
|
|
53
|
+
CAPTURE_LIMIT_BYTES まで溜め、超過分は捨てる capture を作る。SSO のログインレジストリも同じ上限を使うので export する。
|
|
79
54
|
*/
|
|
80
55
|
export function createCapture() {
|
|
81
56
|
const chunks = [];
|
|
@@ -95,11 +70,7 @@ export function createCapture() {
|
|
|
95
70
|
};
|
|
96
71
|
}
|
|
97
72
|
/**
|
|
98
|
-
`aws <args>` を起動し、
|
|
99
|
-
|
|
100
|
-
timeout 到達・abort では SIGTERM を送り、`timedOut` / signal を結果へ載せる。
|
|
101
|
-
起動 error は reject せず `exitCode: null` + `spawnErrorCode` に詰める。
|
|
102
|
-
そのため呼び出し側は、失敗種別の判定を 1 か所(classifyFailure)に集約できる。
|
|
73
|
+
`aws <args>` を起動し、capture した stdout / stderr を返す。起動 error は reject せず spawnErrorCode に詰める。
|
|
103
74
|
*/
|
|
104
75
|
export async function runAwscli(arguments_,
|
|
105
76
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- AbortSignal は内部状態を持つ
|
|
@@ -140,11 +111,8 @@ options) {
|
|
|
140
111
|
runningChildren.delete(child);
|
|
141
112
|
clearTimeout(timer);
|
|
142
113
|
options.signal?.removeEventListener('abort', onAbort);
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
// 通常の close 後もここを通る(Node 24 の確認で child.killed は false のまま)。
|
|
146
|
-
// ただし _handle が既に null で kill() は no-op のため無害。
|
|
147
|
-
// catch は防御網で、通常は到達しない。
|
|
114
|
+
// 'error' の直後に 'close' も発火するため、二重 finalize は settled で抑止する。
|
|
115
|
+
// child が生存したまま残る稀な場合に備えて kill する。_handle が null なら no-op で無害。
|
|
148
116
|
/* c8 ignore start -- catch 節(既に終了済みの kill が throw する場合)は観測されない防御網 */
|
|
149
117
|
if (!child.killed) {
|
|
150
118
|
try {
|
|
@@ -178,10 +146,7 @@ options) {
|
|
|
178
146
|
*/
|
|
179
147
|
const probeCache = { pending: undefined };
|
|
180
148
|
/**
|
|
181
|
-
検出した版が最低版以上か。
|
|
182
|
-
|
|
183
|
-
major / minor / patch を 1 つの数へ畳む比較は minor / patch が 1000 以上で桁が衝突するため、
|
|
184
|
-
上位から順に見るタプル比較にする。
|
|
149
|
+
検出した版が最低版以上か。minor / patch が 1000 以上で桁が衝突しないよう、上位から順に比べる。
|
|
185
150
|
*/
|
|
186
151
|
function isAtLeastMinimum(major, minor, patch) {
|
|
187
152
|
const [minimumMajor, minimumMinor, minimumPatch] = MINIMUM_VERSION;
|
|
@@ -197,7 +162,7 @@ async function probeAwscli() {
|
|
|
197
162
|
return {
|
|
198
163
|
ok: false,
|
|
199
164
|
category: 'awscli-missing',
|
|
200
|
-
message: `awscli
|
|
165
|
+
message: `awscli not found. Install awscli v2 (${MINIMUM_VERSION_TEXT} or later) and add it to PATH`,
|
|
201
166
|
};
|
|
202
167
|
}
|
|
203
168
|
const matched = VERSION_PATTERN.exec(`${result.stdout}\n${result.stderr}`) ?? undefined;
|
|
@@ -205,7 +170,7 @@ async function probeAwscli() {
|
|
|
205
170
|
return {
|
|
206
171
|
ok: false,
|
|
207
172
|
category: 'awscli-missing',
|
|
208
|
-
message: `awscli
|
|
173
|
+
message: `Could not determine the awscli version: ${result.stdout.trim()}`,
|
|
209
174
|
};
|
|
210
175
|
}
|
|
211
176
|
const major = Number(matched[1]);
|
|
@@ -217,16 +182,11 @@ async function probeAwscli() {
|
|
|
217
182
|
: {
|
|
218
183
|
ok: false,
|
|
219
184
|
category: 'awscli-missing',
|
|
220
|
-
message: `awscli ${MINIMUM_VERSION_TEXT}
|
|
185
|
+
message: `awscli ${MINIMUM_VERSION_TEXT} or later is required (detected: ${version})`,
|
|
221
186
|
};
|
|
222
187
|
}
|
|
223
188
|
/**
|
|
224
|
-
awscli
|
|
225
|
-
|
|
226
|
-
毎回 `aws --version` を起こすとツール 1 件あたりの子プロセスが 2 本になるため、
|
|
227
|
-
Promise 自体を memo する(並行呼び出しでも実行は 1 回)。
|
|
228
|
-
ただし失敗(未導入・版不足)を memo すると、利用者が awscli を導入してもサーバ再起動まで全ツールが awscli-missing を返し続ける。
|
|
229
|
-
そのため失敗時は memo を捨てて、次の呼び出しで再判定する。
|
|
189
|
+
awscli の存在と版を確認し、成功結果だけを memo する。失敗を memo すると導入後も再起動まで awscli-missing が続くため捨てる。
|
|
230
190
|
*/
|
|
231
191
|
export async function ensureAwscli() {
|
|
232
192
|
const pending = probeCache.pending ?? probeAwscli();
|
|
@@ -4,15 +4,7 @@ import type { FailureCategory } from '../../domain/allowlist.js';
|
|
|
4
4
|
*/
|
|
5
5
|
export declare const SSO_LOGIN_TIMEOUT_MS = 600000;
|
|
6
6
|
/**
|
|
7
|
-
URL
|
|
8
|
-
|
|
9
|
-
research R5 と契約書が定めるのは 10 分だけ。
|
|
10
|
-
だが `sso login` が何も出さずに生き続けると、呼び出し元の tools/call が 10 分ブロックする。
|
|
11
|
-
応答を返せない状態を早く畳むための上限で、契約書側には未反映の差分。
|
|
12
|
-
|
|
13
|
-
確定までの計測値は 0.65 秒。
|
|
14
|
-
この待ちは、mcp/executor.ts の createBoundedExecutor(4) の実行枠を占有し続ける。
|
|
15
|
-
そのため `aws --version` の待ち時間(infra/awscli.ts の PROBE_TIMEOUT_MS)と同じ上限に揃える。
|
|
7
|
+
URL とコードが確定するまでの上限。実行枠を占有し続けないよう、`aws --version` の PROBE_TIMEOUT_MS と揃える。
|
|
16
8
|
*/
|
|
17
9
|
export declare const SSO_PROMPT_TIMEOUT_MS = 15000;
|
|
18
10
|
/**
|
|
@@ -47,10 +39,7 @@ export interface SsoLoginSession {
|
|
|
47
39
|
*/
|
|
48
40
|
readonly category?: FailureCategory;
|
|
49
41
|
/**
|
|
50
|
-
awscli
|
|
51
|
-
|
|
52
|
-
応答の exitCode は許可リストの呼び出しと同じく、awscli の終了コードをそのまま返す規約。
|
|
53
|
-
ここで持たないと、ツール層が値を作れない。シグナルで終わった子は null になる。
|
|
42
|
+
awscli の終了コード。子がまだ終わっていなければキーごと無い。シグナルで終わった子は null。
|
|
54
43
|
*/
|
|
55
44
|
readonly exitCode?: number | null;
|
|
56
45
|
}
|
|
@@ -79,17 +68,11 @@ export interface LoginRegistry {
|
|
|
79
68
|
shutdown(): void;
|
|
80
69
|
}
|
|
81
70
|
/**
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
MCP サーバは 1 プロセスなので実運用のインスタンスは 1 つ(loginRegistry)だが、
|
|
85
|
-
テストが状態を持ち越さずに検証できるようファクトリとして公開する。
|
|
71
|
+
ログインレジストリを作る。実運用は loginRegistry の 1 つだが、テストが状態を持ち越さないようファクトリで公開する。
|
|
86
72
|
*/
|
|
87
73
|
export declare function createLoginRegistry(options?: Readonly<LoginRegistryOptions>): LoginRegistry;
|
|
88
74
|
/**
|
|
89
|
-
MCP
|
|
90
|
-
|
|
91
|
-
ツール(mcp/tools/sso.ts)と終了配線(mcp/serve.ts)が同じインスタンスを見ないと、
|
|
92
|
-
開始したフローを status が引けず、終了時に子も落ちない。
|
|
75
|
+
MCP サーバプロセスで共有するレジストリ。ツールと終了配線が同じインスタンスを見ないと、status が引けず子も落ちない。
|
|
93
76
|
*/
|
|
94
77
|
export declare function loginRegistry(): LoginRegistry;
|
|
95
78
|
/**
|
|
@@ -1,18 +1,7 @@
|
|
|
1
|
-
// `aws sso login` の子プロセスを預かるレジストリ(research R5 / FR-007
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
// parseSsoLoginOutput へ掛けて、URL とコードが揃った時点で start() を resolve する。
|
|
6
|
-
// 子はそのまま生かし、完了・失敗・時間切れは close とタイマで拾う。
|
|
7
|
-
//
|
|
8
|
-
// 子は spawn 直後に生存集合へ登録する。URL / コードの確定前も後始末の対象で、登録が遅れると
|
|
9
|
-
// サーバ終了時に PPID 1 へ再親付けされた子が最大 10 分動き続ける。
|
|
10
|
-
//
|
|
11
|
-
// 出力に対する加工の分担は「秘匿化はここ、応答の上限はツール層」。infra から mcp を import
|
|
12
|
-
// できないため、limitOutput は mcp/tools/sso.ts が掛ける。
|
|
13
|
-
/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types -- LoginProcess は mutable。
|
|
14
|
-
Node 由来の型を抱える。中身は ChildProcess・出力 capture・タイマ配列。
|
|
15
|
-
Readonly<T> では deep readonly の要求を満たせない。読み取りしかしない関数として扱う。 */
|
|
1
|
+
// `aws sso login` の子プロセスを預かるレジストリ(research R5 / FR-007)。
|
|
2
|
+
// URL とコードが揃った時点で start() を resolve し、子は生かしたまま close とタイマで拾う。
|
|
3
|
+
// 秘匿化はここで、応答の上限はツール層で掛ける(infra から mcp は import できない)。
|
|
4
|
+
/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types -- LoginProcess は mutable */
|
|
16
5
|
import { spawn } from 'node:child_process';
|
|
17
6
|
import { classifyFailure } from '../../domain/failure.js';
|
|
18
7
|
import { redactSecrets } from '../../domain/redact.js';
|
|
@@ -23,15 +12,7 @@ import { AWSCLI_COMMAND, createCapture, mergeStderr } from '../awscli.js';
|
|
|
23
12
|
*/
|
|
24
13
|
export const SSO_LOGIN_TIMEOUT_MS = 600_000;
|
|
25
14
|
/**
|
|
26
|
-
URL
|
|
27
|
-
|
|
28
|
-
research R5 と契約書が定めるのは 10 分だけ。
|
|
29
|
-
だが `sso login` が何も出さずに生き続けると、呼び出し元の tools/call が 10 分ブロックする。
|
|
30
|
-
応答を返せない状態を早く畳むための上限で、契約書側には未反映の差分。
|
|
31
|
-
|
|
32
|
-
確定までの計測値は 0.65 秒。
|
|
33
|
-
この待ちは、mcp/executor.ts の createBoundedExecutor(4) の実行枠を占有し続ける。
|
|
34
|
-
そのため `aws --version` の待ち時間(infra/awscli.ts の PROBE_TIMEOUT_MS)と同じ上限に揃える。
|
|
15
|
+
URL とコードが確定するまでの上限。実行枠を占有し続けないよう、`aws --version` の PROBE_TIMEOUT_MS と揃える。
|
|
35
16
|
*/
|
|
36
17
|
export const SSO_PROMPT_TIMEOUT_MS = 15_000;
|
|
37
18
|
function isTerminal(state) {
|
|
@@ -61,14 +42,7 @@ function outputOf(record) {
|
|
|
61
42
|
return record.finalOutput ?? computeOutput(record);
|
|
62
43
|
}
|
|
63
44
|
/**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
sessions は終端状態のレコードを次の start か shutdown() まで保持し続ける。
|
|
67
|
-
capture(最大 CAPTURE_LIMIT_BYTES 分の Buffer)を持たせたままにすると、
|
|
68
|
-
常駐する profile 数に比例して解放されない領域が積み上がる。output を確定させた時点で capture を手放す。
|
|
69
|
-
|
|
70
|
-
Node は 'error' の後に 'close' も発火する(awscli.ts の finalize と同じ)。
|
|
71
|
-
そのため二重呼び出しは finalOutput の有無で防ぐ(2 回目は capture が既に無く output が痩せるため)。
|
|
45
|
+
子の stdio が閉じた(close / error)ら呼ぶ。output を確定させた時点で capture を手放し、二重呼び出しは finalOutput の有無で防ぐ。
|
|
72
46
|
*/
|
|
73
47
|
function finalizeOutput(record) {
|
|
74
48
|
if (record.finalOutput !== undefined)
|
|
@@ -93,10 +67,7 @@ function snapshotOf(record) {
|
|
|
93
67
|
};
|
|
94
68
|
}
|
|
95
69
|
/**
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
MCP サーバは 1 プロセスなので実運用のインスタンスは 1 つ(loginRegistry)だが、
|
|
99
|
-
テストが状態を持ち越さずに検証できるようファクトリとして公開する。
|
|
70
|
+
ログインレジストリを作る。実運用は loginRegistry の 1 つだが、テストが状態を持ち越さないようファクトリで公開する。
|
|
100
71
|
*/
|
|
101
72
|
export function createLoginRegistry(options = {}) {
|
|
102
73
|
const timeoutMs = options.timeoutMs ?? SSO_LOGIN_TIMEOUT_MS;
|
|
@@ -114,11 +85,7 @@ export function createLoginRegistry(options = {}) {
|
|
|
114
85
|
*/
|
|
115
86
|
const running = new Set();
|
|
116
87
|
/**
|
|
117
|
-
shutdown() 済みの印。
|
|
118
|
-
|
|
119
|
-
startLogin は registry.start() の前に ensureAwscli()(最長 15 秒)を await する。
|
|
120
|
-
その待ち中に stdin の EOF が来て runShutdown が先に実行されると、再開した start() が誰も回収しない子を spawn する。
|
|
121
|
-
子の stdio パイプがイベントループを保持し、親は loginTimer が最長 10 分後に発火するまで終了できない。start() の冒頭でここを見て spawn 自体をしない。
|
|
88
|
+
shutdown() 済みの印。ensureAwscli() の待ち中に終了処理が先に動くと、start() が回収されない子を spawn するため、冒頭で見る。
|
|
122
89
|
*/
|
|
123
90
|
let isStopped = false;
|
|
124
91
|
const beginLogin = (key, profile) => new Promise((resolve) => {
|
|
@@ -175,14 +142,10 @@ export function createLoginRegistry(options = {}) {
|
|
|
175
142
|
child.stderr.on('error', ignoreStreamError);
|
|
176
143
|
child.stdout.on('data', (chunk) => {
|
|
177
144
|
record.stdout?.push(chunk);
|
|
178
|
-
//
|
|
179
|
-
// autofill URL はその後の行に届き得るので、揃うまでは再 parse を続けて prompt を差し替える
|
|
180
|
-
// (確定そのものは遅らせない)。全部そろえば以後の chunk では何もしない。
|
|
145
|
+
// 累積全文をそのつど掛ける。autofill URL は後の行に届き得るので、揃うまで再 parse して prompt を差し替える。
|
|
181
146
|
if (record.prompt?.verificationUrlComplete !== undefined)
|
|
182
147
|
return;
|
|
183
|
-
//
|
|
184
|
-
// 含まない awscli(確認結果: 2.36.40 は含む)では、以後の chunk を処理せず返す。
|
|
185
|
-
// 累積全文(最大 CAPTURE_LIMIT_BYTES)の split と走査を、毎 chunk 実行しないため。
|
|
148
|
+
// URL / コード確定後は、autofill 行を含む chunk だけ再 parse する。毎 chunk の split と走査を避けるため。
|
|
186
149
|
if (record.prompt !== undefined && !chunk.toString('utf8').includes(AUTOFILL_MARKER))
|
|
187
150
|
return;
|
|
188
151
|
const prompt = parseSsoLoginOutput(record.stdout?.text() ?? '');
|
|
@@ -220,10 +183,7 @@ export function createLoginRegistry(options = {}) {
|
|
|
220
183
|
// 時間切れ・prompt 待ちの打ち切りで既に終端なら、その理由を SIGTERM 由来の失敗で
|
|
221
184
|
// 上書きしない。
|
|
222
185
|
if (!isTerminal(record.state)) {
|
|
223
|
-
// 確定前の exit 0
|
|
224
|
-
// awscli 2.36.40 で確認したところ、--use-device-code を指定するとキャッシュトークンの有無に依らず
|
|
225
|
-
// 毎回デバイスフローへ入る。そのため未観測で、ここは防御として completed に倒している。
|
|
226
|
-
// research R5 の「抽出できずに終了 = failed」は、SSO 設定なし(exit ≠ 0)を想定した記述。
|
|
186
|
+
// 確定前の exit 0 は防御として completed に倒す。2.36.40 は --use-device-code で毎回デバイスフローに入り、未観測。
|
|
227
187
|
if (code === 0)
|
|
228
188
|
record.state = 'completed';
|
|
229
189
|
else
|
|
@@ -258,7 +218,7 @@ export function createLoginRegistry(options = {}) {
|
|
|
258
218
|
profile: key,
|
|
259
219
|
state: 'failed',
|
|
260
220
|
startedAt: Date.now(),
|
|
261
|
-
output: '
|
|
221
|
+
output: 'Cannot start a new login because the server is shutting down',
|
|
262
222
|
category: 'other',
|
|
263
223
|
};
|
|
264
224
|
}
|
|
@@ -296,10 +256,7 @@ export function createLoginRegistry(options = {}) {
|
|
|
296
256
|
}
|
|
297
257
|
const sharedRegistry = {};
|
|
298
258
|
/**
|
|
299
|
-
MCP
|
|
300
|
-
|
|
301
|
-
ツール(mcp/tools/sso.ts)と終了配線(mcp/serve.ts)が同じインスタンスを見ないと、
|
|
302
|
-
開始したフローを status が引けず、終了時に子も落ちない。
|
|
259
|
+
MCP サーバプロセスで共有するレジストリ。ツールと終了配線が同じインスタンスを見ないと、status が引けず子も落ちない。
|
|
303
260
|
*/
|
|
304
261
|
export function loginRegistry() {
|
|
305
262
|
sharedRegistry.current ??= createLoginRegistry();
|
package/cli/dist/mcp/serve.d.ts
CHANGED
|
@@ -9,16 +9,8 @@ export interface WritableTarget {
|
|
|
9
9
|
write(chunk: string | Uint8Array, callback?: (error?: Error | null) => void): boolean;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
返した Writable だけが本物の stdout へ届き、それ以外の stdout 書き込み
|
|
15
|
-
(console.log・依存ライブラリの進捗表示など) は stderr へ逃がす。
|
|
16
|
-
|
|
17
|
-
「process.stdout.write を差し替えるだけ」では、transport 自身の JSON-RPC 応答まで stderr に流れる。
|
|
18
|
-
するとクライアントが無応答になる。
|
|
19
|
-
そのため、差し替え前に束縛した元の write を使う専用 Writable を transport へ渡す、という二段構えが必須になる。
|
|
20
|
-
|
|
21
|
-
同じ stdout に対しては冪等で、2 回目以降は初回に作った Writable を返す。
|
|
12
|
+
stdout を JSON-RPC 専有にする。返した Writable だけが本物の stdout へ届き、ほかの書き込みは stderr へ逃がす。
|
|
13
|
+
同じ stdout に対しては冪等。
|
|
22
14
|
*/
|
|
23
15
|
export declare function installStdoutGuard(stdout: WritableTarget, stderr: WritableTarget): Writable;
|
|
24
16
|
/**
|
|
@@ -38,32 +30,18 @@ export interface McpServeDependencies {
|
|
|
38
30
|
readonly stdin: Readable;
|
|
39
31
|
readonly stderr: WritableTarget;
|
|
40
32
|
/**
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
実行中の awscli 子プロセスのように「子プロセスを持つ機能」がここへ登録する。
|
|
44
|
-
子を kill しない限り親は終了できない (子の stdio パイプが Node のイベントループを
|
|
45
|
-
保持するため、stdin の EOF で server.close() を呼んでも待ち続ける)。
|
|
33
|
+
終了時の後始末。子の stdio パイプがイベントループを保持するため、子を kill しないと親は終了できない
|
|
46
34
|
*/
|
|
47
35
|
readonly onShutdown: readonly (() => void)[];
|
|
48
36
|
readonly signals: ProcessSignals;
|
|
49
37
|
}
|
|
50
38
|
/**
|
|
51
|
-
MCP サーバを stdio で待ち受ける。
|
|
52
|
-
|
|
53
|
-
serveStdio が接続ごとに 2025 系と 2026-07-28 系のどちらかを選び、factory のインスタンスを
|
|
54
|
-
1 つ固定して配る。factory は 1 接続で複数回呼ばれ得る (2026-07-28 は探査用インスタンスを捨てることがある)。
|
|
55
|
-
そのため createServer() と onerror の設定以外の副作用は持たせない。
|
|
56
|
-
後始末の配線 (signals / stdin) は factory の外で 1 回だけ行う。
|
|
57
|
-
|
|
58
|
-
接続が完了しても待ち受けは続く。クライアントが stdin を閉じたら子プロセスを落として
|
|
59
|
-
接続を閉じ、SIGTERM を待たずにプロセスが自力で終了できるようにする。
|
|
39
|
+
MCP サーバを stdio で待ち受ける。factory は 1 接続で複数回呼ばれ得るため、createServer() と onerror 以外の副作用を持たせない。
|
|
40
|
+
stdin が閉じたら子プロセスを落として接続を閉じる
|
|
60
41
|
*/
|
|
61
42
|
export declare function runMcpServe(dependencies: Readonly<McpServeDependencies>): StdioServerHandle;
|
|
62
43
|
/**
|
|
63
|
-
`pecot-tools mcp` の action 本体。
|
|
64
|
-
|
|
65
|
-
program.parse は action の Promise を await しない。
|
|
66
|
-
そのため例外はここで捕まえて、process.exitCode へ反映する (unhandled rejection にしない)。
|
|
44
|
+
`pecot-tools mcp` の action 本体。program.parse は Promise を await しないので、例外はここで process.exitCode へ反映する
|
|
67
45
|
*/
|
|
68
46
|
export declare function executeMcpServe(buildDependencies: () => McpServeDependencies): void;
|
|
69
47
|
export declare function mcpServeDefaultDependencies(): McpServeDependencies;
|