Telekom Agentic Commerce Demo Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Telekom Agentic Commerce Demo ServerI need a mobile plan with 30 GB and an iPhone, please."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Telekom agentic commerce — demo MCP server
A runnable MCP server that turns the static prototype into a real journey you can drive by talking to Claude, Gemini or ChatGPT.
Nothing here touches a Telekom system. Every tool answers from the markdown files
in catalogue/, so the offer can be changed by editing a file and restarting —
no code, no deployment, no engineer.
server.py the whole server: 9 tools, 3 prompts, 8 UI widgets
catalogue/tariffs.md 11 tariffs across mobile, fixed and TV/OTT
catalogue/devices.md 6 devices, priced per tariff
catalogue/consents.md TKG 54, AGB, Widerruf, card mandate, and per-line-type extrasRun it
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python server.py # stdio, for a local desktop client
python server.py --http --port 8000 # remote, for a custom connector
python server.py --http --no-mcp-ui # ...without the in-band ui:// widgetThe HTTP endpoint is http://127.0.0.1:8000/mcp.
To reach it from a hosted assistant temporarily, put a tunnel in front:
npx cloudflared tunnel --url http://localhost:8000Use the https://….trycloudflare.com/mcp URL it prints. This is fine for a live
demo but the URL is random and dies when your machine or the tunnel process stops.
Related MCP server: Nexlink Telecom MCP Server
Deploy for real (Render)
Cart state lives in memory in a single Python process (see CARTS in
server.py), so this needs one persistent instance, not an autoscaled or
serverless one. Render's free web service tier fits: one always-running
container, a stable HTTPS domain, no card required.
This repo already includes render.yaml and .python-version (mcp>=2.2.0
requires Python 3.10+, pinned here to 3.11.9).
Push this folder to a new GitHub repo.
On render.com, New + → Blueprint, connect the repo. Render reads
render.yamland creates the web service automatically (build:pip install -r requirements.txt; start:python server.py --http --host 0.0.0.0 --port $PORT).No
render.yaml/Blueprint access? Use New + → Web Service instead and paste the same build/start commands by hand.
Deploy. Render gives you
https://<service-name>.onrender.com.Your MCP endpoint is
https://<service-name>.onrender.com/mcp.
Verify it's alive:
curl -i https://<service-name>.onrender.com/mcp(A 4xx/406 response is expected for a plain GET — the endpoint only speaks JSON-RPC over POST. Anything other than a connection error or 502 means the process is up.)
Then connect it: in Claude, Settings → Connectors → Add custom connector,
paste that /mcp URL.
Free-tier caveats: the service spins down after ~15 minutes idle and takes
30–60s to wake on the next request (the first tool call in a new session may
time out or feel slow — retry it). Every restart or redeploy also wipes
in-memory carts, which is expected for this demo. If you need it always warm,
upgrade the Render service to a paid instance (still the same render.yaml).
Connect it
Claude — Settings → Connectors → Add custom connector → paste the /mcp URL.
All nine tools and the three prompt templates work. The MCP Apps widget does
not render (see "Where the UI renders" below); the priced tables come through
as display_markdown instead.
ChatGPT — Settings → Connectors → Create, same URL. Same position as Claude: tools work, widget rendering depends on Apps SDK support in your workspace.
Gemini — add as an MCP server in the Gemini CLI or any UCP/MCP-capable client.
Any client — npx @mcpjam/inspector@latest and point it at the URL. Best way
to watch the raw JSON-RPC while you demo, and the easiest place to see a widget
actually painted.
Where the UI renders
The server emits its widgets two different ways, because no single mechanism renders everywhere today:
Mechanism | How it travels | Renders in |
MCP Apps (SEP-2133) |
| Nothing yet — the |
MCP-UI (in-band) | an | MCP-UI-aware clients (MCPJam inspector, Goose, custom web chat) |
| a plain field in every tool result | Everywhere, including voice |
The in-band copy is a complete, standalone HTML document with that call's data
already baked in, so it needs no postMessage bridge from the host. Disable it
with --no-mcp-ui or TELEKOM_MCP_UI=0 if a client dumps the raw HTML into the
transcript instead of rendering it.
Verify what a client is actually getting:
curl -s -X POST https://<service>.onrender.com/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-H 'mcp-session-id: <id from initialize>' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search_tariffs","arguments":{"line_type":"mobile"}}}'The display contract
Every tool returns two extra fields, and they are what keeps pricing under Telekom's control rather than the model's:
display_markdown— the priced table as Telekom words it, rendered server-side, including the minimum-term and post-term footnotes.display_note— what the model should do with it, decided per request: when the client negotiated MCP Apps, "the widget shows this, don't restate prices"; otherwise "showdisplay_markdownverbatim".
Tool descriptions and the server instructions carry the price-integrity rules: prices are euro cents and authoritative as returned, never summed or rounded by the model; MagentaEINS is a conditional saving, never an applied one; a payment token is an authorisation, not a completed payment.
Voice surfaces get *_spoken variants (monthly_spoken, monthly_total_spoken)
so "€39.95" is read as "39 euro 95" rather than digit by digit.
Driving the demo
Three prompt templates seed a journey. Pick one from the client's prompt menu:
Prompt | What it opens |
| Tariff, handset, cart, checkout, consents, order |
| DSL and fibre, including the installation-appointment consent |
| MagentaTV and MagentaSport, including the age confirmation |
Or just type: "I need a Telekom mobile plan with about 30 GB and an iPhone."
Expected tool sequence:
search_tariffs → get_tariff_details → search_devices → get_device_details
→ create_cart → update_cart (name, DOB, address, phone)
→ get_payment_methods (tokenise=true) → get_required_consents (grant=[...])
→ complete_orderWhat to point at while presenting
The presentation is ours, on every surface. Price and product reach the
customer as Telekom authored them, not as a language model's paraphrase — as a
rendered widget where the client supports one, and as server-rendered
display_markdown everywhere else. The model is told which, per request.
It degrades honestly. SEP-2133 requires a UI-bound tool to still be useful
without the extension. respond() in server.py branches on
client_supports_apps(): widget present, suppress prose; no widget, print the
server's own markdown. The failure mode this avoids is the demo telling the
model "the widget already shows the price" when nothing is on screen — which
silently hides pricing.
Cards only, and the server says why. get_payment_methods returns SEPA as
unsupported with the reason attached: a card credential can be tokenised and
delegated to an agent under the AP2 mandate model, but a SEPA mandate is an
authorisation granted to the creditor and cannot travel through an agent. The token
it issues is scoped to merchant, cart and a maximum amount, and expires in 15 minutes.
Consents are gated server-side, not by prompt. complete_order refuses with a
list of blockers until the cart has a tariff, all four customer fields, a payment
token and every mandatory consent. Try asking the model to skip them — it cannot.
The order confirmation carries a QR code generated at order time, encoding the MeinMagenta download URL with the order ID appended, to move the customer from the agent surface into our own app.
Guard rails worth demonstrating
Try this | What happens |
Order before granting consents | Refused, with each missing consent named |
Give a date of birth under 18 | Refused — contract capacity check |
Send a full card number | Refused — the tool accepts at most four digits |
Combine a device with a fixed-line tariff | Refused — not device-eligible |
Ask for a price the model invents | The widget shows the server's price; the tool result tells the model not to restate it |
Changing the offer
Edit catalogue/tariffs.md and restart. Every block under a ## ID heading becomes
one tariff; fields are key: value; prices are in euro cents. The same applies to
devices and consents. The three files are also exposed as MCP resources, so the
assistant can read the raw catalogue if asked.
What this demo deliberately does not do
No OAuth. Every session is anonymous, so there is no customer context, no eligibility check against a real account, and no upgrade window.
No handoff to OneShop. The order completes in-session to show the full flow; a production build would hand off a signed, single-use checkout URL at the consent step instead.
No ACP or UCP checkout endpoints. The tool layer is deliberately protocol-neutral; adapters would sit at the edge.
Prices, availability and device line-up are illustrative.
This server cannot be deployed
Maintenance
Related MCP Connectors
Give AI agents a phone layer for consent-based calls, transcripts, summaries, and outcomes.
Build and manage AI-native customer support agents from Claude or any MCP client.
WhatsApp AI sales agent on your own number via QR, no Meta API: audit chats, edit playbook, orders.
Agent-native commerce with trusted catalog, durable carts, and Stripe Checkout via MCP and UCP.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to discover products, build carts, and complete purchases across multiple downstream commerce services through a secure, contract-driven API.-
- FlicenseNot gradedqualityBmaintenanceEnables natural language interaction with telecom network operations data via MCP, providing read-only insights and role-gated write operations such as bandwidth upgrades, with progress tracking and human-in-the-loop elicitation.-
- AlicenseNot gradedqualityBmaintenanceEnables product discovery, session-scoped carts, and confirmation-gated checkout through natural language, with deterministic routing and safety features.9 npmISC
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to provide e-commerce customer service with product search, RAG-based FAQ answers, human handoff, and automated security auditing with a review workflow.-