feat: add create_design tool so the MCP can create canvases
Wrap POST /api/v1/designs so AI clients can create a new design (canvas) and get its id back for use as design_id. Completes the create-and-target canvas workflow alongside list_designs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -134,6 +134,15 @@ def _build_tools() -> list[Tool]:
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {},
|
"properties": {},
|
||||||
}),
|
}),
|
||||||
|
Tool(name="create_design", description="Create a new design (canvas) and return it, including its id for use as design_id", inputSchema={
|
||||||
|
"type": "object",
|
||||||
|
"required": ["name"],
|
||||||
|
"properties": {
|
||||||
|
"name": {"type": "string", "description": "Name of the new canvas."},
|
||||||
|
"icon": {"type": "string", "description": "Icon name for the canvas (default: dashboard)."},
|
||||||
|
"design_type": {"type": "string", "description": "Design type (default: network)."},
|
||||||
|
},
|
||||||
|
}),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -220,4 +229,7 @@ async def _dispatch(name: str, args: dict) -> dict:
|
|||||||
if name == "list_designs":
|
if name == "list_designs":
|
||||||
return await backend.get("/api/v1/designs")
|
return await backend.get("/api/v1/designs")
|
||||||
|
|
||||||
|
if name == "create_design":
|
||||||
|
return await backend.post("/api/v1/designs", args)
|
||||||
|
|
||||||
raise ValueError(f"Unknown tool: {name}")
|
raise ValueError(f"Unknown tool: {name}")
|
||||||
|
|||||||
@@ -249,6 +249,19 @@ async def test_list_designs(mock_backend):
|
|||||||
assert result == [{"id": "d1", "name": "Network Topology", "node_count": 12}]
|
assert result == [{"id": "d1", "name": "Network Topology", "node_count": 12}]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.anyio
|
||||||
|
async def test_create_design(mock_backend):
|
||||||
|
mock_backend.post = AsyncMock(return_value={"id": "d2", "name": "Scan Devices"})
|
||||||
|
result = await _dispatch("create_design", {"name": "Scan Devices"})
|
||||||
|
mock_backend.post.assert_called_once_with("/api/v1/designs", {"name": "Scan Devices"})
|
||||||
|
assert result == {"id": "d2", "name": "Scan Devices"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_design_schema_requires_name():
|
||||||
|
tool = next(t for t in TOOLS if t.name == "create_design")
|
||||||
|
assert tool.inputSchema["required"] == ["name"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
async def test_unknown_tool():
|
async def test_unknown_tool():
|
||||||
with pytest.raises(ValueError, match="Unknown tool"):
|
with pytest.raises(ValueError, match="Unknown tool"):
|
||||||
|
|||||||
Reference in New Issue
Block a user