feat: 初始化 B2B AI Shopping Assistant 项目
- 配置 Docker Compose 多服务编排 - 实现 Chatwoot + Agent 集成 - 配置 Strapi MCP 知识库 - 支持 7 种语言的 FAQ 系统 - 实现 LangGraph AI 工作流 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
29
mcp_servers/strapi_mcp/Dockerfile
Normal file
29
mcp_servers/strapi_mcp/Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy requirements first for better caching
|
||||
COPY requirements.txt .
|
||||
|
||||
# Install Python dependencies
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Note: shared modules are mounted via docker-compose volumes
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8001
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:8001/health || exit 1
|
||||
|
||||
# Run the application
|
||||
CMD ["python", "server.py"]
|
||||
0
mcp_servers/strapi_mcp/__init__.py
Normal file
0
mcp_servers/strapi_mcp/__init__.py
Normal file
404
mcp_servers/strapi_mcp/http_routes.py
Normal file
404
mcp_servers/strapi_mcp/http_routes.py
Normal file
@@ -0,0 +1,404 @@
|
||||
"""
|
||||
HTTP Routes for Strapi MCP Server
|
||||
Provides direct HTTP access to knowledge base functions
|
||||
"""
|
||||
from typing import Optional, List
|
||||
import httpx
|
||||
from fastapi import Request
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
from pydantic_settings import BaseSettings
|
||||
from pydantic import ConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""Server configuration"""
|
||||
strapi_api_url: str
|
||||
strapi_api_token: str = ""
|
||||
log_level: str = "INFO"
|
||||
|
||||
model_config = ConfigDict(env_file=".env")
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
|
||||
# ============ FAQ Categories ============
|
||||
|
||||
FAQ_CATEGORIES = {
|
||||
"register": "faq-register",
|
||||
"order": "faq-order",
|
||||
"pre-order": "faq-pre-order",
|
||||
"payment": "faq-payment",
|
||||
"shipment": "faq-shipment",
|
||||
"return": "faq-return",
|
||||
"other": "faq-other-question",
|
||||
}
|
||||
|
||||
|
||||
# ============ Company Info ============
|
||||
|
||||
async def get_company_info_http(section: str = "contact", locale: str = "en"):
|
||||
"""Get company information - HTTP wrapper
|
||||
|
||||
Args:
|
||||
section: Section identifier (e.g., "contact")
|
||||
locale: Language locale (default: en)
|
||||
Supported: en, nl, de, es, fr, it, tr
|
||||
"""
|
||||
try:
|
||||
# Map section names to API endpoints
|
||||
section_map = {
|
||||
"contact": "info-contact",
|
||||
"about": "info-about",
|
||||
"service": "info-service",
|
||||
}
|
||||
|
||||
endpoint = section_map.get(section, f"info-{section}")
|
||||
|
||||
# Build query parameters
|
||||
headers = {"Content-Type": "application/json"}
|
||||
if settings.strapi_api_token and settings.strapi_api_token.strip():
|
||||
headers["Authorization"] = f"Bearer {settings.strapi_api_token}"
|
||||
|
||||
# Request with populate=deep to get all related data
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
response = await client.get(
|
||||
f"{settings.strapi_api_url}/api/{endpoint}",
|
||||
params={"populate": "deep", "locale": locale},
|
||||
headers=headers
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
if not data.get("data"):
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Section '{section}' not found",
|
||||
"data": None
|
||||
}
|
||||
|
||||
item = data["data"]
|
||||
|
||||
# Extract relevant information
|
||||
result_data = {
|
||||
"id": item.get("id"),
|
||||
"title": item.get("title"),
|
||||
"description": item.get("description"),
|
||||
"section": section,
|
||||
"locale": locale
|
||||
}
|
||||
|
||||
# Add profile information if available
|
||||
if item.get("yehwang_profile"):
|
||||
profile = item["yehwang_profile"]
|
||||
result_data["profile"] = {
|
||||
"title": profile.get("title"),
|
||||
"content": profile.get("content")
|
||||
}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": result_data
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"data": None
|
||||
}
|
||||
|
||||
|
||||
# ============ FAQ Query ============
|
||||
|
||||
async def query_faq_http(
|
||||
category: str = "other",
|
||||
locale: str = "en",
|
||||
limit: int = 10
|
||||
):
|
||||
"""Get FAQ by category - HTTP wrapper
|
||||
|
||||
Args:
|
||||
category: FAQ category (register, order, pre-order, payment, shipment, return, other)
|
||||
locale: Language locale (default: en)
|
||||
limit: Maximum results to return
|
||||
"""
|
||||
try:
|
||||
# Map category to endpoint
|
||||
endpoint = FAQ_CATEGORIES.get(category, f"faq-{category}")
|
||||
|
||||
headers = {"Content-Type": "application/json"}
|
||||
if settings.strapi_api_token and settings.strapi_api_token.strip():
|
||||
headers["Authorization"] = f"Bearer {settings.strapi_api_token}"
|
||||
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
response = await client.get(
|
||||
f"{settings.strapi_api_url}/api/{endpoint}",
|
||||
params={"populate": "deep", "locale": locale},
|
||||
headers=headers
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
# Check if data exists
|
||||
if not data.get("data"):
|
||||
return {
|
||||
"success": True,
|
||||
"count": 0,
|
||||
"category": category,
|
||||
"locale": locale,
|
||||
"results": []
|
||||
}
|
||||
|
||||
# Handle different response formats
|
||||
item_data = data["data"]
|
||||
|
||||
# If it's a single object with nested items
|
||||
faq_list = []
|
||||
if isinstance(item_data, dict):
|
||||
# Check for content array (Yehwang format)
|
||||
if item_data.get("content"):
|
||||
faq_list = item_data["content"]
|
||||
# Check for nested FAQ array
|
||||
elif item_data.get("faqs"):
|
||||
faq_list = item_data["faqs"]
|
||||
# Check for questions array
|
||||
elif item_data.get("questions"):
|
||||
faq_list = item_data["questions"]
|
||||
# The item itself might be the FAQ
|
||||
elif item_data.get("question") and item_data.get("answer"):
|
||||
faq_list = [item_data]
|
||||
else:
|
||||
# Return the whole data as one FAQ
|
||||
faq_list = [item_data]
|
||||
elif isinstance(item_data, list):
|
||||
faq_list = item_data
|
||||
|
||||
# Format results
|
||||
results = []
|
||||
for item in faq_list[:limit]:
|
||||
faq_item = {
|
||||
"id": item.get("id"),
|
||||
"category": category,
|
||||
"locale": locale
|
||||
}
|
||||
|
||||
# Yehwang format: title and content
|
||||
if item.get("title"):
|
||||
faq_item["question"] = item.get("title")
|
||||
if item.get("content"):
|
||||
faq_item["answer"] = item.get("content")
|
||||
|
||||
# Also support other field names
|
||||
if item.get("question"):
|
||||
faq_item["question"] = item.get("question")
|
||||
if item.get("answer"):
|
||||
faq_item["answer"] = item.get("answer")
|
||||
if item.get("description"):
|
||||
faq_item["description"] = item.get("description")
|
||||
|
||||
# Add any other fields
|
||||
for key, value in item.items():
|
||||
if key not in faq_item and key not in ["id", "createdAt", "updatedAt", "publishedAt", "locale"]:
|
||||
faq_item[key] = value
|
||||
|
||||
if "question" in faq_item or "answer" in faq_item:
|
||||
results.append(faq_item)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"count": len(results),
|
||||
"category": category,
|
||||
"locale": locale,
|
||||
"results": results
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"category": category,
|
||||
"results": []
|
||||
}
|
||||
|
||||
|
||||
async def search_faq_http(
|
||||
query: str,
|
||||
locale: str = "en",
|
||||
limit: int = 5
|
||||
):
|
||||
"""Search FAQ across all categories - HTTP wrapper
|
||||
|
||||
Args:
|
||||
query: Search keywords
|
||||
locale: Language locale (default: en)
|
||||
limit: Maximum results per category
|
||||
"""
|
||||
try:
|
||||
all_results = []
|
||||
|
||||
# Search all categories in parallel
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
tasks = []
|
||||
for category_name, endpoint in FAQ_CATEGORIES.items():
|
||||
headers = {"Content-Type": "application/json"}
|
||||
if settings.strapi_api_token and settings.strapi_api_token.strip():
|
||||
headers["Authorization"] = f"Bearer {settings.strapi_api_token}"
|
||||
|
||||
task = client.get(
|
||||
f"{settings.strapi_api_url}/api/{endpoint}",
|
||||
params={"populate": "deep", "locale": locale},
|
||||
headers=headers
|
||||
)
|
||||
tasks.append((category_name, task))
|
||||
|
||||
# Execute all requests
|
||||
for category_name, task in tasks:
|
||||
try:
|
||||
response = await task
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
|
||||
if data.get("data"):
|
||||
item_data = data["data"]
|
||||
|
||||
# Extract FAQ list
|
||||
faq_list = []
|
||||
if isinstance(item_data, dict):
|
||||
# Yehwang format: content array
|
||||
if item_data.get("content"):
|
||||
faq_list = item_data["content"]
|
||||
elif item_data.get("faqs"):
|
||||
faq_list = item_data["faqs"]
|
||||
elif item_data.get("questions"):
|
||||
faq_list = item_data["questions"]
|
||||
elif item_data.get("question") and item_data.get("answer"):
|
||||
faq_list = [item_data]
|
||||
elif isinstance(item_data, list):
|
||||
faq_list = item_data
|
||||
|
||||
# Filter by query and add to results
|
||||
for item in faq_list:
|
||||
# Search in title/content (Yehwang format) or question/answer
|
||||
item_text = (
|
||||
str(item.get("title", "")) +
|
||||
str(item.get("content", "")) +
|
||||
str(item.get("question", "")) +
|
||||
str(item.get("answer", "")) +
|
||||
str(item.get("description", ""))
|
||||
)
|
||||
|
||||
if query.lower() in item_text.lower():
|
||||
result_item = {
|
||||
"id": item.get("id"),
|
||||
"category": category_name,
|
||||
"locale": locale
|
||||
}
|
||||
|
||||
# Use title as question (Yehwang format)
|
||||
if item.get("title"):
|
||||
result_item["question"] = item.get("title")
|
||||
if item.get("content"):
|
||||
result_item["answer"] = item.get("content")
|
||||
|
||||
# Also support other field names
|
||||
if item.get("question"):
|
||||
result_item["question"] = item.get("question")
|
||||
if item.get("answer"):
|
||||
result_item["answer"] = item.get("answer")
|
||||
if item.get("description"):
|
||||
result_item["description"] = item.get("description")
|
||||
|
||||
all_results.append(result_item)
|
||||
|
||||
except Exception as e:
|
||||
# Continue with next category on error
|
||||
pass
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"count": len(all_results),
|
||||
"query": query,
|
||||
"locale": locale,
|
||||
"results": all_results[:limit]
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"results": []
|
||||
}
|
||||
|
||||
|
||||
async def search_knowledge_base_http(query: str, locale: str = "en", limit: int = 10):
|
||||
"""Search knowledge base - HTTP wrapper
|
||||
|
||||
Args:
|
||||
query: Search keywords
|
||||
locale: Language locale
|
||||
limit: Maximum results
|
||||
"""
|
||||
# Search FAQ across all categories
|
||||
return await search_faq_http(query, locale, limit)
|
||||
|
||||
|
||||
async def get_policy_http(policy_type: str, locale: str = "en"):
|
||||
"""Get policy document - HTTP wrapper
|
||||
|
||||
Args:
|
||||
policy_type: Type of policy (return_policy, privacy_policy, etc.)
|
||||
locale: Language locale
|
||||
"""
|
||||
try:
|
||||
# Map policy types to endpoints
|
||||
policy_map = {
|
||||
"return_policy": "policy-return",
|
||||
"privacy_policy": "policy-privacy",
|
||||
"terms_of_service": "policy-terms",
|
||||
"shipping_policy": "policy-shipping",
|
||||
"payment_policy": "policy-payment",
|
||||
}
|
||||
|
||||
endpoint = policy_map.get(policy_type, f"policy-{policy_type}")
|
||||
|
||||
headers = {"Content-Type": "application/json"}
|
||||
if settings.strapi_api_token and settings.strapi_api_token.strip():
|
||||
headers["Authorization"] = f"Bearer {settings.strapi_api_token}"
|
||||
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
response = await client.get(
|
||||
f"{settings.strapi_api_url}/api/{endpoint}",
|
||||
params={"populate": "deep", "locale": locale},
|
||||
headers=headers
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
if not data.get("data"):
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Policy '{policy_type}' not found",
|
||||
"data": None
|
||||
}
|
||||
|
||||
item = data["data"]
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"id": item.get("id"),
|
||||
"type": policy_type,
|
||||
"title": item.get("title"),
|
||||
"content": item.get("content"),
|
||||
"summary": item.get("summary"),
|
||||
"locale": locale
|
||||
}
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"data": None
|
||||
}
|
||||
19
mcp_servers/strapi_mcp/requirements.txt
Normal file
19
mcp_servers/strapi_mcp/requirements.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
# FastMCP Framework
|
||||
fastmcp>=0.1.0
|
||||
|
||||
# HTTP Server
|
||||
fastapi>=0.100.0
|
||||
uvicorn>=0.23.0
|
||||
|
||||
# HTTP Client
|
||||
httpx>=0.26.0
|
||||
|
||||
# Data Validation
|
||||
pydantic>=2.5.0
|
||||
pydantic-settings>=2.1.0
|
||||
|
||||
# Environment & Config
|
||||
python-dotenv>=1.0.0
|
||||
|
||||
# Logging
|
||||
structlog>=24.1.0
|
||||
269
mcp_servers/strapi_mcp/server.py
Normal file
269
mcp_servers/strapi_mcp/server.py
Normal file
@@ -0,0 +1,269 @@
|
||||
"""
|
||||
Strapi MCP Server - FAQ and Knowledge Base
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
# Add shared module to path
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from fastmcp import FastMCP
|
||||
from pydantic_settings import BaseSettings
|
||||
from fastapi import Request
|
||||
from starlette.responses import JSONResponse
|
||||
import uvicorn
|
||||
|
||||
|
||||
from pydantic import ConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""Server configuration"""
|
||||
strapi_api_url: str
|
||||
strapi_api_token: str
|
||||
log_level: str = "INFO"
|
||||
|
||||
model_config = ConfigDict(env_file=".env")
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
# Create MCP server
|
||||
mcp = FastMCP(
|
||||
"Strapi Knowledge Base"
|
||||
)
|
||||
|
||||
|
||||
# Strapi client for this server
|
||||
from shared.strapi_client import StrapiClient
|
||||
strapi = StrapiClient(settings.strapi_api_url, settings.strapi_api_token)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def query_faq(
|
||||
category: str = "other",
|
||||
locale: str = "en",
|
||||
limit: int = 10
|
||||
) -> dict:
|
||||
"""Get FAQ by category
|
||||
|
||||
Args:
|
||||
category: FAQ category
|
||||
Available: register, order, pre-order, payment, shipment, return, other
|
||||
locale: Language locale (default: en)
|
||||
Available: en, nl, de, es, fr, it, tr
|
||||
limit: Maximum results to return (default: 10)
|
||||
|
||||
Returns:
|
||||
List of FAQ items with questions and answers for the specified category
|
||||
"""
|
||||
from http_routes import query_faq_http
|
||||
return await query_faq_http(category, locale, limit)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def get_company_info(
|
||||
section: str,
|
||||
locale: str = "en"
|
||||
) -> dict:
|
||||
"""Get company information by section
|
||||
|
||||
Args:
|
||||
section: Information section (about_us, contact, service_hours, locations, etc.)
|
||||
locale: Language locale (default: zh-CN)
|
||||
|
||||
Returns:
|
||||
Company information for the requested section
|
||||
"""
|
||||
try:
|
||||
response = await strapi.query_collection(
|
||||
"company-infos",
|
||||
filters={"[section][$eq]": section},
|
||||
locale=locale
|
||||
)
|
||||
|
||||
results = strapi.flatten_response(response)
|
||||
|
||||
if not results:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Section '{section}' not found",
|
||||
"data": None
|
||||
}
|
||||
|
||||
item = results[0]
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"section": item.get("section"),
|
||||
"title": item.get("title"),
|
||||
"content": item.get("content"),
|
||||
"metadata": item.get("metadata", {})
|
||||
}
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"data": None
|
||||
}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def get_policy(
|
||||
policy_type: str,
|
||||
locale: str = "en"
|
||||
) -> dict:
|
||||
"""Get policy document
|
||||
|
||||
Args:
|
||||
policy_type: Type of policy (return_policy, privacy_policy, terms_of_service,
|
||||
shipping_policy, payment_policy, etc.)
|
||||
locale: Language locale (default: zh-CN)
|
||||
|
||||
Returns:
|
||||
Policy document with content and metadata
|
||||
"""
|
||||
try:
|
||||
response = await strapi.query_collection(
|
||||
"policies",
|
||||
filters={"[type][$eq]": policy_type},
|
||||
locale=locale
|
||||
)
|
||||
|
||||
results = strapi.flatten_response(response)
|
||||
|
||||
if not results:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Policy '{policy_type}' not found",
|
||||
"data": None
|
||||
}
|
||||
|
||||
item = results[0]
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"type": item.get("type"),
|
||||
"title": item.get("title"),
|
||||
"content": item.get("content"),
|
||||
"summary": item.get("summary"),
|
||||
"version": item.get("version"),
|
||||
"effective_date": item.get("effective_date"),
|
||||
"last_updated": item.get("updatedAt")
|
||||
}
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"data": None
|
||||
}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def search_knowledge_base(
|
||||
query: str,
|
||||
locale: str = "en",
|
||||
limit: int = 10
|
||||
) -> dict:
|
||||
"""Search knowledge base documents across all FAQ categories
|
||||
|
||||
Args:
|
||||
query: Search keywords
|
||||
locale: Language locale (default: en)
|
||||
Available: en, nl, de, es, fr, it, tr
|
||||
limit: Maximum results to return (default: 10)
|
||||
|
||||
Returns:
|
||||
List of matching FAQ documents from all categories
|
||||
"""
|
||||
from http_routes import search_knowledge_base_http
|
||||
return await search_knowledge_base_http(query, locale, limit)
|
||||
|
||||
|
||||
# Health check endpoint
|
||||
@mcp.tool()
|
||||
async def health_check() -> dict:
|
||||
"""Check server health status"""
|
||||
return {
|
||||
"status": "healthy",
|
||||
"service": "strapi_mcp",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Create FastAPI app from MCP
|
||||
mcp_app = mcp.http_app()
|
||||
|
||||
# Add health endpoint
|
||||
async def health_check(request):
|
||||
return JSONResponse({"status": "healthy"})
|
||||
|
||||
# Import HTTP routes
|
||||
from http_routes import (
|
||||
get_company_info_http,
|
||||
query_faq_http,
|
||||
get_policy_http,
|
||||
search_knowledge_base_http
|
||||
)
|
||||
|
||||
# Direct function references for HTTP endpoints
|
||||
async def call_query_faq(request):
|
||||
"""HTTP endpoint for query_faq"""
|
||||
try:
|
||||
data = await request.json()
|
||||
result = await query_faq_http(**data)
|
||||
return JSONResponse(result)
|
||||
except Exception as e:
|
||||
return JSONResponse({"success": False, "error": str(e)}, status_code=500)
|
||||
|
||||
async def call_get_company_info(request):
|
||||
"""HTTP endpoint for get_company_info"""
|
||||
try:
|
||||
data = await request.json()
|
||||
result = await get_company_info_http(**data)
|
||||
return JSONResponse(result)
|
||||
except Exception as e:
|
||||
return JSONResponse({"success": False, "error": str(e)}, status_code=500)
|
||||
|
||||
async def call_get_policy(request):
|
||||
"""HTTP endpoint for get_policy"""
|
||||
try:
|
||||
data = await request.json()
|
||||
result = await get_policy_http(**data)
|
||||
return JSONResponse(result)
|
||||
except Exception as e:
|
||||
return JSONResponse({"success": False, "error": str(e)}, status_code=500)
|
||||
|
||||
async def call_search_knowledge_base(request):
|
||||
"""HTTP endpoint for search_knowledge_base"""
|
||||
try:
|
||||
data = await request.json()
|
||||
result = await search_knowledge_base_http(**data)
|
||||
return JSONResponse(result)
|
||||
except Exception as e:
|
||||
return JSONResponse({"success": False, "error": str(e)}, status_code=500)
|
||||
|
||||
# Add routes using the correct method
|
||||
from fastapi import FastAPI
|
||||
|
||||
# Create a wrapper FastAPI app with custom routes first
|
||||
app = FastAPI()
|
||||
|
||||
# Add custom routes BEFORE mounting mcp_app
|
||||
app.add_route("/health", health_check, methods=["GET"])
|
||||
app.add_route("/tools/query_faq", call_query_faq, methods=["POST"])
|
||||
app.add_route("/tools/get_company_info", call_get_company_info, methods=["POST"])
|
||||
app.add_route("/tools/get_policy", call_get_policy, methods=["POST"])
|
||||
app.add_route("/tools/search_knowledge_base", call_search_knowledge_base, methods=["POST"])
|
||||
|
||||
# Mount MCP app at root (will catch all other routes)
|
||||
app.mount("/", mcp_app)
|
||||
|
||||
uvicorn.run(app, host="0.0.0.0", port=8001)
|
||||
0
mcp_servers/strapi_mcp/tools/__init__.py
Normal file
0
mcp_servers/strapi_mcp/tools/__init__.py
Normal file
Reference in New Issue
Block a user