|
| 1 | +--- |
| 2 | +description: |
| 3 | +globs: **/errors.ts |
| 4 | +alwaysApply: false |
| 5 | +--- |
| 6 | +# Error Flow |
| 7 | + |
| 8 | +```mermaid |
| 9 | +graph TD |
| 10 | + subgraph Core_Logic |
| 11 | + FS[FileSystemService: e.g., FileReadError] --> TM[TaskManager: Throws App Errors, e.g., ProjectNotFound, TaskNotDone] |
| 12 | + TM -->|Untagged App Error| CLI_Handler["cli.ts Command Handler"] |
| 13 | + TM -->|Untagged App Error| ToolExec["toolExecutors.ts: execute"] |
| 14 | + end |
| 15 | + |
| 16 | + subgraph CLI_Path |
| 17 | + CLI_Handler -->|Untagged App Error| CLI_Catch["cli.ts catch block"] |
| 18 | + CLI_Catch -->|Error Object| FormatCLI["client errors.ts formatCliError"] |
| 19 | + FormatCLI -->|Formatted String| ConsoleOut["console.error Output"] |
| 20 | + end |
| 21 | + |
| 22 | + subgraph MCP_Server_Path |
| 23 | + subgraph Validation |
| 24 | + ToolExecVal["toolExecutors.ts Validation"] -->|Throws Tagged Protocol Error jsonRpcCode -32602| ExecToolErrHandler |
| 25 | + end |
| 26 | + |
| 27 | + subgraph Execution |
| 28 | + ToolExec -->|Untagged App Error| ExecToolErrHandler["tools.ts executeToolAndHandleErrors catch block"] |
| 29 | + end |
| 30 | + |
| 31 | + ExecToolErrHandler -->|Error Object| CheckTag["Check if error has jsonRpcCode"] |
| 32 | + CheckTag -- Tagged Error --> ReThrow["Re-throw Tagged Error"] |
| 33 | + CheckTag -- Untagged App Error --> NormalizeErr["utils errors.ts normalizeError"] |
| 34 | + NormalizeErr -->|McpError Object code -32000| FormatResult["Format as isError true result"] |
| 35 | + FormatResult -->|content list with isError true| SDKHandler["server index.ts SDK Handler"] |
| 36 | + |
| 37 | + ReThrow -->|Tagged Protocol Error| SDKHandler |
| 38 | + SDKHandler -- Tagged Error --> SDKFormatError["SDK Formats Top-Level Error"] |
| 39 | + SDKHandler -- isError true Result --> SDKFormatResult["SDK Formats Result Field"] |
| 40 | + |
| 41 | + SDKFormatError -->|JSON-RPC Error Response| MCPClient["MCP Client"] |
| 42 | + SDKFormatResult -->|JSON-RPC Success Response with error details| MCPClient |
| 43 | + end |
| 44 | +``` |
| 45 | + |
| 46 | +**Explanation of Error Flow and Transformations:** |
| 47 | + |
| 48 | +Errors primarily originate from two places: |
| 49 | + |
| 50 | +1. **Core Logic (`TaskManager`, `FileSystemService`):** These modules throw standard JavaScript `Error` objects, often subclassed (e.g., `ProjectNotFoundError`, `FileReadError`) but *without* any special MCP/JSON-RPC tagging (`jsonRpcCode`). These represent application-specific or file system problems. |
| 51 | +2. **Tool Executors (`toolExecutors.ts`) Validation:** Before calling `TaskManager`, the executors validate input arguments. If validation fails, they create a *new* `Error` object and explicitly *tag* it with `jsonRpcCode = -32602` (Invalid Params). |
| 52 | + |
| 53 | +The handling differs significantly between the CLI and the MCP Server: |
| 54 | + |
| 55 | +**1. CLI Error Path (`cli.ts`)** |
| 56 | + |
| 57 | +1. **Origination:** An untagged application error (e.g., `ProjectNotFoundError`) is thrown by `TaskManager`. |
| 58 | +2. **Propagation:** The error propagates directly up the call stack to the `catch` block within the specific command's action handler in `cli.ts`. |
| 59 | +3. **Transformation (`formatCliError`):** |
| 60 | + * The `catch` block calls `formatCliError` from `src/client/errors.ts`. |
| 61 | + * `formatCliError` takes the raw `Error` object. |
| 62 | + * It checks the error's `name` (e.g., 'ReadOnlyFileSystemError', 'FileReadError') to provide specific user-friendly messages for known file system issues. |
| 63 | + * For other errors, it checks if the error has a `.code` property (like the internal `ErrorCode` enum values, e.g., 'ERR_2000') and prepends it to the error message. |
| 64 | + * **Shape Change:** `Error` object -> Formatted `string` suitable for console output. |
| 65 | +4. **Output:** The formatted string is printed to `console.error`. |
| 66 | + |
| 67 | +**2. MCP Server Error Path (`server/index.ts` via `tools.ts`)** |
| 68 | + |
| 69 | +1. **Origination:** |
| 70 | + * **Validation Error:** A *tagged* protocol error (`jsonRpcCode = -32602`) is thrown by `toolExecutors.ts` validation. |
| 71 | + * **Execution Error:** An *untagged* application error (e.g., `TaskNotDone`) is thrown by `TaskManager`. |
| 72 | +2. **Catching (`executeToolAndHandleErrors`):** Both types of errors are caught by the `try...catch` block in `executeToolAndHandleErrors` within `src/server/tools.ts`. |
| 73 | +3. **Branching & Transformation:** |
| 74 | + * **If Tagged Protocol Error:** `executeToolAndHandleErrors` detects the `jsonRpcCode` property and *re-throws* the error unchanged. |
| 75 | + * **If Untagged App Error:** |
| 76 | + * `executeToolAndHandleErrors` calls `normalizeError` from `src/utils/errors.ts`. |
| 77 | + * `normalizeError` takes the raw `Error` object. |
| 78 | + * It converts the error into an `McpError` object, typically assigning the `code` to `-32000` (Server Error - a generic JSON-RPC code for implementation-defined errors). It preserves the original error message (stripping any internal `[ERR_CODE]` prefix) and potentially includes the stack trace in the `data` field for debugging. |
| 79 | + * **Shape Change:** Raw `Error` object -> Standardized `McpError` object (with JSON-RPC code). |
| 80 | + * `executeToolAndHandleErrors` then formats this `McpError` into the MCP `isError: true` structure: `{ content: [{ type: "text", text: "Tool execution failed: <normalized_message>" }], isError: true }`. |
| 81 | + * **Shape Change:** `McpError` object -> MCP Tool Result `object` with `isError: true`. |
| 82 | +4. **SDK Handling (`@modelcontextprotocol/sdk Server`):** The MCP SDK `Server` instance (used in `server/index.ts`) handles the outcome from `executeToolAndHandleErrors`: |
| 83 | + * **If Error was Re-thrown (Tagged Protocol Error):** The SDK catches the *thrown* error. It automatically formats this into a standard JSON-RPC top-level error response (e.g., `{"jsonrpc": "2.0", "error": {"code": -32602, "message": "...", "data": ...}, "id": ...}`). |
| 84 | + * **If `isError: true` Object was Returned (Normalized App Error):** The SDK receives the *returned* object. It treats this as a *successful* tool execution from a protocol perspective, but one where the tool itself reported an error. It formats a standard JSON-RPC success response, placing the `isError: true` object inside the `result` field (e.g., `{"jsonrpc": "2.0", "result": {"content": [...], "isError": true}, "id": ...}`). |
| 85 | +5. **Output:** The final JSON-RPC response (either an error response or a success response containing an `isError` result) is sent to the connected MCP Client. |
| 86 | + |
| 87 | +**Key Functions Changing Error Shapes:** |
| 88 | + |
| 89 | +1. **`toolExecutors.ts` (Validation Logic):** Creates *new* `Error` objects and *tags* them with `jsonRpcCode`. (Raw Error -> Tagged Error) |
| 90 | +2. **`normalizeError` (`src/utils/errors.ts`):** Standardizes various error inputs into `McpError` objects, often using the generic `-32000` code for application errors. (Raw Error -> McpError) |
| 91 | +3. **`executeToolAndHandleErrors` (`src/server/tools.ts`):** Packages the `McpError` from `normalizeError` into the MCP-specific `{ content: [...], isError: true }` return format. (McpError -> MCP Result Object) |
| 92 | +4. **`formatCliError` (`src/client/errors.ts`):** Converts `Error` objects into user-friendly `string` messages for the CLI. (Error -> String) |
| 93 | +5. **`@modelcontextprotocol/sdk Server`:** Formats *thrown*, tagged errors into the top-level JSON-RPC `error` object and *returned* `isError: true` results into the JSON-RPC `result` object. (Tagged Error -> JSON-RPC Error / MCP Result Object -> JSON-RPC Result) |
0 commit comments