Metadata-Version: 2.4
Name: zoo_mcp
Version: 0.28.9
Summary: An MCP server utilizing Zoo's various tools
Keywords: zoo,mcp,kittycad,3d,modeling,server
Author: Ryan Barton
Author-email: Ryan Barton <ryan@zoo.dev>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Dist: aiofiles<26.0
Requires-Dist: httpx>=0.28,<1
Requires-Dist: kittycad>=1.6.1,<2.0
Requires-Dist: mcp[cli]>=2.2.0,<3
Requires-Dist: pillow<13.0
Requires-Dist: pymongo>=4.6.0,<5.0
Requires-Dist: trimesh<6.0
Requires-Dist: truststore<1.0
Requires-Dist: typing-extensions>=4.12
Requires-Dist: websockets>=14.1,<17.0
Requires-Dist: zoo-kcl>=0.3.190
Maintainer: Ryan Barton
Maintainer-email: Ryan Barton <ryan@zoo.dev>
Requires-Python: >=3.11, <3.14
Project-URL: Repository, https://github.com/KittyCAD/mcp
Description-Content-Type: text/markdown

# Zoo Model Context Protocol (MCP) Server

An [MCP server](https://modelcontextprotocol.io/docs/getting-started/intro) housing various Zoo built utilities

<!-- mcp-name: io.github.KittyCAD/zoo-mcp -->

## Prerequisites

1. An API key for Zoo, get one [here](https://zoo.dev/account)
2. An environment variable `ZOO_API_TOKEN` set to your API key
    ```bash
    export ZOO_API_TOKEN="your_api_key_here"
    ```

## Installation

1. [Ensure uv has been installed](https://docs.astral.sh/uv/getting-started/installation/)

2. [Create a uv environment](https://docs.astral.sh/uv/pip/environments/)
    ```bash
    uv venv
    ```

3. [Activate your uv environment (Optional)](https://docs.astral.sh/uv/pip/environments/#using-a-virtual-environment)

4. Install the package from GitHub
    ```bash
    uv pip install git+ssh://git@github.com/KittyCAD/mcp.git
    ```

## Running the Server

The server can be started by using [uvx](https://docs.astral.sh/uv/guides/tools/#running-tools)
```bash
uvx zoo-mcp
```

The server can be started locally by using uv and the zoo_mcp module
```bash
uv run -m zoo_mcp
```

The server can also be run with the [mcp package](https://github.com/modelcontextprotocol/python-sdk)
```bash
uv run mcp run src/zoo_mcp/server.py
```

### Streamable HTTP

The server uses stdio by default. To serve the same tools over Streamable HTTP:

```bash
uvx zoo-mcp --transport streamable-http --host 127.0.0.1 --port 8000
# From a local checkout:
uv run -m zoo_mcp --transport streamable-http
```

Connect an MCP client to `http://127.0.0.1:8000/mcp`. The SDK manages HTTP
sessions and streaming responses. `--host` and `--port` configure the HTTP
listener; their defaults are `127.0.0.1` and `8000`.

### Supplying credentials from Python

Local entry points capture credentials when Zoo MCP is imported at startup.
They accept `ZOO_API_TOKEN`, `KITTYCAD_API_TOKEN`, or the registry's `ZOO_TOKEN`
alias, and `ZOO_HOST` or `KITTYCAD_HOST` for the API origin.

Applications can supply credentials explicitly without changing the process
environment or adding authentication arguments to MCP tools:

```python
from zoo_mcp.credentials import ZooCredentials, use_credentials
from zoo_mcp.server import mcp

with use_credentials(ZooCredentials(token=api_token, base_url=api_origin)):
    result = await mcp.call_tool("list_org_datasets", {})
```

The context supplies the same token and origin to the KittyCAD SDK and KCL
session constructors and restores the previous configuration on exit. New
calls can use refreshed credentials. Finish child tasks before leaving their
credential context.

Credential contexts configure downstream calls. Applications serving different
owners must also isolate their process-wide modeling sessions and workspaces.

### Prebuilt binaries

Each [GitHub release](https://github.com/KittyCAD/mcp/releases) also attaches standalone executables (built with PyInstaller) for Linux (`x86_64`, `arm64`), macOS (`arm64`, `x86_64`), and Windows (`x86_64`) — no Python toolchain required. Download the binary for your platform, set `ZOO_API_TOKEN`, and run it directly, e.g.:
```bash
ZOO_API_TOKEN="your_api_key_here" ./zoo-mcp-linux-x86_64
```
> The binaries are not code-signed, so macOS Gatekeeper and Windows SmartScreen may warn on first run.

## Integrations

The server can be used as is by [running the server](#running-the-server) or importing directly into your python code.
```python
from zoo_mcp.server import mcp

mcp.run()
```

Individual tools can be used in your own python code as well. At Zoo we use
zoo-mcp like this with ZooKeeper to save on resources. Instead of spinning up
one MCP server per agent, each agent in a sense "embeds" the server in their own
runtime. It has the additional benefit of preventing shared state.

```python
from mcp.server.mcpserver import MCPServer
from zoo_mcp.zoo_tools import ResultZooExecuteKcl, zoo_execute_kcl

mcp = MCPServer(name="My Example Server")


@mcp.tool()
async def my_execute_kcl(kcl_code: str) -> ResultZooExecuteKcl:
    """
    Example tool that uses the zoo_execute_kcl function from zoo_mcp.zoo_tools
    """
    return await zoo_execute_kcl(kcl_code=kcl_code)
```

The server can be integrated with [Claude desktop](https://claude.ai/download) using the following command
```bash 
uv run mcp install src/zoo_mcp/server.py
```

The server can also be integrated with [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) using the following command
```bash
claude mcp add --scope project "Zoo-MCP" uv -- --directory "$PWD"/src/zoo_mcp run server.py
```

The server can also be tested using the [MCP Inspector](https://modelcontextprotocol.io/legacy/tools/inspector#python)
```bash
uv run mcp dev src/zoo_mcp/server.py
```

For running with [codex-cli](https://github.com/openai/codex)
```bash
codex \
  -c 'mcp_servers.zoo.command="uvx"' \
  -c 'mcp_servers.zoo.args=["zoo-mcp"]' \
  -c mcp_servers.zoo.env.ZOO_API_TOKEN="$ZOO_API_TOKEN"
```

You can also use the helper script included in this repo:
```bash
./codex-zoo.sh
```
The script prompts for a request, runs Codex with the Zoo MCP server, and saves a JSONL transcript (including token usage) to `codex-run-<timestamp>.jsonl`.

## Architecture

Tools are defined in `src/zoo_mcp/*.py`, where they are then imported into
`src/zoo_mcp/server.py` and tied to actual `@mcp.tool()` decorated functions.

`src/zoo_mcp/zoo_tools.py` acts as a large toolset to interact with Zoo's KCL and
engine facilities. This source file houses other utilities like `parse_unit` or
`normalize_ext` (normalizing file extensions).

Modeling scenes use explicit persistent sessions, with at most one session open
per server process. Call `get_modeling_sessions` to recover its ID after a client
reconnect, or call `start_modeling_session` when none exists. Populate the
session with `execute_kcl`, `exec_kcl_project`, or `import_cad_file`; pass the
same `session_id` to `snapshot` and modeling tools; then call
`stop_modeling_session` when finished.

As of 0.28.0, `execute_kcl` and `exec_kcl_project` run mock execution before real
execution and return separate `mock_preflight` and `real_execution` objects.
Each contains `status` (`succeeded`, `failed`, or `not_run`), `message`, and
`diagnostics` grouped by severity. Stage messages are short summaries; the
top-level `message` retains the full report for existing callers. Failed stages
also expose `error_family`, including `ZooMCPTimeoutError` for session timeouts.
Mock errors or an aborted mock execution
return immediately with `ok: false` and `real_execution.status: "not_run"`.
Mock warnings remain in `mock_preflight.diagnostics` even if real execution fails.
The known `planeOf` mock-engine limitation is reported as a warning so the real
engine can evaluate it; other mock errors still block execution.
Session responses expose mock diagnostics; the engine does not return real-stage
diagnostics for session execution.

Path inputs capture the entrypoint, its transitive imports (including linked
modules and glTF buffers), and `project.toml` once. Both stages use that copy
without scanning unrelated files in the containing directory. Dependencies and
symlink targets must stay inside the entrypoint's directory; external paths are
rejected before file reads or execution. Transient local real-execution failures
retain their bounded retries using the same copy without repeating mock execution.
Diagnostics refer to the original source paths. Inline `kcl_code` accepts
self-contained code and standard-library imports; filesystem imports require
`kcl_path` so their dependencies can be captured within an explicit directory.
`exec_kcl_project` now returns
this structured result instead of a path string: check `ok`, then read
`path_artifact_graph` on session success. The standalone `mock_execute_kcl` tool
continues to return its existing boolean/message pair.

## Contributing

Contributions are welcome! Please open an issue or submit a pull request on the [GitHub repository](https://github.com/KittyCAD/mcp)

PRs will need to pass tests and linting before being merged.

### [ruff](https://docs.astral.sh/ruff/) is used for linting and formatting.
```bash
uvx ruff check
uvx ruff format
```

### [ty](https://docs.astral.sh/ty/) is used for type checking.
```bash
uvx ty check
```

## Testing

The server includes tests located in [`tests`](`tests`). To run the tests, use the following command:
```bash
uv run pytest -n auto
```
