forked from 1b-agent/opencadstudio-upstream
Make native MCP self-describing
This commit is contained in:
1 parent
200ed541f5
commit
0e4f755e6f
5 files changed
+407
-17
No files matched your search
@@ -31,9 +31,28 @@ The normal flow is to call `ocs_sessions`, read the chosen session with `ocs_rea
|
||||
}
|
||||
```
|
||||
|
||||
To discover command syntax without reading application source, call `ocs_read` with `op: "commands"`. The unfiltered response lists commands and actions. Add `parameters: {"name": "PLINE"}` for a command's batch examples and interactive guidance.
|
||||
|
||||
A complete command can be sent with `run`. Prompt answers are separated by spaces, points use `x,y` or `x,y,z`, and option answers use their displayed token:
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "SESSION_FROM_OCS_SESSIONS",
|
||||
"request": {
|
||||
"op": "run",
|
||||
"request_id": "polyline-1",
|
||||
"document_id": 1,
|
||||
"revision": 12,
|
||||
"cmd": "PLINE 0,0 10,0 10,10 C"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For unfamiliar or conditional commands, use `start`, then inspect `state.command` in every response. Its `accepts` array gives the valid MCP input kinds, `options` gives the current tokens, and `input_example` gives the next request shape. Add the current state fields and a new `request_id` to each step.
|
||||
|
||||
Every `ocs_execute` request requires a caller-generated `request_id`. Reuse that ID only to retry the identical request after a timeout, together with the same session ID, document ID, expected revision, and selection. Commands report `waiting_input` while more input is required and asynchronous work remains `running` until its real callback finishes. Geometry stays in OpenCADStudio and its geometry kernel.
|
||||
|
||||
The source-tree protocol smoke test uses only the Python standard library:
|
||||
The source-tree protocol smoke test treats the executable as a black box and uses only the Python standard library. It checks both supported protocol styles, the published schemas, structured errors, and the four-tool surface:
|
||||
|
||||
```sh
|
||||
cargo build
|
||||
|
||||
@@ -44,12 +44,21 @@ def legacy(server: Path) -> None:
|
||||
"params": {"protocolVersion": "2025-11-25", "capabilities": {}, "clientInfo": {"name": "smoke", "version": "1"}},
|
||||
})
|
||||
assert initialized["result"]["serverInfo"]["name"] == "OpenCADStudio"
|
||||
assert initialized["result"]["instructions"]
|
||||
assert "state.command.accepts" in initialized["result"]["instructions"]
|
||||
process.stdin.write(json.dumps({"jsonrpc": "2.0", "method": "notifications/initialized"}) + "\n")
|
||||
process.stdin.flush()
|
||||
tools = request(process, {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}})
|
||||
names = {tool["name"] for tool in tools["result"]["tools"]}
|
||||
definitions = {tool["name"]: tool for tool in tools["result"]["tools"]}
|
||||
names = set(definitions)
|
||||
assert names == TOOLS, names
|
||||
execute_request = definitions["ocs_execute"]["inputSchema"]["properties"]["request"]
|
||||
assert len(execute_request["oneOf"]) == 14
|
||||
assert execute_request["properties"]["cmd"]["examples"][0] == "LINE 0,0 10,10"
|
||||
assert execute_request["properties"]["kind"]["enum"] == [
|
||||
"text", "token", "point", "entity", "structure", "selection", "enter"
|
||||
]
|
||||
for name in {"ocs_sessions", "ocs_read", "ocs_execute"}:
|
||||
assert definitions[name]["outputSchema"]["type"] == "object"
|
||||
assert "resultType" not in tools["result"]
|
||||
sessions = request(process, {
|
||||
"jsonrpc": "2.0",
|
||||
@@ -89,6 +98,23 @@ def modern(server: Path) -> None:
|
||||
assert tools["result"]["ttlMs"] >= 0
|
||||
assert tools["result"]["cacheScope"] in {"public", "private"}
|
||||
|
||||
missing_cmd = request(process, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "missing-cmd",
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "ocs_execute",
|
||||
"arguments": {
|
||||
"session_id": "missing",
|
||||
"request": {"op": "run", "request_id": "run-1"},
|
||||
},
|
||||
"_meta": meta,
|
||||
},
|
||||
})
|
||||
error = missing_cmd["result"]["structuredContent"]
|
||||
assert error["code"] == "invalid_arguments"
|
||||
assert "LINE 0,0 10,10" in error["error"]
|
||||
|
||||
invalid_mutation = request(process, {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "invalid-mutation",
|
||||
|
||||
Reference in new issue
Block a user