silpo-py-mcp

Typed Python client for the official Silpo MCP server
(https://mcp.silpo.ua/mcp).
Built on FastMCP 3.4.7 for the Silpo AI Factory
hackathon. One library, two modes:
- Real server — Streamable HTTP transport with OAuth 2.1 + PKCE, encrypted
on-disk token storage, 40 typed methods mirroring the documented
silpo_*tools. - In-memory mock — a FastMCP server that implements the same 40 tools with realistic fixtures, so you can develop and test without a Silpo account.
Requires Python 3.12+.
Install
pip install silpo-py-mcp
# or with uv
uv add silpo-py-mcp
# local development
uv sync
Quick start (mock — no auth needed)
import asyncio
from silpo_py_mcp import SilpoClient
async def main() -> None:
async with SilpoClient.for_mock() as client:
result = await client.get_products(
"bran-1",
"DeliveryHome",
"2026-09-06T10:00:00+03:00",
"2026-09-06T11:00:00+03:00",
category="Молочні продукти",
)
for product in result.items:
print(product.title, product.price)
cart = await client.get_cart()
await client.add_or_update_cart_products(
cart.cart_id,
[
{
"productId": product.product_id,
"companyId": product.company_id,
"branchId": product.branch_id,
"quantity": 2,
}
for product in result.items
],
)
full = await client.get_cart_by_id(cart.cart_id)
print("Total:", full.totals.total_price)
asyncio.run(main())
Quick start (real server)
The first connection opens a browser for login at auth.silpo.ua
(phone + OTP or password). Tokens are encrypted and stored on disk, and the
client refreshes them automatically.
import asyncio
from silpo_py_mcp import SilpoClient
async def main() -> None:
async with SilpoClient.for_real_server() as client:
tools = await client.list_tools()
print(f"Connected. {len(tools)} tools available.")
branches = await client.call_tool("silpo_list_branches", {"limit": 1})
print("Branch:", branches["branches"][0]["address"])
asyncio.run(main())
Note on typed methods vs the real server. The typed methods and the mock mirror the live
tools/listschemas and response shapes (verified Sep 2026; re-verified Sep 7 2026; reconciled with server release-1.110.0 on Sep 10 2026; reconciled with server release-1.110.1 on Sep 11 2026; re-verified Sep 14 2026 — still 40 tools, no renames, no required-set changes). Context arguments such asbranchId/deliveryType/timeslotStart/timeslotEndare required where the live schema requires them — since 1.110.1 this includessilpo_get_similar_products— cart tools takeshoppingCartId, andsilpo_add_or_update_cart_productstakesproducts([{productId, companyId, branchId, quantity, addQuantity?, comment?}]— omitted/falseaddQuantityreplaces the quantity,trueadds to it).silpo_add_or_update_certificatestakescertificatesToAdd/certificatesToRemoveas[{barcode, pincode?}]objects (plain barcode strings are converted automatically). Product results carrydisplayPrice(fromPrice/toPricefilter by it, not byprice);get_product_detailscarriesdisplayPrice/image/specialPrices/externalProductIdplushasOfferAtBranch(release-1.110.0:price/displayPrice/stock/availableare the requested branch's real offer — checkhas_offer_at_branchfirst,Falsemeans no real offer at that branch); batch empty entries are skipped (meta.droppedCount→BatchProductResult.dropped_count). Coupon eligibility comes fromcanBeAppliedToOrderonsilpo_get_coupon_details— never infer it fromactive/statealone. The mock cart tools (silpo_get_shopping_cart_by_id,silpo_clear_shopping_cart, ...) accept onlyshoppingCartId— the legacycartIdalias was removed in 0.3.1 to match the live schema.call_toolalways passes arguments through verbatim for one-off calls. Responses come back JSON-like (nested FastMCPRootdataclasses are unwrapped automatically).
Smoke test against the real server
examples/real_smoke.py verifies the live contract and runs a read-only
battery of calls:
uv run examples/real_smoke.py
On the first run a browser opens for login at auth.silpo.ua; afterwards the
encrypted token in ~/.silpo_py_mcp is reused. The script checks:
- live
tools/listmatches the 40 documented tools and prints every live signature (arg names/types), - a read-only battery of
call_toolcalls built from the live schemas (branches, address, delivery types, time slots, categories tree, promotions, products, profile, favorites, loyalty, coupons, orders, promos, certificates).
Failures are reported per check without aborting — server-side schema bugs and
drift between the real server and the mock show up as ✗ lines. Exits
non-zero if the tool-name contract is violated or a battery call fails.
Known server-side quirks (re-verified live, Sep 2026 — mitigated in examples/real_smoke.py)
| Tool | Symptom | Mitigation |
|---|---|---|
silpo_get_products |
400 Bad Request on plain limit without filter |
smoke uses category or set: klatsniznyzhky |
silpo_get_time_slots |
-32602 for deliveryTypes: ["B2B"] |
smoke filters B2B from get_available_delivery_types |
silpo_get_my_favorites |
Cannot read properties of null (reading 'id') — a corrupted favorites entry on the server side. The typed get_favorites() raises SilpoToolExecutionError; it is not a client/model drift. |
smoke treats it as skipped; until Silpo fixes it, wrap get_favorites() in try/except SilpoToolExecutionError or use call_tool("silpo_get_my_favorites", ...) and handle the failure |
silpo_get_product_details |
slug: null chain failure |
resolved once get_products returns real slugs |
Previously reported quirks that no longer reproduce (re-verified live, Sep 2026):
silpo_get_category no longer triggers the fastmcp id rejection (it validates
cleanly), and silpo_get_my_certificates — although still intermittently returning
HTTP 500 — now responds with a normal certificates envelope (unwrapped by the
client) that validates cleanly when it does respond.
Configuration
Configuration is read from environment variables (prefix SILPO_) or a .env
file. Key settings:
| Variable | Default | Description |
|---|---|---|
SILPO_MCP_URL |
https://mcp.silpo.ua/mcp |
Server endpoint |
SILPO_OAUTH_STORAGE_DIR |
~/.silpo_py_mcp |
Encrypted token store location |
SILPO_OAUTH_ENCRYPTION_KEY |
auto-generated | Fernet key (base64) |
SILPO_OAUTH_CLIENT_NAME |
silpo-py-mcp |
Client name for OAuth registration |
SILPO_OAUTH_TOKEN_ENDPOINT_AUTH_METHOD |
none |
DCR auth method: none (public client + PKCE, default), client_secret_post, client_secret_basic |
SILPO_OAUTH_CALLBACK_TIMEOUT |
300.0 |
Seconds to wait for the browser callback |
SILPO_DEFAULT_REQUEST_TIMEOUT |
30.0 |
Per-request timeout |
SILPO_MAX_RATE_LIMIT_RETRIES |
3 |
Retries on HTTP 429 |
Programmatic overrides are supported via SilpoSettings(...) or
SilpoClient.from_fastmcp(client, mcp_url=...).
Schema-driven by design
The exact tool schemas (arguments, JSON Schema) are only known from
tools/list after authentication, per the official docs.
SilpoClient therefore exposes:
list_tools()— the live schemas from the server.call_tool(name, arguments)— pass-through calls with typed error mapping.- Typed convenience methods — wrappers over the live tool schemas
(
get_products,get_cart_by_id,add_or_update_cart_products, ...).
If Silpo renames or reshapes tools, only the affected convenience method needs
updating; call_tool keeps working.
Error handling
silpo_py_mcp.exceptions maps Silpo's documented error responses:
| Server response | Raised |
|---|---|
401 invalid_token |
SilpoAuthError |
403 |
SilpoForbiddenError |
429 (rate limit) |
SilpoRateLimitError |
-32601 method not found |
SilpoToolNotFoundError |
| Other tool failures | SilpoToolExecutionError |
| Schema mismatch / bad response | SilpoValidationError |
| Connection / protocol failures | SilpoConnectionError |
Development
uv sync # install deps
uv run pytest # run tests (all against the in-memory mock)
uv run ruff format . # format
uv run ruff check . # lint
uv run pyrefly check # type check (strict)
uv run pre-commit install # install git hooks (format/lint/type/tests)
Project layout
src/silpo_py_mcp/
├── client.py # SilpoClient — typed methods + error mapping
├── mock_server.py # SilpoMockServer — in-memory FastMCP server (40 tools)
├── auth.py # OAuth 2.1 + PKCE helper, encrypted token storage
├── config.py # pydantic-settings configuration
├── exceptions.py # typed exceptions
└── models/ # Pydantic models (product, cart, branch, category, order)
License
MIT
Release files for silpo-py-mcp 0.5.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| silpo_py_mcp-0.5.2.tar.gz | 37.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| silpo_py_mcp-0.5.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 81.2 kB
Release files / silpo_py_mcp-0.5.2.tar.gz
| Download URL | silpo_py_mcp-0.5.2.tar.gz |
|---|---|
| Size | 37.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e0cada1e53594753b1345ce1d01305c1d3ec04989c0d83f61c416ff8037e83c1
|
|
BLAKE2b-256 checksum How to use checksums |
a16f00e6b3c7961efacc964ff431e5c9f2060326291ee2a75b8789c288388636
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / silpo_py_mcp-0.5.2-py3-none-any.whl
| Download URL | silpo_py_mcp-0.5.2-py3-none-any.whl |
|---|---|
| Size | 43.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c2500f74cada855f76fc19e92f40dba454691d9ddda06ee6ec8e5cfca8a2d609
|
|
BLAKE2b-256 checksum How to use checksums |
27468103358f65f7513392135bf626b6519f36a76271fab316ee42521b28996c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|