@c-na/pantry-mcp
Version:
Minimal MCP for getpantry.cloud JSON storage API.
112 lines (111 loc) • 4.03 kB
JavaScript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import axios from 'axios';
const PANTRY_API_BASE = 'https://getpantry.cloud/apiv1/pantry';
function resolvePantryVars(input) {
const pantryId = input.pantryId || process.env.PANTRY_ID;
const basketName = input.basketName || process.env.BASKET_NAME;
if (!pantryId || !basketName) {
throw new Error('pantryId and basketName are required (either as input or environment variables)');
}
return { pantryId, basketName };
}
// Parse CLI args like --pantry-id=xxx --basket-name=yyy
process.argv.forEach((arg) => {
if (arg.startsWith('--pantry-id=')) {
process.env.PANTRY_ID = arg.split('=')[1];
}
if (arg.startsWith('--basket-name=')) {
process.env.BASKET_NAME = arg.split('=')[1];
}
});
async function main() {
const server = new McpServer({
name: 'Pantry MCP Server',
version: '1.0.0',
});
// GET basket
server.tool('getBasket', {
pantryId: z.string().optional(),
basketName: z.string().optional(),
}, async ({ pantryId, basketName }) => {
const { pantryId: pid, basketName: bname } = resolvePantryVars({
pantryId,
basketName,
});
const url = `${PANTRY_API_BASE}/${pid}/basket/${bname}`;
const response = await axios.get(url);
return {
content: [{ type: 'text', text: JSON.stringify(response.data) }],
};
});
// POST basket (create/replace)
server.tool('postBasket', {
pantryId: z.string().optional(),
basketName: z.string().optional(),
value: z.record(z.any()),
}, async ({ pantryId, basketName, value }) => {
const { pantryId: pid, basketName: bname } = resolvePantryVars({
pantryId,
basketName,
});
const url = `${PANTRY_API_BASE}/${pid}/basket/${bname}`;
const response = await axios.post(url, value);
return {
content: [{ type: 'text', text: JSON.stringify(response.data) }],
};
});
// PUT basket (update/merge)
server.tool('putBasket', {
pantryId: z.string().optional(),
basketName: z.string().optional(),
value: z.record(z.any()),
}, async ({ pantryId, basketName, value }) => {
const { pantryId: pid, basketName: bname } = resolvePantryVars({
pantryId,
basketName,
});
const url = `${PANTRY_API_BASE}/${pid}/basket/${bname}`;
// Fetch current basket
let current = {};
try {
const getResp = await axios.get(url);
current = getResp.data;
}
catch (e) {
// If not found, treat as empty
if (!(e && e.response && e.response.status === 404))
throw e;
}
// Merge current and new value
const merged = { ...current, ...value };
const response = await axios.post(url, merged);
return {
content: [{ type: 'text', text: JSON.stringify(response.data) }],
};
});
// DELETE basket
server.tool('deleteBasket', {
pantryId: z.string().optional(),
basketName: z.string().optional(),
}, async ({ pantryId, basketName }) => {
const { pantryId: pid, basketName: bname } = resolvePantryVars({
pantryId,
basketName,
});
const url = `${PANTRY_API_BASE}/${pid}/basket/${bname}`;
const response = await axios.delete(url);
return {
content: [{ type: 'text', text: JSON.stringify(response.data) }],
};
});
// Add more tools as needed for other CRUD operations
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch((err) => {
console.error('Failed to start Pantry MCP server:', err);
process.exit(1);
});