Rene Fichtmueller 92f42832bf feat: Phase 2 — MCP Server with 12 tools
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
2026-03-27 16:48:34 +13:00

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),
}],
};
}
);
}