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? /ingest is 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

  1. Discord bot first. 1-2 days. Validates which queries actually matter weekly. Surfaces the operation set that’s worth canonicalizing.
  2. MCP server second. 1 day, mostly mechanical once the bot has proven what operations to wrap.
  3. Skill on top of MCP when a back-office agent is actually running.
  4. 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> against savedSearches.action or similar. Need to probe.
  • Search-by-author. Same shape, probably searchBy=Contributor.
  • Live stock-only fetch. Currently fetch_product_detail_async returns everything; a stock-only version would be cheaper if we’re doing it from a bot every few seconds.

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.