ArchitectureIntelligence MCP Exposure — Roadmap

Intelligence MCP Exposure — Roadmap

Auth (2026-08-20). Les cles bei_ sont desormais verifiees sur les DEUX surfaces — MCP et REST: via un resolveur unique. Voir « Ce qui tourne reellement » sous Quotas. Status (2026-05-22, numerotation corrigee le…

Auth (2026-08-20). Les cles bei_ sont desormais verifiees sur les DEUX surfaces — MCP et REST: via un resolveur unique. Voir « Ce qui tourne reellement » sous Quotas.

Status (2026-05-22, numerotation corrigee le 2026-09). Le serveur vit dans src/app/api/mcp/intelligence/route.ts, pas dans le module propose plus bas. Ce qui tourne : JSON-RPC + SSE transport, real per-org IntelligenceApiKey bearer auth (replacing the earlier stub), 12 read tools live, releve dans TOOLS (les dix premiers le 20 aout 2026) : searchStoreIntelligence, listKnownStores, searchSimilarStores, getTopStores, inspectDomain, getStoreGraph, predictStoreTrajectory, findEmergingNiches, getWinningAngles, getSupplierIntel, puis list_watched_stores et store_changes (octobre 2026, voir « Surface agents » plus bas). (La doc disait 5 : les cinq derniers ont ete ajoutes sans que cette ligne suive.) Issuance UI at /[orgSlug]/~/settings/api-keys. Restent absents : write tools, un vrai flow OAuth avec refresh, et les notifications tools/listChanged sur un pub/sub reel.

Le numero de phase ne veut rien dire pour ce serveur, et cette ligne le dit plutot que de choisir : ce doc annoncait « Phase 3 shipped », l'en-tete de la route disait « Phase 1 transport » et le manifeste GET rend status: "phase_2_runtime". Ce qui fait foi est la liste de capacites ci-dessus.

Why

BoostEcom Intelligence (/intelligence) already exposes:

  • A web UI for browsing the canonical record graph (rankings + detail pages)
  • A REST API mirror (/api/intelligence/top/[category], /api/intelligence/inspect/[tool])
  • A marketplace listing pointing to those surfaces (/marketplace/mcp/boostecom-intelligence)

The missing piece is a real MCP server so any MCP-aware agent (Claude desktop/code, Cursor, Continue, Windsurf, Zed, ChatGPT desktop, custom bots speaking stdio/HTTP/SSE) can call the Intelligence tools the same way they call the existing Shopify Admin MCP at src/features/shopify/mcp/.

Scope of the follow-up

Tools to expose

ToolMirrors REST endpointNotes
listCategoriesGET /api/intelligence/topIndex — agent self-discovery.
topStoresGET /api/intelligence/top/top-storesWraps getTopItems("top-stores", …).
topAdsGET /api/intelligence/top/top-ads
topEmailsGET /api/intelligence/top/top-emails
topAdvertorialsGET /api/intelligence/top/top-advertorials
topProductPagesGET /api/intelligence/top/top-product-pages
topSalesPagesGET /api/intelligence/top/top-sales-pages
topCartPagesGET /api/intelligence/top/top-cart-pages
topSectionsGET /api/intelligence/top/top-sections
topBlogsGET /api/intelligence/top/top-blogs
topThemesGET /api/intelligence/top/top-themes
topAppsGET /api/intelligence/top/top-apps
topMarketsGET /api/intelligence/top/top-markets
topReviewsVendorsGET /api/intelligence/top/top-reviews-vendors
topPopupsGET /api/intelligence/top/top-popups
topSocialGET /api/intelligence/top/top-social
inspectThemePOST /api/intelligence/inspect/themeOne-shot probe on a single domain.
inspectAppsPOST /api/intelligence/inspect/apps
inspectAdsPOST /api/intelligence/inspect/ads
inspectEmailsPOST /api/intelligence/inspect/emails
searchCanonical(no public REST equivalent yet)Power-tool — fetch the full canonical record for a domain. Auth-gated.

Transport

Livre le 5 septembre 2026 (integrations/0230). Le dispatcher JSON-RPC ecrit a la main a ete remplace par McpServer + WebStandardStreamableHTTPServerTransport du SDK officiel, sans etat (aucun Mcp-Session-Id). Ce qui ne marchait pas avant : un client conforme envoie toujours Accept: application/json, text/event-stream, et l'ancienne route lisait cet en-tete comme « streame-moi », donc chaque tools/call repondait des evenements tool_start / done qui ne sont pas des messages JSON-RPC ; notifications/initialized — le deuxieme message de tout handshake — recevait -32601 ; et la version annoncee etait 2024-11-05. Le produit ne fonctionnait qu'avec un client maison.

Le manifeste a demenage : GET /api/mcp/intelligence sert desormais le transport (405 sur un serveur sans etat), et le manifeste de decouverte est sur /api/mcp/intelligence/manifest. llms.txt et la fiche marketplace nomment ce chemin.

Ces trois lignes ont nomme, jusqu'en septembre 2026, trois fichiers qui n'ont jamais existe : src/features/shopify/mcp/http.ts, src/features/shopify/mcp/security.ts et src/features/intelligence/mcp/stdio.ts. Le serveur MCP vit sous src/app/api/mcp/, et src/features/shopify/mcp/ ne contient qu'un sous-repertoire shopify/ (le client Admin GraphQL). « Reuse the pattern at X » sur un X inexistant coute plus qu'une phrase fausse : c'est une instruction d'implementation qui envoie lire un fichier vide.

Quotas

AudienceThroughputAuth
Anonymous IP60 reads / min, 30 inspects / minNone (matches REST today)
Free account600 reads / min, 300 inspects / minAPI key in Authorization
Paid plan6000 reads / min, 3000 inspects / minAPI key + plan check
EnterpriseNegotiatedAPI key + signed contract

Ce qui tourne reellement (20 aout 2026)

Le tableau ci-dessus est la cible ; voici l'etat livre, pour qu'on n'ait pas a lire le code pour repondre a « une cle sert a quoi ? ».

SurfaceSans cleAvec cle bei_
/api/mcp/intelligence (Streamable HTTP, SDK officiel)60 req/min par IP6000 req/min par organisation
REST /api/intelligence/* (16 routes gardees par guardIntelligenceRoute)plafond propre a la route (20 a 120 req/min) par IPle meme plafond x10, par organisation

Trois proprietes tiennent desormais sur les deux surfaces, parce qu'elles partagent le meme resolveur (lib/security/intelligence-api-key.ts) :

  • Une cle revoquee est refusee. Avant, le REST ignorait purement les cles et le MCP retrogradait silencieusement en tier public : dans les deux cas, revoquer une cle fuitee ne changeait rien d'observable. Un token present mais inconnu ou revoque recoit maintenant un 401 avec WWW-Authenticate.
  • Le bucket est l'ORGANISATION, pas l'IP — et pas la cle. Les agents, la CI et les runtimes serverless partagent des adresses : un porteur de cle etait puni pour du trafic qui n'etait pas le sien. Le bucket a d'abord ete caller.keyId, ce qui faisait suivre le quota a la credential : emettre une deuxieme cle doublait le plafond, une dixieme le multipliait par dix, et l'emission est en libre-service depuis le tableau de bord. Depuis security-identity/0387 les deux surfaces bucketisent sur caller.orgId. IntelligenceApiKey.orgId est NOT NULL : aucune cle deja emise n'est a reemettre, la seule chose qui change pour un consommateur mono-cle est le NOM du seau, donc une remise a zero de sa fenetre au deploiement. Le plafond de 20 cles actives par org (intelligence/0317) reste, mais il n'est plus qu'une mesure d'hygiene : il ne borne plus un debit.
  • lastUsedAt est ecrit au plus une fois par 5 min, donc le dashboard peut montrer l'usage sans transformer une API de lecture en API d'ecriture.

Pas de gate par plan (ligne « Free » vs « Paid » du tableau cible) : elle n'existe pas encore, et l'ecrire ici comme si elle existait serait exactement le genre de doc qui ment. Les lectures publiques restent publiques : c'est un choix produit (donnees publiques par defaut, llms.txt qui invite les crawlers IA), pas un oubli. « Hub public » ici designe la POSTURE d'acces, pas une page : /intelligence et /intelligence/<category> sont des 308 vers /features/intelligence depuis que le moteur browse/rank vit dans le Hub OS de la home (intelligence/0240) ; ce sont des regles de next.config.mjs depuis growth-web/3018, limitees aux quinze slugs du registre. La fiche d'un store a UNE URL, /intelligence/stores/<domain> (intelligenceStorePath), et c'est elle que les outils MCP rendent ; les quinze formes /<category>/<domain> y redirigent pour toujours.

Module layout (propose en 2026-05, jamais construit)

Ce layout n'existe pas : src/features/intelligence/ n'a jamais ete cree. Le serveur, son catalogue d'outils et son auth vivent dans src/app/api/mcp/intelligence/route.ts. Il est garde ici comme trace de la proposition, pas comme carte du code.

src/features/intelligence/mcp/
├── server.ts         # MCP server factory (mirrors features/shopify/mcp/shopify/server.ts)
├── tools/            # One file per tool — each wraps an aggregator/inspector call
│   ├── top-stores.ts
│   ├── top-ads.ts
│   ├── …
│   ├── inspect-theme.ts
│   └── search-canonical.ts
├── http.ts           # HTTP transport
├── stdio.ts          # stdio entry-point
└── security.ts       # OAuth/API-key adapter

Acceptance criteria

  1. claude mcp add --transport http boostecom-intelligence https://boostecom.app/api/mcp/intelligence connects, lists tools, returns sample data. Le transport est conforme depuis integrations/0230 ; src/app/api/mcp/intelligence/route.test.ts joue le handshake complet (initialize -> notifications/initialized -> tools/list -> tools/call) avec l'en-tete Accept qu'un vrai client envoie. Reste a le confirmer une fois contre un client reel.
  2. The marketplace listing at /marketplace/mcp/boostecom-intelligence is updated with the canonical endpoint URLs.
  3. The TODO in /llms.txt referencing this doc is removed.
  4. Rate limits + auth observed in production for one week without alerting incidents.
  5. An examples/ folder ships with prompt + tool-call samples for Claude, Cursor and Continue.

Out of scope

  • Write tools (mutate canonical record / trigger scans): handled separately because they need stricter auth, cost-accounting, and prepay credits.
  • Streaming SSE transport — defer until at least one production consumer asks for it.

Owner / timing

  • Owner: TBD (likely the Intelligence pod once the REST + UI ship)
  • Estimate: 2-3 dev days for the HTTP transport, +1 day for stdio, +1 day for auth/quotas wiring.
  • Trigger: at least one of (a) a paying customer asks for the MCP, (b) the REST surface clears 10k req/day stable, (c) a partner integration commits to it.

Surface agents (octobre 2026)

Ce que le serveur dit a un agent, et comment il le dit. Tout vit dans src/app/api/mcp/intelligence/.

  • Regle de lecture. initialize renvoie instructions (agent-notes.ts) : null, locked et unknown ne sont pas zero, nommer la couche (L1 / L2 / L4) et la source, une valeur L4 est une estimation, ne jamais inventer. La meme phrase (NULL_NOTE) termine la description de CHAQUE outil ; tool-catalog.test.ts echoue si un outil n'en porte pas.
  • Outils de suivi (lecture seule). list_watched_stores et store_changes lisent la liste de suivi de l'ORGANISATION de la cle (StoreTracker, StoreAnomaly, CatalogDelta) sur les N derniers jours. Ils exigent la portee read:tracker (keyHasScope : l'anonyme ne la passe pas, contrairement a read:intelligence) et repondent key_required / insufficient_scope plutot qu'une liste vide. Un store suivi dont la fiche n'est pas publique est liste avec intelligence.available: false, sans dire pourquoi. Chaque sortie passe par la porte de champs : mouvements d'annonces (tier ads), de promo (commerce) et journal produit par produit (products) sont details: null
    • locked + required_plan en dessous.
  • Etat de collecte du trafic. La projection hub (hub de inspectDomain, comme lookup?rich=1 et hub/scan) porte traffic_collection: { state: "measured" | "no_coverage" | "never", checked_at } : l'issue de la derniere collecte qui a reellement atteint la source. no_coverage permet de dire « la source ne couvre pas ce store » (date), never seulement « pas encore collecte », jamais zero. Public sur tous les plans, comme la feuille canonique traffic.collection (field-gate.ts). Contrat : docs/audits/2026-09-10-spy-ext-coherence.md §D.
  • Cles. Une cle est emise avec read:intelligence seul ; read:tracker se demande par son nom, dans une liste fermee (api-keys/route.ts). Aucune portee d'ecriture ne peut etre stockee.
  • Quotas. Chaque reponse MCP porte X-RateLimit-Limit, -Remaining, -Reset (et Retry-After au refus). Un lot JSON-RPC de N messages depense N coups, plafonne (limits.ts).
  • Budget quotidien de fiches completes, MCP compris. Le quota ci-dessus compte des REQUETES ; un second budget compte les FICHES completes servies, par organisation et par jour (DAILY_FULL_READS : free 0, Pro 5 000, Max 20 000 ; alerte de rafale a 500 par heure avec courriel a l'operateur). Il ne s'appliquait qu'aux routes REST du hub (lookup, hub/stores, hub/scan) : une cle payante pouvait donc paginer tout le catalogue par MCP sans jamais l'entamer. Il s'applique maintenant aux deux portes, via la meme fonction de comptage (chargeFullReads de lib/hub/read-budget.ts) : l'alerte, le courriel et les fenetres glissantes ne sont pas dupliques.
    • Ce qui compte pour une fiche (api/mcp/intelligence/read-budget.ts) : searchStoreIntelligence et inspectDomain (1 chacun), getStoreGraph, predictStoreTrajectory, getSupplierIntel (1 vue payante par magasin), un element de getTopStores quand le plan ouvre une metrique payante de la categorie (CA, depense pub, valeur du catalogue), un magasin suivi dont le flux store_changes porte une charge utile reservee a un palier. Ne comptent PAS : le palier gratuit (il ne lit jamais de fiche complete), listKnownStores, searchSimilarStores, list_watched_stores (domaine, couche, date : l'apercu), findEmergingNiches et getWinningAngles (pas des fiches de magasin). Le MCP /api/mcp/[storeId] ne lit que le magasin du marchand connecte : hors perimetre.
    • Identite : readBudgetIdentity, donc l'ORGANISATION de la cle (une deuxieme cle n'achete pas un deuxieme budget). Ce endpoint ne resout un plan payant que depuis une cle bei_ ; le repli sur l'utilisateur de la session ne servirait qu'a un futur appelant OAuth.
    • Lot : un compteur par requete HTTP est partage par tous les appels d'outils du lot, donc un lot qui renvoie N fiches en depense N, repartis comme ils viennent ; le quota de requetes (un coup par message) est inchange.
    • Budget epuise : ce n'est pas une erreur. Les fiches reviennent en apercu gratuit avec access.reason: "daily_read_budget", le resultat porte un bloc read_budget (note READ_BUDGET_NOTE, dite pour qu'un modele ne lise pas l'apercu comme des zeros) et la reponse l'en-tete X-Intelligence-Read-Budget: exhausted, comme sur le REST. Un classement ou un flux dont le budget tombe a mi-liste est resservi EN ENTIER comme le palier gratuit : tronquer la fin garderait l'ordre paye de la tete.
    • Panne : Redis injoignable, rateLimit ne leve pas et retombe sur un compteur par instance (durable: false) ; le budget continue de compter, en plus lache, comme sur le REST (limite anti-abus, pas garde de depense). Une exception inattendue de la couche budget ferme vers l'apercu (pas d'erreur d'outil, qui ferait reessayer l'agent).
  • /SKILL.md. Document Agent Skills genere (skill-doc.ts) a partir des memes constantes que celles que le limiteur et la porte de champs lisent : il ne peut pas deriver.