printables-mcp 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.
- printables_mcp-0.1.0/.github/dependabot.yml +31 -0
- printables_mcp-0.1.0/.github/workflows/ci.yml +70 -0
- printables_mcp-0.1.0/.github/workflows/uv-publish.yml +53 -0
- printables_mcp-0.1.0/.gitignore +18 -0
- printables_mcp-0.1.0/.python-version +1 -0
- printables_mcp-0.1.0/CLAUDE.md +37 -0
- printables_mcp-0.1.0/Justfile +17 -0
- printables_mcp-0.1.0/LICENSE +21 -0
- printables_mcp-0.1.0/PKG-INFO +99 -0
- printables_mcp-0.1.0/README.md +69 -0
- printables_mcp-0.1.0/README.ru.md +69 -0
- printables_mcp-0.1.0/pyproject.toml +95 -0
- printables_mcp-0.1.0/src/printables_mcp/__init__.py +5 -0
- printables_mcp-0.1.0/src/printables_mcp/client.py +115 -0
- printables_mcp-0.1.0/src/printables_mcp/markdown.py +26 -0
- printables_mcp-0.1.0/src/printables_mcp/models.py +349 -0
- printables_mcp-0.1.0/src/printables_mcp/py.typed +0 -0
- printables_mcp-0.1.0/src/printables_mcp/queries.py +110 -0
- printables_mcp-0.1.0/src/printables_mcp/refs.py +88 -0
- printables_mcp-0.1.0/src/printables_mcp/server.py +297 -0
- printables_mcp-0.1.0/tests/conftest.py +126 -0
- printables_mcp-0.1.0/tests/fixtures/author_16.json +11 -0
- printables_mcp-0.1.0/tests/fixtures/author_models_16.json +81 -0
- printables_mcp-0.1.0/tests/fixtures/categories.json +1196 -0
- printables_mcp-0.1.0/tests/fixtures/files_3161.json +108 -0
- printables_mcp-0.1.0/tests/fixtures/licenses.json +121 -0
- printables_mcp-0.1.0/tests/fixtures/link_premium_error.json +16 -0
- printables_mcp-0.1.0/tests/fixtures/model_1202139.json +112 -0
- printables_mcp-0.1.0/tests/fixtures/model_3161.json +89 -0
- printables_mcp-0.1.0/tests/fixtures/model_missing.json +5 -0
- printables_mcp-0.1.0/tests/fixtures/search_benchy.json +117 -0
- printables_mcp-0.1.0/tests/test_client.py +105 -0
- printables_mcp-0.1.0/tests/test_live.py +67 -0
- printables_mcp-0.1.0/tests/test_markdown.py +26 -0
- printables_mcp-0.1.0/tests/test_refs.py +72 -0
- printables_mcp-0.1.0/tests/test_server.py +298 -0
- printables_mcp-0.1.0/uv.lock +1666 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
updates:
|
|
4
|
+
# Python tooling: update the dev dependency group only.
|
|
5
|
+
# allow=development limits PRs to non-production deps; the runtime contract
|
|
6
|
+
# (mcp, httpx2, pydantic, markdownify, anyio) is bumped by hand.
|
|
7
|
+
- package-ecosystem: "uv"
|
|
8
|
+
directory: "/"
|
|
9
|
+
schedule:
|
|
10
|
+
interval: "monthly"
|
|
11
|
+
allow:
|
|
12
|
+
- dependency-type: "development"
|
|
13
|
+
groups:
|
|
14
|
+
python-dev:
|
|
15
|
+
patterns:
|
|
16
|
+
- "*"
|
|
17
|
+
commit-message:
|
|
18
|
+
prefix: "chore"
|
|
19
|
+
include: "scope"
|
|
20
|
+
|
|
21
|
+
# GitHub Actions pinned in the workflows.
|
|
22
|
+
- package-ecosystem: "github-actions"
|
|
23
|
+
directory: "/"
|
|
24
|
+
schedule:
|
|
25
|
+
interval: "monthly"
|
|
26
|
+
groups:
|
|
27
|
+
github-actions:
|
|
28
|
+
patterns:
|
|
29
|
+
- "*"
|
|
30
|
+
commit-message:
|
|
31
|
+
prefix: "ci"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
workflow_call:
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
name: "Lint"
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v9.0.0
|
|
20
|
+
with:
|
|
21
|
+
enable-cache: true
|
|
22
|
+
cache-dependency-glob: uv.lock
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: uv sync --frozen
|
|
25
|
+
- name: Check lockfile
|
|
26
|
+
run: uv lock --check
|
|
27
|
+
- name: Ruff lint
|
|
28
|
+
run: uv run ruff check --output-format=github
|
|
29
|
+
- name: Ruff format
|
|
30
|
+
run: uv run ruff format --check
|
|
31
|
+
- name: Ty
|
|
32
|
+
run: uv run ty check
|
|
33
|
+
|
|
34
|
+
test:
|
|
35
|
+
name: "Test (${{ matrix.python }})"
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
strategy:
|
|
38
|
+
fail-fast: false
|
|
39
|
+
matrix:
|
|
40
|
+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v7
|
|
43
|
+
- name: Install uv
|
|
44
|
+
uses: astral-sh/setup-uv@v9.0.0
|
|
45
|
+
with:
|
|
46
|
+
enable-cache: true
|
|
47
|
+
cache-dependency-glob: uv.lock
|
|
48
|
+
python-version: ${{ matrix.python }}
|
|
49
|
+
- name: Install dependencies
|
|
50
|
+
run: uv sync --frozen
|
|
51
|
+
- name: Pytest
|
|
52
|
+
run: uv run pytest --cov
|
|
53
|
+
|
|
54
|
+
build:
|
|
55
|
+
name: "Build"
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v7
|
|
59
|
+
- name: Install uv
|
|
60
|
+
uses: astral-sh/setup-uv@v9.0.0
|
|
61
|
+
with:
|
|
62
|
+
enable-cache: true
|
|
63
|
+
cache-dependency-glob: uv.lock
|
|
64
|
+
- name: Build
|
|
65
|
+
run: uv build
|
|
66
|
+
# The installed wheel must import cleanly and expose all tools
|
|
67
|
+
- name: Smoke test the wheel
|
|
68
|
+
run: >-
|
|
69
|
+
uv run --isolated --no-project --with dist/*.whl
|
|
70
|
+
python -c "from printables_mcp.server import TOOLS, create_server; create_server(); assert len(TOOLS) == 8"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: uv package publish
|
|
2
|
+
on:
|
|
3
|
+
release:
|
|
4
|
+
types: ["published"]
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
ci:
|
|
8
|
+
name: "CI"
|
|
9
|
+
uses: ./.github/workflows/ci.yml
|
|
10
|
+
|
|
11
|
+
check-version:
|
|
12
|
+
name: "Check tag matches project version"
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v7
|
|
16
|
+
- name: Compare release tag with pyproject version
|
|
17
|
+
env:
|
|
18
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
19
|
+
run: |
|
|
20
|
+
python3 - <<'EOF'
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
import tomllib
|
|
24
|
+
|
|
25
|
+
with open("pyproject.toml", "rb") as f:
|
|
26
|
+
version = tomllib.load(f)["project"]["version"]
|
|
27
|
+
|
|
28
|
+
tag = os.environ["RELEASE_TAG"].removeprefix("v")
|
|
29
|
+
if tag != version:
|
|
30
|
+
sys.exit(f"Release tag {tag!r} does not match project version {version!r}")
|
|
31
|
+
print(f"Release tag matches project version {version}")
|
|
32
|
+
EOF
|
|
33
|
+
|
|
34
|
+
publish:
|
|
35
|
+
name: "Build and publish release"
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
needs: [ci, check-version]
|
|
38
|
+
environment:
|
|
39
|
+
name: pypi
|
|
40
|
+
url: https://pypi.org/project/printables-mcp/
|
|
41
|
+
permissions:
|
|
42
|
+
id-token: write
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@v7
|
|
45
|
+
- name: Install uv
|
|
46
|
+
uses: astral-sh/setup-uv@v9.0.0
|
|
47
|
+
with:
|
|
48
|
+
enable-cache: true
|
|
49
|
+
cache-dependency-glob: uv.lock
|
|
50
|
+
- name: Build
|
|
51
|
+
run: uv build
|
|
52
|
+
- name: Publish
|
|
53
|
+
run: uv publish
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# printables-mcp
|
|
2
|
+
|
|
3
|
+
Public PyPI package: an MCP server (stdio) for Printables.com built on the official `mcp` SDK 2.x (`MCPServer`). Console script `printables-mcp` → `printables_mcp.server:main`.
|
|
4
|
+
|
|
5
|
+
**Keep this file up to date** when the API knowledge or design decisions change.
|
|
6
|
+
|
|
7
|
+
## Layout
|
|
8
|
+
|
|
9
|
+
src-layout, `src/printables_mcp/`:
|
|
10
|
+
|
|
11
|
+
- `client.py`: `PrintablesClient`, an async `httpx2` client with one `execute(query, variables)` method. Maps every failure to `PrintablesError`; retries network errors and 5xx (3 attempts, linear backoff).
|
|
12
|
+
- `queries.py`: the GraphQL documents.
|
|
13
|
+
- `models.py`: pydantic result models (structured tool output), built from raw payloads via `from_api`.
|
|
14
|
+
- `refs.py`: parsing model/author references (IDs, URLs, handles) and building site/media URLs.
|
|
15
|
+
- `markdown.py`: description HTML → Markdown (`markdownify`), site-relative targets made absolute.
|
|
16
|
+
- `server.py`: module-level tool functions (`TOOLS`), `create_server(transport=None)` registering them with read-only annotations, `main()`.
|
|
17
|
+
|
|
18
|
+
## Printables API facts (verified by hand; introspection and field suggestions are disabled)
|
|
19
|
+
|
|
20
|
+
- Endpoint `https://api.printables.com/graphql/`; the website itself sits behind Cloudflare (403 for scripts), the API does not. An honest User-Agent is accepted.
|
|
21
|
+
- Validation errors come back as **HTTP 400 with a JSON `errors` body**: read the body before looking at the status. Out-of-range values (e.g. `limit: 101`) give a 400 with a non-JSON body.
|
|
22
|
+
- `searchPrints2(query, printType: print, limit ≤ 100, offset, ordering: SearchChoicesEnum, categoryId: ID, paid: PaidEnum{all,free,paid}, licenses: [ID], publishedDateLimitDays: Int, hasMake: Boolean)` → `{totalCount, items}`; `totalCount` caps at 10000.
|
|
23
|
+
- `print(id)` returns `null` for a missing model. `printDuration` is hours as a decimal string, `weight` grams, `ratingAvg` a decimal string, `price` a decimal string or null, `premium` = club-only.
|
|
24
|
+
- `getDownloadLink(id, printId, fileType: stl|gcode|sla|other|pack, source: model_detail)` → `{ok, errors{field messages}, output{link ttl}}`; paid/club files fail with `cannot_download_premium_file_as_anonymous_user`.
|
|
25
|
+
- `user(id)` accepts a numeric ID or `"@handle"` (case-insensitive) and returns `null` when unknown. `userModels(userId, limit, cursor, ordering)` → `{cursor, items}`; ordering is a string (`[-]date_published`, `likes_count`, `download_count`, `rating_avg`, `makes_count`), cursor is `""` on an empty page.
|
|
26
|
+
|
|
27
|
+
## Decisions
|
|
28
|
+
|
|
29
|
+
- Own implementation, not a fork of any existing Printables MCP server.
|
|
30
|
+
- Links only: the server never writes files to disk.
|
|
31
|
+
- Per-file download failures are returned per file; only model-level failures raise `ToolError`.
|
|
32
|
+
|
|
33
|
+
## Conventions
|
|
34
|
+
|
|
35
|
+
- ruff `select = ["ALL"]` (preview), ty `all = "error"`, pytest with 100% branch coverage. Suppressions use `# ruff: ignore[rule-name]`.
|
|
36
|
+
- Tests use `httpx2.MockTransport` with recorded responses in `tests/fixtures/` and an in-memory `mcp.Client`; live tests are marked `live` and excluded by default (`just test-live`).
|
|
37
|
+
- Conventional Commits, GPG-signed, no Co-Authored-By trailer.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Show available recipes.
|
|
2
|
+
help:
|
|
3
|
+
@just --list --unsorted
|
|
4
|
+
|
|
5
|
+
# Check formatting, lint rules and types.
|
|
6
|
+
lint:
|
|
7
|
+
uv run ruff format --check
|
|
8
|
+
uv run ruff check
|
|
9
|
+
uv run ty check
|
|
10
|
+
|
|
11
|
+
# Run the test suite with coverage.
|
|
12
|
+
test:
|
|
13
|
+
uv run pytest --cov
|
|
14
|
+
|
|
15
|
+
# Run the end-to-end tests against the real Printables API.
|
|
16
|
+
test-live:
|
|
17
|
+
uv run pytest -m live
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fedor Ratschew
|
|
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,99 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: printables-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for Printables.com: search 3D models, read model cards, list files and get download links
|
|
5
|
+
Project-URL: Homepage, https://github.com/feodor-ra/printables-mcp
|
|
6
|
+
Project-URL: Repository, https://github.com/feodor-ra/printables-mcp
|
|
7
|
+
Project-URL: Issues, https://github.com/feodor-ra/printables-mcp/issues
|
|
8
|
+
Author-email: Fedor Ratschew <feodor.ra@me.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: 3d-printing,llm,mcp,model-context-protocol,printables
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: anyio>=4.9
|
|
25
|
+
Requires-Dist: httpx2>=2.10
|
|
26
|
+
Requires-Dist: markdownify>=1.2
|
|
27
|
+
Requires-Dist: mcp<3,>=2.3
|
|
28
|
+
Requires-Dist: pydantic>=2.12
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# printables-mcp
|
|
32
|
+
|
|
33
|
+
**English** · [Русский](README.ru.md)
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/printables-mcp/)
|
|
36
|
+

|
|
37
|
+

|
|
38
|
+
[](https://github.com/feodor-ra/printables-mcp/actions/workflows/ci.yml)
|
|
39
|
+
|
|
40
|
+
**An [MCP](https://modelcontextprotocol.io) server for [Printables.com](https://www.printables.com)**: lets an AI agent search 3D models, read model cards, list files and get direct download links.
|
|
41
|
+
|
|
42
|
+
It talks to the public Printables GraphQL API directly: no HTML scraping, no browser, no account. All tools are read-only and return structured results (JSON with an output schema), so agents get exact fields instead of prose.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv tool install printables-mcp
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then register the `printables-mcp` command with your MCP client. Claude Code:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
claude mcp add printables -s user -- printables-mcp
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Any other client that speaks stdio:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"mcpServers": {
|
|
61
|
+
"printables": { "command": "printables-mcp" }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Without installing: `uvx printables-mcp`.
|
|
67
|
+
|
|
68
|
+
## Tools
|
|
69
|
+
|
|
70
|
+
| Tool | What it does |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `search_models` | Text search with pagination (`limit`, `offset`, total count) and filters: category, price (`free` / `paid`), licenses, published within N days, has user prints. Orderings: best match, popular, latest, rating, makes count. |
|
|
73
|
+
| `get_model` | Full model card: description as Markdown, tags, category path, license, materials, printer, print time, filament weight, images, PDF, remix sources, stats, price. |
|
|
74
|
+
| `list_model_files` | Every file group in one request: model files (STL/3MF/STEP…), G-code with printer, material, layer height, nozzle and print time, SLA files, other files, ZIP packs. |
|
|
75
|
+
| `get_download_links` | Direct download URLs (valid 24 h) for all or selected files, fetched in parallel. Files of paid or club-only models come back with a per-file reason instead of failing the call. |
|
|
76
|
+
| `list_categories` | All categories with their full paths (IDs for the search filter). |
|
|
77
|
+
| `list_licenses` | All licenses (IDs for the search filter). |
|
|
78
|
+
| `get_author` | Author profile by ID, handle or profile URL. |
|
|
79
|
+
| `list_author_models` | An author's models, cursor-paginated, ordered by newest, oldest, likes, downloads, rating or makes. |
|
|
80
|
+
|
|
81
|
+
Models and authors can be passed as IDs or as printables.com URLs (locale prefixes such as `/de/` are fine).
|
|
82
|
+
|
|
83
|
+
## Limitations
|
|
84
|
+
|
|
85
|
+
- Anonymous access only: paid and Printables Club models are visible, but their files cannot be downloaded.
|
|
86
|
+
- The API is undocumented and may change without notice; the end-to-end suite (`just test-live`) checks it against the live service.
|
|
87
|
+
|
|
88
|
+
## Development
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
uv sync
|
|
92
|
+
just lint # ruff format/check + ty
|
|
93
|
+
just test # pytest with 100% branch coverage
|
|
94
|
+
just test-live # end-to-end tests against the real API
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# printables-mcp
|
|
2
|
+
|
|
3
|
+
**English** · [Русский](README.ru.md)
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/printables-mcp/)
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
[](https://github.com/feodor-ra/printables-mcp/actions/workflows/ci.yml)
|
|
9
|
+
|
|
10
|
+
**An [MCP](https://modelcontextprotocol.io) server for [Printables.com](https://www.printables.com)**: lets an AI agent search 3D models, read model cards, list files and get direct download links.
|
|
11
|
+
|
|
12
|
+
It talks to the public Printables GraphQL API directly: no HTML scraping, no browser, no account. All tools are read-only and return structured results (JSON with an output schema), so agents get exact fields instead of prose.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uv tool install printables-mcp
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then register the `printables-mcp` command with your MCP client. Claude Code:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
claude mcp add printables -s user -- printables-mcp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Any other client that speaks stdio:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"mcpServers": {
|
|
31
|
+
"printables": { "command": "printables-mcp" }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Without installing: `uvx printables-mcp`.
|
|
37
|
+
|
|
38
|
+
## Tools
|
|
39
|
+
|
|
40
|
+
| Tool | What it does |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `search_models` | Text search with pagination (`limit`, `offset`, total count) and filters: category, price (`free` / `paid`), licenses, published within N days, has user prints. Orderings: best match, popular, latest, rating, makes count. |
|
|
43
|
+
| `get_model` | Full model card: description as Markdown, tags, category path, license, materials, printer, print time, filament weight, images, PDF, remix sources, stats, price. |
|
|
44
|
+
| `list_model_files` | Every file group in one request: model files (STL/3MF/STEP…), G-code with printer, material, layer height, nozzle and print time, SLA files, other files, ZIP packs. |
|
|
45
|
+
| `get_download_links` | Direct download URLs (valid 24 h) for all or selected files, fetched in parallel. Files of paid or club-only models come back with a per-file reason instead of failing the call. |
|
|
46
|
+
| `list_categories` | All categories with their full paths (IDs for the search filter). |
|
|
47
|
+
| `list_licenses` | All licenses (IDs for the search filter). |
|
|
48
|
+
| `get_author` | Author profile by ID, handle or profile URL. |
|
|
49
|
+
| `list_author_models` | An author's models, cursor-paginated, ordered by newest, oldest, likes, downloads, rating or makes. |
|
|
50
|
+
|
|
51
|
+
Models and authors can be passed as IDs or as printables.com URLs (locale prefixes such as `/de/` are fine).
|
|
52
|
+
|
|
53
|
+
## Limitations
|
|
54
|
+
|
|
55
|
+
- Anonymous access only: paid and Printables Club models are visible, but their files cannot be downloaded.
|
|
56
|
+
- The API is undocumented and may change without notice; the end-to-end suite (`just test-live`) checks it against the live service.
|
|
57
|
+
|
|
58
|
+
## Development
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
uv sync
|
|
62
|
+
just lint # ruff format/check + ty
|
|
63
|
+
just test # pytest with 100% branch coverage
|
|
64
|
+
just test-live # end-to-end tests against the real API
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## License
|
|
68
|
+
|
|
69
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# printables-mcp
|
|
2
|
+
|
|
3
|
+
[English](README.md) · **Русский**
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/printables-mcp/)
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
[](https://github.com/feodor-ra/printables-mcp/actions/workflows/ci.yml)
|
|
9
|
+
|
|
10
|
+
**[MCP](https://modelcontextprotocol.io)-сервер для [Printables.com](https://www.printables.com).** Через него ИИ-агент ищет 3D-модели, читает их карточки, смотрит список файлов и получает прямые ссылки на скачивание.
|
|
11
|
+
|
|
12
|
+
Сервер обращается напрямую к публичному GraphQL API Printables: без парсинга HTML, без браузера и без аккаунта. Все инструменты только читают данные и возвращают структурированный результат (JSON со схемой вывода), поэтому агент получает точные поля, а не текст.
|
|
13
|
+
|
|
14
|
+
## Установка
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uv tool install printables-mcp
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Затем подключите команду `printables-mcp` в MCP-клиенте. Claude Code:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
claude mcp add printables -s user -- printables-mcp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Любой другой клиент с транспортом stdio:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"mcpServers": {
|
|
31
|
+
"printables": { "command": "printables-mcp" }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Без установки: `uvx printables-mcp`.
|
|
37
|
+
|
|
38
|
+
## Инструменты
|
|
39
|
+
|
|
40
|
+
| Инструмент | Что делает |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `search_models` | Поиск по тексту с пагинацией (`limit`, `offset`, общее число результатов) и фильтрами: категория, цена (`free` / `paid`), лицензии, опубликовано за N дней, есть фото печати. Сортировки: по релевантности, популярности, новизне, рейтингу, числу печатей. |
|
|
43
|
+
| `get_model` | Полная карточка модели: описание в Markdown, теги, путь категории, лицензия, материалы, принтер, время печати, вес филамента, фото, PDF, источники ремикса, статистика, цена. |
|
|
44
|
+
| `list_model_files` | Все группы файлов одним запросом: файлы модели (STL/3MF/STEP…), G-code с принтером, материалом, высотой слоя, соплом и временем печати, файлы SLA, прочие файлы, ZIP-архивы. |
|
|
45
|
+
| `get_download_links` | Прямые ссылки на скачивание (живут 24 часа) для всех или выбранных файлов, запрашиваются параллельно. Для файлов платных моделей и моделей Printables Club по каждому файлу приходит причина отказа, остальные ссылки при этом выдаются. |
|
|
46
|
+
| `list_categories` | Все категории с полным путём (ID для фильтра поиска). |
|
|
47
|
+
| `list_licenses` | Все лицензии (ID для фильтра поиска). |
|
|
48
|
+
| `get_author` | Профиль автора по ID, нику или ссылке на профиль. |
|
|
49
|
+
| `list_author_models` | Модели автора постранично (курсор), сортировка: новые, старые, по лайкам, скачиваниям, рейтингу или числу печатей. |
|
|
50
|
+
|
|
51
|
+
Модели и авторов можно передавать по ID или ссылкой на printables.com (префиксы локали вроде `/de/` поддерживаются).
|
|
52
|
+
|
|
53
|
+
## Ограничения
|
|
54
|
+
|
|
55
|
+
- Доступ только анонимный: платные модели и модели Printables Club видны, но их файлы скачать нельзя.
|
|
56
|
+
- API не документирован и может поменяться без предупреждения. Сквозные тесты (`just test-live`) проверяют его на живом сервисе.
|
|
57
|
+
|
|
58
|
+
## Разработка
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
uv sync
|
|
62
|
+
just lint # ruff format/check + ty
|
|
63
|
+
just test # pytest со 100% покрытием веток
|
|
64
|
+
just test-live # сквозные тесты против реального API
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Лицензия
|
|
68
|
+
|
|
69
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "printables-mcp"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "MCP server for Printables.com: search 3D models, read model cards, list files and get download links"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Fedor Ratschew", email = "feodor.ra@me.com" },
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
keywords = ["mcp", "model-context-protocol", "printables", "3d-printing", "llm"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
23
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"anyio>=4.9",
|
|
28
|
+
"httpx2>=2.10",
|
|
29
|
+
"markdownify>=1.2",
|
|
30
|
+
"mcp>=2.3,<3",
|
|
31
|
+
"pydantic>=2.12",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
printables-mcp = "printables_mcp.server:main"
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://github.com/feodor-ra/printables-mcp"
|
|
39
|
+
Repository = "https://github.com/feodor-ra/printables-mcp"
|
|
40
|
+
Issues = "https://github.com/feodor-ra/printables-mcp/issues"
|
|
41
|
+
|
|
42
|
+
[build-system]
|
|
43
|
+
requires = ["hatchling>=1.27"]
|
|
44
|
+
build-backend = "hatchling.build"
|
|
45
|
+
|
|
46
|
+
[tool.ruff]
|
|
47
|
+
line-length = 120
|
|
48
|
+
preview = true
|
|
49
|
+
|
|
50
|
+
[tool.ruff.format]
|
|
51
|
+
quote-style = "double"
|
|
52
|
+
|
|
53
|
+
[tool.ruff.lint]
|
|
54
|
+
select = ["ALL"]
|
|
55
|
+
ignore = [
|
|
56
|
+
"CPY", # flake8-copyright: no per-file copyright notices
|
|
57
|
+
"missing-trailing-comma", # COM812: conflicts with the formatter
|
|
58
|
+
"relative-imports", # TID252: relative imports inside the package are welcome
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint.pydocstyle]
|
|
62
|
+
convention = "google"
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint.per-file-ignores]
|
|
65
|
+
"tests/**" = [
|
|
66
|
+
"assert", # S101: pytest is built on assert
|
|
67
|
+
"implicit-namespace-package", # INP001: the tests directory is not a package
|
|
68
|
+
"magic-value-comparison", # PLR2004: comparing to literal counts is normal in tests
|
|
69
|
+
"import-private-name", # PLC2701: tests exercise the package's private internals
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
[tool.ty.rules]
|
|
73
|
+
all = "error"
|
|
74
|
+
|
|
75
|
+
[tool.pytest.ini_options]
|
|
76
|
+
testpaths = ["tests"]
|
|
77
|
+
addopts = "--strict-config --strict-markers -m 'not live'"
|
|
78
|
+
markers = ["live: hits the real Printables API (run with `-m live`)"]
|
|
79
|
+
|
|
80
|
+
[tool.coverage.run]
|
|
81
|
+
source = ["printables_mcp"]
|
|
82
|
+
branch = true
|
|
83
|
+
|
|
84
|
+
[tool.coverage.report]
|
|
85
|
+
show_missing = true
|
|
86
|
+
fail_under = 100
|
|
87
|
+
|
|
88
|
+
[dependency-groups]
|
|
89
|
+
dev = [
|
|
90
|
+
"anyio~=4.15.1",
|
|
91
|
+
"pytest~=9.2.0",
|
|
92
|
+
"pytest-cov~=7.1.0",
|
|
93
|
+
"ruff==0.17.3",
|
|
94
|
+
"ty==0.0.95",
|
|
95
|
+
]
|