Consumer Context API + MCP Service
Consumer Context API + MCP Service
Section titled “Consumer Context API + MCP Service”Context
Section titled “Context”Three systems (consumer-graph-worker, rover-agent, rover-mcp) independently call overlapping upstream services to enrich product, offer, user, and purchase data. Each maintains its own caches (or none), resulting in duplicated client code, inconsistent data, and wasted upstream calls. This plan defines a single Context API service (consumer-context-service) with centralized caching, plus an MCP layer on top that replaces rover-mcp entirely. The name is intentionally broad — this service will grow to include additional context domains (e.g., Fetch Play) beyond products.
Architecture
Section titled “Architecture”Single binary, two transports: REST API + MCP. One process = one shared cache layer.
┌──────────────────────────────────────┐ │ consumer-context-service service │ │ │ upstream ◄──────│ internal/enricher/ (core logic) │ services │ internal/clients/ (adapters) │ (+ future: │ internal/cache/ (shared cache) │ Fetch Play, │ │ etc.) │ ┌──────────┐ ┌───────────┐ │ │ │ REST API │ │ MCP Server │ │ │ │ :8080 │ │ :8081 │ │ │ └────┬─────┘ └─────┬─────┘ │ └───────┼──────────────┼───────────────┘ │ │ ┌─────────────┤ ┌────┴────────────────┐ │ │ │ │ ┌──────▼───┐ ┌──────▼──┐ ┌────▼──────┐ ┌──────────▼──┐ │ scheduler │ │ rover- │ │ consumer- │ │ future │ │ (HTTP) │ │ agent │ │ agent │ │ agents │ └───────────┘ │ (HTTP) │ │ (MCP) │ │ (MCP) │ └─────────┘ └───────────┘ └─────────────┘ UX streaming all agents use MCP gatewayConsumers
Section titled “Consumers”- REST API — programmatic/streaming consumers: scheduler, rover-agent (UX streaming gateway). Direct HTTP calls, typed responses.
- MCP — all agents: consumer-agent, future scheduler agent, future agents. Tool-based interface for LLM reasoning.
Why a service (not a library)
Section titled “Why a service (not a library)”- Shared cache — one warm FidoIndex, one Button merchant cache, one FPS metadata cache. Every consumer benefits.
- Cache TTLs tuned once — consumers don’t manage staleness.
- Independent scaling — cache-heavy service scales separately.
- Consistent data — all callers see the same enriched view.
- Extensible — new context sources (Fetch Play, etc.) added once, available to all consumers immediately.
New module: github.com/fetch-rewards/consumer-context-service
Section titled “New module: github.com/fetch-rewards/consumer-context-service”Package layout
Section titled “Package layout”consumer-context-service/├── go.mod├── Makefile├── cmd/│ └── server/│ └── main.go # Single binary: REST + MCP on two ports├── internal/│ ├── config/│ │ └── config.go # YAML + env var config│ ││ ├── clients/ # Top-level: all upstream client adapters│ │ ├── fps.go # FIDO Product Service│ │ ├── fidora.go # FIDORA (retailer-specific product data)│ │ ├── offer_guardian.go # Offer Guardian adapter│ │ ├── fido_index.go # FIDO reverse index with periodic refresh│ │ ├── neli.go # Neli (user offer eligibility)│ │ ├── button.go # Button Merchant Service│ │ ├── retailer.go # Retailer Service│ │ ├── ereceipt.go # eReceipt dual-provider check│ │ ├── neo4j.go # Neo4j read-only graph queries│ │ ├── offer_search.go # Offer Search (semantic search)│ │ ├── fido_search.go # FIDO Search (product search)│ │ ├── lidar.go # LIDAR (location-based offers)│ │ ├── purchase_history.go # Purchase History Service (PHS)│ │ ├── user.go # User Service (profile, points)│ │ └── web.go # Web fetch + Google search│ ││ ├── cache/│ │ └── cache.go # Generic TTL cache│ ││ │ # ── Domain services (common access layer) ──│ │ # Both REST handlers and MCP tools call these.│ │ # Each domain owns its types, business logic, and scoring.│ ││ ├── offers/ # Offer domain│ │ ├── service.go # GetOffersForProduct(), SearchOffers(), SearchNearby()│ │ ├── service_test.go│ │ └── types.go # OfferContext, OfferSearchResult│ ││ ├── users/ # User domain│ │ ├── service.go # GetProfile(), GetHistory(), GetCandidates()│ │ ├── service_test.go│ │ └── types.go # UserProfile, PurchaseHistory, RepurchaseCandidate│ ││ ├── products/ # Product enrichment domain│ │ ├── service.go # GetProductContext(), BatchGetProductContext(), SearchProducts()│ │ ├── service_test.go│ │ ├── scoring.go # EV, total points, stickers, CTA│ │ ├── scoring_test.go│ │ └── types.go # ProductContext, RetailerContext, ScoringContext, ProductSearchResult│ ││ ├── shopping/ # Shopping list domain│ │ ├── service.go # CreateList(), OptimizeList()│ │ └── types.go│ ││ ├── context/ # Cross-domain orchestration│ │ ├── service.go # FullEnrich() — composes product + offer + user domains│ │ ├── service_test.go│ │ └── types.go # FullContext (union response)│ ││ │ # ── Transports (thin, no business logic) ──│ ││ ├── api/ # REST transport│ │ ├── handler.go # Routes + handlers, calls domain services│ │ └── handler_test.go│ ││ └── mcp/ # MCP transport│ ├── tools.go # Tool definitions + handlers, calls domain services│ └── tools_test.go│├── pkg/│ └── client/│ └── client.go # Go HTTP client (optional convenience)├── config/│ ├── local.yaml│ ├── stage.yaml│ └── prod.yaml└── consumer-context-service.yml # FSD ECS deploymentKey design decisions
Section titled “Key design decisions”- Single binary, two transports — REST on
:8080for programmatic consumers, MCP on:8081for agents. Both call the same domain services. - Common access layer — domain
service.gofiles are the boundary. REST handlers and MCP tools are thin transport adapters: parse params → call service → format response. Zero business logic in transports. - Clients at top level — shared across domains. Neo4j, Neli, Offer Guardian, etc. are used by multiple domain services.
- Centralized caching — FidoIndex, Button merchants, FPS metadata all in-memory. Shared across all request paths.
- Graceful degradation — each enrichment tier is independent. Missing FIDORA = nil
RetailerProduct, but offers/history/scoring still populate. - Replaces rover-mcp entirely — all existing tools migrate here.
- Domain-namespaced — new context domains (Fetch Play, etc.) are self-contained additions. Searches live under their domain (offer search under
offers/, product search underproducts/).
Upstream services (full union)
Section titled “Upstream services (full union)”Enrichment sources (8)
Section titled “Enrichment sources (8)”| # | Client | Service | What It Provides |
|---|---|---|---|
| 1 | FPS | FIDO Product Service | Generic product metadata: name, brand, category |
| 2 | FIDORA | FIDO Assortment Service | Retailer-specific: price, image, URL, retailerID, availability |
| 3 | Offer Guardian | Offer Guardian + FidoIndex | All offers for a FIDO, reverse-indexed |
| 4 | Neli | Neli Eligibility | Per-user offer eligibility |
| 5 | Button | Button Merchant Service | Merchant PPD, logo, stickers |
| 6 | Retailer | Retailer Service | Venue type classification |
| 7 | eReceipt | eReceipt Provider + Handbook | eReceipt scanning support |
| 8 | Neo4j | Consumer Graph | Purchase history, repurchase metrics |
Search & discovery sources (from rover-mcp)
Section titled “Search & discovery sources (from rover-mcp)”| # | Client | Service | What It Provides |
|---|---|---|---|
| 9 | Offer Search | Offer Search Service | Semantic offer search with eligibility filtering |
| 10 | FIDO Search | FIDO Search Service | Product search by natural language description |
| 11 | LIDAR | LIDAR Service | Location-based offer discovery |
| 12 | PHS | Purchase History Service | Canonical purchase history with item details |
| 13 | User Service | User Service | User profile, points balance |
REST API surface
Section titled “REST API surface”Enrichment endpoints
Section titled “Enrichment endpoints”| Method | Path | Description |
|---|---|---|
GET | /v1/products/:fido_id/context?user_id=X | Full enrichment, single product |
POST | /v1/products/context | Batch enrichment ({"fido_ids": [...], "user_id": "X"}) |
GET | /v1/products/:fido_id/offers?user_id=X | Offers for a product (with eligibility) |
GET | /v1/users/:user_id/history?fido_id=X | Graph purchase history |
GET | /v1/users/:user_id/candidates?lookback_days=90 | Repurchase candidates |
Offer search endpoints
Section titled “Offer search endpoints”| Method | Path | Description |
|---|---|---|
POST | /v1/offers/search | Semantic offer search |
POST | /v1/offers/search/nearby | Location-based offer search |
Product search endpoints
Section titled “Product search endpoints”| Method | Path | Description |
|---|---|---|
POST | /v1/products/search | Product search by description |
User endpoints
Section titled “User endpoints”| Method | Path | Description |
|---|---|---|
GET | /v1/users/:user_id/purchases | Purchase history (PHS) |
GET | /v1/users/:user_id/profile | User profile + points |
Infrastructure
Section titled “Infrastructure”| Method | Path | Description |
|---|---|---|
GET | /health | Health check |
MCP tools (replaces all rover-mcp tools)
Section titled “MCP tools (replaces all rover-mcp tools)”Product enrichment (new)
Section titled “Product enrichment (new)”| Tool | Inputs | Maps to |
|---|---|---|
get_product_context | fido_id, user_id | GET /v1/products/:fido_id/context |
get_product_contexts | fido_ids[], user_id | POST /v1/products/context |
get_offers_by_fido | fido_id, user_id? | GET /v1/products/:fido_id/offers |
get_purchase_history | fido_id, user_id | GET /v1/users/:user_id/history |
get_repurchase_candidates | user_id, lookback_days? | GET /v1/users/:user_id/candidates |
Offer search (migrated from rover-mcp)
Section titled “Offer search (migrated from rover-mcp)”| Tool | Inputs | Maps to |
|---|---|---|
search_offers | query, limit?, user_id? | POST /v1/offers/search |
search_nearby_offers | query, latitude, longitude, user_id? | POST /v1/offers/search/nearby |
Product search (migrated from rover-mcp)
Section titled “Product search (migrated from rover-mcp)”| Tool | Inputs | Maps to |
|---|---|---|
search_products | descriptions[] | POST /v1/products/search |
User (migrated from rover-mcp)
Section titled “User (migrated from rover-mcp)”| Tool | Inputs | Maps to |
|---|---|---|
get_user_purchase_history | user_id, start_time?, end_time?, category?, brand? | GET /v1/users/:user_id/purchases |
get_user_profile | user_id? | GET /v1/users/:user_id/profile |
Utility (migrated from rover-mcp)
Section titled “Utility (migrated from rover-mcp)”| Tool | Inputs | Notes |
|---|---|---|
create_shopping_list | criteria?, preferences[]?, exclude[]?, user_id? | Uses enrichment + offer data |
optimize_shopping_list | items[]?, optimize_for?, user_id? | Uses enrichment + offer data |
fetch_webpage | url, depth | Generic web fetch |
web_search | query, num_results | Google Custom Search |
llm_feedback | message | Logging only |
welcome | — | Static widget content |
Caching strategy
Section titled “Caching strategy”| Data | Source | Cache TTL | Refresh |
|---|---|---|---|
| FIDO reverse index | Offer Guardian (all active) | In-memory | Periodic rebuild every 5 min |
| Button merchants | Button Merchant Service | Until restart | Lazy-load on first access |
| FPS product metadata | FIDO Product Service | 1 hour | Per-key TTL |
| FIDORA retailer data | FIDORA API | 15 min | Per-key TTL |
| Neli eligibility | Neli service | 5 min | Per user-offer TTL |
| Neo4j history | Neo4j graph | 1 min | Per user-product TTL |
| Retailer venue type | Retailer Service | 30 min | Per-store TTL |
Implementation phases
Section titled “Implementation phases”Phase 1: Foundation
Section titled “Phase 1: Foundation”Create the new module with domain types, services, scoring, and both transports.
Files to create:
go.modcmd/server/main.go— dual-port server (REST + MCP)Makefileinternal/products/types.go— ProductContext, RetailerContext, ScoringContext, DisplayContext, ProductSearchResultinternal/products/service.go—GetProductContext(),BatchGetProductContext(),SearchProducts()internal/products/scoring.go— EV, total points, stickers, CTA (ported from calculator.go + rover-agent)internal/products/scoring_test.gointernal/offers/types.go— OfferContext, OfferSearchResultinternal/offers/service.go—GetOffersForProduct(),SearchOffers(),SearchNearby()internal/users/types.go— UserProfile, PurchaseHistory, RepurchaseCandidateinternal/users/service.go—GetProfile(),GetHistory(),GetCandidates()internal/context/types.go— FullContext (cross-domain union)internal/context/service.go—FullEnrich()composing all domainsinternal/api/handler.go— REST routes + handlers (thin, calls services)internal/mcp/tools.go— all 16 MCP tool definitions (thin, calls services)
Phase 2: Client adapters + caching
Section titled “Phase 2: Client adapters + caching”Top-level clients (all in internal/clients/):
fps.go,fidora.go,offer_guardian.go,fido_index.goneli.go,button.go,retailer.go,ereceipt.go,neo4j.gooffer_search.go,fido_search.go,lidar.gopurchase_history.go,user.go,web.go
Cache:
internal/cache/cache.go— generic TTL cache
Phase 3: Consumer integration
Section titled “Phase 3: Consumer integration”consumer-graph-worker (scheduler → REST API):
cmd/unified-worker/dependencies.go— add consumer-context-service HTTP clientinternal/notification/repurchase/handler.go— call context API for offer enrichmentinternal/scheduler/eligibility.go— use context API response for offers + eligibility
rover-agent / UX streaming gateway (→ REST API):
internal/enrichment/product/product_enricher.go— replace 4 individual client calls (FIDORA, Button, eReceipt, rover-mcp JSON-RPC) with singleGET /v1/products/:fido_id/contextcall- Deprecate rover-mcp dependency
consumer-agent + all future agents (→ MCP):
- Point MCP client at consumer-context-service service
:8081 - Gains all enrichment tools + migrated rover-mcp tools
- Future scheduler agent uses MCP for the same data the scheduler gets via REST
Verification
Section titled “Verification”cd consumer-context-service && go test ./...— scoring tests passcd consumer-context-service && go build ./cmd/server/— server buildscurl localhost:8080/v1/products/test-fido/context?user_id=test— returns enriched JSON- MCP tool calls via
:8081return same data cd consumer-graph-worker && go build ./cmd/unified-worker/— compiles after integration
Existing implementations reference
Section titled “Existing implementations reference”Code to port
Section titled “Code to port”| Source | From | To |
|---|---|---|
| Product scoring (EV, days since, predicted next) | consumer-graph-worker/internal/scheduler/calculator.go | internal/products/scoring.go |
| Total points calculation | rover-agent internal/enrichment/product/points.go | internal/products/scoring.go |
| Sticker generation | rover-agent internal/enrichment/product/stickers.go | internal/products/scoring.go |
| CTA deep link builder | rover-agent internal/enrichment/product/cta.go | internal/products/scoring.go |
| FIDO Product Service client | consumer-graph-worker/internal/api/fido/client.go | internal/clients/fps.go |
| FIDORA client | rover-agent internal/enrichment/product/fidora_client.go | internal/clients/fidora.go |
| Offer Guardian + FidoIndex | rover-mcp pkg/api/service/offer-guardian/ | internal/clients/offer_guardian.go + fido_index.go |
| Neli client | consumer-graph-worker/internal/api/neli/client.go | internal/clients/neli.go |
| Button Merchant client | rover-agent internal/enrichment/product/button_client.go | internal/clients/button.go |
| Retailer Service client | consumer-graph-worker/internal/api/retailer/client.go | internal/clients/retailer.go |
| eReceipt client | rover-agent internal/enrichment/product/ereceipt_client.go | internal/clients/ereceipt.go |
| Neo4j read queries | consumer-graph-worker/internal/notification/repurchase/neo4j.go | internal/clients/neo4j.go |
| Offer Search client | rover-mcp pkg/api/service/offer-search/ | internal/clients/offer_search.go |
| FIDO Search client | rover-mcp pkg/api/service/fido-product/ | internal/clients/fido_search.go |
| LIDAR client | rover-mcp pkg/api/service/lidar/ | internal/clients/lidar.go |
| Purchase History client | rover-mcp pkg/api/service/purchase-history/ | internal/clients/purchase_history.go |
| User Service client | rover-mcp pkg/api/service/ (user profile methods) | internal/clients/user.go |
| MCP tool registry pattern | rover-mcp pkg/tools/registry.go | internal/mcp/tools.go |