OpenAI SDK Integration
Integrate PlateBreaker with AI agents using the OpenAI SDK and Model Context Protocol.
Overview
Section titled “Overview”The PlateBreaker MCP server enables AI applications built with the OpenAI SDK to access personalized nutrition data, recommend recipes, and help users plan meals. Compatible with Claude, GPT-4, and other models that support function calling.
Quick Start
Section titled “Quick Start”1. Install Dependencies
Section titled “1. Install Dependencies”npm install openai @anthropic-ai/sdk @modelcontextprotocol/sdk2. Configure MCP Server
Section titled “2. Configure MCP Server”Create mcp-config.json:
{ "mcpServers": { "platebreaker": { "command": "npx", "args": ["-y", "@platebreaker/mcp-server"], "env": { "PLATEBREAKER_API_KEY": "your_api_key_here" } } }}3. Initialize Client
Section titled “3. Initialize Client”import { Anthropic } from '@anthropic-ai/sdk';import { MCPClient } from '@modelcontextprotocol/sdk/client/index.js';
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY,});
const mcp = new MCPClient({ name: 'platebreaker', version: '1.0.0',});
await mcp.connect({ command: 'npx', args: ['-y', '@platebreaker/mcp-server'], env: { PLATEBREAKER_API_KEY: process.env.PLATEBREAKER_API_KEY, },});Available Tools
Section titled “Available Tools”The MCP server exposes these tools to AI agents:
get_nutrition_targets
Section titled “get_nutrition_targets”Get authenticated user’s daily nutritional targets.
{ name: "get_nutrition_targets", description: "Get user's personalized daily nutrition targets", input_schema: { type: "object", properties: { userId: { type: "string", description: "Authenticated user ID" } }, required: ["userId"] }}get_current_intake
Section titled “get_current_intake”Get user’s nutrition intake for today.
{ name: "get_current_intake", description: "Get user's current nutrition intake for the day", input_schema: { type: "object", properties: { userId: { type: "string", description: "Authenticated user ID" }, date: { type: "string", description: "Date in YYYY-MM-DD format (defaults to today)" } }, required: ["userId"] }}get_nutrient_gaps
Section titled “get_nutrient_gaps”Identify user’s current nutrient deficiencies.
{ name: "get_nutrient_gaps", description: "Calculate which nutrients user is low on today", input_schema: { type: "object", properties: { userId: { type: "string", description: "Authenticated user ID" }, priority: { type: "string", enum: ["all", "critical", "moderate"], description: "Filter gaps by priority level" } }, required: ["userId"] }}search_recipes_by_nutrients
Section titled “search_recipes_by_nutrients”Search recipes that meet nutritional criteria.
{ name: "search_recipes_by_nutrients", description: "Find recipes matching specific nutrient requirements", input_schema: { type: "object", properties: { nutrients: { type: "array", items: { type: "object", properties: { name: { type: "string" }, min: { type: "number" }, max: { type: "number" } } }, description: "Nutrient criteria (e.g., protein >= 25g)" }, mealType: { type: "string", enum: ["breakfast", "lunch", "dinner", "snack"], description: "Filter by meal type" }, diet: { type: "array", items: { type: "string", enum: ["vegetarian", "vegan", "glutenfree", "dairyfree", "keto", "paleo"] }, description: "Dietary restrictions" }, limit: { type: "number", description: "Maximum results (default: 10)" } } }}recommend_recipes
Section titled “recommend_recipes”Get AI-powered recipe recommendations based on user’s gaps.
{ name: "recommend_recipes", description: "Get recipe recommendations that fill user's nutrient gaps", input_schema: { type: "object", properties: { userId: { type: "string", description: "Authenticated user ID" }, mealType: { type: "string", enum: ["breakfast", "lunch", "dinner", "snack"], description: "What meal to recommend for" }, limit: { type: "number", description: "Number of recommendations (default: 5)" } }, required: ["userId"] }}add_to_meal_plan
Section titled “add_to_meal_plan”Add a recipe to user’s meal plan.
{ name: "add_to_meal_plan", description: "Add recipe to user's meal plan", input_schema: { type: "object", properties: { userId: { type: "string", description: "Authenticated user ID" }, recipeId: { type: "string", description: "Recipe to add" }, date: { type: "string", description: "Date in YYYY-MM-DD format" }, mealType: { type: "string", enum: ["breakfast", "lunch", "dinner", "snack"], description: "Which meal slot" } }, required: ["userId", "recipeId", "date", "mealType"] }}log_meal
Section titled “log_meal”Log a meal to user’s nutrition tracker.
{ name: "log_meal", description: "Log a meal to user's nutrition tracking", input_schema: { type: "object", properties: { userId: { type: "string", description: "Authenticated user ID" }, recipeId: { type: "string", description: "Recipe that was eaten" }, servings: { type: "number", description: "Number of servings consumed (default: 1)" }, timestamp: { type: "string", description: "When meal was eaten (ISO 8601 format)" } }, required: ["userId", "recipeId"] }}Example: Nutrition Coach Agent
Section titled “Example: Nutrition Coach Agent”import { Anthropic } from '@anthropic-ai/sdk';
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY,});
async function nutritionCoach(userId: string, userMessage: string) { const response = await client.messages.create({ model: 'claude-3-5-sonnet-20241022', max_tokens: 1024, tools: [ { name: 'get_nutrient_gaps', description: "Get user's current nutrient deficiencies", input_schema: { type: 'object', properties: { userId: { type: 'string' }, priority: { type: 'string', enum: ['all', 'critical', 'moderate'] } }, required: ['userId'] } }, { name: 'recommend_recipes', description: "Recommend recipes that fill nutrient gaps", input_schema: { type: 'object', properties: { userId: { type: 'string' }, mealType: { type: 'string' }, limit: { type: 'number' } }, required: ['userId'] } } ], messages: [ { role: 'user', content: userMessage } ], system: `You are a helpful nutrition coach. Help users meet their nutrition goals by: 1. Analyzing their current nutrient gaps 2. Recommending recipes that fill those gaps 3. Explaining the nutritional benefits 4. Providing meal planning advice
Always check the user's current gaps before making recommendations.` });
return response;}
// Usageconst result = await nutritionCoach( 'user_123', "What should I eat for dinner tonight to improve my nutrition?");Example: Multi-Turn Conversation
Section titled “Example: Multi-Turn Conversation”async function handleConversation(userId: string, messages: Array<any>) { const response = await client.messages.create({ model: 'claude-3-5-sonnet-20241022', max_tokens: 2048, tools: mcpTools, messages: messages, system: `You are a nutrition expert helping users achieve their health goals.` });
// Handle tool calls if (response.stop_reason === 'tool_use') { const toolUse = response.content.find(block => block.type === 'tool_use');
// Execute tool via MCP const toolResult = await mcp.callTool({ name: toolUse.name, arguments: toolUse.input });
// Continue conversation with tool result messages.push({ role: 'assistant', content: response.content }); messages.push({ role: 'user', content: [{ type: 'tool_result', tool_use_id: toolUse.id, content: JSON.stringify(toolResult) }] });
// Get final response return await handleConversation(userId, messages); }
return response;}Authentication
Section titled “Authentication”OAuth Flow
Section titled “OAuth Flow”Users must authenticate with PlateBreaker:
// 1. Redirect to PlateBreaker OAuthfunction initiateAuth() { const authUrl = new URL('https://auth.platebreaker.com/oauth/authorize'); authUrl.searchParams.set('client_id', process.env.PB_CLIENT_ID); authUrl.searchParams.set('redirect_uri', 'https://yourapp.com/callback'); authUrl.searchParams.set('scope', 'nutrition.read meal_plan.write'); authUrl.searchParams.set('response_type', 'code');
window.location.href = authUrl.toString();}
// 2. Handle callbackasync function handleCallback(code: string) { const response = await fetch('https://auth.platebreaker.com/oauth/token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ grant_type: 'authorization_code', code, client_id: process.env.PB_CLIENT_ID, client_secret: process.env.PB_CLIENT_SECRET, redirect_uri: 'https://yourapp.com/callback' }) });
const { access_token, user_id } = await response.json(); return { accessToken: access_token, userId: user_id };}Using Tokens
Section titled “Using Tokens”// Set up MCP with user tokenconst mcp = new MCPClient({ name: 'platebreaker', version: '1.0.0',});
await mcp.connect({ command: 'npx', args: ['-y', '@platebreaker/mcp-server'], env: { PLATEBREAKER_API_KEY: process.env.PLATEBREAKER_API_KEY, PLATEBREAKER_USER_TOKEN: userAccessToken, // User-specific token },});Error Handling
Section titled “Error Handling”try { const result = await mcp.callTool({ name: 'get_nutrient_gaps', arguments: { userId: 'user_123' } });} catch (error) { if (error.code === 'UNAUTHORIZED') { // Token expired or invalid await refreshToken(); } else if (error.code === 'NOT_FOUND') { // User doesn't exist console.error('User not found'); } else { // Generic error handling console.error('MCP error:', error); }}Rate Limiting
Section titled “Rate Limiting”- 100 requests/minute per user
- 1000 requests/hour per application
- Rate limit headers included in responses
Next Steps
Section titled “Next Steps”- MCP Overview - Learn about the protocol
- GitHub Repository - View source code