Add MCP tool profiles for workspace chat flows

Expose stable MCP/server tool profiles so chat hosts can start narrow and widen only when needed. This adds vm-run, workspace-core, and workspace-full across the CLI serve path, Pyro.create_server(), and the package-level create_server() factory while keeping workspace-full as the default.

Register profile-specific tool sets from one shared contract mapping, and narrow the workspace-core schemas so secrets, network policy, shells, services, snapshots, and disk tools do not leak into the default persistent chat profile. The full surface remains available unchanged under workspace-full.

Refresh the public docs and examples around the profile progression, add a canonical OpenAI Responses workspace-core example, mark the 3.4.0 roadmap milestone done, and verify with uv lock, UV_CACHE_DIR=.uv-cache make check, UV_CACHE_DIR=.uv-cache make dist-check, and a real guest-backed workspace-core smoke for create, file write, exec, diff, export, reset, and delete.
This commit is contained in:
Thales Maciel 2026-03-12 23:52:13 -03:00
parent 446f7fce04
commit eecfd7a7d7
23 changed files with 984 additions and 511 deletions

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,7 @@ from typing import Any
from pyro_mcp import __version__
from pyro_mcp.api import Pyro
from pyro_mcp.contract import PUBLIC_MCP_PROFILES
from pyro_mcp.demo import run_demo
from pyro_mcp.ollama_demo import DEFAULT_OLLAMA_BASE_URL, DEFAULT_OLLAMA_MODEL, run_ollama_tool_demo
from pyro_mcp.runtime import DEFAULT_PLATFORM, doctor_report
@ -740,24 +741,45 @@ def _build_parser() -> argparse.ArgumentParser:
"Run the MCP server after you have already validated the host and "
"guest execution with `pyro doctor` and `pyro run`."
),
epilog="Example:\n pyro mcp serve",
epilog=dedent(
"""
Examples:
pyro mcp serve --profile vm-run
pyro mcp serve --profile workspace-core
pyro mcp serve --profile workspace-full
"""
),
formatter_class=_HelpFormatter,
)
mcp_subparsers = mcp_parser.add_subparsers(dest="mcp_command", required=True, metavar="MCP")
mcp_subparsers.add_parser(
mcp_serve_parser = mcp_subparsers.add_parser(
"serve",
help="Run the MCP server over stdio.",
description="Expose pyro tools over stdio for an MCP client.",
epilog=dedent(
"""
Example:
pyro mcp serve
pyro mcp serve --profile workspace-core
Profiles:
vm-run: only the vm_run tool
workspace-core: vm_run plus the practical workspace chat loop
workspace-full: the full stable workspace surface
Use this from an MCP client config after the CLI evaluation path works.
"""
),
formatter_class=_HelpFormatter,
)
mcp_serve_parser.add_argument(
"--profile",
choices=PUBLIC_MCP_PROFILES,
default="workspace-full",
help=(
"Expose only one model-facing tool profile. "
"`workspace-full` preserves the current full MCP surface."
),
)
run_parser = subparsers.add_parser(
"run",
@ -2175,7 +2197,7 @@ def main() -> None:
_print_prune_human(prune_payload)
return
if args.command == "mcp":
pyro.create_server().run(transport="stdio")
pyro.create_server(profile=args.profile).run(transport="stdio")
return
if args.command == "run":
command = _require_command(args.command_args)

View file

@ -5,6 +5,8 @@ from __future__ import annotations
PUBLIC_CLI_COMMANDS = ("demo", "doctor", "env", "mcp", "run", "workspace")
PUBLIC_CLI_DEMO_SUBCOMMANDS = ("ollama",)
PUBLIC_CLI_ENV_SUBCOMMANDS = ("inspect", "list", "pull", "prune")
PUBLIC_CLI_MCP_SUBCOMMANDS = ("serve",)
PUBLIC_CLI_MCP_SERVE_FLAGS = ("--profile",)
PUBLIC_CLI_WORKSPACE_SUBCOMMANDS = (
"create",
"delete",
@ -108,6 +110,7 @@ PUBLIC_CLI_RUN_FLAGS = (
"--allow-host-compat",
"--json",
)
PUBLIC_MCP_PROFILES = ("vm-run", "workspace-core", "workspace-full")
PUBLIC_SDK_METHODS = (
"apply_workspace_patch",
@ -204,3 +207,23 @@ PUBLIC_MCP_TOOLS = (
"workspace_sync_push",
"workspace_update",
)
PUBLIC_MCP_VM_RUN_PROFILE_TOOLS = ("vm_run",)
PUBLIC_MCP_WORKSPACE_CORE_PROFILE_TOOLS = (
"vm_run",
"workspace_create",
"workspace_delete",
"workspace_diff",
"workspace_exec",
"workspace_export",
"workspace_file_list",
"workspace_file_read",
"workspace_file_write",
"workspace_list",
"workspace_logs",
"workspace_patch_apply",
"workspace_reset",
"workspace_status",
"workspace_sync_push",
"workspace_update",
)
PUBLIC_MCP_WORKSPACE_FULL_PROFILE_TOOLS = PUBLIC_MCP_TOOLS

View file

@ -4,13 +4,17 @@ from __future__ import annotations
from mcp.server.fastmcp import FastMCP
from pyro_mcp.api import Pyro
from pyro_mcp.api import McpToolProfile, Pyro
from pyro_mcp.vm_manager import VmManager
def create_server(manager: VmManager | None = None) -> FastMCP:
def create_server(
manager: VmManager | None = None,
*,
profile: McpToolProfile = "workspace-full",
) -> FastMCP:
"""Create and return a configured MCP server instance."""
return Pyro(manager=manager).create_server()
return Pyro(manager=manager).create_server(profile=profile)
def main() -> None:

View file

@ -19,7 +19,7 @@ from typing import Any
from pyro_mcp.runtime import DEFAULT_PLATFORM, RuntimePaths
DEFAULT_ENVIRONMENT_VERSION = "1.0.0"
DEFAULT_CATALOG_VERSION = "3.3.0"
DEFAULT_CATALOG_VERSION = "3.4.0"
OCI_MANIFEST_ACCEPT = ", ".join(
(
"application/vnd.oci.image.index.v1+json",