Model Context Protocol allows us to integrate code with LLMs and do A LOT MORE than just generating text.
It was created by Anthropic, founded by OpenAI ex-members. Their chatbot Claude is a AI assistant.
In order to test I wanted to allow Claude to help me calculate my macros and review my diet.
First of all I need to calculate my Basal Metabolic Rate, an approximation of how many calories my body needs per day just to survive. No formula is precise and I wanted to try out different formulas and calculate de average of the results.
If you want to lose weight, your daily caloric intake should be less than your real BMR, so your body starts using body fat to keep working properly. If you want to gain weight, your caloric intake should be more than your BMR. If you are also going to the gym and want to gain muscle, having a higher caloric intake is helpful, because your body will be comfortable repairing and growing your muscles.
I started creating two Zod functions for the formulas. They don't need necessarily to be written with Zod, but it helps.
export const BmrInputSchemaFields = {
weight: z.number().positive(), // in kg
height: z.number().positive(), // in cm
age: z.number().positive(),
gender: z.enum(['male', 'female']),
activityLevel: z.enum([
'sedentary',
'lightly_active',
'moderately_active',
'highly_active',
'extremely_active',
]),
}
const BmrInputSchema = z.object(BmrInputSchemaFields)
export const calculateHarrisBenedictBMR = z
.function()
.args(BmrInputSchema)
.returns(z.number())
.implement(({ weight, height, age, gender, activityLevel }) => {
const activityFactor = activityMultipliers[activityLevel]!
let bmr: number
if (gender === 'male') {
bmr = 66 + 13.7 * weight + 5 * height - 6.8 * age
} else {
bmr = 655 + 9.6 * weight + 1.8 * height - 4.7 * age
}
return bmr * activityFactor
})
export const calculateMifflinStJeorBMR = z
.function()
.args(BmrInputSchema)
.returns(z.number())
.implement(({ weight, height, age, gender, activityLevel }) => {
const activityFactor = activityMultipliers[activityLevel]!
let bmr: number
if (gender === 'male') {
bmr = 10 * weight + 6.25 * height - 5 * age + 5
} else {
bmr = 10 * weight + 6.25 * height - 5 * age - 161
}
return bmr * activityFactor
})
In Model Context Protocol we first initialize a server
and then define tools
using "kebab-case" and the model will be smart enough to identify this tools when needed.
Observe that I had to split BmrInputSchemaFields
and BmrInputSchema
. For some reason MCP doesn't allow zod Objects, just a JSON with the fields.
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
const server = new McpServer({
name: 'Jarvis',
version: '1.0.0',
})
server.tool(
'calculate-harris-benedict-bmr',
BmrInputSchemaFields,
async (input) => {
const res = calculateHarrisBenedictBMR(input)
return { content: [{ type: 'text', text: String(res) }] }
}
)
server.tool(
'calculate-mifflin-st-jeor-bmr',
BmrInputSchemaFields,
async (input) => {
const res = calculateMifflinStJeorBMR(input)
return { content: [{ type: 'text', text: String(res) }] }
}
)
async function main() {
const transport = new StdioServerTransport()
await server.connect(transport)
console.error('MCP Server running on stdio')
}
main().catch((error) => {
console.error('Fatal error in main():', error)
process.exit(1)
})
To use this server we need to download Claude Desktop.
To configure Model Context Protocols you need to go to: File > Settings... > Developer > Edit Config
and paste the following config in claude_desktop_config.json
Remember to change the path in args
to where your server is located.
{
"mcpServers": {
"jarvis": {
"command": "deno",
"args": [
"run",
"-A",
"C:\\Users\\pedro\\Desktop\\apps\\jarvis\\src\\index.ts"
]
}
}
}
In this particular case, Claude should also be able to calculate my BMR without me providing the functions in TypeScript, but that makes it a lot more precise and we can be sure the results will always be the same.
This by itself is enough and Claude should be able to use this functions. But every time you prompt it will ask again for the inputs and also, it doesn't have access to which foods I have neither how many calories each contains.
I personally use Obsidian to my private notes which sit on my Desktop and have two files in it: My Data
and Nutrition Facts
.
import { readFileSync } from 'node:fs'
server.tool('get-data-about-me', {}, async () => {
const text = readFileSync('C:\\Users\\jp\\Desktop\\Obsidian\\My Data.md', {
encoding: 'utf8',
})
return { content: [{ type: 'text', text }] }
})
server.tool('get-data-about-my-foods', {}, async () => {
const text = readFileSync(
'C:\\Users\\jp\\Desktop\\Obsidian\\Nutrition Facts.md',
{ encoding: 'utf8' }
)
return { content: [{ type: 'text', text }] }
})
This is my Nutrion Facts
table file.
| Food Item | Quantity | kcal | Carbs | Protein | Fat | Sodium |
|-------------------------|----------|------|-------|---------|------|--------|
| Whey with Milk | 1 | 243 | 12.7 | 30 | 8 | 163 |
| Milk | 200ml | 117 | 9.6 | 6.2 | 6 | 110 |
| Chicken | 100g | 94 | 0 | 23 | 1 | 52 |
| Dulce de Leche | 20g | 67 | 12 | 1.5 | 1.4 | 30 |
| Rice | 100g | 130 | 28 | 2.7 | 0.3 | 1 |
| Whey | 30g | 126 | 3.1 | 24 | 2 | 53 |
| Light Cream Cheese | 100g | 154 | 1.8 | 12 | 11 | 489 |
| Oats | 30g | 111 | 17 | 4.2 | 2.3 | 0 |
| Ketchup | 12g | 13 | 2.9 | 0.1 | 0 | 71 |
| Beef | 100g | 115 | 0 | 21 | 3.4 | 73 |
| Pork Rump | 100g | 166 | 0.5 | 17 | 11 | 599 |
| Seasoning | 5g | 11 | 0.3 | 0 | 0 | 985 |
| Peanut Butter | 20g | 121 | 1.6 | 5.6 | 10 | 0 |
In My Data
it contains every my BmrInputSchema
schema needs in a unstructured way.
This is what it may looks like
Male
6,0 foot (1,8m)
30 years
Workout 7x a week
Very strong
I prefer to eat whey with milk in the morning, rice and chicken at lunch.
The model will have no trouble trying to understand this two.
Now you can ask Claude something like this and it will do all the heavy lifting for you.
using the fitness MCP I want you to read data about me and my foods and create a 4 meal diet based on the median of my BMR.
Remember than with code you can call APIs, read spreadsheets, interact with files and a lot more. The sky is the limit.