Implements all 12 MCP tools from CONCEPT document: - search_transceivers: Full-text + spec filter search with pricing - check_compatibility: Switch ↔ transceiver compatibility lookup - get_pricing: Current prices + 30-day history across all vendors - compare_prices: Multi-vendor price comparison with savings analysis - get_competitor_stock: Live competitor stock monitoring (sales opportunities) - suggest_alternatives: Similar spec alternatives optimized for price/availability - get_templates: FlexBox coding and switch config template finder - search_knowledge_base: Troubleshooting FAQ search (PostgreSQL full-text) - search_manuals: Switch manual and datasheet search - get_hype_cycle: Norton-Bass adoption forecast + Gartner phase classification - get_market_news: Aggregated news with relevance scoring - generate_blog_draft: Data-driven blog drafts saved to blog_drafts table Transport: stdio (MCP protocol 2024-11-05) Config: .mcp.json for Claude Code integration Verified: all 12 tools registered, search_transceivers returns DB results
145 lines
5.2 KiB
TypeScript
145 lines
5.2 KiB
TypeScript
/**
|
|
* Compatibility & template tools: suggest_alternatives, get_templates
|
|
*/
|
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
import { z } from "zod";
|
|
import { pool } from "../db.js";
|
|
|
|
export async function registerCompatibilityTools(server: McpServer): Promise<void> {
|
|
// --- Tool: suggest_alternatives ---
|
|
server.tool(
|
|
"suggest_alternatives",
|
|
"Suggest alternative transceivers with similar specs. Useful for finding cheaper or more available options.",
|
|
{
|
|
part_number: z.string().describe("Part number, slug, or standard name"),
|
|
optimize_for: z.enum(["price", "availability", "performance"]).default("price"),
|
|
},
|
|
async ({ part_number, optimize_for }) => {
|
|
// Find the reference transceiver
|
|
const refResult = await pool.query(
|
|
`SELECT t.id, t.slug, t.standard_name, t.form_factor, t.speed_gbps,
|
|
t.reach_meters, t.fiber_type, t.wdm_type, t.connector
|
|
FROM transceivers t
|
|
WHERE t.slug ILIKE $1 OR t.part_number ILIKE $1 OR t.standard_name ILIKE $1
|
|
LIMIT 1`,
|
|
[`%${part_number}%`]
|
|
);
|
|
|
|
if (refResult.rows.length === 0) {
|
|
return {
|
|
content: [{ type: "text", text: `Reference transceiver not found: "${part_number}"` }],
|
|
};
|
|
}
|
|
|
|
const ref = refResult.rows[0];
|
|
|
|
// Find alternatives: same form_factor + speed, similar reach
|
|
const altResult = await pool.query(
|
|
`SELECT t.id, t.slug, t.standard_name, t.form_factor, t.speed, t.reach_label,
|
|
t.fiber_type, t.connector, v.name as vendor,
|
|
(SELECT MIN(po.price) FROM price_observations po
|
|
WHERE po.transceiver_id = t.id
|
|
AND po.time > NOW() - INTERVAL '7 days'
|
|
) as min_price,
|
|
(SELECT COUNT(DISTINCT po.source_vendor_id) FROM price_observations po
|
|
WHERE po.transceiver_id = t.id
|
|
AND po.stock_level = 'in_stock'
|
|
AND po.time > NOW() - INTERVAL '7 days'
|
|
) as vendor_count
|
|
FROM transceivers t
|
|
LEFT JOIN vendors v ON v.id = t.vendor_id
|
|
WHERE t.form_factor = $1
|
|
AND t.speed_gbps = $2
|
|
AND t.fiber_type = $3
|
|
AND ABS(t.reach_meters - $4) <= $4 * 0.2
|
|
AND t.id != $5
|
|
ORDER BY
|
|
CASE $6
|
|
WHEN 'price' THEN (SELECT MIN(po.price) FROM price_observations po WHERE po.transceiver_id = t.id)
|
|
WHEN 'availability' THEN -(SELECT COUNT(DISTINCT po.source_vendor_id) FROM price_observations po WHERE po.transceiver_id = t.id AND po.stock_level = 'in_stock')
|
|
ELSE t.power_consumption_w
|
|
END ASC NULLS LAST
|
|
LIMIT 10`,
|
|
[ref.form_factor, ref.speed_gbps, ref.fiber_type, ref.reach_meters, ref.id, optimize_for]
|
|
);
|
|
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: JSON.stringify({
|
|
reference: {
|
|
slug: ref.slug,
|
|
standard: ref.standard_name,
|
|
form_factor: ref.form_factor,
|
|
speed_gbps: ref.speed_gbps,
|
|
reach_meters: ref.reach_meters,
|
|
},
|
|
optimize_for,
|
|
alternatives: altResult.rows,
|
|
}, null, 2),
|
|
}],
|
|
};
|
|
}
|
|
);
|
|
|
|
// --- Tool: get_templates ---
|
|
server.tool(
|
|
"get_templates",
|
|
"Find FlexBox coding templates or switch configuration templates for specific vendor/technology combinations.",
|
|
{
|
|
type: z.enum(["flexbox_coding", "switch_config"]).describe("Template type"),
|
|
switch_vendor: z.string().optional().describe("Switch vendor, e.g. 'Cisco', 'Juniper', 'Arista'"),
|
|
transceiver_type: z.string().optional().describe("e.g. 'SFP+', 'QSFP28', '10G', '100G'"),
|
|
technology: z.string().optional().describe("CWDM, DWDM, BiDi, SR, LR, ER, ZR, etc."),
|
|
},
|
|
async ({ type, switch_vendor, transceiver_type, technology }) => {
|
|
const conditions = [`t.template_type = $1`];
|
|
const values: unknown[] = [type];
|
|
let idx = 2;
|
|
|
|
if (switch_vendor) {
|
|
conditions.push(`t.switch_vendor ILIKE $${idx}`);
|
|
values.push(`%${switch_vendor}%`);
|
|
idx++;
|
|
}
|
|
if (transceiver_type) {
|
|
conditions.push(`t.transceiver_type ILIKE $${idx}`);
|
|
values.push(`%${transceiver_type}%`);
|
|
idx++;
|
|
}
|
|
if (technology) {
|
|
conditions.push(`t.technology ILIKE $${idx}`);
|
|
values.push(`%${technology}%`);
|
|
idx++;
|
|
}
|
|
|
|
const result = await pool.query(
|
|
`SELECT t.id, t.name, t.template_type, t.switch_vendor, t.transceiver_type,
|
|
t.technology, t.content, t.description, t.notes,
|
|
t.verified, t.firmware_version
|
|
FROM templates t
|
|
WHERE ${conditions.join(" AND ")}
|
|
ORDER BY t.verified DESC, t.name ASC
|
|
LIMIT 20`,
|
|
values
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: `No ${type} templates found for the given criteria. Templates are added as they are verified in the field.`,
|
|
}],
|
|
};
|
|
}
|
|
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: JSON.stringify({ templates: result.rows, count: result.rows.length }, null, 2),
|
|
}],
|
|
};
|
|
}
|
|
);
|
|
}
|