Skip to content

Commit 94aa376

Browse files
committed
feat: v2.5.0 — add 4 prompts (find_best_tool, compare_pricing, evaluate_tool, category_overview), fix stdin close race condition, expose prompts capability
1 parent 954ffcc commit 94aa376

3 files changed

Lines changed: 790 additions & 8 deletions

File tree

index.js

Lines changed: 125 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env node
22
/**
3-
* ComparEdge MCP Server v2.4.0
3+
* ComparEdge MCP Server v2.5.0
44
* MCP protocol version 2025-03-26
55
* JSON-RPC 2.0 over stdio, zero npm dependencies
66
* Data source: comparedge.com (508 products, live)
@@ -604,14 +604,102 @@ function makeError(id, code, message) {
604604
return JSON.stringify({ jsonrpc: '2.0', id, error: { code, message } });
605605
}
606606

607+
// --- Prompts ---
608+
609+
const PROMPT_DEFINITIONS = [
610+
{
611+
name: 'find_best_tool',
612+
description: 'Find the best software tool for a specific use case, job, or requirement. Returns ranked recommendations with pricing and free plan status.',
613+
arguments: [
614+
{ name: 'use_case', description: 'What you need to do (e.g. "manage customer relationships", "write code faster", "send email campaigns")', required: true },
615+
],
616+
},
617+
{
618+
name: 'compare_pricing',
619+
description: 'Side-by-side pricing and feature comparison of two software tools. Shows plan names, prices, and key differences to help with a buying decision.',
620+
arguments: [
621+
{ name: 'tool_a', description: 'First tool name or slug (e.g. "Notion", "notion")', required: true },
622+
{ name: 'tool_b', description: 'Second tool name or slug (e.g. "Linear", "linear")', required: true },
623+
],
624+
},
625+
{
626+
name: 'evaluate_tool',
627+
description: 'Full evaluation of a software tool: profile overview, verified pricing plans, and top alternatives in the same category.',
628+
arguments: [
629+
{ name: 'tool', description: 'Tool name or slug (e.g. "HubSpot", "hubspot")', required: true },
630+
],
631+
},
632+
{
633+
name: 'category_overview',
634+
description: 'Overview of the best tools in a software category. Returns top-rated tools with pricing, free plan availability, and ComparEdge links.',
635+
arguments: [
636+
{ name: 'category', description: 'Software category (e.g. "CRM", "project management", "AI coding", "LLM")', required: true },
637+
],
638+
},
639+
];
640+
641+
async function getPrompt(name, args) {
642+
const a = args || {};
643+
if (name === 'find_best_tool') {
644+
const use_case = a.use_case || 'productivity tools';
645+
return {
646+
messages: [{
647+
role: 'user',
648+
content: {
649+
type: 'text',
650+
text: `I need to find the best software tool for: "${use_case}". Please search ComparEdge using search_tools to find relevant options, then use get_pricing on the top 2-3 results to compare costs. Present a ranked recommendation with: tool name, starting price, free plan availability, and a direct link. Focus on verified pricing data.`,
651+
},
652+
}],
653+
};
654+
}
655+
if (name === 'compare_pricing') {
656+
const a_name = a.tool_a || 'notion';
657+
const b_name = a.tool_b || 'linear';
658+
return {
659+
messages: [{
660+
role: 'user',
661+
content: {
662+
type: 'text',
663+
text: `Compare the pricing of "${a_name}" vs "${b_name}". Use search_tools to find the correct slug for each, then call compare_tools to get a structured comparison. Also call get_pricing on both for full plan details. Present the results as a clear side-by-side table showing: plan names, prices, billing intervals, and which is better value at each tier.`,
664+
},
665+
}],
666+
};
667+
}
668+
if (name === 'evaluate_tool') {
669+
const tool = a.tool || 'notion';
670+
return {
671+
messages: [{
672+
role: 'user',
673+
content: {
674+
type: 'text',
675+
text: `Give me a full evaluation of "${tool}". Use search_tools to find the slug, then: (1) call get_tool for the full profile, (2) call get_pricing for all pricing plans, (3) call get_alternatives to see what competitors exist. Present: overview, pricing table, pros/cons, and top 3 alternatives with prices.`,
676+
},
677+
}],
678+
};
679+
}
680+
if (name === 'category_overview') {
681+
const category = a.category || 'crm';
682+
return {
683+
messages: [{
684+
role: 'user',
685+
content: {
686+
type: 'text',
687+
text: `Give me an overview of the best "${category}" tools. Use list_categories to find the correct category slug, then call get_leaderboard for the top tools. For the top 3, call get_pricing to get plan details. Present a ranked comparison table with: rank, tool name, rating, starting price, free plan, and a ComparEdge link.`,
688+
},
689+
}],
690+
};
691+
}
692+
throw new Error(`Prompt not found: ${name}`);
693+
}
694+
607695
async function handleRequest(req) {
608696
const { id, method, params } = req;
609697

610698
if (method === 'initialize') {
611699
return makeResponse(id, {
612700
protocolVersion: '2025-03-26',
613-
capabilities: { tools: {} },
614-
serverInfo: { name: 'comparedge-mcp-server', version: '2.4.0' },
701+
capabilities: { tools: {}, prompts: {} },
702+
serverInfo: { name: 'comparedge-mcp-server', version: '2.5.0' },
615703
});
616704
}
617705

@@ -634,6 +722,20 @@ async function handleRequest(req) {
634722
}
635723
}
636724

725+
if (method === 'prompts/list') {
726+
return makeResponse(id, { prompts: PROMPT_DEFINITIONS });
727+
}
728+
729+
if (method === 'prompts/get') {
730+
const { name, arguments: args } = params || {};
731+
try {
732+
const result = await getPrompt(name, args);
733+
return makeResponse(id, result);
734+
} catch (err) {
735+
return makeError(id, -32602, err.message);
736+
}
737+
}
738+
637739
if (method === 'notifications/initialized') {
638740
return null;
639741
}
@@ -645,6 +747,13 @@ async function handleRequest(req) {
645747

646748
const rl = createInterface({ input: process.stdin, terminal: false });
647749

750+
let _pendingRequests = 0;
751+
let _stdinClosed = false;
752+
753+
function maybeExit() {
754+
if (_stdinClosed && _pendingRequests === 0) process.exit(0);
755+
}
756+
648757
rl.on('line', async (line) => {
649758
const trimmed = line.trim();
650759
if (!trimmed) return;
@@ -655,10 +764,19 @@ rl.on('line', async (line) => {
655764
process.stdout.write(makeError(null, -32700, 'Parse error') + '\n');
656765
return;
657766
}
658-
const response = await handleRequest(req);
659-
if (response !== null) {
660-
process.stdout.write(response + '\n');
767+
_pendingRequests++;
768+
try {
769+
const response = await handleRequest(req);
770+
if (response !== null) {
771+
process.stdout.write(response + '\n');
772+
}
773+
} finally {
774+
_pendingRequests--;
775+
maybeExit();
661776
}
662777
});
663778

664-
rl.on('close', () => process.exit(0));
779+
rl.on('close', () => {
780+
_stdinClosed = true;
781+
maybeExit();
782+
});

0 commit comments

Comments
 (0)