# Execution Model
task-mcp が recipe 引数をどのように処理するかの説明。recipe 著者向け。
## 引数の流れ
```
MCP Client (JSON)
│
▼
task-mcp (Rust)
│ Command::new("just").arg(recipe_name).arg(value)
│ ※ shell を経由しない (OS-level argv 直渡し)
▼
just
│ recipe body 内の {{param}} をテキスト置換
│ ※ shebang / linewise 問わず置換が発生する
▼
shell / interpreter
│ 置換済みの recipe body を実行
│ ※ linewise: 各行を sh -c で実行
│ ※ shebang: body を一時ファイルに書き出して実行
▼
実行結果
```
## task-mcp の責務
task-mcp は引数を **制御文字 (`\n`, `\r`) のみ拒否** する。shell metacharacter (`|`, `&`, `;`, `$`, `` ` `` 等) は拒否しない。
理由:
- task-mcp は `Command::new("just").arg()` で just を起動しており、shell を経由しない
- MCP 引数は構造化 JSON であり、shell 入力ではない
- OWASP MCP05:2025 の safe pattern (structured parameters) に準拠済み
- shell metacharacter の拒否は「防御対象が存在しない防御」
## just の責務 (recipe body 内の引数展開)
**ここが recipe 著者の責任範囲。**
just は recipe body 内の `{{param}}` をテキスト置換する。この置換は shebang / linewise を問わず発生する。置換結果がどう解釈されるかは recipe body の書き方に依存する。
### 危険なパターン
```just
# NG: ダブルクォート内に {{param}} を直接埋め込む
[group('allow-agent')]
example title:
echo "{{title}}"
```
この場合、`title` に `"`, `` ` ``, `$` が含まれると shell が解釈する:
| `"` | クォートが壊れる (データ欠損 or 構文エラー) |
| `` ` `` | コマンド置換として実行される |
| `$` | 変数展開される (`$HOME` → 実際のパス、`$1` → unbound variable エラー) |
### 安全なパターン: positional-arguments
```just
set positional-arguments
# OK: just が {{param}} 置換せず、shell の $1 として安全に渡す
[group('allow-agent')]
[positional-arguments]
example title:
#!/usr/bin/env bash
echo "$1"
```
`positional-arguments` を使うと:
1. just は `{{param}}` のテキスト置換を**行わない**
2. 引数は shell の positional parameter (`$1`, `$2`, ...) として渡される
3. `"$1"` でダブルクォートすれば shell 解釈を完全に回避できる
### 安全なパターン: jq --arg 経由
JSON を構築する recipe では、jq の `--arg` に shell 変数を渡す:
```just
set positional-arguments
[group('allow-agent')]
[positional-arguments]
create-item title:
#!/usr/bin/env bash
jq -n --arg title "$1" '{ title: $title }'
```
## content型引数
通常の `args`(positional argument)とは別に、`content` フィールドで任意の UTF-8 テキスト(改行含む)を渡せる。
### フロー
```
MCP Client (JSON)
│ content: {"body": "line1\nline2"}
▼
task-mcp (Rust)
│ Command::env("TASK_MCP_CONTENT_BODY", "line1\nline2")
│ ※ positional arg ではなく env var として渡す
│ ※ validate_arg_value をバイパス(改行含む任意 UTF-8 が通る)
▼
just
│ env var を継承して recipe を実行
▼
shell / interpreter
│ $TASK_MCP_CONTENT_BODY として参照可能
▼
実行結果
```
### args との違い
| 渡し方 | positional argument (`just recipe value`) | env var (`TASK_MCP_CONTENT_KEY=value`) |
| バリデーション | `\n` / `\r` を拒否 | key のみ検証、value はバリデーションなし |
| 改行 | 不可 | 可(任意 UTF-8) |
| recipe 側の参照 | `{{param}}` または `$N` | `$TASK_MCP_CONTENT_KEY` |
### key のバリデーション
key は `^[A-Za-z][A-Za-z0-9_]*$` に一致する必要がある(env var 名として有効な文字のみ)。
### env var 命名規則
`TASK_MCP_CONTENT_{KEY}` — KEY は大文字に変換される。
例: `"body"` → `TASK_MCP_CONTENT_BODY`
### recipe 使用例
```just
[group('allow-agent')]
write-note:
#!/usr/bin/env bash
echo "$TASK_MCP_CONTENT_BODY" > /tmp/note.txt
```
呼び出し JSON:
```json
{
"task_name": "write-note",
"content": {
"body": "line1\nline2\nline3"
}
}
```
`args` と `content` の組み合わせも可能(`args` は positional argument、`content` は env var として独立して渡される)。
### サイズ制限
content value は OS の env var サイズ制限に依存する(通常 128 KB/var 程度、合計 ~2 MB)。巨大テキストの渡し方には注意し、必要に応じてファイルパス渡しを検討する。
## 推奨事項
| recipe 形式 | shebang recipe (`#!/usr/bin/env bash`) |
| 引数アクセス | `positional-arguments` + `"$1"` |
| `{{param}}` の使用 | 固定値・フラグのみ (ユーザー入力を `{{param}}` で渡さない) |
| JSON 構築 | `jq --arg` 経由 (文字列を直接埋め込まない) |
| content 引数 | shebang recipe + `$TASK_MCP_CONTENT_*` 環境変数参照 |
## まとめ
```
task-mcp の責務: 制御文字の拒否 (args)、content key のバリデーション
recipe 著者の責務: 引数を安全に扱う recipe body の記述
```
task-mcp は recipe 内部の実装詳細に介入しない。recipe が引数をどう使うかは recipe 著者が制御する。`positional-arguments` パターンを使えば任意の文字列を安全に渡せる。改行を含むテキストは `content` フィールドを使う。