asmy-mcp-server

asmy-mcp-server

Exposes the Asmy Digital Store product catalog, promo codes, and shop statistics as MCP tools for AI agents.

Category
访问服务器

README

asmy-mcp-server

Serveur MCP pour Asmy Digital Store. Expose le catalogue produits, les codes promo et les stats de la boutique sous forme d'outils MCP, consommables par un agent (Junior.so, Claude Desktop, ou n'importe quel client MCP).

Backend de données : Firestore. Deux transports : stdio et HTTP (SSE + Streamable HTTP).

Prérequis

  • Node >= 18 (Bun recommandé, les scripts npm l'utilisent)
  • Un projet Firebase avec Firestore activé
  • Une clé de compte de service Firebase

Installation

bun install          # ou npm install
cp .env.example .env

Configuration

Firebase

Le serveur résout les credentials dans cet ordre (firebase-config.js) :

  1. FIREBASE_SERVICE_ACCOUNT — le JSON complet, sur une ligne ou en base64
  2. FIREBASE_SERVICE_ACCOUNT_FILE — chemin vers le fichier
  3. ./firebase-service-account.json — fallback, à côté de index.js

Si FIREBASE_SERVICE_ACCOUNT est présent mais invalide, il y a fallback sur le fichier avec un warning sur stderr, pas un crash.

En local : le fichier. En prod : la variable d'env (base64 évite les galères d'échappement sur private_key).

Le fichier firebase-service-account.json est déjà dans .gitignore. Il n'y a aucune raison de l'en sortir.

Variables d'environnement

Variable Défaut Rôle
PORT 3000 Port HTTP
HOST 0.0.0.0 Interface d'écoute
ALLOWED_HOSTS — Hôtes autorisés, séparés par des virgules. Requis quand on bind sur 0.0.0.0
FIREBASE_SERVICE_ACCOUNT — JSON du service account (une ligne ou base64)
FIREBASE_SERVICE_ACCOUNT_FILE — Chemin alternatif vers le JSON
MCP_STDIO — 1 force le transport stdio
OPENAI_API_KEY — Clients de test uniquement
OPENAI_MODEL gpt-4o-mini Clients de test uniquement
MCP_SERVER_URL — test-mcp-url.js uniquement

localhost, 127.0.0.1, [::1] et RAILWAY_PUBLIC_DOMAIN (injecté par Railway) sont ajoutés automatiquement à la liste des hôtes autorisés.

Note : env.js est un parseur .env maison, sans dépendance. Il gère les valeurs JSON multi-lignes (accolades) — utile pour coller un service account brut dans le fichier. Il ne gère pas grand-chose d'autre : pas d'interpolation, pas d'échappements exotiques.

Lancer

bun start          # HTTP sur $PORT
bun run dev        # HTTP + watch
bun run start:stdio  # transport stdio

Le serveur choisit stdio si --stdio est passé en argument ou si MCP_STDIO=1. Sinon, HTTP.

Endpoints HTTP

Route Méthode Description
/ GET Health check, renvoie le nom du serveur et la liste des endpoints
/mcp ALL Streamable HTTP (spec 2025-11-25), sessions via header mcp-session-id
/sse GET SSE legacy (spec 2024-11-05) — c'est ce que consomme Junior.so
/messages POST Canal retour du transport SSE, session via ?sessionId=

Les deux transports coexistent. Une session ouverte en SSE ne peut pas être reprise sur /mcp (retour -32000).

Les sessions sont stockées en mémoire dans un objet local. Conséquence : une seule instance. Pas de scaling horizontal sans sticky sessions ou store partagé.

Outils exposés

16 outils, tous définis dans index.js.

Produits (collection products)

Outil Description
list_products Catalogue avec statut, prix, stock
get_product Détail par slug ou id Firestore
create_product Création. autoFindImage=true déclenche la recherche d'icône
update_product Mise à jour partielle
set_product_status active / draft / featured
duplicate_product Copie avec nouveau nom optionnel
delete_product Suppression définitive
find_product_image Recherche d'icône seule, sans écrire en base

Promotions (collection promotions)

Outil Description
list_promos Tous les codes
get_promo Recherche par code
create_promo Refuse les doublons de code
update_promo Code, réduction ou expiration
delete_promo Suppression

Divers

Outil Description
get_stats Lit stats/overview
update_stats Ajoute une entrée et recalcule les totaux du mois
get_current_date Date courante en Africa/Douala (UTC+1)

get_current_date existe parce que les LLM se trompent de date et génèrent des expiresAt dans le passé. Un agent qui crée un code promo doit l'appeler d'abord.

Conventions

  • Les codes promo sont normalisés en majuscules, expiresAt au format YYYY-MM-DD, discount entre 1 et 100.
  • Le slug produit est dérivé du nom (NFD, accents retirés, non-alphanumériques remplacés par des tirets).
  • Chaque produit renvoyé porte une url construite sur https://asmystore.shop/abonnements/{slug}.
  • update_stats push dans history, puis recalcule monthTotal et monthOrders en filtrant sur le préfixe YYYY-MM. Écrire deux fois la même entrée la compte deux fois — il n'y a pas de déduplication.

Recherche d'images

product-image.js résout une icône produit en deux passes :

  1. Clearbit (logo.clearbit.com/{domaine}), avec une table de correspondance marque → domaine et un fallback {slug}.com / {slug}.io
  2. DuckDuckGo Images

Chaque candidat est validé par un HEAD (puis un GET avec Range si le HEAD ne renvoie rien d'exploitable) pour vérifier que le content-type est bien une image.

Deux réserves à garder en tête : l'endpoint i.js de DuckDuckGo n'est pas une API publique — il faut d'abord scraper un token vqd, et ça peut casser sans préavis. Et les logos récupérés appartiennent aux marques concernées ; à toi de vérifier ce que tu as le droit d'afficher sur la boutique.

En cas d'échec, l'outil renvoie une erreur explicite et le produit est créé sans icône.

Tests

Deux clients de test, tous les deux branchés sur OpenAI pour faire du tool calling en langage naturel. OPENAI_API_KEY requise.

bun run test:mcp                          # spawn le serveur en stdio
bun run test:mcp "Liste les produits actifs"

MCP_SERVER_URL=https://xxx.up.railway.app bun run test:mcp:url
bun test-mcp-url.js https://xxx.up.railway.app "Quelle est la date ?"

Sans argument, les deux entrent en mode REPL. Max 10 tours d'appels d'outils.

Déploiement (Railway)

  1. Push le repo (privé) sur GitHub
  2. Railway → New Project → Deploy from GitHub
  3. Variables : FIREBASE_SERVICE_ACCOUNT (base64), PORT si besoin
  4. RAILWAY_PUBLIC_DOMAIN est injecté automatiquement et ajouté aux hôtes autorisés — sinon, renseigner ALLOWED_HOSTS à la main

Le serveur gère SIGINT / SIGTERM : les transports ouverts sont fermés avant l'exit.

Connexion depuis Junior.so

Dashboard → agent → Integrations → Create → Custom → MCP, puis l'URL SSE :

https://<ton-domaine>.up.railway.app/sse

Junior parle la spec 2024-11-05, donc /sse et pas /mcp. Les 16 outils sont listés automatiquement après connexion.

Migration des promos

migrate-promos.js est un script one-shot pour rapatrier les codes promo du localStorage du front vers Firestore.

  1. Sur asmystore.shop, console dev : localStorage.getItem("asmy-admin-promos")
  2. Coller le JSON dans la constante PROMOS_JSON du script
  3. bun migrate-promos.js

Attention : ce script lit firebase-service-account.json en dur, il ne passe pas par firebase-config.js. Les variables d'env ne fonctionnent pas ici, il faut le fichier. Le script est destiné à être lancé une fois puis oublié.

Côté front, penser à basculer les lectures/écritures promo de localStorage vers la collection promotions, sinon les deux sources divergent.

Dépannage

Firebase non configuré au démarrage — ni FIREBASE_SERVICE_ACCOUNT, ni FIREBASE_SERVICE_ACCOUNT_FILE, ni le fichier par défaut n'ont été trouvés.

JSON Firebase incomplet — le JSON est parsé mais il manque type: "service_account" ou private_key. Typiquement un copier-coller tronqué ou un base64 mal terminé.

Requêtes rejetées en prod alors que ça marche en local — protection contre le DNS rebinding. Le host n'est pas dans allowedHosts. Renseigner ALLOWED_HOSTS.

Session not found sur /messages — la session SSE a expiré ou le serveur a redémarré. Les sessions sont en mémoire, un redéploiement les efface toutes.

Le client ne voit aucun outil — vérifier qu'on tape bien /sse (spec legacy) et pas /mcp, et que GET / répond.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选