mcp/download/
[!IMPORTANT] Unofficial. This project is a community effort and is not affiliated with, endorsed by, or supported by GS1 in any way. "GS1" and "My Product Manager" are the property of their respective owners. Use at your own risk.
The My Product Manager Download MCP server — a standalone Python package (FastMCP)
exposing only the recipient-role half of the API. It is one of two independent
successors to the deprecated combined server (see
../combined/README.md for the migration guide); its sibling
is ../upload/. A data-recipient integrator installs this package alone
and its agent never sees a destructive write tool (upsert_and_await_validation) in
its tool list — see issue
#82.
Unlike its Upload sibling, this server builds no tools from an OpenAPI spec — the
raw GET /tradeitems is entirely replaced by the hand-written search_trade_items
below, so there is nothing for FastMCP.from_openapi() to generate here (#75). It is
a plain FastMCP server with one tool.
The 1-tool recipient-role surface
| Tool | Origin | Behavior |
|---|---|---|
search_trade_items(filters..., cursor?, limit?) |
hand-written | single page + nextCursor — never auto-paginates |
The raw GET /tradeitems HAL envelope (_links/_embedded) is replaced by
search_trade_items's flat {tradeItems, nextCursor} shape rather than a literal
rename, so the pagination token is a field an agent can read directly instead of
something it has to parse out of a URL.
The sharp edge this surface removes
The list read can flood the context window. A broad filter can match up to 1000
items server-side (docs/research/gs1-api-facts.md §4). search_trade_items never
auto-follows HAL's _links.next — it returns one bounded page plus an opaque
nextCursor the agent can choose to follow. Non-2xx responses (401/403/404) also
surface as a structured ToolError ({title, status, detail}) instead of an opaque
raise_for_status() string.
Auth — the Python leg of the two-layer design
The server's httpx.AsyncClient carries one Gs1BeluAuth (httpx.Auth), vendored
byte-identically from ../shared/ (map #76 — the same class the
Upload server uses, so a fix to this security-critical path is made once, not in two
hand-maintained copies that can drift):
- OAuth2 client-credentials Bearer token — fetched from the environment's token
endpoint, cached in memory, and proactively refreshed on a skew margin (default 60s)
before the token's runtime
expires_inelapses. Concurrent callers coalesce onto a single fetch via double-checked locking. Ocp-Apim-Subscription-Keyheader — stamped on every request, header only; the spec's query-param alternative is never used, so the key never lands in a URL or log.
Credentials are single-tenant, loaded from environment variables, one credential set
for this process (never GS1BELU_UPLOAD_* — that's the sibling server's role):
| Variable | Required | Meaning |
|---|---|---|
GS1BELU_ENVIRONMENT |
yes | uat or prod — the only knob; it derives the API host, token host, and OAuth audience (trailing slash baked in). There is no raw base-URL setting anywhere. |
GS1BELU_API_VERSION |
no (default v17) |
the contract version segment in the API path. |
GS1BELU_DOWNLOAD_CLIENT_ID / _CLIENT_SECRET / _SUBSCRIPTION_KEY |
yes | Download API credential set. |
A RateLimiter (sliding window, 10 req/s), also vendored from ../shared/, is
attached to the same client alongside the auth and structured-error hooks — this
process's own limiter, never shared with the Upload server (#77).
Sunset monitoring
GS1 can announce an API-version retirement in-band via the Sunset header (RFC
8594) — the Download manual documents monitoring it as best practice.
warn_on_sunset (vendored from ../shared/) is a response hook that logs a WARNING
through logging.getLogger("gs1belu_mpm_download._shared.sunset") whenever a response
carries the header. It never alters, retries, or fails a request — observation only.
Quickstart
No clone, no build. Point an MCP-capable client at uvx gs1belu-mpm-download-mcp
with the documented env vars, e.g. in .mcp.json:
{
"mcpServers": {
"gs1belu-mpm-download": {
"command": "uvx",
"args": ["gs1belu-mpm-download-mcp"],
"env": {
"GS1BELU_ENVIRONMENT": "uat",
"GS1BELU_DOWNLOAD_CLIENT_ID": "...",
"GS1BELU_DOWNLOAD_CLIENT_SECRET": "...",
"GS1BELU_DOWNLOAD_SUBSCRIPTION_KEY": "..."
}
}
}
}
Running locally (for development)
export GS1BELU_ENVIRONMENT=uat
export GS1BELU_DOWNLOAD_CLIENT_ID=... GS1BELU_DOWNLOAD_CLIENT_SECRET=... GS1BELU_DOWNLOAD_SUBSCRIPTION_KEY=...
just run-mcp-download # launches the standalone Download server over stdio
Or point an MCP-capable client at
uv run --project mcp/download gs1belu-mpm-download-mcp with the same environment
variables set.
Testing
just test-mcp drives build_download_server() through FastMCP's in-memory
Client, with a fake httpx transport and a mocked token endpoint injected under
the server's AsyncClient — no network, no live GS1, no subprocess. See
tests/:
test_tool_surface.py— pins the exact 1-tool list; the rawget-tradeitemsand any Upload-role tool are absent.test_search.py— one page +nextCursor, no auto-pagination, a follow-up call with the cursor advances.test_config.py— loads onlyGS1BELU_DOWNLOAD_*; a strayGS1BELU_UPLOAD_*value is never read.
Auth/rate-limit/sunset/error-mapping behavior is verified once, directly against
../shared/'s own test suite — not duplicated here (#79's Testing Decisions).
Layout
pyproject.toml—gs1belu-mpm-download-mcp, thegs1belu-mpm-download-mcpconsole entry point, the custom build hook that vendors../shared/. No spec bundling (#75) — this package's wheel carries no schema artifact.hatch_build.py— build hook: copies../shared/src/_shared/intosrc/gs1belu_mpm_download/_shared/(map #76). Never hand-edit it — it's a git-ignored build output this hook (re)populates.src/gs1belu_mpm_download/config.py— env-var credential loading for this role only, built on the sharedparse_environment()+CredentialSet.from_env()seam.search.py—search_trade_itemsas a plain, independently testable async function.server.py— assembles the server (build_download_server()): a plainFastMCPcarrying the one hand-written tool.cli.py— thegs1belu-mpm-download-mcpconsole entry point._shared/— vendored copy of../shared/src/_shared/; git-ignored, regenerated by the build hook.
tests/— the in-memoryClient+ fake-transport suite described above.
Publishing
Release-and-publish machinery — server.json, the mcp-name README marker above,
publish-mcp.yml's PyPI + MCP-registry OIDC publish (parameterized across all three
mcp* packages, #82), and the release-please mcp-download component/tag
(mcp-download-v*) — is configured per
#53 and
#82. See
docs/release-execution-checklist.md for
the human-run, prerequisite-gated steps below the first live publish.
Release files for gs1belu-mpm-download-mcp 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gs1belu_mpm_download_mcp-0.2.0.tar.gz | 15.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gs1belu_mpm_download_mcp-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 33.1 kB
Release files / gs1belu_mpm_download_mcp-0.2.0.tar.gz
| Download URL | gs1belu_mpm_download_mcp-0.2.0.tar.gz |
|---|---|
| Size | 15.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
11c2a13abbb16d0864a55244e833d94074e6072678742ef77fc485828492727b
|
|
BLAKE2b-256 checksum How to use checksums |
c55b356a65d970e46f84c762aa94d3f59557ff464492b08559c3eec8bcce37e4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 27, 2026.
Transparency logRelease files / gs1belu_mpm_download_mcp-0.2.0-py3-none-any.whl
| Download URL | gs1belu_mpm_download_mcp-0.2.0-py3-none-any.whl |
|---|---|
| Size | 17.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
31ccf61e836d590d9fd32ae8a96e64efdb1ab03d11da6ad3c203cad59e6d6d5c
|
|
BLAKE2b-256 checksum How to use checksums |
2498e06f233e764fee36c375d46b4fd370f1f7c8960e63cc303961717cfccf4e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 27, 2026.
Transparency log