fix(mcp): apply_patch preserves schema-defaulted ids in multi-op batches
SceneBridge.applyPatch now threads the Zod-parsed node through to the apply phase instead of the raw input. Previously, when a caller sent a create patch without an `id` field, the schema's objectId default ran during dry-run parsing but only in `res.data`; the apply phase pushed the unparsed `p.node` (no id) to the store, so subsequent tools that walked `level.children` crashed on undefined entries (e.g. duplicate_level -> cloneLevelSubtree -> extractIdPrefix(undefined)). Also adds test-reports/ artefacts from live end-to-end testing: - t1-stdio: 21/21 tools pass via stdio (~106ms) - t2-http: connect/single-session behaviour (HTTP transport quirk documented) - t3-scenario: 2-bedroom apartment built end-to-end — 12/12 steps after this fix (24 final nodes, validate=true, apartment.json exported) - t4-errors: 24/24 invalid-input cases rejected with proper MCP errors - t5-resources-prompts: 4/4 resources, 3/3 prompts, dev server /api/health 200 OK Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
a6f1c4140f
commit
b37e88cb83
@@ -0,0 +1,728 @@
|
||||
# T4 — Error Contract Verification Report
|
||||
|
||||
Server: `http://localhost:3917/`
|
||||
Run date: 2026-04-18T16:17:28.757Z
|
||||
|
||||
## Summary
|
||||
|
||||
- PASS: 24
|
||||
- WARN: 0
|
||||
- FAIL: 0
|
||||
- Total cases: 24
|
||||
|
||||
Baseline node count: 3
|
||||
Final node count: 3 (delta=0)
|
||||
Final validation: valid=true, errors=0
|
||||
|
||||
## Cases
|
||||
|
||||
### T4-01 — `get_node` — nonexistent id
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"id": "node_doesnotexist_xyz"
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** McpError InvalidParams (-32602) "Node not found" OR structured tool error
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Node not found: node_doesnotexist_xyz",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Node not found: node_doesnotexist_xyz"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-02 — `describe_node` — nonexistent id
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"id": "node_missing_123"
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** McpError InvalidParams (-32602) "Node not found"
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Node not found: node_missing_123",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Node not found: node_missing_123"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-03 — `find_nodes` — invalid type enum "hamster"
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"type": "hamster"
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** Zod validation error (MCP InvalidParams -32602)
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Input validation error: Invalid arguments for tool find_nodes: [\n {\n \"code\": \"invalid_value\",\n \"values\": [\n \"site\",\n \"building\",\n \"level\",\n \"wall\",\n \"fence\",\n \"zone\",\n \"slab\",\n \"ceiling\",\n \"roof\",\n \"roof-segment\",\n \"stair\",\n \"stair-segment\",\n \"item\",\n \"door\",\n \"window\",\n \"scan\",\n \"guide\"\n ],\n \"path\": [\n \"type\"\n ],\n \"message\": \"Invalid option: expected one of \\\"site\\\"|\\\"building\\\"|\\\"level\\\"|\\\"wall\\\"|\\\"fence\\\"|\\\"zone\\\"|\\\"slab\\\"|\\\"ceiling\\\"|\\\"roof\\\"|\\\"roof-segment\\\"|\\\"stair\\\"|\\\"stair-segment\\\"|\\\"item\\\"|\\\"door\\\"|\\\"window\\\"|\\\"scan\\\"|\\\"guide\\\"\"\n }\n]",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Input validation error: Invalid arguments for tool find_nodes: [\n {\n \"code\": \"invalid_value\",\n \"values\": [\n \"site\",\n \"building\",\n \"level\",\n \"wall\",\n \"fence\",\n \"zone\",\n \"slab\",\n \"ceiling\",\n \"roof\",\n \"roof-segment\",\n \"stair\",\n \"stair-segment\",\n \"item\",\n \"door\",\n \"window\",\n \"scan\",\n \"guide\"\n ],\n \"path\": [\n \"type\"\n ],\n \"message\": \"Invalid option: expected one of \\\"site\\\"|\\\"building\\\"|\\\"level\\\"|\\\"wall\\\"|\\\"fence\\\"|\\\"zone\\\"|\\\"slab\\\"|\\\"ceiling\\\"|\\\"roof\\\"|\\\"roof-segment\\\"|\\\"stair\\\"|\\\"stair-segment\\\"|\\\"item\\\"|\\\"door\\\"|\\\"window\\\"|\\\"scan\\\"|\\\"guide\\\"\"\n }\n]"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-04 — `measure` — nonexistent fromId
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"fromId": "node_nosuch_f",
|
||||
"toId": "site_watn4a0qt2xpgri7"
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** McpError InvalidParams "Node not found"
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Node not found: node_nosuch_f",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Node not found: node_nosuch_f"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-05 — `apply_patch` — patches with one invalid node (missing type)
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"patches": [
|
||||
{
|
||||
"op": "create",
|
||||
"node": {
|
||||
"foo": "bar"
|
||||
},
|
||||
"parentId": "level_tl2aravmn2u9afft"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** McpError InvalidParams, all-or-nothing rollback (no partial state change)
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: invalid patch: patches[0] create node failed schema: [\n {\n \"code\": \"invalid_union\",\n \"errors\": [],\n \"note\": \"No matching discriminator\",\n \"discriminator\": \"type\",\n \"path\": [\n \"type\"\n ],\n \"message\": \"Invalid input\"\n }\n]",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: invalid patch: patches[0] create node failed schema: [\n {\n \"code\": \"invalid_union\",\n \"errors\": [],\n \"note\": \"No matching discriminator\",\n \"discriminator\": \"type\",\n \"path\": [\n \"type\"\n ],\n \"message\": \"Invalid input\"\n }\n]"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-06 — `apply_patch` — delete nonexistent id
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"patches": [
|
||||
{
|
||||
"op": "delete",
|
||||
"id": "node_nonexistent_delete_xyz"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** McpError InvalidParams, no state change
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: invalid patch: patches[0] delete id \"node_nonexistent_delete_xyz\" not found",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: invalid patch: patches[0] delete id \"node_nonexistent_delete_xyz\" not found"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-07 — `create_level` — buildingId is not a building (passed a wall/level/site id)
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"buildingId": "level_tl2aravmn2u9afft",
|
||||
"elevation": 0
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** McpError InvalidParams "expected building"
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Node level_tl2aravmn2u9afft is a level, expected building",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Node level_tl2aravmn2u9afft is a level, expected building"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-08 — `create_wall` — levelId doesn't exist
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"levelId": "level_nosuch_999",
|
||||
"start": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"end": [
|
||||
5,
|
||||
0
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** McpError InvalidParams "Level not found"
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Level not found: level_nosuch_999",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Level not found: level_nosuch_999"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-09 — `create_wall` — start not a tuple
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"levelId": "level_tl2aravmn2u9afft",
|
||||
"start": "not-a-tuple",
|
||||
"end": [
|
||||
5,
|
||||
0
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** Zod validation error (MCP InvalidParams -32602)
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Input validation error: Invalid arguments for tool create_wall: [\n {\n \"expected\": \"tuple\",\n \"code\": \"invalid_type\",\n \"path\": [\n \"start\"\n ],\n \"message\": \"Invalid input: expected tuple, received string\"\n }\n]",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Input validation error: Invalid arguments for tool create_wall: [\n {\n \"expected\": \"tuple\",\n \"code\": \"invalid_type\",\n \"path\": [\n \"start\"\n ],\n \"message\": \"Invalid input: expected tuple, received string\"\n }\n]"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-10 — `place_item` — targetNodeId doesn't exist
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"catalogItemId": "chair-1",
|
||||
"targetNodeId": "node_nosuch_target",
|
||||
"position": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** McpError InvalidParams "Target node not found"
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Target node not found: node_nosuch_target",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Target node not found: node_nosuch_target"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-11 — `cut_opening` — wallId is not a wall
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"wallId": "site_watn4a0qt2xpgri7",
|
||||
"type": "door",
|
||||
"position": 0.5,
|
||||
"width": 0.8,
|
||||
"height": 2
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** McpError InvalidParams "expected wall"
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Node site_watn4a0qt2xpgri7 is a site, expected wall",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Node site_watn4a0qt2xpgri7 is a site, expected wall"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-12 — `cut_opening` — position out of [0,1]
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"wallId": "missing_wall",
|
||||
"type": "door",
|
||||
"position": 2.5,
|
||||
"width": 0.8,
|
||||
"height": 2
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** Zod validation error (MCP InvalidParams) — position must be <= 1
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Input validation error: Invalid arguments for tool cut_opening: [\n {\n \"origin\": \"number\",\n \"code\": \"too_big\",\n \"maximum\": 1,\n \"inclusive\": true,\n \"path\": [\n \"position\"\n ],\n \"message\": \"Too big: expected number to be <=1\"\n }\n]",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Input validation error: Invalid arguments for tool cut_opening: [\n {\n \"origin\": \"number\",\n \"code\": \"too_big\",\n \"maximum\": 1,\n \"inclusive\": true,\n \"path\": [\n \"position\"\n ],\n \"message\": \"Too big: expected number to be <=1\"\n }\n]"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-13 — `set_zone` — polygon with < 3 points
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"levelId": "level_tl2aravmn2u9afft",
|
||||
"polygon": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
5,
|
||||
0
|
||||
]
|
||||
],
|
||||
"label": "Tiny"
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** Zod validation error (MCP InvalidParams) — polygon must have >= 3 points
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Input validation error: Invalid arguments for tool set_zone: [\n {\n \"origin\": \"array\",\n \"code\": \"too_small\",\n \"minimum\": 3,\n \"inclusive\": true,\n \"path\": [\n \"polygon\"\n ],\n \"message\": \"Too small: expected array to have >=3 items\"\n }\n]",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Input validation error: Invalid arguments for tool set_zone: [\n {\n \"origin\": \"array\",\n \"code\": \"too_small\",\n \"minimum\": 3,\n \"inclusive\": true,\n \"path\": [\n \"polygon\"\n ],\n \"message\": \"Too small: expected array to have >=3 items\"\n }\n]"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-14 — `duplicate_level` — levelId is not a level
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"levelId": "site_watn4a0qt2xpgri7"
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** McpError InvalidParams "expected level"
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Node site_watn4a0qt2xpgri7 is a site, expected level",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Node site_watn4a0qt2xpgri7 is a site, expected level"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-15 — `delete_node` — cascade=false with children (target site site_watn4a0qt2xpgri7 children=1)
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"id": "site_watn4a0qt2xpgri7",
|
||||
"cascade": false
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** McpError InvalidRequest "node has children" (no delete)
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32600: node has 2 descendant(s); pass cascade: true to delete recursively",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32600: node has 2 descendant(s); pass cascade: true to delete recursively"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-16a — `undo` — negative steps
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"steps": -1
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** Zod validation error (MCP InvalidParams) — steps must be positive int
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Input validation error: Invalid arguments for tool undo: [\n {\n \"origin\": \"number\",\n \"code\": \"too_small\",\n \"minimum\": 0,\n \"inclusive\": false,\n \"path\": [\n \"steps\"\n ],\n \"message\": \"Too small: expected number to be >0\"\n }\n]",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Input validation error: Invalid arguments for tool undo: [\n {\n \"origin\": \"number\",\n \"code\": \"too_small\",\n \"minimum\": 0,\n \"inclusive\": false,\n \"path\": [\n \"steps\"\n ],\n \"message\": \"Too small: expected number to be >0\"\n }\n]"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-16b — `redo` — negative steps
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"steps": -2
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** Zod validation error (MCP InvalidParams) — steps must be positive int
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Input validation error: Invalid arguments for tool redo: [\n {\n \"origin\": \"number\",\n \"code\": \"too_small\",\n \"minimum\": 0,\n \"inclusive\": false,\n \"path\": [\n \"steps\"\n ],\n \"message\": \"Too small: expected number to be >0\"\n }\n]",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Input validation error: Invalid arguments for tool redo: [\n {\n \"origin\": \"number\",\n \"code\": \"too_small\",\n \"minimum\": 0,\n \"inclusive\": false,\n \"path\": [\n \"steps\"\n ],\n \"message\": \"Too small: expected number to be >0\"\n }\n]"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-17 — `export_json` — pretty='yes' (string not bool)
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"pretty": "yes"
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** Zod validation error (MCP InvalidParams) — pretty must be boolean
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32602: Input validation error: Invalid arguments for tool export_json: [\n {\n \"expected\": \"boolean\",\n \"code\": \"invalid_type\",\n \"path\": [\n \"pretty\"\n ],\n \"message\": \"Invalid input: expected boolean, received string\"\n }\n]",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32602: Input validation error: Invalid arguments for tool export_json: [\n {\n \"expected\": \"boolean\",\n \"code\": \"invalid_type\",\n \"path\": [\n \"pretty\"\n ],\n \"message\": \"Invalid input: expected boolean, received string\"\n }\n]"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-18 — `check_collisions` — levelId doesn't exist
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"levelId": "level_nosuch_zzz"
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** Empty collisions result OR graceful error
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "unexpected_success",
|
||||
"message": "tool returned successfully with no isError flag",
|
||||
"structuredContent": {
|
||||
"collisions": []
|
||||
},
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "{\"collisions\":[]}"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** returned empty collisions (graceful)
|
||||
|
||||
### T4-19 — `validate_scene` — baseline: no args
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
**Expected:** Success — structured { valid, errors[] }
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "unexpected_success",
|
||||
"message": "tool returned successfully with no isError flag",
|
||||
"structuredContent": {
|
||||
"valid": true,
|
||||
"errors": []
|
||||
},
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "{\"valid\":true,\"errors\":[]}"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** baseline passed
|
||||
|
||||
### T4-20a — `analyze_floorplan_image` — image: '' (empty string)
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"image": ""
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** Validation error OR sampling_unavailable
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32600: sampling_unavailable",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32600: sampling_unavailable"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-20b — `analyze_floorplan_image` — image: 'not-a-url-or-base64'
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"image": "not-a-url-or-base64"
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** Validation error OR sampling_unavailable
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32600: sampling_unavailable",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32600: sampling_unavailable"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-21a — `analyze_room_photo` — image: '' (empty string)
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"image": ""
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** Validation error OR sampling_unavailable
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32600: sampling_unavailable",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32600: sampling_unavailable"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### T4-21b — `analyze_room_photo` — image: 'not-a-url-or-base64'
|
||||
|
||||
**Verdict:** ✅ PASS
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"image": "not-a-url-or-base64"
|
||||
}
|
||||
```
|
||||
|
||||
**Expected:** Validation error OR sampling_unavailable
|
||||
|
||||
**Actual:**
|
||||
```json
|
||||
{
|
||||
"kind": "tool_error",
|
||||
"message": "MCP error -32600: sampling_unavailable",
|
||||
"rawContent": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "MCP error -32600: sampling_unavailable"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user