/** * Content tools: get_market_news, generate_blog_draft */ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { pool } from "../db.js"; export async function registerContentTools(server: McpServer): Promise { // --- Tool: get_market_news --- server.tool( "get_market_news", "Get latest news from the optics and networking industry. Filtered by relevance to transceiver technology.", { query: z.string().optional().describe("Optional: search within news content"), event: z.enum(["OFC", "ECOC", "CIOE", "PHOTONICS_WEST"]).optional().describe("Filter by trade show/event"), days_back: z.number().default(30).describe("How many days back to look (default: 30)"), min_relevance: z.number().default(0).describe("Minimum relevance score (0=all, 3=transceiver-related, 9=highly relevant)"), }, async ({ query, event, days_back, min_relevance }) => { const conditions = [`na.published_at > NOW() - INTERVAL '${days_back} days'`]; const values: unknown[] = []; let idx = 1; if (query) { conditions.push(`(na.search_vector @@ plainto_tsquery('english', $${idx}) OR na.title ILIKE $${idx + 1})`); values.push(query, `%${query}%`); idx += 2; } if (event) { conditions.push(`na.event = $${idx}`); values.push(event); idx++; } if (min_relevance > 0) { conditions.push(`na.relevance_score >= $${idx}`); values.push(min_relevance); idx++; } const result = await pool.query( `SELECT na.title, na.source, na.published_at, na.summary, na.source_url, na.relevance_score, na.event, na.mentioned_vendors, na.mentioned_products FROM news_articles na WHERE ${conditions.join(" AND ")} ORDER BY na.relevance_score DESC, na.published_at DESC LIMIT 20`, values ); const bySource: Record = {}; for (const row of result.rows) { bySource[row.source] = (bySource[row.source] || 0) + 1; } return { content: [{ type: "text", text: JSON.stringify({ articles: result.rows, count: result.rows.length, sources: bySource, period: `Last ${days_back} days`, }, null, 2), }], }; } ); // --- Tool: generate_blog_draft --- server.tool( "generate_blog_draft", "Generate a blog post draft based on market data, price trends, hype cycle position, and recent news. Saved to blog_drafts table for review.", { topic: z.enum(["hype_cycle", "price_trend", "new_product", "comparison", "tutorial"]), technology: z.string().optional().describe("Technology to focus on, e.g. '800G OSFP', 'DWDM', '400G'"), target_audience: z.enum(["sales", "technical", "customer", "seo"]).default("technical"), }, async ({ topic, technology, target_audience }) => { // Gather data for the blog post const data: Record = { topic, technology, target_audience }; // Get relevant transceivers if (technology) { const txResult = await pool.query( `SELECT t.standard_name, t.form_factor, t.speed, t.reach_label, t.fiber_type, t.category, (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 MAX(po.price) FROM price_observations po WHERE po.transceiver_id = t.id AND po.time > NOW() - INTERVAL '7 days') as max_price FROM transceivers t WHERE t.standard_name ILIKE $1 OR t.speed ILIKE $1 LIMIT 10`, [`%${technology}%`] ); data.transceivers = txResult.rows; } // Get recent news const newsResult = await pool.query( `SELECT title, source, published_at, summary FROM news_articles WHERE ($1 IS NULL OR title ILIKE $1 OR summary ILIKE $1) ORDER BY relevance_score DESC, published_at DESC LIMIT 5`, [technology ? `%${technology}%` : null] ); data.recent_news = newsResult.rows; // Get price trends const priceResult = await pool.query( `SELECT t.standard_name, t.speed, AVG(po.price) as avg_price, MIN(po.price) as min_price, COUNT(DISTINCT po.source_vendor_id) as vendor_count FROM price_observations po JOIN transceivers t ON t.id = po.transceiver_id WHERE ($1 IS NULL OR t.standard_name ILIKE $1 OR t.speed ILIKE $1) AND po.time > NOW() - INTERVAL '30 days' GROUP BY t.id, t.standard_name, t.speed ORDER BY vendor_count DESC LIMIT 10`, [technology ? `%${technology}%` : null] ); data.price_trends = priceResult.rows; // Generate blog outline based on topic and audience const outlines: Record> = { hype_cycle: { sales: [ `## Where is ${technology || "The Market"} on the Hype Cycle?`, "## What This Means for Your Customers", "## When to Buy: Timing the Market", "## Flexoptix Recommendation", ], technical: [ `## ${technology || "Technology"} Market Analysis — Norton-Bass Diffusion Model`, "## Current Phase: Technical Readiness", "## Vendor Ecosystem Status", "## Price Trajectory & ASP Forecast", "## Deployment Considerations", ], customer: [ `## Is ${technology || "This Technology"} Right for You?`, "## Cost vs. Performance Analysis", "## Compatibility & Migration Path", "## When Will Prices Drop?", ], seo: [ `## ${technology || "Optical Transceiver"} Market 2026: Complete Guide`, "## Best Vendors & Pricing Comparison", "## Compatibility Guide", "## FAQ", ], }, price_trend: { sales: ["## Price Alert", "## Competitor Pricing Analysis", "## Sales Opportunity"], technical: ["## Price Trend Analysis", "## ASP History", "## Market Drivers"], customer: ["## How Much Should You Pay?", "## Price Forecast", "## When to Buy"], seo: ["## Price Guide 2026", "## Best Deals", "## Comparison Table"], }, comparison: { sales: ["## Why Flexoptix Beats the Competition", "## Price Advantage", "## Quality & Compatibility"], technical: ["## Vendor Comparison", "## Spec Analysis", "## Performance Benchmarks"], customer: ["## OEM vs. Compatible: The Facts", "## Risk Analysis", "## Cost Savings"], seo: ["## Best Transceiver Vendors 2026", "## Comparison Table", "## Reviews"], }, tutorial: { technical: ["## Prerequisites", "## Step-by-Step Configuration", "## Troubleshooting", "## Verification"], customer: ["## Getting Started", "## Installation Guide", "## Tips & Tricks"], sales: ["## Product Overview", "## Use Cases", "## Getting Support"], seo: ["## How To Guide", "## Step-by-Step", "## FAQ"], }, new_product: { sales: ["## New Product Alert", "## What's New", "## Pricing & Availability"], technical: ["## Technical Specs", "## Compatibility Matrix", "## Performance Data"], customer: ["## What You Get", "## Why You Need It", "## How to Order"], seo: ["## Product Announcement", "## Specs & Features", "## Where to Buy"], }, }; const outline = outlines[topic]?.[target_audience] || []; // Build draft content const draft = { title: `${technology || "Optical Transceiver"} ${topic.replace(/_/g, " ")} — ${new Date().getFullYear()} Analysis`, topic, technology, target_audience, outline, data_points: data, generation_note: "This is a data-driven draft. Review and enrich with specific product details before publishing.", generated_at: new Date().toISOString(), status: "draft", }; // Save to blog_drafts table await pool.query( `INSERT INTO blog_drafts (title, topic, technology, target_audience, outline, draft_content, status) VALUES ($1, $2, $3, $4, $5, $6, 'draft') ON CONFLICT DO NOTHING`, [ draft.title, topic, technology || null, target_audience, JSON.stringify(outline), JSON.stringify(draft), ] ); return { content: [{ type: "text", text: JSON.stringify({ draft, saved_to_database: true, next_steps: [ "Review the outline and data points", "Enrich with specific product examples from search_transceivers", "Add competitor pricing from compare_prices", "Include current news context from get_market_news", "Submit to content team for writing/editing", ], }, null, 2), }], }; } ); }