å¥åº·äžå¥èº«æç»MCPæ¯äžæ¬Ÿå šé¢çAIå¥èº«è¿œèžªåºçšçšåºïŒå®éè¿æš¡åäžäžæåè®®ïŒMCPïŒ ïŒåŒ¥åäºäŒ ç»å¥èº«åºçšäžæºèœAIèŸ å©ä¹éŽçå·®è·ïŒäžºçšæ·æäŸäžªæ§åçå¥èº«æå¯Œåæºèœåæã
git clone https://github.com/your-username/health-fitness-coach-mcp.git
cd health-fitness-coach-mcp
npm install
cp env.example .env.local
# æ·»å çšäºAIçæè®¡åçOPENAI_API_KEY
npm run dev
{
"mcpServers": {
"health-fitness-coach": {
"url": "http://localhost:3000/sse"
}
}
}
{
"mcpServers": {
"health-fitness-coach": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:3000/mcp"]
}
}
}
# çŽæ¥æµè¯MCPå·¥å
·
npm run test:fitness
# æµè¯ç¹å®ç«¯ç¹
curl http://localhost:3000/api/context?userId=default-user
http://localhost:3000 è¿è¡å¯è§åå¥èº«è¿œèžªãæ¬é¡¹ç®ç±äž€äžªäž»èŠç»ä»¶ææïŒ
ç³»ç»æ¶æåŠäžïŒ
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â å¥åº·äžå¥èº«æç»çæç³»ç» â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â â
â âââââââââââââââââââââââ âââââââââââââââââââââââ âââââââââââââââââââ â
â â ð WEBåºçš â â ð€ MCPæå¡åš â â ð§ AI客æ·ç«¯ â â
â â (Next.js) â â (åè®®å±) â â (Cursor/Claude) â â
â â â â â â â â
â â ⢠å¥èº«ä»ªè¡šç âââââºâ ⢠7䞪æºèœå·¥å
· âââââºâ ⢠èªç¶è¯èšäº€äº â â
â â ⢠掻åšè®°åœ â â â¢ æ°æ®å€ç â â ⢠äžäžææç¥ â â
â â ⢠è¿åºŠè·èžª â â ⢠äžäžæåæ â â ⢠计åçæ â â
â â ⢠计åå¯è§å â â ⢠APIéæ â â ⢠æºèœæ¥è¯¢ â â
â â â¢ å®æ¶æŽæ° â â ⢠åè®®åè§æ§ â â ⢠工å
·è°çš â â
â âââââââââââââââââââââââ âââââââââââââââââââââââ âââââââââââââââââââ â
â â â â â
â âââââââââââââââââââââââââââââŒââââââââââââââââââââââââââââ â
â â â
â âââââââââââââââââââââââââââââââââââââââŒââââââââââââââââââââââââââââââââââââââ â
â â ð ç»äžæ°æ®å± â â
â â â â
â â âââââââââââââââ âââââââââââââââ âââââââââââââââ âââââââââââââââ â â
â â â ðïž é»çŒè®°åœ â â ð è¥å
»è®°åœ â â ð 计åè®°åœ â â ð åéŠè®°åœ â â â
â â â ⢠é»çŒæ¶æ®µ â â ⢠é€é£ä¿¡æ¯ â â ⢠AIçæè®¡å â â ⢠è¿åºŠåéŠ â â â
â â â ⢠æç»æ¶éŽ â â ⢠å¡è·¯éæå
¥ â â ⢠æ¯åšè®¡å â â ⢠ç¬è®°ä¿¡æ¯ â â â
â â â ⢠é»çŒç±»å â â ⢠é£ç©é¡¹ç® â â â¢ æ¯æ¥è®¡å â â ⢠æŽå¯åæ â â â
â â âââââââââââââââ âââââââââââââââ âââââââââââââââ âââââââââââââââ â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
æš¡åäžäžæåè®®ïŒMCPïŒ æ¯AIåºçšçšåºè¿æ¥å€éšæ°æ®æºåå·¥å ·çæ ååæ¹åŒãæä»¬çMCPæå¡åšå åœäžäžªæºèœå¥èº«æ°æ®çœå ³ïŒå ·æä»¥äžåèœïŒ
MCPæå¡åšæäŸäº7䞪æºèœå·¥å ·ïŒæ¹åäºAIäžå¥èº«æ°æ®çäº€äºæ¹åŒïŒ
log-workout - é»çŒäŒè¯è·èžª// åèœïŒè®°åœé»çŒäŒè¯å¹¶è¿è¡æºèœåç±»
{
tool: "log-workout",
parameters: {
userId: "user123",
date: "2025-01-07",
type: "strength training", // èªåšåç±»ïŒææ°§è¿åšãåéè®ç»ãæé§æ§è®ç»
duration: 45, // æŽ»åšæç»æ¶éŽïŒåéïŒ
distance: 0 // ææ°§è¿åšå¯éåæ°
}
}
// AIäžäžæïŒâæä»å€©è¿è¡äº45åéçåéè®ç»â
log-nutrition - é€é£äžå¡è·¯éè·èžª// åèœïŒè®°åœé€é£å¹¶è¿è¡æºèœè¥å
»åæ
{
tool: "log-nutrition",
parameters: {
userId: "user123",
date: "2025-01-07",
meal: "breakfast", // èªå𿣿µïŒæ©é€/åé€/æé€/é¶é£
items: ["oatmeal", "banana", "almonds"], // èªç¶è¯èšæè¿°çé£ç©é¡¹ç®
calories: 350 // è®¡ç®æäŒ°ç®çå¡è·¯é
}
}
// AIäžäžæïŒâææ©é€åäºç麊çãéŠèåæä»â
log-feedback - è¿åºŠäžåšåè·èžª// åèœïŒè®°åœäž»è§çå¥èº«äœéªåè¿åºŠç¬è®°
{
tool: "log-feedback",
parameters: {
userId: "user123",
date: "2025-01-07",
notes: "ç»è¿æç»é»çŒïŒæè§æŽåŒºå£®äºïŒåå€æææŽéçééïŒ"
}
}
// AIäžäžæïŒè·èžªåšåãèœéæ°Žå¹³åäž»è§è¿åºŠ
generate-plan - AI驱åšçå¥èº«è§å// åèœïŒå建䞪æ§åçé»çŒåè¥å
»è®¡å
{
tool: "generate-plan",
parameters: {
userId: "user123"
}
}
// AIéæ³ïŒåææšçåå²è®°åœãå奜åç®æ ïŒå建ïŒ
// - æå€©çé»çŒè®¡å
// - æ¯åšé»çŒæ¶éŽè¡š
// - é€é£å»ºè®®
// - 鿥å¢å éŸåºŠçè°æŽ
view-context - å
šé¢çå¥èº«åæ// åèœïŒäžºAIå³çæäŸå®æŽçå¥èº«æ¡£æ¡
{
tool: "view-context",
parameters: {
userId: "user123"
}
}
// è¿åïŒå®æŽçå¥èº«åå²è®°åœãæš¡åŒãç®æ åæŽå¯ä¿¡æ¯
// 䜿AIèœå€ååºææºçæç»å³ç
set-weekly-target - ç®æ 讟å®äžè·èžª// åèœïŒè®Ÿå®å¹¶è·èžªå¥èº«ç®æ
{
tool: "set-weekly-target",
parameters: {
userId: "user123",
weekStart: "2025-01-06",
targetRuns: 3, // æ¯åšææ°§è¿åšç®æ
calorieBudget: 2000 // æ¯æ¥å¡è·¯éç®æ
}
}
// AIäžäžæïŒå®ç°ä»¥ç®æ 䞺富åçæç»æå¯Œåè¿åºŠè·èžª
echo - ç³»ç»å¥åº·äžæµè¯// åèœïŒæµè¯MCPè¿æ¥åç³»ç»å¥åº·ç¶åµ
{
tool: "echo",
parameters: {
message: "Testing MCP connection"
}
}
// çšéïŒè°è¯å¹¶ç¡®ä¿MCPæå¡åšååºæ£åžž
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â MCPæ°æ®æµ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â â
â çšæ·åšAIå·¥å
·äžèŸå
¥ïŒâæä»å€©åäº20äžªä¿¯å§æïŒæç»äº15åéâ â
â â â
â ⌠â
â âââââââââââââââââââââââ âââââââââââââââââââââââ âââââââââââââââââââ â
â â ð§ AIå€ç â â ð€ MCPåè®® â â ð æ°æ®ååš â â
â â â â â â â â
â â ⢠解ææåŸ âââââºâ ⢠工å
·éæ© âââââºâ ⢠ååšé»çŒè®°åœ â â
â â ⢠æåæ°æ® â â â¢ åæ°æ å° â â â¢ æŽæ°ç»è®¡ä¿¡æ¯ â â
â â â¢ éæ©æäœ â â ⢠æ§è¡å·¥å
·è°çš â â ⢠计ç®è¿åºŠ â â
â â â¢ æ ŒåŒåååº ââââââ ⢠è¿åç»æ ââââââ â â
â âââââââââââââââââââââââ âââââââââââââââââââââââ âââââââââââââââââââ â
â â â
â ⌠â
â AIååºïŒâåŸæ£ïŒæå·²è®°åœæš15åéçä¿¯å§æé»çŒãæšä»æ¥çé»çŒç®æ 已宿15/60åéãâ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â 忥å£ç³»ç» â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â â
â âââââââââââââââââââââââ âââââââââââââââââââââââ â
â â ð Webçé¢ â â ð€ AIçé¢ â â
â â â â â â
â â ⢠å¯è§å仪衚ç â â ⢠èªç¶è¯èšäº€äº â â
â â ⢠ç¹å»è®°åœåèœ â â ⢠äžäžææ¥è¯¢ â â
â â ⢠è¿åºŠåŸè¡šå±ç€º â â ⢠æºèœè§å â â
â â â¢ è®¡åæŸç€ºåèœ â â ⢠暡åŒåæ â â
â âââââââââââââââââââââââ âââââââââââââââââââââââ â
â â â â
â ⌠⌠â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â ð MCPæå¡åšæ žå¿ â â
â â â â
â â ⢠䞺䞀䞪æ¥å£æäŸç»äžçæ°æ®è®¿é® â â
â â â¢ ä¿æäžèŽçäžå¡é»èŸåéªè¯è§å â â
â â ⢠å®ç°WebåºçšåAIå·¥å
·ä¹éŽç宿¶åæ¥ â â
â â ⢠æºèœçŒååæ§èœäŒå â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
ð€ çšæ·åšCursoräžèŸå
¥ïŒâæåå宿äº30åéçHIITé»çŒâ
ð€ AI + MCPå€çæµçšïŒ
1. AIè¯å«é»çŒè®°åœæåŸã
2. MCPè°çš `log-workout` å·¥å
·ã
3. ååšïŒç±»å="HIIT"ïŒæç»æ¶éŽ=30ïŒæ¥æ=仿¥ã
4. æŽæ°æ¯æ¥è¿åºŠè®¡æ°åšã
5. AIåå€éŒå±ä¿¡æ¯å¹¶æŽæ°è¿åºŠã
ð± WebåºçšïŒèªåšæŸç€ºæŽæ°åçé»çŒæ¶é¿åé»çŒå®ææ
åµã
ð€ çšæ·åšClaudeäžèŸå
¥ïŒâæ ¹æ®ææ¬åšçé»çŒæ
åµïŒäžºæå建æå€©çé»çŒè®¡åâ
ð€ AI + MCPå€çæµçšïŒ
1. MCPè°çš `view-context` åææ¬åšçé»çŒæŽ»åšã
2. AIè¯å«æš¡åŒïŒäž»èŠæ¯äžè¢é»çŒïŒææ°§è¿åšèŸå°ã
3. MCPè°çš `generate-plan` å¹¶æäŸäžäžæä¿¡æ¯ã
4. å建äžäžªå¹³è¡¡ç计åïŒåŒºè°è
¿éšé»çŒåææ°§è¿åšã
5. ååšæå€©çé»çŒè®¡åã
ð± WebåºçšïŒæå€©çé»çŒéšåå°å¡«å
AIçæçé»çŒé¡¹ç®ã
ð€ çšæ·åšAIäžèŸå
¥ïŒâææ³ååŸæŽå¥åº·ïŒåé€åºè¯¥åä»ä¹ïŒâ
ð€ AI + MCPå€çæµçšïŒ
1. MCPè°çš `view-context` æ£æ¥ä»æ¥çé€é£åå¡è·¯éç®æ ã
2. AIåæè¥å
»çŒºå£åå¡è·¯éé¢ç®ã
3. 建议å
·äœçé€é£å¹¶æäŸå¡è·¯é计æ°ã
4. çšæ·ç¡®è®€ïŒâæå°åç€éž¡èæ²æâã
5. MCPè°çš `log-nutrition` è®°åœé€é£ã
ð± WebåºçšïŒæŽæ°è¥å
»è¿åºŠåé€é£åå²è®°åœã
// 笊åMCPçå·¥å
·å®ä¹
export const logWorkoutTool = {
name: "log-workout",
description: "è®°åœé»çŒäŒè¯ïŒå
æ¬ç±»åãæç»æ¶éŽåå¯éçè·çŠ»",
inputSchema: {
type: "object",
properties: {
userId: { type: "string", description: "å¯äžçšæ·æ è¯ç¬Š" },
date: { type: "string", description: "æ¥æïŒæ ŒåŒäžºYYYY-MM-DD" },
type: { type: "string", description: "é»çŒç±»åïŒåŠ 'è·æ¥'ã'åéè®ç»'ïŒ" },
duration: { type: "number", description: "æç»æ¶éŽïŒåéïŒ" },
distance: { type: "number", description: "è·çŠ»ïŒå
¬éïŒå¯éïŒ" }
},
required: ["userId", "date", "type", "duration"]
}
}
// å
·ææä¹
åé©åçå
åååš
export const workoutStore = createInMemoryStore<WorkoutEntry>()
export const nutritionStore = createInMemoryStore<NutritionEntry>()
export const planStore = createInMemoryStore<PlanEntry>()
// MCPå·¥å
·å®ç°
export async function logWorkout(params: LogWorkoutParams) {
const entry: WorkoutEntry = {
id: generateId(),
userId: params.userId,
date: params.date,
type: params.type,
duration: params.duration,
distance: params.distance,
timestamp: new Date().toISOString()
}
workoutStore.set(entry.id, entry)
return { success: true, entry }
}
// äžMCPéæçè倩æ¥å£
export async function processFitnessQuery(message: string, userId: string) {
const intent = detectIntent(message)
switch (intent.type) {
case 'log_workout':
return await callMCPTool('log-workout', {
userId,
date: intent.date,
type: intent.workoutType,
duration: intent.duration
})
case 'generate_plan':
return await callMCPTool('generate-plan', { userId })
case 'view_progress':
return await callMCPTool('view-context', { userId })
}
}
# æµè¯ææMCPå·¥å
·
npm run test:fitness
# æµè¯HTTPäŒ èŸ
npm run test:http
# æµè¯MCPéæ
npm run test:mcp
# è°è¯å·¥å
·åèœ
npm run debug:tools
// æµè¯é»çŒè®°åœ
const result = await mcpClient.callTool('log-workout', {
userId: 'test-user',
date: '2025-01-07',
type: 'running',
duration: 30,
distance: 5.0
})
console.log('é»çŒè®°åœæå:', result)
npm run dev å¯åšåŒåæå¡åšãhttp://localhost:3000/mcp 访é®ãhttp://localhost:3000 访é®ã# çšäºAIçæå¥èº«è®¡åçOpenAI APIå¯é¥
OPENAI_API_KEY=your_openai_api_key_here
# å¯éçRedisïŒçšäºæé«æ§èœ
UPSTASH_REDIS_REST_URL=your_redis_url
UPSTASH_REDIS_REST_TOKEN=your_redis_token
# åçšæ·è®Ÿçœ®çèªå®ä¹çšæ·ID
DEFAULT_USER_ID=my-fitness-journey
# çšäºè°è¯çæ¥å¿çº§å«
LOG_LEVEL=debug
# åŒåçšçèªå®ä¹ç«¯å£
PORT=3000
// åštools/ç®åœäžå建æ°å·¥å
·
export const myCustomTool = {
name: "my-custom-tool",
description: "该工å
·çåèœæè¿°",
inputSchema: {
// å®ä¹åæ°
},
handler: async (params) => {
// å®ç°é»èŸ
}
}
// åštools/index.tsäžæ³šå
export const tools = [
// ... ç°æå·¥å
·
myCustomTool
]
components/ ç®åœäžæ·»å æ°ç»ä»¶ãapp/api/ äžå建æ°çAPIè·¯ç±ãapp/page.tsx äžæŽæ°äž»ä»ªè¡šçãæ¬é¡¹ç®éçšMIT讞å¯è¯ïŒæšå¯ä»¥èªç±äœ¿çšæ¬é¡¹ç®äœäžºåºç¡ïŒåŒåèªå·±çMCPæå¡åšåå¥èº«åºçšçšåºãæ¬é¡¹ç®å±ç€ºäºåŠäœæå»ºç产就绪çMCPæå¡åšïŒå°AIå·¥å ·äžç¹å®é¢åçåºçšçšåºè¿æ¥èµ·æ¥ïŒäžºå ¶ä»é¢åçç±»äŒŒéææäŸäºæš¡æ¿ã
ð€ ç±æš¡åäžäžæå议驱åš
å°AIæºèœäžç°å®äžççå¥èº«æ°æ®çžè¿æ¥