iPage code as an undocumented API: Discord bot, MCP server, skill
The realization
After all the catalog work this week, packages/ipage/src/ is effectively a reverse-engineered private Ingram API. Discrete operations we now have packaged that nobody else does:
auto_login.login(user, pass, totp) -> session cookie (Okta TOTP)
session_util.make_session(cookie) -> authenticated requests session
download_csv.run(env, target) -> CSV bytes for any list/search
download_csv.parse(bytes) -> structured rows
pipeline.fetch_product_detail_async(c, isbn) -> BISAC, stock, dims, series, price
pipeline.fetch_cover_async(c, isbn) -> cover bytes
pipeline.run_ingest(env, target) -> discover + enrich + upsert
pipeline.run_enrich(env, isbns) -> enrich + upsert by ISBN
pipeline._lookup_existing_covers(...) -> queue-DB inventory check
Plus the queue DB itself is a rich data source (current stock, sync history, content hashes, sync_status).
Four exposure surfaces, ranked by build order
1. Discord bot — concrete, immediate operator value
Carrie’s daily flow (“does Ingram have X? when can we get it? how many left?”) maps directly to bot commands. Smallest useful set:
/stock <isbn-or-title> live per-DC on_hand + on_order, with verdict
/lookup <isbn> full detail card (cover, BISAC, price, stock, pub date)
/orderable <isbn> yes/no + reasoning
/whats-new <list-name> diff: what's in this Ingram list that we don't carry
/ingest <list-url> trigger ingest, post progress + summary in channel
First three are 80% of value. Pull out phone at register → ask bot → answer in seconds.
Open questions for build:
- Discord bot framework choice (discord.py, py-cord, or hikari)
- Where it lives: separate repo,
packages/bot/inside catalog, or its own thing - Hosting: Railway worker, same as catalog? Run-on-demand vs always-on
- Auth: which servers/users can run commands?
/ingestis destructive - Resolving “title” search to ISBN — needs an iPage search-by-title call we haven’t packaged yet
2. MCP server — strategic, unlocks AI agent workflows
Same operations exposed as MCP tools, callable from Claude (or any MCP-compatible client). Single-call usefulness is medium; the unlock is composition:
“What’s new on BookTok-May that we don’t carry, sorted by stock availability at our primary DC?”
becomes: download CSV from BookTok-May → cross-ref against queue DB → fetch live stock for each → rank. With MCP tools, an agent does this end-to-end without humans gluing scripts.
Tool surface to start:
ipage.book(isbn) live detail
ipage.stock(isbn) just the stock dict (cheap, no full parse)
ipage.list(target) ISBNs + basic metadata from any iPage list
catalog.queue(filter?) query queue DB
catalog.diff_list(target) "what's in this list vs our store"
medusa.product(isbn) cross-ref what we actually carry
medusa.pending() what's awaiting human import decision
Cost: ~200 lines wrapping existing Python functions.
3. Skill for the back-office agent
When/if there’s a Claude-Code-style agent running back-office tasks, the skill encodes domain rules on top of the MCP substrate:
“When evaluating a book for inclusion, prefer titles with positive on_order at primary DC. ACOTAR preorders are an exception — always include even if stock is zero.”
“Romantasy books with > 1000 on-order across the network and a release date within 90 days are auto-flagged for the store.”
Built on the MCP tools. Skill is configuration; MCP is substrate.
4. HTTP API — only build if forced
Both Discord bot and MCP can consume the Python directly. Standalone REST is only useful if a non-Python consumer demands it (e.g., a Next.js storefront wanting a “check stock now” button that hits iPage live without proxying through Medusa). Build it then; don’t preemptively.
Build order
- Discord bot first. 1-2 days. Validates which queries actually matter weekly. Surfaces the operation set that’s worth canonicalizing.
- MCP server second. 1 day, mostly mechanical once the bot has proven what operations to wrap.
- Skill on top of MCP when a back-office agent is actually running.
- HTTP API only if a non-Python consumer demands it.
Operations we’d need to add to support these surfaces
Current package only handles ISBN-known operations. The Discord bot specifically needs:
- Search-by-title. iPage supports title search (the search box on the dashboard). We haven’t packaged it. Probably
searchBy=Title_Keyword&searchTerm=<query>againstsavedSearches.actionor similar. Need to probe. - Search-by-author. Same shape, probably
searchBy=Contributor. - Live stock-only fetch. Currently
fetch_product_detail_asyncreturns everything; a stock-only version would be cheaper if we’re doing it from a bot every few seconds.
Cross-link
See medusa-inventory — the inventory model question matters here because /stock and /orderable commands should reflect what Medusa actually shows on the storefront, not just raw iPage numbers.
See indie-vault (TBD) — Indie Vault list URL would be one of the first targets fed to /whats-new / /ingest.