import { Router, Request, Response } from 'express';
import { products, orders } from './data';
const router = Router();
// MCP Protocol Types
interface JsonRpcRequest {
jsonrpc: string;
method: string;
params?: any;
id: number | string | null;
}
interface JsonRpcResponse {
jsonrpc: string;
result?: any;
error?: {
code: number;
message: string;
data?: any;
};
id: number | string | null;
}
// Server capabilities and info
const SERVER_INFO = {
name: "azure-product-catalog-server",
version: "1.0.0"
};
const CAPABILITIES = {
tools: {},
resources: {}
};
// Tool definitions
const TOOLS = [
{
name: "get_products",
description: "Retrieve a list of all products in the catalog",
inputSchema: {
type: "object",
properties: {},
required: []
}
},
{
name: "get_product_by_id",
description: "Get details of a specific product by its ID",
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: "The product ID"
}
},
required: ["id"]
}
},
{
name: "search_products",
description: "Search products by query string (searches name, description, and category)",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "The search query"
}
},
required: ["query"]
}
},
{
name: "get_orders",
description: "Retrieve a list of all orders",
inputSchema: {
type: "object",
properties: {},
required: []
}
},
{
name: "create_order",
description: "Create a new order for a product",
inputSchema: {
type: "object",
properties: {
productId: {
type: "string",
description: "ID of the product to order"
},
quantity: {
type: "number",
description: "Quantity to order"
}
},
required: ["productId", "quantity"]
}
}
];
// Tool handlers
function handleGetProducts(): any {
return {
content: [{ type: "text", text: JSON.stringify(products, null, 2) }]
};
}
function handleGetProductById(args: { id: string }): any {
const product = products.find(p => p.id === args.id);
if (!product) {
return {
content: [{ type: "text", text: JSON.stringify({ error: "Product not found" }) }],
isError: true
};
}
return {
content: [{ type: "text", text: JSON.stringify(product, null, 2) }]
};
}
function handleSearchProducts(args: { query: string }): any {
const query = args.query.toLowerCase();
const results = products.filter(p =>
p.name.toLowerCase().includes(query) ||
p.description.toLowerCase().includes(query) ||
p.category.toLowerCase().includes(query)
);
return {
content: [{ type: "text", text: JSON.stringify(results, null, 2) }]
};
}
function handleGetOrders(): any {
return {
content: [{ type: "text", text: JSON.stringify(orders, null, 2) }]
};
}
function handleCreateOrder(args: { productId: string; quantity: number }): any {
const product = products.find(p => p.id === args.productId);
if (!product) {
return {
content: [{ type: "text", text: JSON.stringify({ error: `Product ${args.productId} not found` }) }],
isError: true
};
}
const newOrder = {
orderId: `ord-${Date.now()}`,
productId: args.productId,
productName: product.name,
quantity: args.quantity,
unitPrice: product.price,
totalPrice: product.price * args.quantity,
status: "created" as const,
createdAt: new Date().toISOString()
};
orders.push(newOrder);
return {
content: [{ type: "text", text: JSON.stringify(newOrder, null, 2) }]
};
}
// MCP Request Handlers
function handleInitialize(params: any): any {
return {
protocolVersion: "2024-11-05",
capabilities: CAPABILITIES,
serverInfo: SERVER_INFO
};
}
function handleToolsList(): any {
return { tools: TOOLS };
}
function handleToolsCall(params: { name: string; arguments?: any }): any {
const toolName = params.name;
const args = params.arguments || {};
switch (toolName) {
case "get_products":
return handleGetProducts();
case "get_product_by_id":
return handleGetProductById(args);
case "search_products":
return handleSearchProducts(args);
case "get_orders":
return handleGetOrders();
case "create_order":
return handleCreateOrder(args);
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
// POST /api/mcp - MCP endpoint
router.post('/mcp', (req: Request, res: Response) => {
try {
console.log('MCP request body:', JSON.stringify(req.body));
const body = req.body as JsonRpcRequest;
if (!body || !body.method) {
res.status(400).json({
jsonrpc: "2.0",
error: { code: -32600, message: "Invalid request: missing method" },
id: null
});
return;
}
console.log(`MCP request: ${body.method}`);
let result: any;
switch (body.method) {
case "initialize":
result = handleInitialize(body.params);
break;
case "tools/list":
result = handleToolsList();
break;
case "tools/call":
result = handleToolsCall(body.params);
break;
case "notifications/initialized":
res.status(204).send();
return;
default:
const errorResponse: JsonRpcResponse = {
jsonrpc: "2.0",
error: { code: -32601, message: `Method not found: ${body.method}` },
id: body.id
};
res.json(errorResponse);
return;
}
const response: JsonRpcResponse = {
jsonrpc: "2.0",
result,
id: body.id
};
res.json(response);
} catch (error: any) {
console.error("MCP error:", error);
const response: JsonRpcResponse = {
jsonrpc: "2.0",
error: { code: -32603, message: error.message || "Internal error" },
id: null
};
res.json(response);
}
});
export default router;