artifacta-mcp 1.0.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.
- artifacta_mcp-1.0.0/.gitignore +64 -0
- artifacta_mcp-1.0.0/LICENSE +21 -0
- artifacta_mcp-1.0.0/PKG-INFO +213 -0
- artifacta_mcp-1.0.0/README.md +180 -0
- artifacta_mcp-1.0.0/pyproject.toml +82 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/__init__.py +3 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/allowlist.py +29 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/cli.py +130 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/client_factory.py +30 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/errors.py +283 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/ids.py +9 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/path_confinement.py +322 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/safety.py +185 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/sdk_compat.py +93 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/server.py +136 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/__init__.py +46 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/_common.py +45 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/complete_upload.py +63 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/create_download_link.py +95 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/delete_artifact.py +70 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/errors_fallback.py +15 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/get_artifact.py +59 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/get_artifact_download_url.py +59 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/list_artifacts.py +98 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/list_sessions.py +72 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/request_upload_url.py +135 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/seal_session.py +66 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/store_artifact.py +280 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/tools/whoami.py +47 -0
- artifacta_mcp-1.0.0/src/artifacta_mcp/whoami_cache.py +23 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Environment secrets
|
|
2
|
+
.env
|
|
3
|
+
.env.local
|
|
4
|
+
.env.*.local
|
|
5
|
+
.env.local.override
|
|
6
|
+
.env.production
|
|
7
|
+
.env.staging
|
|
8
|
+
|
|
9
|
+
# Python
|
|
10
|
+
__pycache__/
|
|
11
|
+
*.py[cod]
|
|
12
|
+
*$py.class
|
|
13
|
+
*.egg-info/
|
|
14
|
+
dist/
|
|
15
|
+
build/
|
|
16
|
+
.eggs/
|
|
17
|
+
*.egg
|
|
18
|
+
.venv/
|
|
19
|
+
venv/
|
|
20
|
+
.mypy_cache/
|
|
21
|
+
.ruff_cache/
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
|
|
24
|
+
# Node.js
|
|
25
|
+
node_modules/
|
|
26
|
+
.next/
|
|
27
|
+
out/
|
|
28
|
+
.vercel
|
|
29
|
+
|
|
30
|
+
# Cloudflare
|
|
31
|
+
.wrangler/
|
|
32
|
+
.dev.vars
|
|
33
|
+
|
|
34
|
+
# Supabase
|
|
35
|
+
.temp/
|
|
36
|
+
|
|
37
|
+
# IDE
|
|
38
|
+
.vscode/
|
|
39
|
+
.idea/
|
|
40
|
+
*.swp
|
|
41
|
+
*.swo
|
|
42
|
+
*~
|
|
43
|
+
|
|
44
|
+
# OS
|
|
45
|
+
.DS_Store
|
|
46
|
+
Thumbs.db
|
|
47
|
+
|
|
48
|
+
# Coverage
|
|
49
|
+
htmlcov/
|
|
50
|
+
.coverage
|
|
51
|
+
coverage.xml
|
|
52
|
+
|
|
53
|
+
# QA smoke fixtures — regenerated locally per docs/qa/cli/CLI_PreLaunch_Smoke_Tests.md
|
|
54
|
+
qa-fixtures/medium.bin
|
|
55
|
+
|
|
56
|
+
# Admin tooling audit log — local-only, never commit
|
|
57
|
+
scripts/admin/.audit.log
|
|
58
|
+
|
|
59
|
+
# MCP §9.3 manual-HITL transcript exports — operator stores locally; never commit.
|
|
60
|
+
# Keep the per-phase directories (and their .gitkeep) tracked so the archive
|
|
61
|
+
# convention survives; ignore every transcript file the operator drops in.
|
|
62
|
+
docs/qa/mcp/transcripts/**/*
|
|
63
|
+
!docs/qa/mcp/transcripts/**/
|
|
64
|
+
!docs/qa/mcp/transcripts/**/.gitkeep
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Artifacta
|
|
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.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: artifacta-mcp
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Artifacta MCP server — exposes the Artifacta artifact store to AI agents via the Model Context Protocol
|
|
5
|
+
Project-URL: Homepage, https://artifacta.io
|
|
6
|
+
Project-URL: Documentation, https://docs.artifacta.io/mcp
|
|
7
|
+
Project-URL: Repository, https://github.com/sagapeak/artifacta
|
|
8
|
+
Project-URL: Issues, https://github.com/sagapeak/artifacta/issues
|
|
9
|
+
Author-email: Artifacta <team@artifacta.io>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agents,ai,artifacts,mcp,model-context-protocol
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: artifacta-cli<2.0.0,>=0.3.0
|
|
24
|
+
Requires-Dist: jsonschema>=4.20.0
|
|
25
|
+
Requires-Dist: mcp>=1.0.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: build>=1.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.8.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# artifacta-mcp
|
|
35
|
+
|
|
36
|
+
Artifacta MCP server (Python) — the artifact store for AI agents, exposed
|
|
37
|
+
via the Model Context Protocol.
|
|
38
|
+
|
|
39
|
+
This is the Python port of [`@artifacta/mcp`](../typescript). Same tool
|
|
40
|
+
surface, same path-confinement engine, same error contract. Reuses the
|
|
41
|
+
[`artifacta`](https://pypi.org/project/artifacta-cli/) SDK as the single
|
|
42
|
+
HTTP client — no parallel client implementation.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
Recommended (no global install):
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pipx run artifacta-mcp
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or install into a virtualenv:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
python -m venv .venv && source .venv/bin/activate
|
|
56
|
+
pip install artifacta-mcp
|
|
57
|
+
artifacta-mcp --version
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Python 3.10+ required.
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
| Source | How to set |
|
|
65
|
+
|--------|-----------|
|
|
66
|
+
| `ARTIFACTA_API_KEY` env var | `export ARTIFACTA_API_KEY=ak_live_...` (required) |
|
|
67
|
+
| `ARTIFACTA_API_URL` env var | Override API base (default: `https://api.artifacta.io`) |
|
|
68
|
+
| `--allow-path=PATH` | Add `PATH` (absolute) to the path-confinement allow-list. May be passed multiple times. |
|
|
69
|
+
| `ARTIFACTA_MCP_ALLOW_PATH` | Colon-separated additional allow-list paths. |
|
|
70
|
+
| `--allow-destructive` | Expose destructive tools (`delete_artifact`, `seal_session`, `create_download_link`) to clients that do **not** advertise `experimental.confirmations`. Compliant clients always get them with `requiresConfirmation` set. |
|
|
71
|
+
| `ARTIFACTA_MCP_REQUIRE_WRITE_CONFIRM=1` | For compliant clients, also require confirmation for `store_artifact`, `request_upload_url`, `complete_upload`, and `create_download_link`. |
|
|
72
|
+
|
|
73
|
+
Obtain an API key at [https://app.artifacta.io/dashboard/keys](https://app.artifacta.io/dashboard/keys).
|
|
74
|
+
|
|
75
|
+
## Register with Claude Desktop
|
|
76
|
+
|
|
77
|
+
Add this block to `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
78
|
+
(macOS) or the equivalent on your platform:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"mcpServers": {
|
|
83
|
+
"artifacta": {
|
|
84
|
+
"command": "pipx",
|
|
85
|
+
"args": ["run", "artifacta-mcp"],
|
|
86
|
+
"env": {
|
|
87
|
+
"ARTIFACTA_API_KEY": "ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Restart Claude Desktop. The `whoami` tool is the fastest way to confirm
|
|
95
|
+
authentication.
|
|
96
|
+
|
|
97
|
+
## Register with Cursor
|
|
98
|
+
|
|
99
|
+
Add to `~/.cursor/mcp.json`:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"mcpServers": {
|
|
104
|
+
"artifacta": {
|
|
105
|
+
"command": "pipx",
|
|
106
|
+
"args": ["run", "artifacta-mcp"],
|
|
107
|
+
"env": {
|
|
108
|
+
"ARTIFACTA_API_KEY": "ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Tool surface
|
|
116
|
+
|
|
117
|
+
Same eleven tools as the TypeScript package (plan §2):
|
|
118
|
+
|
|
119
|
+
| Tool | Safety | Notes |
|
|
120
|
+
|------|--------|-------|
|
|
121
|
+
| `whoami` | safe | Identity + plan + quota |
|
|
122
|
+
| `list_artifacts` | safe | Pagination via opaque `cursor`; metadata filter requires Pro for multi-key |
|
|
123
|
+
| `get_artifact` | safe | Metadata only — no bytes |
|
|
124
|
+
| `get_artifact_download_url` | safe | Presigned R2 URL, 1-hour expiry |
|
|
125
|
+
| `list_sessions` | safe | Sessions synthesized from artifacts |
|
|
126
|
+
| `store_artifact` | writeIdempotent | Inline `content` ≤10 MB OR local `path` ≤500 MB |
|
|
127
|
+
| `request_upload_url` | writeNonIdempotent | Pro only; 5xx is ambiguous — read §6.1 guidance |
|
|
128
|
+
| `complete_upload` | writeIdempotent | Finalizes a `request_upload_url` artifact |
|
|
129
|
+
| `create_download_link` | destructive | Public URL — gated behind `--allow-destructive` for non-compliant clients |
|
|
130
|
+
| `delete_artifact` | destructive | Soft-delete; hard-delete after 30 days |
|
|
131
|
+
| `seal_session` | destructive | **Irreversible** — no `unseal` endpoint |
|
|
132
|
+
|
|
133
|
+
The destructive-tool gating engine (`safety/registry`) hides destructive
|
|
134
|
+
tools from clients that do not advertise `experimental.confirmations` in
|
|
135
|
+
`initialize`, unless `--allow-destructive` is passed at launch. When that
|
|
136
|
+
flag exposes a destructive tool, each call emits a `[artifacta-mcp]
|
|
137
|
+
destructive call: <tool>(<args>)` audit line to stderr with secret
|
|
138
|
+
redaction and 200-char truncation.
|
|
139
|
+
|
|
140
|
+
## Path confinement
|
|
141
|
+
|
|
142
|
+
`store_artifact` with the `path` argument validates the path against:
|
|
143
|
+
|
|
144
|
+
- An **allow-list** of absolute roots (CWD by default; widen with
|
|
145
|
+
`--allow-path=/abs/dir` or `ARTIFACTA_MCP_ALLOW_PATH=/a:/b`).
|
|
146
|
+
- A built-in **deny-list** that always wins: `~/.ssh`, `~/.aws`, `~/.gnupg`,
|
|
147
|
+
`~/.config/gh`, `~/.kube`, `~/.artifacta`, `~/.netrc`, `~/Library/Keychains`,
|
|
148
|
+
`/etc`, `/private/etc`, `/var/lib`, `/proc`, `/sys`, `/dev`.
|
|
149
|
+
- Filename pattern denies: `credentials.json`, `.env*`.
|
|
150
|
+
- A **500 MB** size ceiling.
|
|
151
|
+
- Symlink resolution **before** the allow/deny check (so a symlink under your
|
|
152
|
+
CWD that points into `/etc/passwd` is denied as `/etc/passwd`, not allowed
|
|
153
|
+
by allow-list membership of the link site).
|
|
154
|
+
|
|
155
|
+
The Python engine is cross-validated against the TypeScript engine via the
|
|
156
|
+
shared fixture at `mcp/shared/path-confinement-fixture.json`. Identical
|
|
157
|
+
accept/reject is a tested contract.
|
|
158
|
+
|
|
159
|
+
## Troubleshooting
|
|
160
|
+
|
|
161
|
+
### "Authentication failed" on the first call
|
|
162
|
+
|
|
163
|
+
The error message includes structured remediation:
|
|
164
|
+
1. Confirm `ARTIFACTA_API_KEY` is set in the MCP server's environment
|
|
165
|
+
(the host process — Claude Desktop, Cursor — must pass it through).
|
|
166
|
+
2. Verify the key at <https://app.artifacta.io/dashboard/keys>.
|
|
167
|
+
3. The error message includes the last 4 chars of the cached key
|
|
168
|
+
(`****<last4>`) when known, so you can confirm which key was used.
|
|
169
|
+
|
|
170
|
+
### `delete_artifact` / `seal_session` / `create_download_link` not visible
|
|
171
|
+
|
|
172
|
+
By design — these are destructive and hidden from clients that do not
|
|
173
|
+
advertise `experimental.confirmations`. Either:
|
|
174
|
+
|
|
175
|
+
- Use a client that advertises the capability (it gets the tool with
|
|
176
|
+
`requiresConfirmation: true`), or
|
|
177
|
+
- Pass `--allow-destructive` at launch (you give up the consent surface and
|
|
178
|
+
accept the per-call stderr audit line as your only signal).
|
|
179
|
+
|
|
180
|
+
### `invalid_request: Path '…' is outside the MCP server's allow-list`
|
|
181
|
+
|
|
182
|
+
Path confinement refused the upload. Widen with `--allow-path=/abs/dir` or
|
|
183
|
+
send bytes inline via the `content` field. If the path resolved to
|
|
184
|
+
`/etc`, `~/.ssh`, `.env`, or similar — that's the deny-list firing and is
|
|
185
|
+
**not** overridable.
|
|
186
|
+
|
|
187
|
+
### `request_upload_url` failed with "Artifacta API failed mid-write"
|
|
188
|
+
|
|
189
|
+
`request_upload_url` is non-idempotent (the API does not honor
|
|
190
|
+
`Idempotency-Key` here). On 5xx or network error the reservation may have
|
|
191
|
+
been created. Per plan §6.1: call `list_artifacts` with the same
|
|
192
|
+
`session_id`/`agent_id` before retrying to avoid duplicate artifacts.
|
|
193
|
+
|
|
194
|
+
## Versioning
|
|
195
|
+
|
|
196
|
+
The Python package shares the **major.minor** version with the TypeScript
|
|
197
|
+
package (`@artifacta/mcp`). Patch versions are independent. See the
|
|
198
|
+
[CHANGELOG](../typescript/CHANGELOG.md) for the canonical release notes.
|
|
199
|
+
|
|
200
|
+
## Development
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
cd mcp/python
|
|
204
|
+
python -m venv .venv && source .venv/bin/activate
|
|
205
|
+
pip install -e '.[dev]'
|
|
206
|
+
pytest
|
|
207
|
+
ruff check src tests
|
|
208
|
+
python -m build
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# artifacta-mcp
|
|
2
|
+
|
|
3
|
+
Artifacta MCP server (Python) — the artifact store for AI agents, exposed
|
|
4
|
+
via the Model Context Protocol.
|
|
5
|
+
|
|
6
|
+
This is the Python port of [`@artifacta/mcp`](../typescript). Same tool
|
|
7
|
+
surface, same path-confinement engine, same error contract. Reuses the
|
|
8
|
+
[`artifacta`](https://pypi.org/project/artifacta-cli/) SDK as the single
|
|
9
|
+
HTTP client — no parallel client implementation.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Recommended (no global install):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pipx run artifacta-mcp
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install into a virtualenv:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
python -m venv .venv && source .venv/bin/activate
|
|
23
|
+
pip install artifacta-mcp
|
|
24
|
+
artifacta-mcp --version
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Python 3.10+ required.
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
| Source | How to set |
|
|
32
|
+
|--------|-----------|
|
|
33
|
+
| `ARTIFACTA_API_KEY` env var | `export ARTIFACTA_API_KEY=ak_live_...` (required) |
|
|
34
|
+
| `ARTIFACTA_API_URL` env var | Override API base (default: `https://api.artifacta.io`) |
|
|
35
|
+
| `--allow-path=PATH` | Add `PATH` (absolute) to the path-confinement allow-list. May be passed multiple times. |
|
|
36
|
+
| `ARTIFACTA_MCP_ALLOW_PATH` | Colon-separated additional allow-list paths. |
|
|
37
|
+
| `--allow-destructive` | Expose destructive tools (`delete_artifact`, `seal_session`, `create_download_link`) to clients that do **not** advertise `experimental.confirmations`. Compliant clients always get them with `requiresConfirmation` set. |
|
|
38
|
+
| `ARTIFACTA_MCP_REQUIRE_WRITE_CONFIRM=1` | For compliant clients, also require confirmation for `store_artifact`, `request_upload_url`, `complete_upload`, and `create_download_link`. |
|
|
39
|
+
|
|
40
|
+
Obtain an API key at [https://app.artifacta.io/dashboard/keys](https://app.artifacta.io/dashboard/keys).
|
|
41
|
+
|
|
42
|
+
## Register with Claude Desktop
|
|
43
|
+
|
|
44
|
+
Add this block to `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
45
|
+
(macOS) or the equivalent on your platform:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"mcpServers": {
|
|
50
|
+
"artifacta": {
|
|
51
|
+
"command": "pipx",
|
|
52
|
+
"args": ["run", "artifacta-mcp"],
|
|
53
|
+
"env": {
|
|
54
|
+
"ARTIFACTA_API_KEY": "ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Restart Claude Desktop. The `whoami` tool is the fastest way to confirm
|
|
62
|
+
authentication.
|
|
63
|
+
|
|
64
|
+
## Register with Cursor
|
|
65
|
+
|
|
66
|
+
Add to `~/.cursor/mcp.json`:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcpServers": {
|
|
71
|
+
"artifacta": {
|
|
72
|
+
"command": "pipx",
|
|
73
|
+
"args": ["run", "artifacta-mcp"],
|
|
74
|
+
"env": {
|
|
75
|
+
"ARTIFACTA_API_KEY": "ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Tool surface
|
|
83
|
+
|
|
84
|
+
Same eleven tools as the TypeScript package (plan §2):
|
|
85
|
+
|
|
86
|
+
| Tool | Safety | Notes |
|
|
87
|
+
|------|--------|-------|
|
|
88
|
+
| `whoami` | safe | Identity + plan + quota |
|
|
89
|
+
| `list_artifacts` | safe | Pagination via opaque `cursor`; metadata filter requires Pro for multi-key |
|
|
90
|
+
| `get_artifact` | safe | Metadata only — no bytes |
|
|
91
|
+
| `get_artifact_download_url` | safe | Presigned R2 URL, 1-hour expiry |
|
|
92
|
+
| `list_sessions` | safe | Sessions synthesized from artifacts |
|
|
93
|
+
| `store_artifact` | writeIdempotent | Inline `content` ≤10 MB OR local `path` ≤500 MB |
|
|
94
|
+
| `request_upload_url` | writeNonIdempotent | Pro only; 5xx is ambiguous — read §6.1 guidance |
|
|
95
|
+
| `complete_upload` | writeIdempotent | Finalizes a `request_upload_url` artifact |
|
|
96
|
+
| `create_download_link` | destructive | Public URL — gated behind `--allow-destructive` for non-compliant clients |
|
|
97
|
+
| `delete_artifact` | destructive | Soft-delete; hard-delete after 30 days |
|
|
98
|
+
| `seal_session` | destructive | **Irreversible** — no `unseal` endpoint |
|
|
99
|
+
|
|
100
|
+
The destructive-tool gating engine (`safety/registry`) hides destructive
|
|
101
|
+
tools from clients that do not advertise `experimental.confirmations` in
|
|
102
|
+
`initialize`, unless `--allow-destructive` is passed at launch. When that
|
|
103
|
+
flag exposes a destructive tool, each call emits a `[artifacta-mcp]
|
|
104
|
+
destructive call: <tool>(<args>)` audit line to stderr with secret
|
|
105
|
+
redaction and 200-char truncation.
|
|
106
|
+
|
|
107
|
+
## Path confinement
|
|
108
|
+
|
|
109
|
+
`store_artifact` with the `path` argument validates the path against:
|
|
110
|
+
|
|
111
|
+
- An **allow-list** of absolute roots (CWD by default; widen with
|
|
112
|
+
`--allow-path=/abs/dir` or `ARTIFACTA_MCP_ALLOW_PATH=/a:/b`).
|
|
113
|
+
- A built-in **deny-list** that always wins: `~/.ssh`, `~/.aws`, `~/.gnupg`,
|
|
114
|
+
`~/.config/gh`, `~/.kube`, `~/.artifacta`, `~/.netrc`, `~/Library/Keychains`,
|
|
115
|
+
`/etc`, `/private/etc`, `/var/lib`, `/proc`, `/sys`, `/dev`.
|
|
116
|
+
- Filename pattern denies: `credentials.json`, `.env*`.
|
|
117
|
+
- A **500 MB** size ceiling.
|
|
118
|
+
- Symlink resolution **before** the allow/deny check (so a symlink under your
|
|
119
|
+
CWD that points into `/etc/passwd` is denied as `/etc/passwd`, not allowed
|
|
120
|
+
by allow-list membership of the link site).
|
|
121
|
+
|
|
122
|
+
The Python engine is cross-validated against the TypeScript engine via the
|
|
123
|
+
shared fixture at `mcp/shared/path-confinement-fixture.json`. Identical
|
|
124
|
+
accept/reject is a tested contract.
|
|
125
|
+
|
|
126
|
+
## Troubleshooting
|
|
127
|
+
|
|
128
|
+
### "Authentication failed" on the first call
|
|
129
|
+
|
|
130
|
+
The error message includes structured remediation:
|
|
131
|
+
1. Confirm `ARTIFACTA_API_KEY` is set in the MCP server's environment
|
|
132
|
+
(the host process — Claude Desktop, Cursor — must pass it through).
|
|
133
|
+
2. Verify the key at <https://app.artifacta.io/dashboard/keys>.
|
|
134
|
+
3. The error message includes the last 4 chars of the cached key
|
|
135
|
+
(`****<last4>`) when known, so you can confirm which key was used.
|
|
136
|
+
|
|
137
|
+
### `delete_artifact` / `seal_session` / `create_download_link` not visible
|
|
138
|
+
|
|
139
|
+
By design — these are destructive and hidden from clients that do not
|
|
140
|
+
advertise `experimental.confirmations`. Either:
|
|
141
|
+
|
|
142
|
+
- Use a client that advertises the capability (it gets the tool with
|
|
143
|
+
`requiresConfirmation: true`), or
|
|
144
|
+
- Pass `--allow-destructive` at launch (you give up the consent surface and
|
|
145
|
+
accept the per-call stderr audit line as your only signal).
|
|
146
|
+
|
|
147
|
+
### `invalid_request: Path '…' is outside the MCP server's allow-list`
|
|
148
|
+
|
|
149
|
+
Path confinement refused the upload. Widen with `--allow-path=/abs/dir` or
|
|
150
|
+
send bytes inline via the `content` field. If the path resolved to
|
|
151
|
+
`/etc`, `~/.ssh`, `.env`, or similar — that's the deny-list firing and is
|
|
152
|
+
**not** overridable.
|
|
153
|
+
|
|
154
|
+
### `request_upload_url` failed with "Artifacta API failed mid-write"
|
|
155
|
+
|
|
156
|
+
`request_upload_url` is non-idempotent (the API does not honor
|
|
157
|
+
`Idempotency-Key` here). On 5xx or network error the reservation may have
|
|
158
|
+
been created. Per plan §6.1: call `list_artifacts` with the same
|
|
159
|
+
`session_id`/`agent_id` before retrying to avoid duplicate artifacts.
|
|
160
|
+
|
|
161
|
+
## Versioning
|
|
162
|
+
|
|
163
|
+
The Python package shares the **major.minor** version with the TypeScript
|
|
164
|
+
package (`@artifacta/mcp`). Patch versions are independent. See the
|
|
165
|
+
[CHANGELOG](../typescript/CHANGELOG.md) for the canonical release notes.
|
|
166
|
+
|
|
167
|
+
## Development
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
cd mcp/python
|
|
171
|
+
python -m venv .venv && source .venv/bin/activate
|
|
172
|
+
pip install -e '.[dev]'
|
|
173
|
+
pytest
|
|
174
|
+
ruff check src tests
|
|
175
|
+
python -m build
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.18"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "artifacta-mcp"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Artifacta MCP server — exposes the Artifacta artifact store to AI agents via the Model Context Protocol"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Artifacta", email = "team@artifacta.io" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["artifacts", "ai", "agents", "mcp", "model-context-protocol"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 5 - Production/Stable",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Topic :: Software Development :: Libraries",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
# Shared HTTP layer — single client implementation across CLI/SDK/MCP (plan §8.1).
|
|
29
|
+
# Floor is 0.3.0 because that minor adds Client.request_upload_url,
|
|
30
|
+
# Client.complete_upload, and the Client.push content_type kwarg — all
|
|
31
|
+
# surface this MCP server hard-depends on. Anything below 0.3 will
|
|
32
|
+
# AttributeError / TypeError at first call. Upper bound stays at <2.0 to
|
|
33
|
+
# preserve the original major-version tolerance; SDK 1.x will be revisited
|
|
34
|
+
# when it ships (cross-validation tests pin the wire-shape contract).
|
|
35
|
+
"artifacta-cli>=0.3.0,<2.0.0",
|
|
36
|
+
# Official MCP Python SDK
|
|
37
|
+
"mcp>=1.0.0",
|
|
38
|
+
# Schema validation for tool inputs (matches Ajv role in the TS server)
|
|
39
|
+
"jsonschema>=4.20.0",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://artifacta.io"
|
|
44
|
+
Documentation = "https://docs.artifacta.io/mcp"
|
|
45
|
+
Repository = "https://github.com/sagapeak/artifacta"
|
|
46
|
+
Issues = "https://github.com/sagapeak/artifacta/issues"
|
|
47
|
+
|
|
48
|
+
[project.scripts]
|
|
49
|
+
artifacta-mcp = "artifacta_mcp.cli:main"
|
|
50
|
+
|
|
51
|
+
[project.optional-dependencies]
|
|
52
|
+
dev = [
|
|
53
|
+
"pytest>=8.0",
|
|
54
|
+
"pytest-asyncio>=0.23",
|
|
55
|
+
"ruff>=0.8.0",
|
|
56
|
+
"build>=1.0",
|
|
57
|
+
"twine>=5.0",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[tool.hatch.build.targets.wheel]
|
|
61
|
+
packages = ["src/artifacta_mcp"]
|
|
62
|
+
|
|
63
|
+
[tool.hatch.build.targets.sdist]
|
|
64
|
+
include = [
|
|
65
|
+
"src/artifacta_mcp",
|
|
66
|
+
"README.md",
|
|
67
|
+
"LICENSE",
|
|
68
|
+
"pyproject.toml",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[tool.ruff]
|
|
72
|
+
line-length = 100
|
|
73
|
+
target-version = "py310"
|
|
74
|
+
|
|
75
|
+
[tool.ruff.lint]
|
|
76
|
+
select = ["E", "F", "W", "I", "B", "UP"]
|
|
77
|
+
ignore = ["E501"] # line length is enforced by formatter, not lint
|
|
78
|
+
|
|
79
|
+
[tool.pytest.ini_options]
|
|
80
|
+
testpaths = ["tests"]
|
|
81
|
+
pythonpath = ["src"]
|
|
82
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Module-level singleton for the path-confinement allow-list.
|
|
2
|
+
|
|
3
|
+
Mirrors `mcp/typescript/src/path/allowlist.ts`. cli.py calls
|
|
4
|
+
`build_allow_list(argv)` once at startup, then stores the resolved roots
|
|
5
|
+
via `set_allow_roots`. The store_artifact path branch reads them with
|
|
6
|
+
`get_allow_roots()` at handler time.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
_roots: list[str] | None = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def set_allow_roots(roots: list[str]) -> None:
|
|
14
|
+
global _roots
|
|
15
|
+
_roots = list(roots)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_allow_roots() -> list[str]:
|
|
19
|
+
if _roots is None:
|
|
20
|
+
raise RuntimeError(
|
|
21
|
+
"Path allow-list not initialised. Call set_allow_roots() at startup "
|
|
22
|
+
"before invoking path-based tools."
|
|
23
|
+
)
|
|
24
|
+
return _roots
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def reset_allow_roots() -> None:
|
|
28
|
+
global _roots
|
|
29
|
+
_roots = None
|