contree-mcp 0.1.0__py3-none-any.whl

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.
Files changed (46) hide show
  1. contree_mcp/__init__.py +0 -0
  2. contree_mcp/__main__.py +25 -0
  3. contree_mcp/app.py +240 -0
  4. contree_mcp/arguments.py +35 -0
  5. contree_mcp/auth/__init__.py +2 -0
  6. contree_mcp/auth/registry.py +236 -0
  7. contree_mcp/backend_types.py +301 -0
  8. contree_mcp/cache.py +208 -0
  9. contree_mcp/client.py +711 -0
  10. contree_mcp/context.py +53 -0
  11. contree_mcp/docs.py +1203 -0
  12. contree_mcp/file_cache.py +381 -0
  13. contree_mcp/prompts.py +238 -0
  14. contree_mcp/py.typed +0 -0
  15. contree_mcp/resources/__init__.py +17 -0
  16. contree_mcp/resources/guide.py +715 -0
  17. contree_mcp/resources/image_lineage.py +46 -0
  18. contree_mcp/resources/image_ls.py +32 -0
  19. contree_mcp/resources/import_operation.py +52 -0
  20. contree_mcp/resources/instance_operation.py +52 -0
  21. contree_mcp/resources/read_file.py +33 -0
  22. contree_mcp/resources/static.py +12 -0
  23. contree_mcp/server.py +77 -0
  24. contree_mcp/tools/__init__.py +39 -0
  25. contree_mcp/tools/cancel_operation.py +36 -0
  26. contree_mcp/tools/download.py +128 -0
  27. contree_mcp/tools/get_guide.py +54 -0
  28. contree_mcp/tools/get_image.py +30 -0
  29. contree_mcp/tools/get_operation.py +26 -0
  30. contree_mcp/tools/import_image.py +99 -0
  31. contree_mcp/tools/list_files.py +80 -0
  32. contree_mcp/tools/list_images.py +50 -0
  33. contree_mcp/tools/list_operations.py +46 -0
  34. contree_mcp/tools/read_file.py +47 -0
  35. contree_mcp/tools/registry_auth.py +71 -0
  36. contree_mcp/tools/registry_token_obtain.py +80 -0
  37. contree_mcp/tools/rsync.py +46 -0
  38. contree_mcp/tools/run.py +97 -0
  39. contree_mcp/tools/set_tag.py +31 -0
  40. contree_mcp/tools/upload.py +50 -0
  41. contree_mcp/tools/wait_operations.py +79 -0
  42. contree_mcp-0.1.0.dist-info/METADATA +450 -0
  43. contree_mcp-0.1.0.dist-info/RECORD +46 -0
  44. contree_mcp-0.1.0.dist-info/WHEEL +4 -0
  45. contree_mcp-0.1.0.dist-info/entry_points.txt +2 -0
  46. contree_mcp-0.1.0.dist-info/licenses/LICENSE +176 -0
@@ -0,0 +1,50 @@
1
+ import base64
2
+ import os
3
+
4
+ from contree_mcp.backend_types import FileResponse
5
+ from contree_mcp.context import CLIENT
6
+
7
+
8
+ async def upload(
9
+ path: str | None = None, content: str | None = None, content_base64: str | None = None
10
+ ) -> FileResponse:
11
+ """
12
+ Upload file to Contree for use in containers. Free (no VM).
13
+
14
+ TL;DR:
15
+ - PURPOSE: Upload single file, get UUID for run's tool files param
16
+ - PREFER RSYNC: For multiple files or directories (has caching)
17
+ - BINARY: Use content_base64 for binary files
18
+
19
+ USAGE:
20
+ - Upload single file for injection into containers
21
+ - Pass returned UUID to run's tool via files parameter
22
+ - Use rsync instead for multiple files or directories
23
+
24
+ RETURNS: uuid, sha256
25
+
26
+ GUIDES:
27
+ - [USEFUL] contree://guide/quickstart - File sync + execute patterns
28
+ """
29
+
30
+ if not path and not content and not content_base64:
31
+ raise ValueError("One of 'path', 'content', or 'content_base64' is required")
32
+
33
+ if path:
34
+ path = os.path.expanduser(path)
35
+ if not os.path.exists(path):
36
+ raise ValueError(f"File not found: {path}")
37
+
38
+ client = CLIENT.get()
39
+
40
+ if path:
41
+ with open(path, "rb") as f:
42
+ result = await client.upload_file(f)
43
+ return FileResponse(uuid=result.uuid, sha256=result.sha256)
44
+ elif content_base64:
45
+ data = base64.b64decode(content_base64)
46
+ else:
47
+ data = content.encode("utf-8") # type: ignore[union-attr]
48
+
49
+ result = await client.upload_file(data)
50
+ return FileResponse(uuid=result.uuid, sha256=result.sha256)
@@ -0,0 +1,79 @@
1
+ import asyncio
2
+ from typing import Literal
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+ from contree_mcp.backend_types import OperationKind, OperationResponse, OperationStatus
7
+ from contree_mcp.context import CLIENT
8
+
9
+
10
+ class WaitOperationsOutput(BaseModel):
11
+ results: dict[str, OperationResponse] = Field(description="Map of operation_id to result")
12
+ completed: list[str] = Field(description="List of completed operation IDs")
13
+ cancelled: list[str] = Field(description="List of timed out and cancelled operation IDs")
14
+ timed_out: bool = Field(default=False, description="True if wait exceeded timeout")
15
+
16
+
17
+ async def wait_operations(
18
+ operation_ids: list[str],
19
+ timeout: float = 300.0,
20
+ mode: Literal["all", "any"] = "all",
21
+ ) -> WaitOperationsOutput:
22
+ """
23
+ Wait for multiple operations to complete. Free (no VM).
24
+
25
+ TL;DR:
26
+ - PURPOSE: Block until async operations finish
27
+ - MODES: 'all' waits for all, 'any' returns on first completion but cancels others
28
+ - COST: Free (no VM)
29
+
30
+ USAGE:
31
+ - Wait for parallel commands launched with wait=false
32
+ - Use mode='any' for race conditions (first result wins, others cancelled)
33
+ - Use mode='all' (default) to collect all results
34
+
35
+ RETURNS: results dict, completed list, pending list, timed_out bool
36
+
37
+ GUIDES:
38
+ - [ESSENTIAL] contree://guide/async - Parallel execution patterns
39
+ """
40
+
41
+ client = CLIENT.get()
42
+
43
+ results: dict[str, OperationResponse] = {}
44
+
45
+ async def wait_one(op_id: str) -> None:
46
+ nonlocal results
47
+ try:
48
+ result = await client.wait_for_operation(op_id, max_wait=timeout)
49
+ results[op_id] = result
50
+ except Exception as e:
51
+ # On error (timeout, connection error, etc.), mark as failed
52
+ results[op_id] = OperationResponse(
53
+ uuid=op_id,
54
+ status=OperationStatus.FAILED,
55
+ kind=OperationKind.INSTANCE,
56
+ error=str(e),
57
+ )
58
+
59
+ done, pending = await asyncio.wait(
60
+ list(map(asyncio.create_task, map(wait_one, set(operation_ids)))),
61
+ return_when=asyncio.ALL_COMPLETED if mode == "all" else asyncio.FIRST_COMPLETED,
62
+ )
63
+
64
+ for task in pending:
65
+ if not task.done():
66
+ task.cancel()
67
+
68
+ await asyncio.gather(*pending, return_exceptions=True)
69
+
70
+ cancelled_ids = [op_id for op_id in operation_ids if op_id not in results]
71
+ # timed_out is True only if we have pending tasks AND mode was "all"
72
+ # For mode="any", having pending tasks is expected behavior
73
+ timed_out = len(pending) > 0 and mode == "all"
74
+ return WaitOperationsOutput(
75
+ results=results,
76
+ completed=list(results.keys()),
77
+ cancelled=cancelled_ids,
78
+ timed_out=timed_out,
79
+ )
@@ -0,0 +1,450 @@
1
+ Metadata-Version: 2.4
2
+ Name: contree-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server for Contree container management system
5
+ License-Expression: Apache-2.0
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: aiosqlite>=0.19.0
9
+ Requires-Dist: argclass>=1.0.0
10
+ Requires-Dist: httpx>=0.28.0
11
+ Requires-Dist: mcp>=1.0.0
12
+ Requires-Dist: pydantic>=2.0.0
13
+ Provides-Extra: dev
14
+ Requires-Dist: furo>=2024.0.0; extra == 'dev'
15
+ Requires-Dist: mypy>=1.8.0; extra == 'dev'
16
+ Requires-Dist: myst-parser>=3.0.0; extra == 'dev'
17
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
18
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
19
+ Requires-Dist: ruff>=0.2.0; extra == 'dev'
20
+ Requires-Dist: sphinx-copybutton>=0.5.0; extra == 'dev'
21
+ Requires-Dist: sphinx-design>=0.5.0; extra == 'dev'
22
+ Requires-Dist: sphinx>=7.0.0; extra == 'dev'
23
+ Requires-Dist: sphinxcontrib-mermaid>=0.9.0; extra == 'dev'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Contree MCP Server
27
+
28
+ [![PyPI](https://img.shields.io/pypi/v/contree-mcp.svg)](https://pypi.org/project/contree-mcp/)
29
+ [![Tests](https://github.com/nebius/contree-mcp/actions/workflows/tests.yml/badge.svg)](https://github.com/nebius/contree-mcp/actions/workflows/tests.yml)
30
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
31
+
32
+ Run code in isolated cloud containers. Contree gives AI agents secure sandboxed execution environments with full root access, network, and persistent images.
33
+
34
+ ## Why Contree?
35
+
36
+ **Fearless experimentation.** Agents can:
37
+ - Run destructive commands (`rm -rf /`, `dd`, kernel exploits) - nothing escapes the sandbox
38
+ - Make mistakes freely - revert to any previous image UUID at zero cost
39
+ - Execute potentially dangerous user requests - Contree IS the safe runtime for risky operations
40
+ - Break things on purpose - corrupt filesystems, crash kernels, test failure modes
41
+
42
+ Every container is isolated. Every image is immutable. Branching is cheap. Mistakes are free.
43
+
44
+ ## Quick Setup
45
+
46
+ ### 1. Create Config File
47
+
48
+ Store credentials in `~/.config/contree/mcp.ini`:
49
+
50
+ ```ini
51
+ [DEFAULT]
52
+ url = https://contree.dev/
53
+ token = <TOKEN HERE>
54
+ ```
55
+
56
+ ### 2. Configure Your MCP Client
57
+
58
+ #### Claude Code
59
+
60
+ Add to `~/.claude/settings.json`:
61
+
62
+ ```json
63
+ {"mcpServers": {"contree": {"command": "uvx", "args": ["contree-mcp"]}}}
64
+ ```
65
+
66
+ Restart Claude Code or run `/mcp` to verify.
67
+
68
+ #### OpenAI Codex CLI
69
+
70
+ Add to `~/.codex/config.toml`:
71
+
72
+ ```toml
73
+ [mcp_servers.contree]
74
+ command = "uvx"
75
+ args = ["contree-mcp"]
76
+ ```
77
+
78
+ #### Claude Desktop
79
+
80
+ Add to config file:
81
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
82
+ - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
83
+
84
+ ```json
85
+ {"mcpServers": {"contree": {"command": "uvx", "args": ["contree-mcp"]}}}
86
+ ```
87
+
88
+ > **Note:** You can alternatively pass credentials via environment variables (`CONTREE_MCP_TOKEN`, `CONTREE_MCP_URL`) in your MCP client config, but this is not recommended as tokens may appear in process listings.
89
+
90
+ ## Manual Installation
91
+
92
+ ```bash
93
+ # Using uv
94
+ uv pip install contree-mcp
95
+
96
+ # Using pip
97
+ pip install contree-mcp
98
+
99
+ # Run manually
100
+ contree-mcp --token YOUR_TOKEN
101
+
102
+ # HTTP mode (for network access)
103
+ contree-mcp --mode http --http-port 9452 --token YOUR_TOKEN
104
+
105
+ # Visit http://localhost:9452/ for interactive documentation with
106
+ # setup guides, tool reference, and best practices.
107
+ ```
108
+
109
+ ### Container Installation (Alpine/Ubuntu/Debian)
110
+
111
+ PEP 668 requires additional flags:
112
+
113
+ ```bash
114
+ pip install --break-system-packages contree-mcp
115
+ uv pip install --break-system-packages --python /usr/bin/python3 contree-mcp
116
+ ```
117
+
118
+ ## Configuration
119
+
120
+ | Argument | Environment Variable | Default |
121
+ |----------|---------------------|---------|
122
+ | - | `CONTREE_MCP_CONFIG` | `~/.config/contree/mcp.ini` |
123
+ | `--token` | `CONTREE_MCP_TOKEN` | (required) |
124
+ | `--url` | `CONTREE_MCP_URL` | `https://contree.dev/` |
125
+ | `--mode` | `CONTREE_MCP_MODE` | `stdio` |
126
+ | `--http-port` | `CONTREE_MCP_HTTP_PORT` | `9452` |
127
+ | `--log-level` | `CONTREE_MCP_LOG_LEVEL` | `warning` |
128
+
129
+ ## Available Tools
130
+
131
+ ### Command Execution
132
+
133
+ | Tool | Description |
134
+ |------|-------------|
135
+ | `contree_run` | Execute command in container (spawns microVM). Supports `wait=false` for async execution. |
136
+
137
+ ### Image Management
138
+
139
+ | Tool | Description |
140
+ |------|-------------|
141
+ | `contree_list_images` | List available container images |
142
+ | `contree_get_image` | Get image details by UUID or tag |
143
+ | `contree_import_image` | Import OCI image from registry (requires authentication) |
144
+ | `contree_registry_token_obtain` | Open browser to create PAT for registry authentication |
145
+ | `contree_registry_auth` | Validate and store registry credentials |
146
+ | `contree_set_tag` | Set or remove a tag for an image |
147
+
148
+ ### File Transfer
149
+
150
+ | Tool | Description |
151
+ |------|-------------|
152
+ | `contree_upload` | Upload a file to Contree for use in containers |
153
+ | `contree_download` | Download a file from a container image to local filesystem |
154
+ | `contree_rsync` | Sync local files to Contree with caching and deduplication |
155
+
156
+ ### Image Inspection
157
+
158
+ | Tool | Description |
159
+ |------|-------------|
160
+ | `contree_list_files` | List files and directories in an image (no VM needed) |
161
+ | `contree_read_file` | Read a file from an image (no VM needed) |
162
+
163
+ ### Operations
164
+
165
+ | Tool | Description |
166
+ |------|-------------|
167
+ | `contree_list_operations` | List operations (running or completed) |
168
+ | `contree_get_operation` | Get operation status and result |
169
+ | `contree_wait_operations` | Wait for multiple async operations to complete |
170
+ | `contree_cancel_operation` | Cancel a running operation |
171
+
172
+ ### Documentation
173
+
174
+ | Tool | Description |
175
+ |------|-------------|
176
+ | `contree_get_guide` | Get agent guide sections (workflow, quickstart, async, etc.) |
177
+
178
+ ## Resource Templates
179
+
180
+ MCP resource templates expose image files and documentation directly via URIs. Fast operations, no VM required.
181
+
182
+ | Resource | URI Template | Description |
183
+ |----------|--------------|-------------|
184
+ | `contree_image_read` | `contree://image/{image}/read/{path}` | Read a file from an image |
185
+ | `contree_image_ls` | `contree://image/{image}/ls/{path}` | List directory in an image |
186
+ | `contree_image_lineage` | `contree://image/{image}/lineage` | View image parent-child relationships |
187
+ | `contree_guide` | `contree://guide/{section}` | Agent guide and best practices |
188
+
189
+ **URI Examples:**
190
+ - `contree://image/abc-123-uuid/read/etc/passwd` - Read file by image UUID
191
+ - `contree://image/tag:alpine:latest/read/etc/os-release` - Read file by tag
192
+ - `contree://image/abc-123-uuid/ls/.` - List root directory
193
+ - `contree://image/tag:python:3.11/ls/usr/local/lib` - List nested directory
194
+ - `contree://image/abc-123-uuid/lineage` - View image ancestry and children
195
+ - `contree://guide/reference` - Tool reference
196
+ - `contree://guide/quickstart` - Common workflow patterns
197
+
198
+ **Guide Sections:** `workflow`, `reference`, `quickstart`, `state`, `async`, `tagging`, `errors`
199
+
200
+ ## Examples
201
+
202
+ ### Prepare a Reusable Environment (Recommended First Step)
203
+
204
+ **Step 1: Check for existing environment**
205
+ ```json
206
+ // contree_list_images
207
+ {"tag_prefix": "common/python-ml"}
208
+ ```
209
+
210
+ **Step 2: If not found, build and tag it**
211
+ ```json
212
+ // contree_import_image
213
+ {"registry_url": "docker://docker.io/python:3.11-slim"}
214
+
215
+ // contree_run (install packages)
216
+ {"command": "pip install numpy pandas scikit-learn", "image": "<result_image>", "disposable": false}
217
+
218
+ // contree_set_tag
219
+ {"image_uuid": "<result_image>", "tag": "common/python-ml/python:3.11-slim"}
220
+ ```
221
+
222
+ **Step 3: Use the prepared environment**
223
+ ```json
224
+ // contree_run
225
+ {"command": "python train_model.py", "image": "tag:common/python-ml/python:3.11-slim"}
226
+ ```
227
+
228
+ ### Run a command
229
+
230
+ **contree_run:**
231
+ ```json
232
+ {"command": "python -c 'print(\"Hello from Contree!\")'", "image": "tag:python:3.11"}
233
+ ```
234
+
235
+ ### Parallel Execution (Async Pattern)
236
+
237
+ Launch multiple instances simultaneously with `wait: false`, then poll for results:
238
+
239
+ **contree_run** (x3):
240
+ ```json
241
+ {"command": "python experiment_a.py", "image": "tag:python:3.11", "wait": false}
242
+ {"command": "python experiment_b.py", "image": "tag:python:3.11", "wait": false}
243
+ {"command": "python experiment_c.py", "image": "tag:python:3.11", "wait": false}
244
+ ```
245
+
246
+ Each returns immediately with `operation_id`. Poll with **contree_get_operation**:
247
+ ```json
248
+ {"operation_id": "op-1"}
249
+ ```
250
+
251
+ ### Trie-like Exploration Tree
252
+
253
+ Build branching structures where results become new source images.
254
+
255
+ **contree_run** - create branch point with `disposable: false`:
256
+ ```json
257
+ {"command": "pip install numpy pandas", "image": "tag:python:3.11", "disposable": false}
258
+ ```
259
+ Returns `result_image: "img-with-deps"`.
260
+
261
+ **contree_run** - branch into parallel experiments:
262
+ ```json
263
+ {"command": "python test_numpy.py", "image": "img-with-deps", "wait": false}
264
+ {"command": "python test_pandas.py", "image": "img-with-deps", "wait": false}
265
+ ```
266
+
267
+ ### Sync Local Files to Container
268
+
269
+ **contree_rsync** - sync a project directory:
270
+ ```json
271
+ {
272
+ "source": "/path/to/project",
273
+ "destination": "/app",
274
+ "exclude": ["__pycache__", "*.pyc", ".git", "node_modules"]
275
+ }
276
+ ```
277
+ Returns `directory_state_id: "ds_abc123"`.
278
+
279
+ **contree_run** - run with injected files:
280
+ ```json
281
+ {
282
+ "command": "python /app/main.py",
283
+ "image": "tag:python:3.11",
284
+ "directory_state_id": "ds_abc123"
285
+ }
286
+ ```
287
+
288
+ ### List images
289
+
290
+ **contree_list_images:**
291
+ ```json
292
+ {"tag_prefix": "python"}
293
+ ```
294
+
295
+ ### Read a file (Resource Template)
296
+
297
+ Use the `contree_image_file` resource template:
298
+ ```
299
+ contree://image/tag:busybox:latest/read/etc/passwd
300
+ ```
301
+
302
+ ### Import an image
303
+
304
+ **Step 1: Authenticate with registry (first time only)**
305
+
306
+ ```json
307
+ // contree_registry_token_obtain - opens browser for PAT creation
308
+ {"registry_url": "docker://docker.io/alpine:latest"}
309
+
310
+ // contree_registry_auth - validate and store credentials
311
+ {"registry_url": "docker://docker.io/alpine:latest", "username": "myuser", "token": "dckr_pat_xxx"}
312
+ ```
313
+
314
+ **Step 2: Import the image**
315
+
316
+ ```json
317
+ // contree_import_image
318
+ {"registry_url": "docker://docker.io/alpine:latest"}
319
+ ```
320
+
321
+ To make it reusable, tag after importing:
322
+ ```json
323
+ // contree_set_tag
324
+ {"image_uuid": "<result_image>", "tag": "common/base/alpine:latest"}
325
+ ```
326
+
327
+ ### Track Image Lineage
328
+
329
+ View parent-child relationships and navigate image history using the `contree_image_lineage` resource:
330
+
331
+ ```
332
+ contree://image/abc-123-uuid/lineage
333
+ ```
334
+
335
+ Returns:
336
+ ```json
337
+ {
338
+ "image": "abc-123-uuid",
339
+ "parent": {"image": "parent-uuid", "command": "pip install numpy", "exit_code": 0},
340
+ "children": [{"image": "child-uuid", "command": "python test.py", ...}],
341
+ "ancestors": [/* parent chain up to root */],
342
+ "root": {"image": "root-uuid", "registry_url": "docker://python:3.11", "is_import": true},
343
+ "depth": 2,
344
+ "is_known": true
345
+ }
346
+ ```
347
+
348
+ Use this to rollback to any ancestor or understand how an image was created.
349
+
350
+ ### Download a build artifact
351
+
352
+ **contree_download:**
353
+ ```json
354
+ {"image": "img-build-result", "path": "/app/dist/binary", "destination": "./binary", "executable": true}
355
+ ```
356
+
357
+ ## Dependencies
358
+
359
+ - `mcp` - Model Context Protocol SDK
360
+ - `httpx` - Async HTTP client
361
+ - `argclass` - Argument parsing
362
+ - `aiosqlite` - Async SQLite database
363
+ - `pydantic` - Data validation
364
+
365
+ ## Development
366
+
367
+ **Requirements:** Python 3.10+
368
+
369
+ ```bash
370
+ # Clone and install in dev mode
371
+ git clone https://github.com/nebius/contree-mcp.git
372
+ cd contree-mcp
373
+ uv sync --group dev
374
+ ```
375
+
376
+ ### Development Workflow
377
+
378
+ Follow this sequence when making changes:
379
+
380
+ 1. **Make code changes** - Edit files in `contree_mcp/`
381
+
382
+ 2. **Run tests** - Ensure all tests pass
383
+ ```bash
384
+ uv run pytest tests/ -v
385
+ ```
386
+
387
+ 3. **Run linter** - Fix any style issues
388
+ ```bash
389
+ uv run ruff check contree_mcp
390
+ uv run ruff format contree_mcp # Auto-fix formatting
391
+ ```
392
+
393
+ 4. **Type check** (optional but recommended)
394
+ ```bash
395
+ uv run mypy contree_mcp
396
+ ```
397
+
398
+ 5. **Update documentation** - Keep docs in sync with code
399
+ - `README.md` - User-facing docs, examples, tool descriptions
400
+ - `llm.txt` - Shared context for AI agents (architecture, class hierarchy, internals)
401
+
402
+ ### Quick Commands
403
+
404
+ ```bash
405
+ # Full validation cycle
406
+ uv run pytest tests/ -q && uv run ruff check contree_mcp && echo "All checks passed"
407
+
408
+ # Run specific test file
409
+ uv run pytest tests/test_tools/test_run.py -v
410
+
411
+ # Auto-fix linting issues
412
+ uv run ruff check contree_mcp --fix
413
+ ```
414
+
415
+ ### Testing GitHub Actions Locally
416
+
417
+ Use [act](https://github.com/nektos/act) to run GitHub Actions workflows locally before pushing:
418
+
419
+ ```bash
420
+ # Install act
421
+ brew install act # macOS
422
+ sudo pacman -S act # Arch Linux
423
+ sudo apt install act # Debian/Ubuntu (via nix or manual install)
424
+
425
+ # List available jobs
426
+ act -l
427
+
428
+ # Run lint and typecheck jobs (fast)
429
+ act -j lint
430
+ act -j typecheck
431
+
432
+ # Run tests for Linux only (act simulates Linux)
433
+ act -j test --matrix os:ubuntu-latest
434
+
435
+ # Run specific Python version
436
+ act -j test --matrix os:ubuntu-latest --matrix python-version:3.12
437
+
438
+ # Run all jobs sequentially (stop on first failure)
439
+ act -j lint && act -j typecheck && act -j test --matrix os:ubuntu-latest
440
+
441
+ # Dry run (show what would execute)
442
+ act -n
443
+ ```
444
+
445
+ **Note:** act uses Docker containers that simulate Linux runners. macOS/Windows matrix jobs will run in Linux
446
+ containers, so use `--matrix os:ubuntu-latest` for accurate local testing.
447
+
448
+ # Copyright
449
+
450
+ Nebius B.V. 2026, Licensed under the Apache License, Version 2.0 (see "LICENSE" file).
@@ -0,0 +1,46 @@
1
+ contree_mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ contree_mcp/__main__.py,sha256=5Tq-hL2bbaZr7-fPQqdAbiidKhyku2xNqX1gZlAEkuk,616
3
+ contree_mcp/app.py,sha256=vQERw5bvgpKviEeHTibO0WA1tfBtj-htxswYDzGRq0Y,9132
4
+ contree_mcp/arguments.py,sha256=AtIO5xx2nqhZHQ2EJ1QS1Z6yVW3k86nHc4tTXcxjpfQ,1047
5
+ contree_mcp/backend_types.py,sha256=Rx_Wn4gtsJ_zR4RB5hSefbO56ljf7BQeJWLGdxknYA0,9375
6
+ contree_mcp/cache.py,sha256=5_ItLP-An1nKHlezlmSgpsA8AwgTPR-uNxcy8PzI4dw,8487
7
+ contree_mcp/client.py,sha256=dXNY7Uc2hcv3XmrCwWfqEG9bjTdHWsIdPdpGxKe3FxI,26506
8
+ contree_mcp/context.py,sha256=YGMXp4FT31MIRBvfihE-YCw5kPkJqTH-a_tTLIrq98o,1749
9
+ contree_mcp/docs.py,sha256=WOciz0aZPLrHNjjRgR61-cSxBbPPCI_BmoF5o5wxeTQ,29436
10
+ contree_mcp/file_cache.py,sha256=ZS9D5MGM2ACblHTidUw2zRSalpfC5NmHv_9uECcqu5M,14055
11
+ contree_mcp/prompts.py,sha256=EzFLqCHWBjpVshvHfmtmB300gb6mpboUp7nVB6Cuokk,8250
12
+ contree_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ contree_mcp/server.py,sha256=3D5v-WqzV-HGkPIckDDGQCrSoWZ8CEqeU-QgQYQeVCs,2721
14
+ contree_mcp/auth/__init__.py,sha256=vhtYeBrAl3363anBmk5-Fa23Wdl9m6_1Gn_VKyDK9-E,104
15
+ contree_mcp/auth/registry.py,sha256=3gmV4v_Dn2eYchum56s1gQYl2kSvII_6thvWKz4OyX0,8237
16
+ contree_mcp/resources/__init__.py,sha256=WzGWOxspHVfBhkLLlVDlZU7nsUpntoY3Td_NMNOgNcc,423
17
+ contree_mcp/resources/guide.py,sha256=JEm94_wq1yRJtILDgoXK98eTKvBdFKA19xDJP99pabc,20164
18
+ contree_mcp/resources/image_lineage.py,sha256=CBb5ZOpVTx11RGv06-ToDN3YdcRnZvTaC34HrHfc4xI,1594
19
+ contree_mcp/resources/image_ls.py,sha256=W_DoesnFTLNCwOSmi0j9wtSnpC0xtwzS96RQkhyYq5Q,1108
20
+ contree_mcp/resources/import_operation.py,sha256=ocfnEF0ey4RvI_ia6KA-DlNXdFdYqoPG1_vohXxqago,1572
21
+ contree_mcp/resources/instance_operation.py,sha256=kCHO0fGQ__j136y2LfHf6Ghf5y8rHZJTXxnrl5AeDpA,1939
22
+ contree_mcp/resources/read_file.py,sha256=39kXv1FpAzKYrB67ba_r1IZBE-nGTYdNbuVH0svpwjc,1132
23
+ contree_mcp/resources/static.py,sha256=kHH1u910C1DsorCzVlRm5h6_JPENSE1OU0wvuuNANpg,306
24
+ contree_mcp/tools/__init__.py,sha256=SR3Z22vV_AvXnT58q_3_I4YkLwK1ysdM8dC13Xpe-ok,978
25
+ contree_mcp/tools/cancel_operation.py,sha256=QdGb956PEoy9oYmOIWHuxYlm0hakHg4uv_NIgrX3i9k,1111
26
+ contree_mcp/tools/download.py,sha256=ZpRzRYVwpAA-TMzA1c-2cWaujR1PPdimolc4ZNS3wng,4351
27
+ contree_mcp/tools/get_guide.py,sha256=5PNi_hNXAFCquS5xEZ9fH6_16jchlpT20K1YRJjxn_w,1853
28
+ contree_mcp/tools/get_image.py,sha256=3VipR0OLWkDeggoY4rh-1l8U93s83D_x-wO8R_e0H8c,922
29
+ contree_mcp/tools/get_operation.py,sha256=QDLCR0dmiOSAMI_a1VufQcn2Il2FjJmPCBehIKxzK-0,814
30
+ contree_mcp/tools/import_image.py,sha256=gB4JwXd1jpgVayCYq6ktWKWwwpBG9G7SsQ13fmQE1sk,3884
31
+ contree_mcp/tools/list_files.py,sha256=xMJeQPpkjJVDEnRPiicqYsgPEDbOxOQIzDae-MkjqQo,2202
32
+ contree_mcp/tools/list_images.py,sha256=2Z65nnW2yItGHsh10v7tOwJmUQupp3lnVAzcoMY72Vk,1531
33
+ contree_mcp/tools/list_operations.py,sha256=2p31Xqt4YY_3QSKU2yrOxv4vqS_L-sD4aaGM5Acix8U,1300
34
+ contree_mcp/tools/read_file.py,sha256=f5c88J08rtdFBbCd4o9MU47-qMYX_9ORqYX9OpM-qKw,1328
35
+ contree_mcp/tools/registry_auth.py,sha256=9piWm9fhLhEjngIaMURhLXrnr4h_rpoT21GRi4sQr0w,2066
36
+ contree_mcp/tools/registry_token_obtain.py,sha256=pS2dmXBGJTJRjvheiGDhjjVS1dThqHOmK8UNFKdRsHU,2628
37
+ contree_mcp/tools/rsync.py,sha256=ZE6pbKjclEdsuuaI7pbCg6QfeEj-L2-scWZBmQ7hhQk,1333
38
+ contree_mcp/tools/run.py,sha256=QS-QgLVQB2_-z9kBfYgE0gu-cnu-nLB6w8uyNb_J8QM,3614
39
+ contree_mcp/tools/set_tag.py,sha256=WFlGPSHthOA4UbfjI9FVbA2tZN0p-Qvw_i8x13uefUI,1145
40
+ contree_mcp/tools/upload.py,sha256=IdXDiy8VZENbyu7APYPe7wC2af7QkEVdU4EI2GpdJl0,1566
41
+ contree_mcp/tools/wait_operations.py,sha256=BEexOTYWQ-a5sGxyfKDASHUVFoDiBuqYzo_6JGuw8YA,2745
42
+ contree_mcp-0.1.0.dist-info/METADATA,sha256=ZKfIuWSUzItsucpklEgNcYdK7PiXoTMyRLGlVoJucZE,13141
43
+ contree_mcp-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
44
+ contree_mcp-0.1.0.dist-info/entry_points.txt,sha256=Jv9r9PfrtYFtYkYu5usTpZelyQwNbbv_XAivokhcyT8,58
45
+ contree_mcp-0.1.0.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
46
+ contree_mcp-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ contree-mcp = contree_mcp.__main__:main