#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
interface GenerateTestimonialInput {
product_name: string;
customer_type: string;
outcome: string;
tone?: "professional" | "casual" | "enthusiastic";
include_metrics?: boolean;
}
interface GeneratedTestimonial {
name: string;
role: string;
company: string;
avatar: string;
text: string;
rating: number;
}
async function generateTestimonial(
input: GenerateTestimonialInput
): Promise<GeneratedTestimonial> {
const { product_name, customer_type, outcome, tone = "professional", include_metrics = true } = input;
const metricsInstruction = include_metrics
? "Include a specific metric or number (like '34% increase' or 'saved 10 hours/week') to make it credible."
: "Focus on qualitative benefits rather than specific numbers.";
const toneInstructions = {
professional: "Use a polished, business-appropriate tone.",
casual: "Use a friendly, conversational tone with personality.",
enthusiastic: "Use an excited, high-energy tone that shows genuine delight.",
};
const prompt = `Generate a realistic customer testimonial for a product. Return ONLY valid JSON, no other text.
Product: ${product_name}
Customer type: ${customer_type}
Key outcome/benefit: ${outcome}
Tone: ${toneInstructions[tone]}
${metricsInstruction}
Create a fictional but realistic person who fits the customer type. The testimonial should:
- Be 2-3 sentences max
- Sound authentic (not marketing-speak)
- Mention the specific outcome
- Feel like something a real person would say
Return this exact JSON structure:
{
"name": "Full Name",
"role": "Job Title",
"company": "Company Name",
"avatar": "XX",
"text": "The testimonial text...",
"rating": 5
}
Where avatar is the person's initials (2 letters).`;
const message = await anthropic.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 500,
messages: [{ role: "user", content: prompt }],
});
const responseText =
message.content[0].type === "text" ? message.content[0].text : "";
// Parse JSON from response
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
if (!jsonMatch) {
throw new Error("Failed to parse testimonial response");
}
const testimonial = JSON.parse(jsonMatch[0]) as GeneratedTestimonial;
// Validate required fields
if (!testimonial.name || !testimonial.text || !testimonial.role) {
throw new Error("Generated testimonial missing required fields");
}
return testimonial;
}
// Create MCP server
const server = new Server(
{
name: "proofbase-mcp",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "generate_testimonial",
description:
"Generate a realistic customer testimonial for a product. Creates a fictional but believable testimonial with customer details, suitable for social proof.",
inputSchema: {
type: "object",
properties: {
product_name: {
type: "string",
description: "Name of the product the testimonial is for",
},
customer_type: {
type: "string",
description:
"Type of customer (e.g., 'SaaS founder', 'Marketing manager', 'Freelance designer')",
},
outcome: {
type: "string",
description:
"The key benefit or outcome to highlight (e.g., 'increased conversions', 'saved time', 'better customer engagement')",
},
tone: {
type: "string",
enum: ["professional", "casual", "enthusiastic"],
description: "Tone of the testimonial (default: professional)",
},
include_metrics: {
type: "boolean",
description:
"Whether to include specific metrics/numbers (default: true)",
},
},
required: ["product_name", "customer_type", "outcome"],
},
},
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "generate_testimonial") {
try {
const input = args as unknown as GenerateTestimonialInput;
if (!input.product_name || !input.customer_type || !input.outcome) {
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Missing required fields: product_name, customer_type, outcome",
}),
},
],
isError: true,
};
}
const testimonial = await generateTestimonial(input);
return {
content: [
{
type: "text",
text: JSON.stringify(testimonial, null, 2),
},
],
};
} catch (error) {
const message = error instanceof Error ? error.message : "Unknown error";
return {
content: [
{
type: "text",
text: JSON.stringify({ error: message }),
},
],
isError: true,
};
}
}
return {
content: [
{
type: "text",
text: JSON.stringify({ error: `Unknown tool: ${name}` }),
},
],
isError: true,
};
});
// Start server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("ProofBase MCP server running on stdio");
}
main().catch(console.error);