Make workspace-core the default MCP profile

Flip bare pyro mcp serve, create_server(), and Pyro.create_server() to default to workspace-core in 4.0.0 while keeping workspace-full as the explicit advanced opt-in surface.

Rewrite the MCP-facing docs and host-specific examples around the bare default command, update package and catalog compatibility to 4.x, and move the public-contract wording from 3.x compatibility guidance to the new stable default.

Adjust the server, API, and contract tests so bare server creation now asserts the workspace-core tool set, while explicit workspace-full coverage continues to prove shells, services, snapshots, and disk tools remain available.

Validation: uv lock; .venv/bin/pytest --no-cov tests/test_cli.py tests/test_api.py tests/test_server.py tests/test_public_contract.py; UV_CACHE_DIR=.uv-cache make check; UV_CACHE_DIR=.uv-cache make dist-check; real guest-backed smoke for bare Pyro.create_server() plus explicit profile="workspace-full".
This commit is contained in:
Thales Maciel 2026-03-13 14:14:15 -03:00
parent 68d8e875e0
commit c00c699a9f
25 changed files with 170 additions and 121 deletions

View file

@ -37,7 +37,7 @@ def test_pyro_run_in_vm_delegates_to_manager(tmp_path: Path) -> None:
assert str(result["stdout"]) == "ok\n"
def test_pyro_create_server_registers_full_profile_and_shell_read_schema(tmp_path: Path) -> None:
def test_pyro_create_server_defaults_to_workspace_core_profile(tmp_path: Path) -> None:
pyro = Pyro(
manager=VmManager(
backend_name="mock",
@ -52,6 +52,34 @@ def test_pyro_create_server_registers_full_profile_and_shell_read_schema(tmp_pat
tool_map = {tool.name: tool.model_dump() for tool in tools}
return sorted(tool_map), tool_map
tool_names, tool_map = asyncio.run(_run())
assert tuple(tool_names) == tuple(sorted(PUBLIC_MCP_WORKSPACE_CORE_PROFILE_TOOLS))
create_properties = tool_map["workspace_create"]["inputSchema"]["properties"]
assert "network_policy" not in create_properties
assert "secrets" not in create_properties
exec_properties = tool_map["workspace_exec"]["inputSchema"]["properties"]
assert "secret_env" not in exec_properties
assert "shell_open" not in tool_map
assert "service_start" not in tool_map
assert "snapshot_create" not in tool_map
assert "workspace_disk_export" not in tool_map
def test_pyro_create_server_workspace_full_profile_keeps_shell_read_schema(tmp_path: Path) -> None:
pyro = Pyro(
manager=VmManager(
backend_name="mock",
base_dir=tmp_path / "vms",
network_manager=TapNetworkManager(enabled=False),
)
)
async def _run() -> tuple[list[str], dict[str, dict[str, Any]]]:
server = pyro.create_server(profile="workspace-full")
tools = await server.list_tools()
tool_map = {tool.name: tool.model_dump() for tool in tools}
return sorted(tool_map), tool_map
tool_names, tool_map = asyncio.run(_run())
assert tuple(tool_names) == tuple(sorted(PUBLIC_MCP_WORKSPACE_FULL_PROFILE_TOOLS))
shell_read_properties = tool_map["shell_read"]["inputSchema"]["properties"]
@ -568,7 +596,7 @@ def test_pyro_create_server_workspace_disk_tools_delegate() -> None:
return cast(dict[str, Any], structured)
async def _run() -> tuple[dict[str, Any], ...]:
server = pyro.create_server()
server = pyro.create_server(profile="workspace-full")
stopped = _extract_structured(
await server.call_tool("workspace_stop", {"workspace_id": "workspace-123"})
)
@ -1078,7 +1106,7 @@ def test_pyro_create_server_workspace_status_shell_and_service_delegate() -> Non
return cast(dict[str, Any], structured)
async def _run() -> tuple[dict[str, Any], ...]:
server = pyro.create_server()
server = pyro.create_server(profile="workspace-full")
status = _extract_structured(
await server.call_tool("workspace_status", {"workspace_id": "workspace-123"})
)