@pecot/tools-mcp 1.0.1 → 1.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.
Files changed (39) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/cli/dist/cli.d.ts +1 -2
  3. package/cli/dist/cli.js +9 -13
  4. package/cli/dist/diagnostics.d.ts +38 -0
  5. package/cli/dist/diagnostics.js +46 -0
  6. package/cli/dist/domain/arguments.d.ts +2 -15
  7. package/cli/dist/domain/arguments.js +38 -99
  8. package/cli/dist/domain/athena-query.d.ts +2 -8
  9. package/cli/dist/domain/athena-query.js +10 -35
  10. package/cli/dist/domain/s3-input.d.ts +2 -9
  11. package/cli/dist/domain/s3-input.js +8 -21
  12. package/cli/dist/infra/awscli.d.ts +8 -36
  13. package/cli/dist/infra/awscli.js +15 -55
  14. package/cli/dist/infra/sso/login-registry.d.ts +4 -21
  15. package/cli/dist/infra/sso/login-registry.js +13 -56
  16. package/cli/dist/mcp/serve.d.ts +6 -28
  17. package/cli/dist/mcp/serve.js +20 -64
  18. package/cli/dist/mcp/server.d.ts +4 -20
  19. package/cli/dist/mcp/server.js +9 -35
  20. package/cli/dist/mcp/tools/_shared.d.ts +6 -27
  21. package/cli/dist/mcp/tools/_shared.js +36 -65
  22. package/cli/dist/mcp/tools/athena.js +17 -27
  23. package/cli/dist/mcp/tools/cloudwatch.js +13 -15
  24. package/cli/dist/mcp/tools/ec2.js +7 -10
  25. package/cli/dist/mcp/tools/ecr.js +10 -16
  26. package/cli/dist/mcp/tools/index.js +1 -5
  27. package/cli/dist/mcp/tools/lambda.js +10 -10
  28. package/cli/dist/mcp/tools/logs.js +22 -22
  29. package/cli/dist/mcp/tools/rds.js +15 -18
  30. package/cli/dist/mcp/tools/s3.js +19 -29
  31. package/cli/dist/mcp/tools/scheduler.js +10 -13
  32. package/cli/dist/mcp/tools/sqs.js +8 -8
  33. package/cli/dist/mcp/tools/sso.d.ts +1 -5
  34. package/cli/dist/mcp/tools/sso.js +33 -59
  35. package/cli/dist/mcp/tools/sts.js +2 -2
  36. package/cli/dist/node-runtime.d.ts +6 -1
  37. package/cli/dist/node-runtime.js +16 -8
  38. package/npm-shrinkwrap.json +3 -3
  39. package/package.json +2 -2
@@ -1,42 +1,22 @@
1
- // `pecot-tools mcp` — MCP サーバとして stdio で待ち受ける実装。
2
- //
3
- // cli.ts は薄い Command だけを持つ。
4
- // 本モジュールは action 内の dynamic import で初めて読み込まれる (MCP SDK の読み込みを mcp 実行時に限定するため)。
5
- //
6
- // exit code 規約:
7
- // 0 = 正常終了 (クライアントが stdin を閉じるまで待ち受ける)
8
- // 1 = 起動失敗 (transport 接続エラー等) / stdout への書き込み失敗
1
+ // `pecot-tools mcp` の実装。cli.ts の dynamic import で読む。exit code は 0=正常 / 1=起動失敗・stdout 書き込み失敗。
9
2
  import { Writable } from 'node:stream';
10
3
  import { serveStdio, StdioServerTransport } from '@modelcontextprotocol/server/stdio';
4
+ import { createDiagnostics } from '../diagnostics.js';
11
5
  import { killRunningAwscli } from '../infra/awscli.js';
12
6
  import { shutdownLoginRegistry } from '../infra/sso/login-registry.js';
13
7
  import { createMcpServer } from './server.js';
14
8
  import { allTools } from './tools/index.js';
15
9
  import { ignoreRejection } from '../infra/ignore-rejection.js';
16
10
  /**
17
- 診断行の接頭辞。stdout は JSON-RPC 専有なので、報告先は stderr のみ。
18
- */
19
- const LOG_PREFIX = 'pecot-tools mcp:';
20
- /**
21
11
  SIGTERM / SIGINT を受けたときの終了コード (シェルの慣習どおり 128 + シグナル番号)。
22
12
  */
23
13
  const SIGTERM_EXIT_CODE = 143;
24
14
  const SIGINT_EXIT_CODE = 130;
25
- // 同じ stdout に二重でガードを張ると、2 回目が「1 回目の転送用 write」を元の write として束縛する。
26
- // すると JSON-RPC が stderr へ回る。張り済みの stdout には、最初に作った Writable をそのまま返す。
27
- // キーが stream オブジェクトなので、テストごとに別のフェイクを使っても混入しない。
15
+ // 二重に張ると 2 回目が転送用 write を元の write として束縛し、JSON-RPC が stderr へ回る。張り済みなら初回の Writable を返す。
28
16
  const installedGuards = new WeakMap();
29
17
  /**
30
- stdio の stdout を JSON-RPC 専有にするガードを張る。
31
-
32
- 返した Writable だけが本物の stdout へ届き、それ以外の stdout 書き込み
33
- (console.log・依存ライブラリの進捗表示など) は stderr へ逃がす。
34
-
35
- 「process.stdout.write を差し替えるだけ」では、transport 自身の JSON-RPC 応答まで stderr に流れる。
36
- するとクライアントが無応答になる。
37
- そのため、差し替え前に束縛した元の write を使う専用 Writable を transport へ渡す、という二段構えが必須になる。
38
-
39
- 同じ stdout に対しては冪等で、2 回目以降は初回に作った Writable を返す。
18
+ stdout を JSON-RPC 専有にする。返した Writable だけが本物の stdout へ届き、ほかの書き込みは stderr へ逃がす。
19
+ 同じ stdout に対しては冪等。
40
20
  */
41
21
  export function installStdoutGuard(stdout, stderr) {
42
22
  const installed = installedGuards.get(stdout);
@@ -44,16 +24,14 @@ export function installStdoutGuard(stdout, stderr) {
44
24
  return installed;
45
25
  const isOriginalWrite = stdout.write.bind(stdout);
46
26
  const isForwardToStderr = (chunk, encodingOrCallback, callback) => {
47
- // `process.stdout.write(chunk, encoding?, callback?)` の両シグネチャを受ける。
48
- // encoding は意図的に捨てる: 逃がす先の stderr は既定の utf8 で解釈すればよく、
49
- // ここで扱うのは console 系が書く utf8 文字列か Buffer に限られる。
27
+ // encoding は捨てる。逃がす先の stderr は既定の utf8 で足りる。
50
28
  const done = typeof encodingOrCallback === 'function' ? encodingOrCallback : callback;
29
+ // eslint-disable-next-line no-restricted-syntax -- stdout へ書かれた生の chunk を、行に整えず stderr へ逃がす
51
30
  const isWritten = stderr.write(chunk);
52
31
  done?.();
53
32
  return isWritten;
54
33
  };
55
- // 代入ではなく defineProperty を使うのは、オーバーロード付きの write に単一シグネチャの
56
- // 関数を代入する際の型アサーションを避けるため。
34
+ // 代入だと、オーバーロード付きの write への型アサーションが要る。
57
35
  Object.defineProperty(stdout, 'write', {
58
36
  value: isForwardToStderr,
59
37
  writable: true,
@@ -64,10 +42,9 @@ export function installStdoutGuard(stdout, stderr) {
64
42
  isOriginalWrite(chunk, callback);
65
43
  },
66
44
  });
67
- // stdout の相手が消えた後の書き込み (EPIPE) は Writable の 'error' として上がる。
68
- // 拾わないと uncaught 例外でスタックトレースごと落ちるため、1 行報告して終了コードを立てる。
45
+ // EPIPE は 'error' として上がる。拾わないと uncaught 例外で落ちる。
69
46
  protocolStdout.on('error', (error) => {
70
- stderr.write(`${LOG_PREFIX} stdout への書き込みに失敗しました: ${error.message}\n`);
47
+ createDiagnostics(stderr).error(`failed to write to stdout: ${error.message}`);
71
48
  process.exitCode = 1;
72
49
  });
73
50
  installedGuards.set(stdout, protocolStdout);
@@ -78,10 +55,7 @@ function runShutdown(dependencies) {
78
55
  hook();
79
56
  }
80
57
  /**
81
- 後始末を終了処理へ配線する。
82
-
83
- SIGTERM / SIGINT はハンドラを登録した時点で既定の即終了が無効になるので、明示的に exit する。
84
- 'exit' は最後の網で、同期処理しか実行されないため後始末だけを呼ぶ。
58
+ 後始末を終了処理へ配線する。SIGTERM / SIGINT は既定の即終了が無効になるので、明示的に exit する
85
59
  */
86
60
  function installShutdownHandlers(dependencies) {
87
61
  dependencies.signals.on('SIGTERM', () => {
@@ -97,31 +71,20 @@ function installShutdownHandlers(dependencies) {
97
71
  });
98
72
  }
99
73
  /**
100
- MCP サーバを stdio で待ち受ける。
101
-
102
- serveStdio が接続ごとに 2025 系と 2026-07-28 系のどちらかを選び、factory のインスタンスを
103
- 1 つ固定して配る。factory は 1 接続で複数回呼ばれ得る (2026-07-28 は探査用インスタンスを捨てることがある)。
104
- そのため createServer() と onerror の設定以外の副作用は持たせない。
105
- 後始末の配線 (signals / stdin) は factory の外で 1 回だけ行う。
106
-
107
- 接続が完了しても待ち受けは続く。クライアントが stdin を閉じたら子プロセスを落として
108
- 接続を閉じ、SIGTERM を待たずにプロセスが自力で終了できるようにする。
74
+ MCP サーバを stdio で待ち受ける。factory は 1 接続で複数回呼ばれ得るため、createServer() と onerror 以外の副作用を持たせない。
75
+ stdin が閉じたら子プロセスを落として接続を閉じる
109
76
  */
110
77
  export function runMcpServe(dependencies) {
111
78
  const protocolStdout = dependencies.installStdoutGuard();
112
79
  const transport = dependencies.createTransport(dependencies.stdin, protocolStdout);
113
80
  installShutdownHandlers(dependencies);
114
- // 不正な JSON-RPC やハンドラ不在など、リクエスト単位の応答にならない異常は onerror にしか出てこない。
115
- // stdout は JSON-RPC 専有なので stderr へ 1 行流す。
116
- // 接続側 (options.onerror) とインスタンス内部 (server.server.onerror) は通知元が別なので、同じ報告関数を両方に渡す。
117
- // serveStdio は接続確定後の異常を、接続側とインスタンスの両方へ流す。
118
- // そのため同じ Error は 1 回だけ報告する。
81
+ // 接続側とインスタンスの両方の onerror に同じ Error が流れるので、1 回だけ報告する。
119
82
  const reported = new WeakSet();
120
83
  const report = (error) => {
121
84
  if (reported.has(error))
122
85
  return;
123
86
  reported.add(error);
124
- dependencies.stderr.write(`${LOG_PREFIX} ${error.message}\n`);
87
+ createDiagnostics(dependencies.stderr).error(error.message);
125
88
  };
126
89
  const handle = serveStdio(() => {
127
90
  try {
@@ -131,8 +94,7 @@ export function runMcpServe(dependencies) {
131
94
  return server;
132
95
  }
133
96
  catch (error) {
134
- // factory の失敗は serveStdio が onerror (report) へ流して接続側へ内部エラーを返す。
135
- // 起動に失敗したプロセスは待ち受けを続けても役に立たないため、終了コードを立てて閉じる。
97
+ // 起動に失敗したプロセスは待ち受けても役に立たないため、終了コードを立てて閉じる。
136
98
  process.exitCode = 1;
137
99
  // 終了処理中の close 失敗は報告先が無いため握り潰す。
138
100
  void ignoreRejection(handle.close());
@@ -140,8 +102,7 @@ export function runMcpServe(dependencies) {
140
102
  }
141
103
  }, { transport, onerror: report });
142
104
  dependencies.stdin.once('end', () => {
143
- // 子プロセスを先に落とす。子の stdio パイプがイベントループを保持するため、
144
- // 残したままだと接続を閉じるだけでは自力終了できない。
105
+ // 子を先に落とさないと、接続を閉じても自力終了できない。
145
106
  runShutdown(dependencies);
146
107
  // 終了処理中の close 失敗は報告先が無いため握り潰す。
147
108
  void ignoreRejection(handle.close());
@@ -149,10 +110,7 @@ export function runMcpServe(dependencies) {
149
110
  return handle;
150
111
  }
151
112
  /**
152
- `pecot-tools mcp` の action 本体。
153
-
154
- program.parse は action の Promise を await しない。
155
- そのため例外はここで捕まえて、process.exitCode へ反映する (unhandled rejection にしない)。
113
+ `pecot-tools mcp` の action 本体。program.parse は Promise を await しないので、例外はここで process.exitCode へ反映する
156
114
  */
157
115
  export function executeMcpServe(buildDependencies) {
158
116
  const dependencies = buildDependencies();
@@ -160,7 +118,7 @@ export function executeMcpServe(buildDependencies) {
160
118
  runMcpServe(dependencies);
161
119
  }
162
120
  catch (error) {
163
- dependencies.stderr.write(`${LOG_PREFIX} ${formatError(error)}\n`);
121
+ createDiagnostics(dependencies.stderr).error(formatError(error));
164
122
  process.exitCode = 1;
165
123
  }
166
124
  }
@@ -172,9 +130,7 @@ export function mcpServeDefaultDependencies() {
172
130
  installStdoutGuard: () => installStdoutGuard(process.stdout, process.stderr),
173
131
  stdin: process.stdin,
174
132
  stderr: process.stderr,
175
- // tools/call 実行中の awscli 子プロセスと、SSO ログインで待機中の子プロセスを落とす。
176
- // 親が先に消えると、子は PPID 1 へ再親付けされる。
177
- // timeout を張っていた親が居なくなるので、打ち切りも働かなくなる(SSO は最長 10 分待つ)。
133
+ // 親が先に消えると子が残り、timeout も働かない(SSO は最長 10 分待つ)。
178
134
  onShutdown: [killRunningAwscli, shutdownLoginRegistry],
179
135
  signals: process,
180
136
  };
@@ -2,18 +2,11 @@ import { McpServer } from '@modelcontextprotocol/server';
2
2
  import type { CallToolResult, ToolAnnotations } from '@modelcontextprotocol/server';
3
3
  import type { z } from 'zod';
4
4
  /**
5
- MCP サーバ名。initialize の serverInfo.name として返る値。
6
-
7
- `.mcp.json` の `mcpServers` の登録キーと必ず揃える。食い違うとクライアント側の表示と
8
- ツール名の名前空間(`mcp__plugin_pecot-t_<サーバ名>__*`)が噛み合わなくなる。
5
+ MCP サーバ名。`.mcp.json` の `mcpServers` の登録キーと揃える。食い違うとツール名の名前空間が噛み合わない。
9
6
  */
10
7
  export declare const MCP_SERVER_NAME = "pecot-tools";
11
8
  /**
12
- registerTool に渡すツールのメタ情報。
13
-
14
- inputSchema / outputSchema は自前の zod の `z.object()` で書く。SDK は渡されたスキーマ自身から JSON Schema を作る。
15
- そのため SDK 同梱の zod と混在しない。引数を取らないツールは inputSchema に
16
- `z.object({})` を渡す。
9
+ registerTool に渡すツールのメタ情報。スキーマは自前の zod で書き、SDK 同梱の zod と混在させない。
17
10
  */
18
11
  export interface ToolConfig {
19
12
  readonly title?: string;
@@ -29,10 +22,7 @@ export interface ToolConfig {
29
22
  readonly annotations?: ToolAnnotations;
30
23
  }
31
24
  /**
32
- tools/call ハンドラの第 2 引数。ハンドラが使う面だけを SDK の ServerContext から切り出す。
33
-
34
- signal はクライアントのキャンセル (notifications/cancelled) と stdin の EOF で abort される。
35
- SDK の型を直接渡さないのは、ツール側とテストのフェイクを SDK の型から切り離すため。
25
+ tools/call ハンドラの第 2 引数。使う面だけを切り出し、ツール側とテストのフェイクを SDK の型から切り離す。
36
26
  */
37
27
  export interface ToolCallContext {
38
28
  readonly signal: AbortSignal;
@@ -56,13 +46,7 @@ interface McpServerOptions {
56
46
  readonly progressIntervalMs?: number;
57
47
  }
58
48
  /**
59
- MCP サーバを生成し、渡されたツール定義を登録する。
60
-
61
- 各ハンドラは登録時に実行器で包む。tools/call が並行に届いても、同時に実行されるのは 4 件までになる
62
- (上限はここで構造的に保証し、ツール実装側には依存しない)。
63
- あわせて progress ハートビートもここで包む。全ツールに一律で適用され、ツール実装側の適用判断が要らなくなる。
64
-
65
- 呼び出しごとに新しい McpServer と実行器を生成する (singleton にしない)。
49
+ MCP サーバを生成し、ツール定義を登録する。ハンドラは実行器と progress ハートビートで包み、呼び出しごとに新しく作る。
66
50
  */
67
51
  export declare function createMcpServer(tools?: readonly ToolDefinition[], options?: McpServerOptions): McpServer;
68
52
  export {};
@@ -1,24 +1,13 @@
1
1
  // MCP サーバのファクトリ。ツール登録シームと上限付き並行実行器の所有をここで持つ。
2
- //
3
- // ツール 1 件以上のときは McpServer.registerTool だけを使う。
4
- // 低レベルの setRequestHandler('tools/list', ...) で一覧ハンドラを先に張る。
5
- // すると、後から registerTool したときに SDK が「handler already exists」で throw する。
6
- // 一方でツール 0 件の McpServer は、tools capability と一覧ハンドラを持たない。
7
- // そのため tools/list に -32601 を返す。
8
- // 0 件のときだけ明示登録する。両者は排他。
9
- /* eslint-disable @typescript-eslint/prefer-readonly-parameter-types -- ツールハンドラの第 2 引数は mutable。
10
- SDK の ServerContext が signal / notify 等のメンバを持つ。
11
- readonly 化すると、registerTool のコールバック型と適合しなくなる。 */
2
+ // 1 件以上は registerTool だけを使う(先に一覧ハンドラを張ると SDK が throw する)。
3
+ // 0 件のときだけ一覧ハンドラを明示登録する。
4
+ /* eslint-disable @typescript-eslint/prefer-readonly-parameter-types -- ツールハンドラの第 2 引数は mutable */
12
5
  import { McpServer } from '@modelcontextprotocol/server';
13
6
  import { readVersion } from '../version.js';
14
7
  import { createBoundedExecutor } from './executor.js';
15
8
  import { ignoreRejection } from '../infra/ignore-rejection.js';
16
9
  /**
17
- progress ハートビートの送出周期 (ms)。
18
-
19
- 守る相手は MCP クライアントの「無音タイムアウト」。
20
- Claude Code で計測した値は、30 分の idle watchdog (progress 受信でリセット・判定周期 30s)。
21
- 10s はそこから十分な余裕があり、かつ長時間ツール 1 件で通知が積み上がりすぎない粒度になる。
10
+ progress ハートビートの送出周期 (ms)。クライアントの無音タイムアウト(Claude Code で 30 分)に十分な余裕を取る。
22
11
  */
23
12
  const PROGRESS_HEARTBEAT_INTERVAL_MS = 10_000;
24
13
  /**
@@ -26,20 +15,11 @@ tools/call の同時実行数の上限 (research R9)。
26
15
  */
27
16
  const CONCURRENCY_LIMIT = 4;
28
17
  /**
29
- MCP サーバ名。initialize の serverInfo.name として返る値。
30
-
31
- `.mcp.json` の `mcpServers` の登録キーと必ず揃える。食い違うとクライアント側の表示と
32
- ツール名の名前空間(`mcp__plugin_pecot-t_<サーバ名>__*`)が噛み合わなくなる。
18
+ MCP サーバ名。`.mcp.json` の `mcpServers` の登録キーと揃える。食い違うとツール名の名前空間が噛み合わない。
33
19
  */
34
20
  export const MCP_SERVER_NAME = 'pecot-tools';
35
21
  /**
36
- tools/call 1 件分の progress 通知を開始し、即座に 1 発目 (順番待ち中) を送る。
37
-
38
- MCP では progressToken を伴わないリクエストに notifications/progress を返せない。
39
- そのためクライアントが token を付けてこなかった場合は、何も送らず undefined を返す。
40
-
41
- 送出は run() の前から始める。守りたいのは実行中だけでなく「上限に掛かって順番待ちしている
42
- 間も無音時計が進む」区間で、ハンドラ内に置くラッパーでは待機中を守れない。
22
+ tools/call 1 件分の progress 通知を開始する。token が無ければ undefined。順番待ちの間も守るため、run() の前から始める。
43
23
  */
44
24
  function startProgressHeartbeat(toolName, serverContext, intervalMs) {
45
25
  const { signal } = serverContext.mcpReq;
@@ -47,7 +27,7 @@ function startProgressHeartbeat(toolName, serverContext, intervalMs) {
47
27
  if (progressToken === undefined)
48
28
  return undefined;
49
29
  let progress = 0;
50
- let phase = '順番待ち中';
30
+ let phase = 'queued';
51
31
  const send = () => {
52
32
  progress += 1;
53
33
  // 通知はベストエフォート。クライアント切断後の reject を await の流れに乗せると、
@@ -72,18 +52,12 @@ function startProgressHeartbeat(toolName, serverContext, intervalMs) {
72
52
  send();
73
53
  }
74
54
  return {
75
- markRunning: () => { phase = '実行中'; },
55
+ markRunning: () => { phase = 'running'; },
76
56
  stop,
77
57
  };
78
58
  }
79
59
  /**
80
- MCP サーバを生成し、渡されたツール定義を登録する。
81
-
82
- 各ハンドラは登録時に実行器で包む。tools/call が並行に届いても、同時に実行されるのは 4 件までになる
83
- (上限はここで構造的に保証し、ツール実装側には依存しない)。
84
- あわせて progress ハートビートもここで包む。全ツールに一律で適用され、ツール実装側の適用判断が要らなくなる。
85
-
86
- 呼び出しごとに新しい McpServer と実行器を生成する (singleton にしない)。
60
+ MCP サーバを生成し、ツール定義を登録する。ハンドラは実行器と progress ハートビートで包み、呼び出しごとに新しく作る。
87
61
  */
88
62
  export function createMcpServer(tools = [], options = {}) {
89
63
  const server = new McpServer({ name: MCP_SERVER_NAME, version: readVersion() });
@@ -24,20 +24,13 @@ export declare const COMMAND_NOT_FOUND_EXIT_CODE = 127;
24
24
  */
25
25
  export declare const UNKNOWN_FAILURE_EXIT_CODE = 1;
26
26
  /**
27
- `profile` だけの入力。
28
-
29
- SSO の 2 件は region を取らない(contracts/mcp-tools.md §SSO 認証)。
30
- そのため共通入力から profile だけを切り出し、両方が同じ書式・同じ説明文を公開できるようにする。
27
+ `profile` だけの入力。SSO の 2 件は region を取らないので、共通入力から切り出す。
31
28
  */
32
29
  export declare const PROFILE_INPUT_SHAPE: {
33
30
  profile: z.ZodOptional<z.ZodString>;
34
31
  };
35
32
  /**
36
- 全ツール共通の任意入力。
37
-
38
- 書式の定義は domain/allowlist.ts の PROFILE_PATTERN / REGION_PATTERN にある。
39
- ここはその定数をそのまま inputSchema へ載せる(契約書の共通規約を tools/list で見せるため)。
40
- 値を書き写さないので、定義は 1 箇所のまま。書式違反はハンドラ到達前に入力検証エラーとなる。awscli は起動しない。
33
+ 全ツール共通の任意入力。書式は allowlist.ts の PROFILE_PATTERN / REGION_PATTERN を載せ、tools/list で契約を見せる。
41
34
  */
42
35
  export declare const COMMON_INPUT_SHAPE: {
43
36
  profile: z.ZodOptional<z.ZodString>;
@@ -83,10 +76,7 @@ export interface ExecuteOptions {
83
76
  */
84
77
  readonly timeoutMs?: number;
85
78
  /**
86
- 許可リストの静的宣言だけでは表せない拒否条件(Athena の変更系クエリ、既存ファイルの上書き)。
87
-
88
- validateInput の直後・awscli の存在確認より前に評価する。存在確認を先にすると、
89
- awscli 未導入の環境で「拒否されるはずの呼び出し」が awscli-missing に化ける。
79
+ 静的宣言で表せない拒否条件。awscli 未導入で拒否が awscli-missing に化けないよう、存在確認より前に評価する。
90
80
  */
91
81
  readonly reject?: (arguments_: Readonly<Record<string, unknown>>) => PreExecutionRejection | undefined;
92
82
  /**
@@ -116,26 +106,15 @@ export interface AllowlistedToolSpec {
116
106
  readonly artifactsOf?: ExecuteOptions['artifactsOf'];
117
107
  }
118
108
  /**
119
- 失敗種別と awscli の終了コードから応答の exitCode を決める。
120
-
121
- exitCode を持たない失敗(シグナル終了・awscli を起動していない SSO の状態遷移)は
122
- 省略するか null で渡し、1 以上へ畳む。ok: false と exitCode 0 が同居しないようにする。
109
+ 失敗種別と awscli の終了コードから応答の exitCode を決める。exitCode を持たない失敗は 1 以上へ畳む。
123
110
  */
124
111
  export declare function resolveExitCode(category: FailureCategory, exitCode?: number | null): number;
125
112
  /**
126
- 許可リストのエントリ 1 件を実行し、ExecutionResult を返す。
127
-
128
- 検証で弾いた場合は awscli を起動しない(exitCode 2)。awscli の失敗は種別判定にかけ、
129
- 応答へ載せる stdout / stderr は必ず秘匿化と上限を通す。
113
+ 許可リストのエントリ 1 件を実行し、ExecutionResult を返す。検証で弾けば awscli を起動せず(exitCode 2)、出力は秘匿化と上限を通す。
130
114
  */
131
115
  export declare function executeAllowlisted(entry: Readonly<AllowlistEntry>, arguments_: Readonly<Record<string, unknown>>, call: ToolCallContext, options?: Readonly<ExecuteOptions>): Promise<ExecutionResult>;
132
116
  /**
133
- 許可リストのエントリ 1 件を MCP ツール定義にする。
134
-
135
- 入力の検証は 2 段になる。
136
- inputSchema(型・列挙・書式)は SDK がハンドラ到達前に見て、入力検証エラーを返す。
137
- 許可リスト側の規則(先頭 '-'・排他・shorthand の構成要素)は、executeAllowlisted が見て isError の応答にする。ハンドラで再度 zod parse はしない
138
- (SDK が既に未知キーの除去と default の適用を済ませている)。
117
+ 許可リストのエントリ 1 件を MCP ツール定義にする。zod の検証は SDK が済ませるので、ハンドラで再 parse しない。
139
118
  */
140
119
  export declare function allowlistedTool(spec: Readonly<AllowlistedToolSpec>, options?: Readonly<ToolOptions>): ToolDefinition;
141
120
  //# sourceMappingURL=_shared.d.ts.map
@@ -1,10 +1,6 @@
1
1
  // 全ツール共通の入出力スキーマと、許可リストのエントリを 1 本の実行手順へ通す処理。
2
- //
3
- // 系統別のツール定義(`mcp/tools/{sts,lambda,logs,sqs,athena,s3}.ts`)が宣言するのは、使うエントリと入力スキーマだけ。
4
- // 検証 → 実行前拒否 → 引数組み立て → awscli 起動 → 失敗種別判定 → 秘匿化 → 切り詰め、という手順はここに 1 本だけ置く。
5
- // 手順をツール側へ複製すると、秘匿化や上限の適用を忘れるおそれがある。
6
- /* eslint-disable @typescript-eslint/prefer-readonly-parameter-types -- call.signal は mutable なメンバを持つ。
7
- readonly 化すると、SDK 側の型と適合しなくなる。 */
2
+ // 秘匿化と上限の掛け忘れを防ぐため、検証から切り詰めまでの手順はここに 1 本だけ置く。
3
+ /* eslint-disable @typescript-eslint/prefer-readonly-parameter-types -- call.signal は mutable */
8
4
  import { z } from 'zod';
9
5
  import { PROFILE_PATTERN, REGION_PATTERN, } from '../../domain/allowlist.js';
10
6
  import { buildArguments, validateInput } from '../../domain/arguments.js';
@@ -35,77 +31,70 @@ export const COMMAND_NOT_FOUND_EXIT_CODE = 127;
35
31
  */
36
32
  export const UNKNOWN_FAILURE_EXIT_CODE = 1;
37
33
  /**
38
- `profile` だけの入力。
39
-
40
- SSO の 2 件は region を取らない(contracts/mcp-tools.md §SSO 認証)。
41
- そのため共通入力から profile だけを切り出し、両方が同じ書式・同じ説明文を公開できるようにする。
34
+ `profile` だけの入力。SSO の 2 件は region を取らないので、共通入力から切り出す。
42
35
  */
43
36
  export const PROFILE_INPUT_SHAPE = {
44
37
  profile: z.string().regex(PROFILE_PATTERN).optional()
45
- .describe('AWS プロファイル名。未指定なら awscli の既定解決に従う'),
38
+ .describe('AWS profile name. If omitted, the awscli default resolution applies'),
46
39
  };
47
40
  /**
48
- 全ツール共通の任意入力。
49
-
50
- 書式の定義は domain/allowlist.ts の PROFILE_PATTERN / REGION_PATTERN にある。
51
- ここはその定数をそのまま inputSchema へ載せる(契約書の共通規約を tools/list で見せるため)。
52
- 値を書き写さないので、定義は 1 箇所のまま。書式違反はハンドラ到達前に入力検証エラーとなる。awscli は起動しない。
41
+ 全ツール共通の任意入力。書式は allowlist.ts の PROFILE_PATTERN / REGION_PATTERN を載せ、tools/list で契約を見せる。
53
42
  */
54
43
  export const COMMON_INPUT_SHAPE = {
55
44
  ...PROFILE_INPUT_SHAPE,
56
45
  region: z.string().regex(REGION_PATTERN).optional()
57
- .describe('AWS リージョン(ap-northeast-1 等)。未指定なら awscli の既定解決に従う'),
46
+ .describe('AWS region (e.g. ap-northeast-1). If omitted, the awscli default resolution applies'),
58
47
  };
59
48
  /**
60
49
  クライアント側ページング(`--max-items` / `--starting-token`)を持つツールの入力。
61
50
  */
62
51
  export const MAX_ITEMS_PAGINATION_SHAPE = {
63
- max_items: z.number().int().min(1).optional().describe('1 回の応答に含める最大件数'),
64
- starting_token: z.string().optional().describe('前回応答の nextToken。続きを取得する'),
52
+ max_items: z.number().int().min(1).optional().describe('Maximum number of items in one response'),
53
+ starting_token: z.string().optional().describe('nextToken from the previous response. Fetches the next page'),
65
54
  };
66
55
  /**
67
56
  API 固有ページング(`logs get-log-events`)を持つツールの入力。
68
57
  */
69
58
  export const LIMIT_NEXT_TOKEN_PAGINATION_SHAPE = {
70
- limit: z.number().int().min(1).max(10_000).optional().describe('取得するイベント数の上限(1〜10000)'),
71
- next_token: z.string().optional().describe('前回応答の nextForwardToken / nextBackwardToken'),
72
- start_from_head: z.boolean().default(false).describe('true なら古いイベントから返す'),
59
+ limit: z.number().int().min(1).max(10_000).optional().describe('Maximum number of events to return (1-10000)'),
60
+ next_token: z.string().optional().describe('nextForwardToken / nextBackwardToken from the previous response'),
61
+ start_from_head: z.boolean().default(false).describe('If true, returns the oldest events first'),
73
62
  };
74
63
  /**
75
64
  ExecutionResult と同形の outputSchema。全ツールで共有する。
76
65
  */
77
66
  export const TOOL_OUTPUT_SCHEMA = z.object({
78
- ok: z.boolean().describe('awscli の実行が成功したか'),
79
- exitCode: z.number().describe('終了コード (0 = success、実行前拒否 2、時間切れ 124、未導入 127)'),
80
- category: z.string().optional().describe('失敗種別(成功時は省略)'),
81
- data: z.record(z.string(), z.unknown()).describe('awscli の JSON 出力、または行配列'),
82
- artifacts: z.array(z.string()).describe('書き出したローカルファイルの絶対パス'),
83
- message: z.string().describe('呼び出し元エージェント向けの 1 行サマリ'),
84
- stderr: z.string().optional().describe('秘匿化済みの awscli stderr(失敗時のみ)'),
67
+ ok: z.boolean().describe('Whether the awscli run succeeded'),
68
+ exitCode: z.number().describe('Exit code (0 = success, 2 = rejected before running, 124 = timeout, 127 = not installed)'),
69
+ category: z.string().optional().describe('Failure category (omitted on success)'),
70
+ data: z.record(z.string(), z.unknown()).describe('awscli JSON output, or an array of lines'),
71
+ artifacts: z.array(z.string()).describe('Absolute paths of the local files written'),
72
+ message: z.string().describe('One-line summary for the calling agent'),
73
+ stderr: z.string().optional().describe('Redacted awscli stderr (only on failure)'),
85
74
  });
86
75
  /**
87
76
  失敗種別ごとの再試行方法。message の後半に載せる。
88
77
  */
89
78
  const FAILURE_HINTS = {
90
- 'not-allowed': '許可されていない操作です。公開しているツールの範囲で実行してください',
91
- 'invalid-input': '入力を修正して再実行してください',
92
- 'awscli-missing': 'awscli v2 (2.22 以上) を導入して PATH を通してください',
93
- 'auth-missing': '認証設定がありません。aws_sso_login を実行するか profile を指定してください',
94
- 'auth-expired': '認証の有効期限が切れています。aws_sso_login で再認証してください',
95
- 'permission-denied': '権限が不足しています。profile / role を確認してください',
96
- 'resource-not-found': '対象が見つかりません。名前・リージョンを確認してください',
97
- 'timeout': '時間切れです。取得範囲を狭めて再実行してください',
98
- 'other': 'awscli が失敗しました。stderr を確認してください',
79
+ 'not-allowed': 'Operation not allowed. Use only the exposed tools',
80
+ 'invalid-input': 'Fix the input and run again',
81
+ 'awscli-missing': 'Install awscli v2 (2.22 or later) and add it to PATH',
82
+ 'auth-missing': 'Not authenticated. Run aws_sso_login or specify a profile',
83
+ 'auth-expired': 'Authentication has expired. Run aws_sso_login to sign in again',
84
+ 'permission-denied': 'Permission denied. Check the profile / role',
85
+ 'resource-not-found': 'Resource not found. Check the name and region',
86
+ 'timeout': 'Timed out. Narrow the range and run again',
87
+ 'other': 'awscli failed. Check stderr',
99
88
  };
100
89
  /**
101
90
  出力が上限を超えたときの案内。
102
91
  */
103
- const TRUNCATION_MESSAGE = `出力が上限 ${String(OUTPUT_LIMIT_BYTES)} バイトを超えたため先頭のみ返しました。`
104
- + 'max_items を減らすか starting_token で分割して再取得してください';
92
+ const TRUNCATION_MESSAGE = `Output exceeded the limit of ${String(OUTPUT_LIMIT_BYTES)} bytes, so only the beginning is returned. `
93
+ + 'Reduce max_items or split the request with starting_token.';
105
94
  /**
106
95
  stderr が上限で切り詰められたときに message へ足す注記。
107
96
  */
108
- const STDERR_TRUNCATION_NOTE = '(stderr は上限を超えたため先頭のみ載せています)';
97
+ const STDERR_TRUNCATION_NOTE = '(stderr exceeded the limit, so only the beginning is included)';
109
98
  function isRecord(value) {
110
99
  return value instanceof Object && !Array.isArray(value);
111
100
  }
@@ -132,10 +121,7 @@ function toLines(text) {
132
121
  return body === '' ? [] : body.split('\n');
133
122
  }
134
123
  /**
135
- 失敗種別と awscli の終了コードから応答の exitCode を決める。
136
-
137
- exitCode を持たない失敗(シグナル終了・awscli を起動していない SSO の状態遷移)は
138
- 省略するか null で渡し、1 以上へ畳む。ok: false と exitCode 0 が同居しないようにする。
124
+ 失敗種別と awscli の終了コードから応答の exitCode を決める。exitCode を持たない失敗は 1 以上へ畳む。
139
125
  */
140
126
  export function resolveExitCode(category, exitCode) {
141
127
  if (category === 'timeout')
@@ -146,7 +132,7 @@ export function resolveExitCode(category, exitCode) {
146
132
  }
147
133
  function failureMessage(toolName, category) {
148
134
  // eslint-disable-next-line security/detect-object-injection -- category は FailureCategory の列挙値
149
- return `${toolName} が失敗しました (${category})。${FAILURE_HINTS[category]}`;
135
+ return `${toolName} failed (${category}). ${FAILURE_HINTS[category]}`;
150
136
  }
151
137
  /**
152
138
  成功時の data。JSON 出力はそのまま、非 JSON 出力は行配列にする。
@@ -159,10 +145,7 @@ function successData(entry, stdout) {
159
145
  return typeof nextToken === 'string' && entry.pagination === 'max-items' ? { ...parsed, nextToken } : parsed;
160
146
  }
161
147
  /**
162
- 許可リストのエントリ 1 件を実行し、ExecutionResult を返す。
163
-
164
- 検証で弾いた場合は awscli を起動しない(exitCode 2)。awscli の失敗は種別判定にかけ、
165
- 応答へ載せる stdout / stderr は必ず秘匿化と上限を通す。
148
+ 許可リストのエントリ 1 件を実行し、ExecutionResult を返す。検証で弾けば awscli を起動せず(exitCode 2)、出力は秘匿化と上限を通す。
166
149
  */
167
150
  export async function executeAllowlisted(entry, arguments_, call, options = {}) {
168
151
  const invalid = validateInput(entry, arguments_);
@@ -241,18 +224,11 @@ export async function executeAllowlisted(entry, arguments_, call, options = {})
241
224
  exitCode: 0,
242
225
  data: successData(entry, limited.text),
243
226
  artifacts,
244
- message: `${entry.toolName} が成功しました`,
227
+ message: `${entry.toolName} succeeded`,
245
228
  };
246
229
  }
247
230
  /**
248
- annotations を許可リストの kind から導く。
249
-
250
- 契約表の「読取専用」列は read だけが true で、fetch(ローカルへ書き出す)と
251
- execute(Athena のクエリ起動)は false。destructiveHint は fetch だけ true にする:
252
- `overwrite: true` の呼び出しはローカルの既存ファイルを上書きして戻せない。
253
- openWorldHint は AWS API へ到達するので全件 true。
254
-
255
- 契約書の共通規約も destructiveHint を fetch 2 件だけ true と定めており、ここはそれと一致する。
231
+ annotations を許可リストの kind から導く。destructiveHint は fetch だけ true(`overwrite: true` で既存ファイルを上書きするため)。
256
232
  */
257
233
  function annotationsOf(entry) {
258
234
  return {
@@ -262,12 +238,7 @@ function annotationsOf(entry) {
262
238
  };
263
239
  }
264
240
  /**
265
- 許可リストのエントリ 1 件を MCP ツール定義にする。
266
-
267
- 入力の検証は 2 段になる。
268
- inputSchema(型・列挙・書式)は SDK がハンドラ到達前に見て、入力検証エラーを返す。
269
- 許可リスト側の規則(先頭 '-'・排他・shorthand の構成要素)は、executeAllowlisted が見て isError の応答にする。ハンドラで再度 zod parse はしない
270
- (SDK が既に未知キーの除去と default の適用を済ませている)。
241
+ 許可リストのエントリ 1 件を MCP ツール定義にする。zod の検証は SDK が済ませるので、ハンドラで再 parse しない。
271
242
  */
272
243
  export function allowlistedTool(spec, options = {}) {
273
244
  const executeOptions = {