From 97b34627667fd699f4e0e054533eb89a205a2878 Mon Sep 17 00:00:00 2001 From: tanaka takashi Date: Sun, 13 Sep 2026 14:12:41 +0900 Subject: feat: return composed WAV as MCP audio content Replace the server-local wav_path result with a protocol-native multimedia response. - Split internal composition execution from MCP ToolResult serialization so filesystem paths never enter public payloads. - Return every successfully composed WAV as AudioContent with the audio/wav MIME type, regardless of file size. - Keep structured audio metadata, duration, note sequence, and validation details alongside the multimedia content. - Use CallToolResult.isError as the authoritative MCP execution status while retaining compose success and validate valid as domain fields. - Treat invalid compose/API requests and runtime failures as tool errors, while invalid MML discovered by validate remains a successful validation operation. - Sanitize synthesis, file-write, audio-serialization, and unexpected failure responses so internal paths and exception details are not exposed. - Add protocol-level coverage for base64 round trips, content block types, error semantics, ResourceLink absence, and path non-disclosure. - Update existing tests for the separated execution layer and path-free result contract. - Document AudioContent and isError behavior in the README, MCP schema, and design specification. - Require the tested FastMCP 3.4.4 API line and exclude the incompatible 4.x major version. Verification: 487 tests passed; ruff check passed; git diff --check passed; uv lock --check passed; and a manual MCP call returned TextContent plus AudioContent with no wav_path. --- doc/Design.md | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) (limited to 'doc/Design.md') diff --git a/doc/Design.md b/doc/Design.md index 7c3d076..49543bf 100644 --- a/doc/Design.md +++ b/doc/Design.md @@ -139,21 +139,32 @@ MML文字列 ```json { - "success": true, - "wav_path": "./data/20260711_120000_123/output.wav", - "duration_sec": 2.0, - "note_sequence": { ... }, - "validation": { - "errors": [], - "warnings": [] - } + "content": [ + {"type": "text", "text": "{\"success\":true,...}"}, + {"type": "audio", "data": "UklGR...", "mimeType": "audio/wav"} + ], + "structuredContent": { + "success": true, + "audio": { + "mime_type": "audio/wav", + "size_bytes": 176444, + "sample_rate": 44100 + }, + "duration_sec": 2.0, + "note_sequence": { ... }, + "validation": { + "errors": [], + "warnings": [] + } + }, + "isError": false } ``` | フィールド | 型 | 説明 | |---|---|---| | `success` | boolean | 合成・WAV出力が成功したか | -| `wav_path` | string \| null | 生成されたWAVファイルパス(失敗時はnull) | +| `audio` | object \| null | MIMEタイプ、WAVのバイト数、サンプルレート。失敗時はnull | | `duration_sec` | number | 生成された音声の長さ(秒) | | `note_sequence` | object \| null | 中間表現(IR)のNoteSequence | | `validation.errors` | array | エラーのリスト | @@ -161,7 +172,11 @@ MML文字列 > `compose` は必ず検証を内包する。エラーがあればWAVは生成せず、検証結果のみ返す。 > -> **出力構成**: 生成時刻を表す `YYYYMMDD_HHMMSS_mmm` ディレクトリを作成し、WAVを `output.wav`、合成元のMML原文をUTF-8の `output.mml` として出力する。MCPの戻り値には従来どおりWAVのパスだけを含める。 +> **MCP出力**: 成功時はWAVをサイズにかかわらず`AudioContent`(`audio/wav`、base64)として返す。`ResourceLink`は使用せず、サーバ内部のファイルパスも公開しない。 +> +> **内部出力構成**: 生成時刻を表す `YYYYMMDD_HHMMSS_mmm` ディレクトリを作成し、WAVを `output.wav`、合成元のMML原文をUTF-8の `output.mml` として保存する。 +> +> **成否**: MCPツールの正式な成否は`CallToolResult.isError`で表す。composeおよびAPI入力の失敗は`true`、正常に完了したvalidateは対象MMLが不正でも`false`とし、妥当性は`valid`で表す。composeの`success`は構造化結果として残し、`success == !isError`を不変条件とする。 **`action: "validate"`** — MMLの構文チェックのみ(音声生成なし) @@ -687,6 +702,8 @@ NoteSequence IRを入力としてPulse1、Pulse2、Triangle、Noiseを合成し | 6 | Noise note_number → period マッピング | ppmck: `period=8, mode=0` 固定。pyxel: マッピング式適用 | | 7 | 出力構成 | `YYYYMMDD_HHMMSS_mmm/output.wav`, `YYYYMMDD_HHMMSS_mmm/output.mml` | | 8 | リピート安全上限 | 各トラックの展開後イベント数は100,000件以下。超過時は展開前に意味error | +| 9 | MCP音声応答 | WAVを常に`AudioContent`(`audio/wav`)で返し、`ResourceLink`とファイルパスは公開しない | +| 10 | MCPエラー判定 | 正式な成否は`CallToolResult.isError`。composeの`success`は構造化結果として同期させる | --- @@ -694,7 +711,7 @@ NoteSequence IRを入力としてPulse1、Pulse2、Triangle、Noiseを合成し | カテゴリ | 実装内容 | |---|---| -| MCPツール | `compose_mml`(compose/validate/template) | +| MCPツール | `compose_mml`(compose/validate/template)。`ToolResult`で構造化結果と`AudioContent`を返却 | | Lexer | ppmck/pyxel 両モード対応、共通トークン定義、INVALIDトークン。ppmck向けに `@v`/`@@`/`@MP`/`@EP`/`@EN`/`^`/`D`/`s`/`v+`/`v-` トークンを追加 | | Parser | ppmck/pyxel 別Parser。必須token、トップレベル順序、case、リピート対応を検証し、ppmckの`RepeatBreakStmt`と専用L文法を構築 | | SemanticAnalyzer | ppmck/pyxel別Analyzer、AST→IR変換。共通反復展開、事前イベント上限、値域→チャンネル→状態→IR順の検証、Lのloop_start算出を実装 | -- cgit v1.2.3