cooperage 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cooperage-0.1.0/.env.example +58 -0
- cooperage-0.1.0/.github/workflows/publish-images.yml +70 -0
- cooperage-0.1.0/.github/workflows/test.yml +29 -0
- cooperage-0.1.0/.gitignore +20 -0
- cooperage-0.1.0/CONTRIBUTING.md +89 -0
- cooperage-0.1.0/Dockerfile +13 -0
- cooperage-0.1.0/Dockerfile.ui +15 -0
- cooperage-0.1.0/LICENSE +21 -0
- cooperage-0.1.0/PKG-INFO +18 -0
- cooperage-0.1.0/README.md +231 -0
- cooperage-0.1.0/ROADMAP.md +77 -0
- cooperage-0.1.0/assets/favicon.png +0 -0
- cooperage-0.1.0/assets/logo.png +0 -0
- cooperage-0.1.0/chart/Chart.yaml +6 -0
- cooperage-0.1.0/chart/templates/configmap.yaml +33 -0
- cooperage-0.1.0/chart/templates/deployment.yaml +59 -0
- cooperage-0.1.0/chart/templates/ingress.yaml +33 -0
- cooperage-0.1.0/chart/templates/namespace.yaml +7 -0
- cooperage-0.1.0/chart/templates/pvc.yaml +19 -0
- cooperage-0.1.0/chart/templates/rbac.yaml +40 -0
- cooperage-0.1.0/chart/templates/secrets.yaml +1 -0
- cooperage-0.1.0/chart/templates/service.yaml +18 -0
- cooperage-0.1.0/chart/templates/serviceaccount.yaml +8 -0
- cooperage-0.1.0/chart/templates/ui.yaml +53 -0
- cooperage-0.1.0/chart/values.yaml +83 -0
- cooperage-0.1.0/cooperage/__init__.py +0 -0
- cooperage-0.1.0/cooperage/cli/__init__.py +0 -0
- cooperage-0.1.0/cooperage/cli/main.py +205 -0
- cooperage-0.1.0/cooperage/core/__init__.py +0 -0
- cooperage-0.1.0/cooperage/core/audit.py +97 -0
- cooperage-0.1.0/cooperage/core/auth.py +103 -0
- cooperage-0.1.0/cooperage/core/config.py +58 -0
- cooperage-0.1.0/cooperage/core/errors.py +82 -0
- cooperage-0.1.0/cooperage/core/models.py +57 -0
- cooperage-0.1.0/cooperage/gateway/__init__.py +0 -0
- cooperage-0.1.0/cooperage/gateway/server.py +994 -0
- cooperage-0.1.0/cooperage/orchestrator/__init__.py +16 -0
- cooperage-0.1.0/cooperage/orchestrator/base.py +87 -0
- cooperage-0.1.0/cooperage/orchestrator/docker.py +204 -0
- cooperage-0.1.0/cooperage/orchestrator/kubernetes.py +377 -0
- cooperage-0.1.0/cooperage/registry/__init__.py +0 -0
- cooperage-0.1.0/cooperage/registry/registry.py +41 -0
- cooperage-0.1.0/cooperage/session/__init__.py +0 -0
- cooperage-0.1.0/cooperage/session/manager.py +340 -0
- cooperage-0.1.0/docker-compose.yml +31 -0
- cooperage-0.1.0/docs/writing-servers.md +273 -0
- cooperage-0.1.0/example-servers/image-analyzer/Dockerfile +18 -0
- cooperage-0.1.0/example-servers/image-analyzer/requirements.txt +5 -0
- cooperage-0.1.0/example-servers/image-analyzer/server.py +94 -0
- cooperage-0.1.0/example-servers/synthetic-image-generator/Dockerfile +16 -0
- cooperage-0.1.0/example-servers/synthetic-image-generator/requirements.txt +4 -0
- cooperage-0.1.0/example-servers/synthetic-image-generator/server.py +198 -0
- cooperage-0.1.0/pyproject.toml +43 -0
- cooperage-0.1.0/pytest.ini +6 -0
- cooperage-0.1.0/scripts/bump-version.sh +54 -0
- cooperage-0.1.0/servers/compute/Dockerfile +17 -0
- cooperage-0.1.0/servers/compute/requirements.txt +8 -0
- cooperage-0.1.0/servers/compute/server.py +78 -0
- cooperage-0.1.0/servers/workspace/Dockerfile +16 -0
- cooperage-0.1.0/servers/workspace/requirements.txt +3 -0
- cooperage-0.1.0/servers/workspace/server.py +115 -0
- cooperage-0.1.0/tests/__init__.py +0 -0
- cooperage-0.1.0/tests/conftest.py +12 -0
- cooperage-0.1.0/tests/test_audit.py +161 -0
- cooperage-0.1.0/tests/test_cloud_integration.py +152 -0
- cooperage-0.1.0/tests/test_gateway.py +325 -0
- cooperage-0.1.0/tests/test_gateway_extended.py +190 -0
- cooperage-0.1.0/tests/test_models.py +48 -0
- cooperage-0.1.0/tests/test_orchestrator.py +185 -0
- cooperage-0.1.0/tests/test_orchestrator_kubernetes.py +294 -0
- cooperage-0.1.0/tests/test_registry.py +75 -0
- cooperage-0.1.0/tests/test_sdk.py +83 -0
- cooperage-0.1.0/tests/test_sdk_docs.py +83 -0
- cooperage-0.1.0/tests/test_session_manager.py +167 -0
- cooperage-0.1.0/tests/test_session_manager_extended.py +223 -0
- cooperage-0.1.0/tests/test_simulator_server.py +129 -0
- cooperage-0.1.0/ui/app.py +542 -0
- cooperage-0.1.0/uv.lock +2145 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Copy to .env and fill in values as needed.
|
|
2
|
+
# All variables use the COOPERAGE_ prefix.
|
|
3
|
+
|
|
4
|
+
# ── Orchestrator ─────────────────────────────────────────────────────────────
|
|
5
|
+
# "docker" (default) or "kubernetes"
|
|
6
|
+
COOPERAGE_ORCHESTRATOR=docker
|
|
7
|
+
|
|
8
|
+
# ── Docker ───────────────────────────────────────────────────────────────────
|
|
9
|
+
COOPERAGE_DOCKER_SOCKET=unix:///var/run/docker.sock
|
|
10
|
+
COOPERAGE_CONTAINER_PORT_RANGE_START=9000
|
|
11
|
+
COOPERAGE_CONTAINER_PORT_RANGE_END=9999
|
|
12
|
+
|
|
13
|
+
# ── Kubernetes ───────────────────────────────────────────────────────────────
|
|
14
|
+
COOPERAGE_K8S_NAMESPACE=cooperage
|
|
15
|
+
COOPERAGE_K8S_NODE_PORT_RANGE_START=30000
|
|
16
|
+
COOPERAGE_K8S_NODE_PORT_RANGE_END=32767
|
|
17
|
+
COOPERAGE_K8S_HOST_PATH_PREFIX=/run/cooperage
|
|
18
|
+
|
|
19
|
+
# ── Sessions ─────────────────────────────────────────────────────────────────
|
|
20
|
+
# Session auto-expiry in seconds (default: 30 min)
|
|
21
|
+
COOPERAGE_SESSION_TTL_SECONDS=1800
|
|
22
|
+
# How often to scan for expired sessions (seconds)
|
|
23
|
+
COOPERAGE_SESSION_CLEANUP_INTERVAL=60
|
|
24
|
+
# Stop idle containers after N seconds of no tool calls
|
|
25
|
+
COOPERAGE_CONTAINER_IDLE_TIMEOUT=600
|
|
26
|
+
# Reset session TTL on each tool call
|
|
27
|
+
COOPERAGE_SESSION_EXTEND_ON_ACTIVITY=true
|
|
28
|
+
# How long to wait for a container to become ready (seconds)
|
|
29
|
+
COOPERAGE_CONTAINER_STARTUP_TIMEOUT=120
|
|
30
|
+
|
|
31
|
+
# ── Persistence ──────────────────────────────────────────────────────────────
|
|
32
|
+
# Where to store registry and session state (defaults to ~/.cooperage/)
|
|
33
|
+
# COOPERAGE_REGISTRY_PATH=/data/cooperage/registry.json
|
|
34
|
+
# COOPERAGE_SESSIONS_PATH=/data/cooperage/sessions.json
|
|
35
|
+
|
|
36
|
+
# ── Gateway ──────────────────────────────────────────────────────────────────
|
|
37
|
+
COOPERAGE_GATEWAY_HOST=0.0.0.0
|
|
38
|
+
COOPERAGE_GATEWAY_PORT=8080
|
|
39
|
+
# URL shown to users after session creation (e.g. your hosted UI)
|
|
40
|
+
# COOPERAGE_UI_URL=http://localhost:8501
|
|
41
|
+
|
|
42
|
+
# ── Resource limits ──────────────────────────────────────────────────────────
|
|
43
|
+
COOPERAGE_DEFAULT_CPU_LIMIT=1.0
|
|
44
|
+
COOPERAGE_DEFAULT_MEMORY_LIMIT=512m
|
|
45
|
+
|
|
46
|
+
# ── Network isolation ────────────────────────────────────────────────────────
|
|
47
|
+
COOPERAGE_NETWORK_ISOLATION=true
|
|
48
|
+
|
|
49
|
+
# ── Image registry ───────────────────────────────────────────────────────────
|
|
50
|
+
# Prepend a registry mirror to all image pulls (air-gapped / internal mirror)
|
|
51
|
+
# COOPERAGE_IMAGE_REGISTRY_PREFIX=registry.corp.internal/
|
|
52
|
+
# Path to registry credentials JSON (for private registries)
|
|
53
|
+
# COOPERAGE_REGISTRY_AUTH_PATH=/etc/cooperage/registry-auth.json
|
|
54
|
+
|
|
55
|
+
# ── Enterprise auth & audit ──────────────────────────────────────────────────
|
|
56
|
+
# Authentication, OIDC/SSO, per-tenant RBAC, session quotas, and audit logging
|
|
57
|
+
# are provided by the cooperage-enterprise package. See:
|
|
58
|
+
# https://github.com/cooperage-io/cooperage-enterprise
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
name: Publish container images
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
|
|
8
|
+
env:
|
|
9
|
+
REGISTRY: ghcr.io
|
|
10
|
+
ORG: cooperage-io
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build-and-push:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
packages: write
|
|
18
|
+
|
|
19
|
+
strategy:
|
|
20
|
+
matrix:
|
|
21
|
+
include:
|
|
22
|
+
- image: cooperage
|
|
23
|
+
context: .
|
|
24
|
+
dockerfile: Dockerfile
|
|
25
|
+
- image: cooperage-workspace
|
|
26
|
+
context: ./servers/workspace
|
|
27
|
+
dockerfile: ./servers/workspace/Dockerfile
|
|
28
|
+
- image: cooperage-compute
|
|
29
|
+
context: ./servers/compute
|
|
30
|
+
dockerfile: ./servers/compute/Dockerfile
|
|
31
|
+
- image: cooperage-ui
|
|
32
|
+
context: .
|
|
33
|
+
dockerfile: Dockerfile.ui
|
|
34
|
+
- image: cooperage-synthetic-image-generator
|
|
35
|
+
context: ./example-servers/synthetic-image-generator
|
|
36
|
+
dockerfile: ./example-servers/synthetic-image-generator/Dockerfile
|
|
37
|
+
- image: cooperage-image-analyzer
|
|
38
|
+
context: ./example-servers/image-analyzer
|
|
39
|
+
dockerfile: ./example-servers/image-analyzer/Dockerfile
|
|
40
|
+
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
|
|
44
|
+
- uses: docker/login-action@v3
|
|
45
|
+
with:
|
|
46
|
+
registry: ${{ env.REGISTRY }}
|
|
47
|
+
username: ${{ github.actor }}
|
|
48
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
49
|
+
|
|
50
|
+
- uses: docker/metadata-action@v5
|
|
51
|
+
id: meta
|
|
52
|
+
with:
|
|
53
|
+
images: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ matrix.image }}
|
|
54
|
+
tags: |
|
|
55
|
+
type=raw,value=latest,enable={{is_default_branch}}
|
|
56
|
+
type=sha,prefix=
|
|
57
|
+
type=semver,pattern={{version}}
|
|
58
|
+
type=semver,pattern={{major}}.{{minor}}
|
|
59
|
+
|
|
60
|
+
- uses: docker/setup-buildx-action@v3
|
|
61
|
+
|
|
62
|
+
- uses: docker/build-push-action@v6
|
|
63
|
+
with:
|
|
64
|
+
context: ${{ matrix.context }}
|
|
65
|
+
file: ${{ matrix.dockerfile }}
|
|
66
|
+
push: true
|
|
67
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
68
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
69
|
+
cache-from: type=gha
|
|
70
|
+
cache-to: type=gha,mode=max
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: astral-sh/setup-uv@v4
|
|
14
|
+
- run: uv sync --dev
|
|
15
|
+
- run: uv run ruff check .
|
|
16
|
+
|
|
17
|
+
test:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.13"
|
|
25
|
+
|
|
26
|
+
- uses: astral-sh/setup-uv@v4
|
|
27
|
+
|
|
28
|
+
- run: uv sync --dev
|
|
29
|
+
- run: uv run pytest tests/ -q --ignore=tests/test_cloud_integration.py
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
.env
|
|
6
|
+
*.egg-info/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
.DS_Store
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.ruff_cache/
|
|
12
|
+
.mypy_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
htmlcov/
|
|
15
|
+
*.log
|
|
16
|
+
git_logs.txt
|
|
17
|
+
.claude/settings.local.json
|
|
18
|
+
donot-commit/
|
|
19
|
+
cooperage-enterprise/
|
|
20
|
+
.vscode/
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Contributing to Cooperage
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
git clone https://github.com/cooperage-io/cooperage
|
|
7
|
+
cd cooperage
|
|
8
|
+
uv sync
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Running tests
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv run pytest
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
All tests are mocked — no Docker daemon or Kubernetes cluster required.
|
|
18
|
+
|
|
19
|
+
## Linting
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv run ruff check cooperage/
|
|
23
|
+
uv run ruff check --fix cooperage/ # auto-fix
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Project structure
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
cooperage/ # core package
|
|
30
|
+
cli/ # cooperage CLI (typer)
|
|
31
|
+
core/ # models, config
|
|
32
|
+
gateway/ # MCP gateway server
|
|
33
|
+
orchestrator/ # docker.py, kubernetes.py
|
|
34
|
+
registry/ # server registration
|
|
35
|
+
session/ # session + container lifecycle
|
|
36
|
+
servers/
|
|
37
|
+
workspace/ # built-in workspace server (auto-registered by gateway)
|
|
38
|
+
example-servers/
|
|
39
|
+
image-analyzer/ # example: analyze images with numpy/PIL
|
|
40
|
+
synthetic-image-generator/ # example: generate synthetic satellite imagery
|
|
41
|
+
tests/
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Adding an example server
|
|
45
|
+
|
|
46
|
+
1. Create `example-servers/<name>/server.py` using FastMCP:
|
|
47
|
+
```python
|
|
48
|
+
from mcp.server.fastmcp import FastMCP
|
|
49
|
+
mcp = FastMCP("my-server", json_response=True, stateless_http=True)
|
|
50
|
+
|
|
51
|
+
@mcp.tool()
|
|
52
|
+
def my_tool(arg: str) -> str:
|
|
53
|
+
"""Tool description."""
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
import os, uvicorn
|
|
58
|
+
uvicorn.run(mcp.streamable_http_app(), host="0.0.0.0", port=int(os.environ.get("PORT", "8000")))
|
|
59
|
+
```
|
|
60
|
+
2. Add a `Dockerfile` and `requirements.txt`
|
|
61
|
+
3. Build: `docker build -t my-server:latest example-servers/<name>`
|
|
62
|
+
4. Register: `cooperage register --name <name> --image my-server:latest`
|
|
63
|
+
|
|
64
|
+
## Versioning
|
|
65
|
+
|
|
66
|
+
Cooperage follows [Semantic Versioning](https://semver.org/). The version appears in two places that must stay in sync:
|
|
67
|
+
|
|
68
|
+
- `pyproject.toml` → `project.version`
|
|
69
|
+
- `chart/Chart.yaml` → `version` and `appVersion`
|
|
70
|
+
|
|
71
|
+
### Cutting a release
|
|
72
|
+
|
|
73
|
+
1. Run the bump script from main:
|
|
74
|
+
```bash
|
|
75
|
+
./scripts/bump-version.sh 0.2.0
|
|
76
|
+
```
|
|
77
|
+
This updates both files, commits, and tags in one step.
|
|
78
|
+
2. Push:
|
|
79
|
+
```bash
|
|
80
|
+
git push origin main --tags
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Tags trigger CI to publish versioned images to `ghcr.io/cooperage-io/` (e.g. `cooperage:0.2.0` alongside `cooperage:latest`).
|
|
84
|
+
|
|
85
|
+
## Submitting changes
|
|
86
|
+
|
|
87
|
+
- Keep PRs focused — one thing at a time
|
|
88
|
+
- Tests required for gateway or orchestrator changes
|
|
89
|
+
- Run ruff before pushing
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
FROM python:3.11-slim
|
|
2
|
+
|
|
3
|
+
# Install uv
|
|
4
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
|
5
|
+
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
COPY pyproject.toml .
|
|
9
|
+
COPY cooperage/ cooperage/
|
|
10
|
+
|
|
11
|
+
RUN uv pip install --system --no-cache .
|
|
12
|
+
|
|
13
|
+
CMD ["cooperage", "start", "--sse"]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
FROM python:3.11-slim
|
|
2
|
+
|
|
3
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
|
4
|
+
|
|
5
|
+
WORKDIR /app
|
|
6
|
+
|
|
7
|
+
RUN uv pip install --system --no-cache streamlit httpx
|
|
8
|
+
|
|
9
|
+
COPY ui/app.py app.py
|
|
10
|
+
COPY assets/logo.png logo.png
|
|
11
|
+
COPY assets/favicon.png favicon.png
|
|
12
|
+
|
|
13
|
+
EXPOSE 8501
|
|
14
|
+
|
|
15
|
+
CMD ["streamlit", "run", "app.py", "--server.address=0.0.0.0", "--server.port=8501", "--browser.gatherUsageStats=false"]
|
cooperage-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Evan Lavizadeh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cooperage-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cooperage
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Ephemeral container orchestration for MCP servers
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: anyio>=4.0.0
|
|
8
|
+
Requires-Dist: docker>=7.0.0
|
|
9
|
+
Requires-Dist: fastapi>=0.115.0
|
|
10
|
+
Requires-Dist: httpx-sse>=0.4.0
|
|
11
|
+
Requires-Dist: httpx>=0.27.0
|
|
12
|
+
Requires-Dist: kubernetes>=28.0.0
|
|
13
|
+
Requires-Dist: mcp[cli]>=1.0.0
|
|
14
|
+
Requires-Dist: pydantic-settings>=2.0.0
|
|
15
|
+
Requires-Dist: pydantic>=2.0.0
|
|
16
|
+
Requires-Dist: rich>=13.0.0
|
|
17
|
+
Requires-Dist: typer>=0.12.0
|
|
18
|
+
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo.png" alt="Cooperage" width="420">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
MCP makes it easy to give LLMs tools. Cooperage makes those tools *scalable* — each tool call runs in an isolated container with dedicated compute and a shared workspace volume. No infra to manage. Just register a Docker image and let your LLM orchestrate it.
|
|
8
|
+
|
|
9
|
+
## Why this exists
|
|
10
|
+
|
|
11
|
+
Writing an MCP server is easy. Deploying it somewhere an LLM can actually *use* it — for stateful runs, large datasets, multi-step pipelines — is not. Cooperage is the missing layer between "I have tools" and "my LLM can run them at scale on my own infrastructure."
|
|
12
|
+
|
|
13
|
+
The key thing that makes Cooperage different: **multiple containers per session, one shared `/workspace` volume.** A generator writes a file, an analyzer reads it, a report writer summarizes it — all orchestrated by the LLM, all passing data through the same volume. No other platform does this.
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
LLM (Claude Desktop / API)
|
|
19
|
+
│ MCP (stdio or HTTP)
|
|
20
|
+
▼
|
|
21
|
+
┌─────────────────────────┐
|
|
22
|
+
│ Cooperage Gateway │ ← single MCP server the LLM talks to
|
|
23
|
+
└────────────┬────────────┘
|
|
24
|
+
│ HTTP JSON-RPC
|
|
25
|
+
┌───────┴───────┐
|
|
26
|
+
▼ ▼
|
|
27
|
+
[Container A] [Container B] ephemeral containers,
|
|
28
|
+
your MCP server your MCP server spun up per session
|
|
29
|
+
└───────┬───────┘
|
|
30
|
+
shared /workspace volume data persists across calls
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
**Prerequisites:** Python 3.11+, [uv](https://docs.astral.sh/uv/getting-started/installation/), Docker Desktop running.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Install
|
|
39
|
+
git clone https://github.com/cooperage-io/cooperage.git && cd cooperage
|
|
40
|
+
uv sync
|
|
41
|
+
|
|
42
|
+
# Build the example server
|
|
43
|
+
docker buildx build --load -t cooperage-image-analyzer:latest example-servers/image-analyzer/
|
|
44
|
+
|
|
45
|
+
# Register it
|
|
46
|
+
uv run cooperage register \
|
|
47
|
+
--name image-analyzer \
|
|
48
|
+
--image cooperage-image-analyzer:latest \
|
|
49
|
+
--description "Analyze images and data in /workspace using numpy/PIL"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Connect to Claude Desktop
|
|
53
|
+
|
|
54
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"cooperage": {
|
|
60
|
+
"command": "/Users/YOUR_USERNAME/.local/bin/uv",
|
|
61
|
+
"args": ["--directory", "/path/to/cooperage", "run", "--no-active", "cooperage", "start"]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Restart Claude Desktop. You should see the hammer icon — Cooperage is connected.
|
|
68
|
+
|
|
69
|
+
### HTTP mode (for programmatic access)
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
uv run cooperage start --sse
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This starts the gateway on `http://localhost:8080/mcp` as a Streamable HTTP MCP server.
|
|
76
|
+
|
|
77
|
+
## What the LLM sees
|
|
78
|
+
|
|
79
|
+
These are the tools exposed to the LLM via MCP:
|
|
80
|
+
|
|
81
|
+
| Tool | What it does |
|
|
82
|
+
|------|-------------|
|
|
83
|
+
| `cooperage_list_servers` | List available servers and whether their images are cached. |
|
|
84
|
+
| `cooperage_pull_server` | Pre-pull a server image to avoid cold-start latency. |
|
|
85
|
+
| `cooperage_create_session` | Create a workspace session. Returns a `session_id`. |
|
|
86
|
+
| `cooperage_list_tools` | List tools on a server. Starts the container if needed. |
|
|
87
|
+
| `cooperage_call_tool` | Call a tool on a server. Starts the container if needed. |
|
|
88
|
+
| `cooperage_workspace_read` | Read a file from `/workspace`. Handles binary + base64. |
|
|
89
|
+
| `cooperage_workspace_write` | Write a file to `/workspace`. |
|
|
90
|
+
| `cooperage_workspace_list` | List files in `/workspace`. |
|
|
91
|
+
| `cooperage_run_script` | Run a Python script in the compute container. |
|
|
92
|
+
| `cooperage_run_bash` | Run a bash script in the compute container. |
|
|
93
|
+
| `cooperage_end_session` | Tear down containers and delete the workspace volume. |
|
|
94
|
+
|
|
95
|
+
The gateway also exposes [MCP Resources](https://modelcontextprotocol.io/docs/concepts/resources) for reading registry and session state programmatically.
|
|
96
|
+
|
|
97
|
+
## Multi-container pipelines
|
|
98
|
+
|
|
99
|
+
This is the core use case. Containers in the same session share `/workspace`:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
cooperage_call_tool(session, "scene-generator", "generate", {type: "urban"})
|
|
103
|
+
→ container A starts, writes /workspace/scene.png
|
|
104
|
+
|
|
105
|
+
cooperage_call_tool(session, "image-analyzer", "analyze", {path: "scene.png"})
|
|
106
|
+
→ container B starts, reads /workspace/scene.png
|
|
107
|
+
|
|
108
|
+
cooperage_workspace_read(session, "analysis.json")
|
|
109
|
+
→ LLM reads the result directly
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Two containers. One session. One shared volume. Your proprietary tools stay in your Docker images, on your infrastructure.
|
|
113
|
+
|
|
114
|
+
## Writing your own server
|
|
115
|
+
|
|
116
|
+
Package any MCP server as a Docker image:
|
|
117
|
+
|
|
118
|
+
1. Expose MCP on port `8000` (configurable at registration)
|
|
119
|
+
2. Use `FastMCP` with `json_response=True, stateless_http=True`
|
|
120
|
+
3. Read/write `/workspace` for cross-container data sharing
|
|
121
|
+
|
|
122
|
+
See the full guide: **[Writing Servers for Cooperage](docs/writing-servers.md)**
|
|
123
|
+
|
|
124
|
+
For working examples, see [example-servers/image-analyzer/](example-servers/image-analyzer/) and [example-servers/synthetic-image-generator/](example-servers/synthetic-image-generator/).
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
uv run cooperage register \
|
|
128
|
+
--name my-server \
|
|
129
|
+
--image my-org/my-server:latest \
|
|
130
|
+
--description "Does the thing" \
|
|
131
|
+
--repo-url https://github.com/my-org/my-server # optional — lets the LLM clone and debug
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Kubernetes backend
|
|
135
|
+
|
|
136
|
+
Cooperage has a drop-in Kubernetes backend. Containers run as Pods with NodePort Services; the shared workspace uses `hostPath` volumes (or a ReadWriteMany PVC on multi-node clusters).
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# Bootstrap the namespace
|
|
140
|
+
COOPERAGE_ORCHESTRATOR=kubernetes uv run cooperage init-k8s
|
|
141
|
+
|
|
142
|
+
# Start the gateway
|
|
143
|
+
COOPERAGE_ORCHESTRATOR=kubernetes uv run cooperage start
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The tools and LLM config are identical — only the backend changes.
|
|
147
|
+
|
|
148
|
+
## Web UI
|
|
149
|
+
|
|
150
|
+
Cooperage ships with a Streamlit-based dashboard for monitoring sessions, viewing container status, and browsing workspace files.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
uv run cooperage ui
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Supports file preview (images, HTML, CSV, PDF) and file upload.
|
|
157
|
+
|
|
158
|
+
## Enterprise
|
|
159
|
+
|
|
160
|
+
For multi-tenant deployments with authentication (API keys, JWT, OIDC/SSO), per-tenant RBAC, session quotas, and audit logging, see [cooperage-enterprise](https://github.com/cooperage-io/cooperage-enterprise).
|
|
161
|
+
|
|
162
|
+
**Other features included in the open-source core:**
|
|
163
|
+
- Per-container CPU and memory limits (default: 1 CPU, 512 MB)
|
|
164
|
+
- Per-session network isolation (Docker bridge networks / K8s NetworkPolicy)
|
|
165
|
+
- Private image registry authentication
|
|
166
|
+
- Container idle timeout with automatic cleanup
|
|
167
|
+
- Session TTL extension on activity
|
|
168
|
+
|
|
169
|
+
## Configuration
|
|
170
|
+
|
|
171
|
+
All settings use the `COOPERAGE_` prefix and can go in a `.env` file. See [`.env.example`](.env.example).
|
|
172
|
+
|
|
173
|
+
| Variable | Default | Description |
|
|
174
|
+
|----------|---------|-------------|
|
|
175
|
+
| `COOPERAGE_ORCHESTRATOR` | `docker` | `docker` or `kubernetes` |
|
|
176
|
+
| `COOPERAGE_SESSION_TTL_SECONDS` | `1800` | Session auto-expiry |
|
|
177
|
+
| `COOPERAGE_CONTAINER_IDLE_TIMEOUT` | `600` | Stop idle containers after N seconds |
|
|
178
|
+
| `COOPERAGE_CONTAINER_STARTUP_TIMEOUT` | `120` | Seconds to wait for container readiness |
|
|
179
|
+
| `COOPERAGE_DEFAULT_CPU_LIMIT` | `1.0` | CPU limit per container |
|
|
180
|
+
| `COOPERAGE_DEFAULT_MEMORY_LIMIT` | `512m` | Memory limit per container |
|
|
181
|
+
| `COOPERAGE_NETWORK_ISOLATION` | `true` | Per-session network isolation |
|
|
182
|
+
| `COOPERAGE_UI_URL` | — | UI base URL (shown to users after session creation) |
|
|
183
|
+
| `COOPERAGE_K8S_NAMESPACE` | `cooperage` | Kubernetes namespace |
|
|
184
|
+
|
|
185
|
+
## CLI
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
cooperage register Register a Docker image as an MCP server
|
|
189
|
+
cooperage deregister Remove a server from the registry
|
|
190
|
+
cooperage list-servers List registered servers
|
|
191
|
+
cooperage sessions List active sessions
|
|
192
|
+
cooperage start Start the gateway (stdio default, --sse for HTTP)
|
|
193
|
+
cooperage init-k8s Bootstrap the Cooperage K8s namespace
|
|
194
|
+
cooperage ui Launch the web dashboard
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Comparison
|
|
198
|
+
|
|
199
|
+
| | Containers per session | Shared workspace | Infrastructure | Image source |
|
|
200
|
+
|-|------------------------|-----------------|----------------|--------------|
|
|
201
|
+
| **Cooperage** | Multiple (one per server) | Shared `/workspace` volume | Docker or Kubernetes | Any registry |
|
|
202
|
+
| **Manus** | Single VM | `/home/ubuntu` in one sandbox | Manus cloud only | Pre-installed runtimes |
|
|
203
|
+
| **Docker MCP Toolkit** | Multiple | Each container isolated | Docker Desktop only | Docker catalog |
|
|
204
|
+
| **AWS Bedrock AgentCore** | One (tools colocated) | Partial — 1 GB, preview | AWS only | ECR |
|
|
205
|
+
| **Google ADK + Vertex** | No container orchestration | Memory state only | GCP only | — |
|
|
206
|
+
| **Azure AI Foundry** | No container orchestration | Thread state only | Azure only | — |
|
|
207
|
+
|
|
208
|
+
### Cooperage vs Manus
|
|
209
|
+
|
|
210
|
+
Manus gives every agent a single pre-built Ubuntu VM with Python, Node.js, and Chromium. MCP servers are external API bridges — the agent calls out to them via `manus-mcp-cli`. It's a managed, general-purpose sandbox that works well for everyday tasks with zero setup.
|
|
211
|
+
|
|
212
|
+
Cooperage takes a different approach: there is no pre-built VM. The LLM dynamically spins up purpose-built containers, each running its own MCP server, all sharing a `/workspace` volume. MCP isn't a bridge to external services — it's the native interface between the LLM and the compute.
|
|
213
|
+
|
|
214
|
+
**Choose Manus** if you want a turnkey managed environment and don't need to control the infrastructure.
|
|
215
|
+
|
|
216
|
+
**Choose Cooperage** if you need:
|
|
217
|
+
- **Self-hosted / on-prem deployment** — your cluster, your network, your data
|
|
218
|
+
- **Custom runtimes** — CUDA, GDAL, proprietary SDKs, anything you can put in a Docker image
|
|
219
|
+
- **Multi-tool pipelines** — containers with different dependencies composing through `/workspace`
|
|
220
|
+
- **Enterprise auth and isolation** — OIDC, JWT, per-tenant RBAC, network policies, resource limits
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
## Tests
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
uv run pytest -v
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Cooperage Roadmap
|
|
2
|
+
|
|
3
|
+
## Done
|
|
4
|
+
|
|
5
|
+
### Core
|
|
6
|
+
- [x] MCP gateway with core tools — `list_servers`, `create_session`, `end_session`, `list_sessions`, `call_tool`, `list_tools`, `pull_server`
|
|
7
|
+
- [x] Docker orchestrator — ephemeral containers, shared `/workspace` volume per session, TTL + idle cleanup
|
|
8
|
+
- [x] Kubernetes orchestrator — drop-in backend, Pods + NodePort Services + hostPath workspace, `cooperage init-k8s`, pod affinity for multi-node
|
|
9
|
+
- [x] Built-in workspace server — `workspace_write/read/list/delete`, auto-registered, pre-warmed on session create
|
|
10
|
+
- [x] Built-in compute server — `run_script` (Python) and `run_bash`, numpy/pandas/scipy/matplotlib/sklearn pre-installed, `uv` for live package installs
|
|
11
|
+
- [x] File-based session persistence — stdio and SSE gateway share state via configurable path
|
|
12
|
+
- [x] Container idle timeout + session activity TTL extension
|
|
13
|
+
- [x] Network isolation — per-session bridge networks
|
|
14
|
+
- [x] Resource limits — CPU/memory configurable at registration and via config
|
|
15
|
+
- [x] `repo_url` on `ServerDef` — LLM can inspect server source when debugging
|
|
16
|
+
|
|
17
|
+
### Auth (extracted to [cooperage-enterprise](https://github.com/cooperage-io/cooperage-enterprise))
|
|
18
|
+
- [x] API key auth
|
|
19
|
+
- [x] HS256 JWT auth
|
|
20
|
+
- [x] OIDC / RS256 JWT auth (Azure AD, Okta, Auth0)
|
|
21
|
+
- [x] Per-tenant session isolation and RBAC (`allowed_servers`, `max_sessions`)
|
|
22
|
+
- [x] Audit logging (JSON-lines event log)
|
|
23
|
+
- [x] Plugin interface — open core ships with `AuthProvider` and `AuditSink` protocols
|
|
24
|
+
|
|
25
|
+
### Deployment
|
|
26
|
+
- [x] DigitalOcean droplet — gateway + UI running in production at `137.184.119.104`
|
|
27
|
+
- [x] All images published to `ghcr.io/cooperage-io/` via GitHub Actions CI
|
|
28
|
+
- [x] Hosted Streamlit UI — live session/container/workspace viewer at port 8501
|
|
29
|
+
- [x] `cooperage start --proxy <url>` — Claude Desktop (stdio) → remote cloud gateway bridge
|
|
30
|
+
- [x] `COOPERAGE_UI_URL` — gateway returns session-scoped UI link in `create_session` response
|
|
31
|
+
|
|
32
|
+
### Developer Experience
|
|
33
|
+
- [x] `cooperage ui` — local Streamlit viewer with session selector, container panel, file preview, upload
|
|
34
|
+
- [x] Image preview — binary files base64-encoded, rendered in browser
|
|
35
|
+
- [x] `simulator` + `analysis` example servers
|
|
36
|
+
- [x] 161 unit tests (mocked) + cloud integration test suite against live droplet
|
|
37
|
+
- [x] MIT license, cooperage-io GitHub org
|
|
38
|
+
- [x] Helm chart — gateway Deployment + ServiceAccount + Role + ConfigMap + Ingress + UI sidecar
|
|
39
|
+
- [x] `/health` endpoint — K8s liveness/readiness probes
|
|
40
|
+
- [x] Persistent gateway state — PVC for sessions.json + registry.json
|
|
41
|
+
- [x] Image registry prefix — `COOPERAGE_IMAGE_REGISTRY_PREFIX` for air-gapped clusters
|
|
42
|
+
- [x] CI — GitHub Actions for tests + image publishing
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Up Next
|
|
47
|
+
|
|
48
|
+
- **GPU support for K8s workloads** — allow MCP tool servers to request GPU resources on enterprise Kubernetes clusters.
|
|
49
|
+
- Extend `ResourceLimits` model with `gpu: int | None` and `gpu_type: str | None` (default `nvidia.com/gpu`) fields.
|
|
50
|
+
- Wire GPU limits into the Pod spec in `KubernetesOrchestrator.start_container` as extended resource requests/limits (e.g. `nvidia.com/gpu: "1"`).
|
|
51
|
+
- Add configurable `tolerations` and `nodeSelector` to `ServerDef` so pods land on tainted GPU nodes.
|
|
52
|
+
- Add `default_gpu_tolerations` to `Settings` / Helm `values.yaml` for cluster-wide defaults.
|
|
53
|
+
- GPU-enabled tool server images use `nvidia/cuda` base images with CUDA libraries pre-installed; Cooperage handles scheduling.
|
|
54
|
+
- Docker orchestrator: pass `--gpus` flag via `device_requests` in the Docker SDK for local dev parity.
|
|
55
|
+
- Example server: `gpu-compute` — CUDA-aware compute server with PyTorch/JAX pre-installed, registered with `"resources": {"gpu": 1}`.
|
|
56
|
+
- Enterprise considerations: quota enforcement per tenant (max GPUs), audit log entries for GPU allocation, idle timeout tuning for expensive GPU pods.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Backlog
|
|
61
|
+
|
|
62
|
+
- **Fly.io / Railway backend** — simpler cloud alternative to K8s or bare Docker VM
|
|
63
|
+
- **Dynamic resource sizing** — today resource limits are static (global defaults or per-server overrides at registration). Add support for: resource usage telemetry (CPU/memory per container, exposed as a gateway tool or resource), right-sizing recommendations based on historical usage, and optional Kubernetes VPA (Vertical Pod Autoscaler) integration for containers that consistently over- or under-request.
|
|
64
|
+
- **Workspace storage limits** — workspace volume is currently unbounded (hostPath on K8s, Docker volume locally). Add configurable per-session storage quotas and surface usage in the UI and `cooperage_list_sessions` output.
|
|
65
|
+
- **`cooperage deploy` CLI** — thin wrapper to provision cloud infra (droplet or K8s) and deploy the stack
|
|
66
|
+
- **K8s: RWX PVC workspace** — optional alternative to hostPath + pod affinity for clusters with RWX StorageClass
|
|
67
|
+
- **K8s: Ingress support** — replace NodePort with Ingress/ClusterIP for production on-prem routing
|
|
68
|
+
- **Session sharing** — read-only `session_id` tokens so a user can watch a session without write access
|
|
69
|
+
- **Webhook / event stream** — push session/container lifecycle events to an external URL
|
|
70
|
+
- **Server search / lazy listing** — `cooperage_search_servers` tool that accepts a query and returns matching servers by name/description, so large registries don't flood the context window. `cooperage_list_servers` would return names only (no descriptions) by default, with descriptions opt-in via a flag.
|
|
71
|
+
- **Configurable output paths in example servers** — simulator should accept an `output_path` parameter instead of always overwriting `scene.png`. Prevents the copy-after-generate footgun in multi-step pipelines.
|
|
72
|
+
- **Structured error handling** — tools should return structured error responses (error code, message, partial results) instead of null/silent failures. Critical for enterprise use where tools may run for minutes before failing.
|
|
73
|
+
- **Provenance / artifact lineage** — extend audit log to record which tool wrote which workspace file, enabling full artifact traceability across multi-container pipelines.
|
|
74
|
+
- **Multi-agent demo** — orchestrator agent creates a session and passes `session_id` to parallel subagents, each calling a different server. Validates concurrent tool calls. Target framework: Claude Agent SDK.
|
|
75
|
+
- **`cooperage.io` domain + public landing page** — set up domain, simple landing page with live demo link and install instructions.
|
|
76
|
+
- **Audit log rotation** — cap audit log file size and keep N compressed backups (Python `RotatingFileHandler` or logrotate config). Currently grows unbounded.
|
|
77
|
+
- **Interactive terminal in UI** — web-based terminal (xterm.js) in the Streamlit dashboard that connects to a session's compute container. Enables users to run bash interactively without CLI access to the host. Requires a websocket endpoint on the gateway. `cooperage exec` CLI command as a simpler alternative for local Docker deployments.
|
|
Binary file
|
|
Binary file
|