#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ErrorCode,
ListToolsRequestSchema,
McpError,
} from "@modelcontextprotocol/sdk/types.js";
import { connectDatabase } from "./config/database.js";
import { Product, type Perfume } from "./models/Product.js";
const server = new Server(
{
name: "blue-perfumery-mcp",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Handler for listing available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "list_all_perfumes",
description: "List all perfumes in the Blue Perfumery collection",
inputSchema: {
type: "object",
properties: {},
required: [],
},
},
{
name: "get_perfume_by_id",
description: "Get a specific perfume by its ID",
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: "The perfume ID",
},
},
required: ["id"],
},
},
{
name: "search_perfumes",
description: "Search perfumes by name or brand",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "Search query for perfume name or brand",
},
},
required: ["query"],
},
},
{
name: "get_perfumes_by_category",
description: "Get perfumes by category (men, women, niche)",
inputSchema: {
type: "object",
properties: {
category: {
type: "string",
description: "Category: 'men', 'women', or 'niche'",
enum: ["men", "women", "niche"],
},
},
required: ["category"],
},
},
{
name: "get_purchase_link",
description: "Get the Shopier purchase link for a specific perfume",
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: "The perfume ID",
},
},
required: ["id"],
},
},
],
};
});
// Handler for tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "list_all_perfumes": {
const perfumes = await Product.find({ status: "active" }).lean();
return {
content: [
{
type: "text",
text: JSON.stringify({
success: true,
count: perfumes.length,
perfumes: perfumes,
}, null, 2),
},
],
};
}
case "get_perfume_by_id": {
const { id } = args as { id: string };
if (!id) {
throw new McpError(
ErrorCode.InvalidParams,
"ID parameter is required"
);
}
const perfume = await Product.findOne({ id, status: "active" }).lean();
if (!perfume) {
return {
content: [
{
type: "text",
text: JSON.stringify({
success: false,
error: `Perfume with ID '${id}' not found`,
}, null, 2),
},
],
};
}
return {
content: [
{
type: "text",
text: JSON.stringify({
success: true,
perfume: perfume,
}, null, 2),
},
],
};
}
case "search_perfumes": {
const { query } = args as { query: string };
if (!query) {
throw new McpError(
ErrorCode.InvalidParams,
"Query parameter is required"
);
}
const results = await Product.find({
status: "active",
$or: [
{ name: { $regex: query, $options: "i" } },
{ brand: { $regex: query, $options: "i" } },
{ description: { $regex: query, $options: "i" } },
],
}).lean();
return {
content: [
{
type: "text",
text: JSON.stringify({
success: true,
query: query,
count: results.length,
perfumes: results,
}, null, 2),
},
],
};
}
case "get_perfumes_by_category": {
const { category } = args as { category: string };
if (!category) {
throw new McpError(
ErrorCode.InvalidParams,
"Category parameter is required"
);
}
if (!["men", "women", "niche"].includes(category)) {
throw new McpError(
ErrorCode.InvalidParams,
"Category must be 'men', 'women', or 'niche'"
);
}
let filter: any = { status: "active" };
if (category === "niche" || category === "niches") {
// Niche perfumes - filter by category containing exclusive/artisanal/premium
filter.$or = [
{ category: { $regex: "exclusive", $options: "i" } },
{ category: { $regex: "artisanal", $options: "i" } },
{ category: { $regex: "premium", $options: "i" } },
{ category: "niches" },
];
} else if (category === "men" || category === "male") {
// Men's perfumes - male or unisex
filter.$or = [
{ gender: "male" },
{ gender: "unisex" },
];
} else if (category === "women" || category === "female") {
// Women's perfumes - female or unisex
filter.$or = [
{ gender: "female" },
{ gender: "unisex" },
];
}
const results = await Product.find(filter).lean();
return {
content: [
{
type: "text",
text: JSON.stringify({
success: true,
category: category,
count: results.length,
perfumes: results,
}, null, 2),
},
],
};
}
case "get_purchase_link": {
const { id } = args as { id: string };
if (!id) {
throw new McpError(
ErrorCode.InvalidParams,
"ID parameter is required"
);
}
const perfume = await Product.findOne({ id, status: "active" }).lean();
if (!perfume) {
return {
content: [
{
type: "text",
text: JSON.stringify({
success: false,
error: `Perfume with ID '${id}' not found`,
}, null, 2),
},
],
};
}
if (!perfume.shopierLink) {
return {
content: [
{
type: "text",
text: JSON.stringify({
success: false,
error: `No purchase link available for '${perfume.name}'`,
}, null, 2),
},
],
};
}
return {
content: [
{
type: "text",
text: JSON.stringify({
success: true,
perfume: {
id: perfume.id,
name: perfume.name,
brand: perfume.brand,
price: perfume.price,
},
purchaseLink: perfume.shopierLink,
message: `You can purchase ${perfume.name} from Blue Perfumery via Shopier`,
}, null, 2),
},
],
};
}
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${name}`
);
}
} catch (error) {
if (error instanceof McpError) {
throw error;
}
throw new McpError(
ErrorCode.InternalError,
`Error executing tool ${name}: ${error}`
);
}
});
// Start the server
async function main() {
try {
// Connect to database first
await connectDatabase();
// Then start the MCP server
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Blue Perfumery MCP server running on stdio");
} catch (error) {
console.error("Failed to start MCP server:", error);
process.exit(1);
}
}
main().catch((error) => {
console.error("Server error:", error);
process.exit(1);
});