CoinGecko MCP Server
- src
import { UnifiedChatApi, Message, MODELS_LIST, Role } from "unichat-ts";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListPromptsRequestSchema,
GetPromptRequestSchema,
SetLevelRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
// Environment validation
const MODEL = process.env.UNICHAT_MODEL;
if (!MODEL) {
console.error("UNICHAT_MODEL environment variable required");
process.exit(1);
}
if (!Object.values(MODELS_LIST).flat().includes(MODEL)) {
console.error(`Unsupported model: ${MODEL}`);
process.exit(1);
}
const API_KEY = process.env.UNICHAT_API_KEY;
if (!API_KEY) {
console.error("UNICHAT_API_KEY environment variable required");
process.exit(1);
}
// Validation functions
function validateMessages(messages: Message[]): void {
if (messages.length !== 2) {
throw new Error("Exactly two messages are required: one system message and one user message");
}
if (messages[0].role !== "system") {
throw new Error("First message must have role 'system'");
}
if (messages[1].role !== "user") {
throw new Error("Second message must have role 'user'");
}
}
// Format response helper
function formatResponse(response: string) {
try {
const formatted = { type: "text", text: response.trim() };
return formatted;
} catch (e) {
return { type: "text", text: `Error formatting response: ${String(e)}` };
}
}
// Define prompts
const PROMPTS = [
{
name: "code_review",
description: "Review code for best practices, potential issues, and improvements",
arguments: [{
name: "code",
description: "The code to review",
required: true
}]
},
{
name: "document_code",
description: "Generate documentation for code including docstrings and comments",
arguments: [{
name: "code",
description: "The code to document",
required: true
}]
},
{
name: "explain_code",
description: "Explain how a piece of code works in detail",
arguments: [{
name: "code",
description: "The code to explain",
required: true
}]
},
{
name: "code_rework",
description: "Apply requested changes to the provided code",
arguments: [
{
name: "changes",
description: "The changes to apply",
required: false
},
{
name: "code",
description: "The code to rework",
required: true
}
]
}
];
const PROMPT_TEMPLATES = {
"code_review": `You are a senior software engineer conducting a thorough code review.
Review the following code for:
- Best practices
- Potential bugs
- Performance issues
- Security concerns
- Code style and readability
Code to review:
{code}`,
"document_code": `You are a technical documentation expert.
Generate comprehensive documentation for the following code.
Include:
- Overview
- Function/class documentation
- Parameter descriptions
- Return value descriptions
- Usage examples
Code to document:
{code}`,
"explain_code": `You are a programming instructor explaining code to a beginner level programmer.
Explain how the following code works:
{code}
Break down:
- Overall purpose
- Key components
- How it works step by step
- Any important concepts used`,
"code_rework": `You are a software architect specializing in code optimization and modernization.
With a foucs on:
- Modernizing syntax and approaches
- Improving structure and organization
- Enhancing maintainability
- Optimizing performance
- Applying current best practices
Do: {changes}
Code to transform:
{code}`
};
// Create server instance
export const createServer = () => {
const server = new Server(
{
name: "unichat-ts-mcp-server",
version: "0.2.6",
},
{
capabilities: {
logging: {},
tools: {},
prompts: {},
},
}
);
// Tool handlers
server.setRequestHandler(SetLevelRequestSchema, async (request) => {
const { level } = request.params;
await server.notification({
method: "notifications/message",
params: {
level: "debug",
logger: "unichat-ts-mcp-server",
data: `Logging level set to: ${level}`,
},
});
return {};
});
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "unichat",
description: `Chat with an assistant.
Example tool use message:
Ask the unichat to review and evaluate your proposal.`,
inputSchema: {
type: "object",
properties: {
messages: {
type: "array",
items: {
type: "object",
properties: {
role: {
type: "string",
description: "The role of the message sender. Must be either 'system' or 'user'",
enum: ["system", "user"]
},
content: {
type: "string",
description: "The content of the message. For system messages, this should define the context or task. For user messages, this should contain the specific query."
},
},
required: ["role", "content"],
},
minItems: 2,
maxItems: 2,
description: "Array of exactly two messages: first a system message defining the task, then a user message with the specific query"
},
},
required: ["messages"]
}
}
]
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
switch (request.params.name) {
case "unichat": {
try {
const messages = request.params.arguments?.messages as Message[];
validateMessages(messages);
const client = new UnifiedChatApi({apiKey: API_KEY});
const response = await client.chat.completions.create({
model: MODEL,
messages: messages,
stream: false
});
const formatted_response = formatResponse(response.choices[0].message.content.toString());
return {
content: [formatted_response],
};
} catch (e) {
throw new Error(`An error occurred: ${String(e)}`);
}
}
default:
throw new Error(`Unknown tool: ${request.params.name}`);
}
});
// Prompt handlers
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: [
...Object.values(PROMPTS)
]
};
});
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const promptNames: string[] = PROMPTS.map(prompt => prompt.name);
if (promptNames.includes(name)) {
if (!args) {
throw new Error("Missing arguments");
}
if (!args.code) {
throw new Error("Missing required argument: code");
}
const template = PROMPT_TEMPLATES[name as keyof typeof PROMPT_TEMPLATES];
const systemContent = template
.replace("{code}", args.code)
.replace("{changes}", args.changes || "");
try {
const client = new UnifiedChatApi({apiKey: API_KEY});
const response = await client.chat.completions.create({
model: MODEL,
messages: [
{"role": Role.System, "content": systemContent},
{"role": Role.User, "content": "Please provide your analysis."}
],
stream: false
});
const formatted_response = formatResponse(response.choices[0].message.content.toString());
return {
description: "Requested code manipulation",
messages: [
{
role: "user",
content: formatted_response
}
],
};
} catch (e) {
throw new Error(`An error occurred: ${String(e)}`);
}
}
throw new Error("Unknown prompt");
});
return { server };
};