// src/index.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { createPodiumClient } from '@podium-sdk/node-sdk';
import { z } from 'zod';
const client = createPodiumClient({
apiKey: process.env.PODIUM_API_KEY!,
});
const server = new McpServer({
name: 'podium-commerce',
version: '1.0.0',
});
// --- Product Discovery ---
server.tool(
'search_products',
{
categories: z.string().optional().describe('Comma-separated category filter'),
limit: z.number().min(1).max(50).default(10),
page: z.number().min(1).default(1),
},
async ({ categories, limit, page }) => {
const feed = await client.agentic.listProductsFeed({ categories, limit, page });
const summary = feed.products.map((p: any) => ({
id: p.id,
name: p.name,
brand: p.brand,
price: p.price,
intentScore: p.intentScore,
categories: p.categories,
}));
return {
content: [{
type: 'text',
text: JSON.stringify({ products: summary, total: feed.total, page }, null, 2),
}],
};
}
);
server.tool(
'get_product',
{ productId: z.string().describe('Product ID to look up') },
async ({ productId }) => {
const product = await client.product.get({ id: productId });
return {
content: [{ type: 'text', text: JSON.stringify(product, null, 2) }],
};
}
);
// --- Companion / Taste Profile ---
server.tool(
'get_profile',
{ userId: z.string().describe('Podium user ID') },
async ({ userId }) => {
try {
const profile = await client.companion.listProfile({ userId });
return {
content: [{ type: 'text', text: JSON.stringify(profile, null, 2) }],
};
} catch {
return {
content: [{ type: 'text', text: 'No profile found for this user.' }],
};
}
}
);
server.tool(
'update_profile',
{
userId: z.string(),
skinType: z.string().optional(),
concerns: z.array(z.string()).optional(),
priceMin: z.number().optional(),
priceMax: z.number().optional(),
brands: z.array(z.string()).optional(),
avoidances: z.array(z.string()).optional(),
},
async ({ userId, skinType, concerns, priceMin, priceMax, brands, avoidances }) => {
const requestBody: Record<string, any> = {};
if (skinType) requestBody.skinType = skinType;
if (concerns) requestBody.concerns = concerns;
if (priceMin !== undefined || priceMax !== undefined) {
requestBody.priceRange = { min: priceMin ?? 0, max: priceMax ?? 500 };
}
if (brands) requestBody.brands = brands;
if (avoidances) requestBody.avoidances = avoidances;
const profile = await client.companion.updateProfile({ userId, requestBody });
return {
content: [{ type: 'text', text: JSON.stringify(profile, null, 2) }],
};
}
);
server.tool(
'get_recommendations',
{
userId: z.string().describe('Podium user ID'),
count: z.number().min(1).max(20).default(5),
category: z.string().optional(),
},
async ({ userId, count, category }) => {
const recs = await client.companion.listRecommendations({ userId, count, category });
return {
content: [{ type: 'text', text: JSON.stringify(recs, null, 2) }],
};
}
);
// --- Points & Loyalty ---
server.tool(
'check_points',
{ userId: z.string().describe('Podium user ID') },
async ({ userId }) => {
const points = await client.user.listPoints({ id: userId });
return {
content: [{
type: 'text',
text: `Balance: ${points.balance} points\nLifetime earned: ${points.totalEarned}\nLifetime spent: ${points.totalSpent}`,
}],
};
}
);
// --- Checkout ---
server.tool(
'create_checkout',
{
productId: z.string(),
quantity: z.number().min(1).default(1),
email: z.string().email(),
firstName: z.string(),
lastName: z.string(),
street: z.string(),
city: z.string(),
state: z.string(),
postalCode: z.string(),
countryCode: z.string().default('US'),
},
async ({ productId, quantity, email, firstName, lastName, street, city, state, postalCode, countryCode }) => {
const session = await client.agentic.createCheckoutSessions({
requestBody: {
items: [{ id: productId, quantity }],
shippingAddress: {
firstName,
lastName,
address: {
line1: street,
line2: null,
city,
state,
postalCode,
countryCode,
},
},
},
});
await client.agentic.updateCheckoutSessions({
id: session.id,
requestBody: { email },
});
return {
content: [{
type: 'text',
text: JSON.stringify({
sessionId: session.id,
total: session.total,
status: session.status,
message: 'Checkout session created. Payment can be completed via x402 USDC.',
}, null, 2),
}],
};
}
);
// --- Tasks ---
server.tool(
'list_tasks',
{},
async () => {
const tasks = await client.tasks.listTasks();
return {
content: [{ type: 'text', text: JSON.stringify(tasks, null, 2) }],
};
}
);
// --- Connect and run ---
const transport = new StdioServerTransport();
await server.connect(transport);