Skip to content

OpenAI SDK Integration

Integrate PlateBreaker with AI agents using the OpenAI SDK and Model Context Protocol.

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.

Terminal window
npm install openai @anthropic-ai/sdk @modelcontextprotocol/sdk

Create mcp-config.json:

{
"mcpServers": {
"platebreaker": {
"command": "npx",
"args": ["-y", "@platebreaker/mcp-server"],
"env": {
"PLATEBREAKER_API_KEY": "your_api_key_here"
}
}
}
}
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,
},
});

The MCP server exposes these tools to AI agents:

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 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"]
}
}

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 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)"
}
}
}
}

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 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 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"]
}
}
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;
}
// Usage
const result = await nutritionCoach(
'user_123',
"What should I eat for dinner tonight to improve my nutrition?"
);
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;
}

Users must authenticate with PlateBreaker:

// 1. Redirect to PlateBreaker OAuth
function 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 callback
async 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 };
}
// Set up MCP with user token
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,
PLATEBREAKER_USER_TOKEN: userAccessToken, // User-specific token
},
});
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);
}
}
  • 100 requests/minute per user
  • 1000 requests/hour per application
  • Rate limit headers included in responses