artifacts_colector
by osJedi
README.md
# artifacts_colector
An example MCP server for sharing versioned **skills** and **artifacts** with a
team: by default it serves the latest version, but any older one can be pinned
by tag (e.g. `stable`) or by an exact version number.
Implemented in Python (MCP Python SDK v2). The server talks to its client
(Claude Code) over stdio and is distributed as a Docker image.
## Layout
```
content/
skills/<name>/manifest.json # description + tags (alias -> version)
skills/<name>/versions/<semver>/... # files for one skill version
artifacts/<name>/manifest.json
artifacts/<name>/versions/<semver>/...
server/
models.py # Pydantic response models (LibraryItemSummary, ResolvedItem)
library.py # scans content/, resolves versions/tags, reads files safely
app.py # MCP server: registers tools and serves them over stdio
Dockerfile
requirements.txt
```
Each skill/artifact is a folder with a `manifest.json` (description + optional
tags) and a `versions/` subfolder, where every version is its own folder named
after its semver (`1.0.0`, `1.1.0`, ...). The "latest" version is computed
dynamically as the highest semver among the existing folders, so there is
nothing to update by hand when a new version is added.
## MCP tools
| Tool | Description |
| ---------------- | ------------------------------------------------------------------ |
| `list_skills` | List every skill: name, description, `latest`, available versions/tags |
| `get_skill` | Fetch a skill's content by `name` (+ optional `version`: tag or semver) |
| `list_artifacts` | List every artifact: name, description, `latest`, versions/tags |
| `get_artifact` | Fetch an artifact's content by `name` (+ optional `version`: tag or semver) |
If `version` is omitted, the latest version is returned. If a tag (e.g.
`stable`) is given, it resolves to whatever version that tag points to in
`manifest.json`. If an exact semver is given, that exact version is returned.
## Local run (venv)
Requires Python 3.10+.
```bash
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python -m server.app
```
The process prints nothing and never returns — it's waiting on stdin for an
MCP host to speak first. That is expected.
Local sanity check without Claude Code, via MCP Inspector (requires `npx`):
```bash
npx @modelcontextprotocol/inspector --cli python -m server.app --method tools/list
```
## Running in Docker
Build the image:
```bash
docker build -t artifacts-collector-mcp .
```
Run it manually:
```bash
docker run -i --rm artifacts-collector-mcp
```
(same as above: it prints nothing and blocks, waiting for JSON-RPC requests on
stdin; to actually exercise the tools, point MCP Inspector at the same
`docker run -i --rm ...` command).
## Connecting from Claude Code
### Option 1 — Docker (recommended for team distribution)
```bash
docker build -t artifacts-collector-mcp .
claude mcp add artifacts-collector -- docker run -i --rm artifacts-collector-mcp
```
Or via a `.mcp.json` file at the root of the project where Claude Code runs:
```json
{
"mcpServers": {
"artifacts-collector": {
"command": "docker",
"args": ["run", "-i", "--rm", "artifacts-collector-mcp"]
}
}
}
```
The image can be published to a team registry (e.g.
`ghcr.io/<org>/artifacts-collector-mcp`) and shared via that same `.mcp.json`,
so teammates only need `docker pull` instead of a local build.
### Option 2 — local venv (for developing the server itself)
```bash
claude mcp add artifacts-collector -- /absolute/path/to/artifacts_colector/.venv/bin/python -m server.app
```
## Adding a new skill/artifact version
1. Copy the latest version into a new `versions/<new-semver>/` folder and make your changes.
2. Optionally pin a stable version with a tag in `manifest.json` (`"tags": { "stable": "1.0.0" }`).
3. Nothing else needs registering — `latest` is recomputed automatically.
4. If distributing via Docker, rebuild the image (`docker build ...`).
## Security
A `name` and `version`/tag coming from an MCP client are never substituted
directly into a filesystem path: they're only accepted if they match a real
folder name found by scanning `content/`. This rules out path traversal via
values like `../../etc`. The container also runs as a non-root user
(`USER mcp` in the Dockerfile).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues