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.
Files changed (30) hide show
  1. artifacta_mcp-1.0.0/.gitignore +64 -0
  2. artifacta_mcp-1.0.0/LICENSE +21 -0
  3. artifacta_mcp-1.0.0/PKG-INFO +213 -0
  4. artifacta_mcp-1.0.0/README.md +180 -0
  5. artifacta_mcp-1.0.0/pyproject.toml +82 -0
  6. artifacta_mcp-1.0.0/src/artifacta_mcp/__init__.py +3 -0
  7. artifacta_mcp-1.0.0/src/artifacta_mcp/allowlist.py +29 -0
  8. artifacta_mcp-1.0.0/src/artifacta_mcp/cli.py +130 -0
  9. artifacta_mcp-1.0.0/src/artifacta_mcp/client_factory.py +30 -0
  10. artifacta_mcp-1.0.0/src/artifacta_mcp/errors.py +283 -0
  11. artifacta_mcp-1.0.0/src/artifacta_mcp/ids.py +9 -0
  12. artifacta_mcp-1.0.0/src/artifacta_mcp/path_confinement.py +322 -0
  13. artifacta_mcp-1.0.0/src/artifacta_mcp/safety.py +185 -0
  14. artifacta_mcp-1.0.0/src/artifacta_mcp/sdk_compat.py +93 -0
  15. artifacta_mcp-1.0.0/src/artifacta_mcp/server.py +136 -0
  16. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/__init__.py +46 -0
  17. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/_common.py +45 -0
  18. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/complete_upload.py +63 -0
  19. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/create_download_link.py +95 -0
  20. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/delete_artifact.py +70 -0
  21. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/errors_fallback.py +15 -0
  22. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/get_artifact.py +59 -0
  23. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/get_artifact_download_url.py +59 -0
  24. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/list_artifacts.py +98 -0
  25. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/list_sessions.py +72 -0
  26. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/request_upload_url.py +135 -0
  27. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/seal_session.py +66 -0
  28. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/store_artifact.py +280 -0
  29. artifacta_mcp-1.0.0/src/artifacta_mcp/tools/whoami.py +47 -0
  30. 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,3 @@
1
+ """Artifacta MCP server — Python port of @artifacta/mcp."""
2
+
3
+ __version__ = "1.0.0"
@@ -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