nutri-cal 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/.env +4 -0
  2. package/index.js +132 -0
  3. package/package.json +19 -0
package/.env ADDED
@@ -0,0 +1,4 @@
1
+ REDIS_HOST=127.0.0.1
2
+ REDIS_PORT=6379
3
+ REDIS_PASSWORD=
4
+ REDIS_DB=0
package/index.js ADDED
@@ -0,0 +1,132 @@
1
+ require("dotenv").config();
2
+ const { McpServer } = require("@modelcontextprotocol/sdk/server/mcp.js");
3
+ const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
4
+ const { z } = require("zod");
5
+ const Redis = require("ioredis");
6
+
7
+ // Redis configuration
8
+ const SCORED_REDIS_CONFIG = {
9
+ host: process.env.REDIS_HOST || "127.0.0.1",
10
+ port: process.env.REDIS_PORT || 6379,
11
+ db: process.env.REDIS_DB || 0,
12
+ password: process.env.REDIS_PASSWORD,
13
+ };
14
+
15
+ const redis = new Redis(SCORED_REDIS_CONFIG);
16
+
17
+ // Create an MCP server
18
+ const server = new McpServer({
19
+ name: "nutri-cal-demo",
20
+ version: "1.0.0",
21
+ });
22
+
23
+ // Add the hello_world tool
24
+ // server.tool(
25
+ // "hello_world",
26
+ // {
27
+ // name: z.string().optional().describe("A name to greet"),
28
+ // },
29
+ // async ({ name }) => {
30
+ // const greetingName = name || "World";
31
+ // return {
32
+ // content: [
33
+ // {
34
+ // type: "text",
35
+ // text: `Hello ${greetingName}`,
36
+ // },
37
+ // ],
38
+ // };
39
+ // }
40
+ // );
41
+
42
+ // Add the calculate_nutrition tool
43
+ server.tool(
44
+ "calculate_nutrition",
45
+ {
46
+ chatID: z.string().describe("The chat session ID"),
47
+ ingredientName: z.string().describe("The name of the ingredient"),
48
+ amount: z.number().describe("The amount of ingredient in grams"),
49
+ },
50
+ async ({ chatID, ingredientName, amount }) => {
51
+ try {
52
+ // 1. Fetch food data from Redis
53
+ const foodKey = `food:${ingredientName}`;
54
+ const foodData = await redis.hgetall(foodKey);
55
+
56
+ if (!foodData || Object.keys(foodData).length === 0) {
57
+ return {
58
+ isError: true,
59
+ content: [
60
+ {
61
+ type: "text",
62
+ text: `Food item '${ingredientName}' not found in database.`,
63
+ },
64
+ ],
65
+ };
66
+ }
67
+
68
+ // 2. Parse and calculate nutrition values (based on 100g)
69
+ // Original values are per 100g
70
+ const multiplier = amount / 100.0;
71
+
72
+ const calculatedNutrition = {
73
+ Protein: parseFloat(((parseFloat(foodData.Protein) || 0) * multiplier).toFixed(2)),
74
+ Fats: parseFloat(((parseFloat(foodData.Fats) || 0) * multiplier).toFixed(2)),
75
+ Carbs: parseFloat(((parseFloat(foodData.Carbs) || 0) * multiplier).toFixed(2)),
76
+ Fiber: parseFloat(((parseFloat(foodData.Fiber) || 0) * multiplier).toFixed(2)),
77
+ Calories: parseFloat(((parseFloat(foodData.Calories) || 0) * multiplier).toFixed(2)),
78
+ };
79
+
80
+ // 3. Store/Accumulate in Redis atomically
81
+ const userKey = `cal_res:${chatID}`;
82
+
83
+ const pipeline = redis.multi();
84
+ pipeline.hincrbyfloat(userKey, "Protein", calculatedNutrition.Protein);
85
+ pipeline.hincrbyfloat(userKey, "Fats", calculatedNutrition.Fats);
86
+ pipeline.hincrbyfloat(userKey, "Carbs", calculatedNutrition.Carbs);
87
+ pipeline.hincrbyfloat(userKey, "Fiber", calculatedNutrition.Fiber);
88
+ pipeline.hincrbyfloat(userKey, "Calories", calculatedNutrition.Calories);
89
+ pipeline.expire(userKey, 600); // Set expiration to 10 minutes (600 seconds)
90
+
91
+ await pipeline.exec();
92
+
93
+ return {
94
+ content: [
95
+ {
96
+ type: "text",
97
+ text: `Calculated nutrition for ${amount}g of ${ingredientName}:
98
+ Protein: ${calculatedNutrition.Protein.toFixed(2)}g
99
+ Fats: ${calculatedNutrition.Fats.toFixed(2)}g
100
+ Carbs: ${calculatedNutrition.Carbs.toFixed(2)}g
101
+ Fiber: ${calculatedNutrition.Fiber.toFixed(2)}g
102
+ Calories: ${calculatedNutrition.Calories.toFixed(2)}kcal
103
+
104
+ Successfully added to daily total for ChatID: ${chatID} (Expires in 10 minutes)`,
105
+ },
106
+ ],
107
+ };
108
+
109
+ } catch (error) {
110
+ return {
111
+ isError: true,
112
+ content: [
113
+ {
114
+ type: "text",
115
+ text: `Error processing request: ${error.message}`,
116
+ },
117
+ ],
118
+ };
119
+ }
120
+ }
121
+ );
122
+
123
+ async function main() {
124
+ const transport = new StdioServerTransport();
125
+ await server.connect(transport);
126
+ console.error("Nutri-Cal MCP Server running on stdio");
127
+ }
128
+
129
+ main().catch((error) => {
130
+ console.error("Fatal error in main():", error);
131
+ process.exit(1);
132
+ });
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "nutri-cal",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [],
10
+ "author": "",
11
+ "license": "ISC",
12
+ "type": "commonjs",
13
+ "dependencies": {
14
+ "@modelcontextprotocol/sdk": "^1.25.2",
15
+ "dotenv": "^17.2.3",
16
+ "ioredis": "^5.9.0",
17
+ "zod": "^4.3.5"
18
+ }
19
+ }