server.js•13.5 kB
#!/usr/bin/env node
/**
* MCP Server Example - Learning Demo
*
* This is a comprehensive example of a Model Context Protocol (MCP) server
* that demonstrates the three main capabilities:
* 1. Tools - Functions that the AI can call to perform actions
* 2. Resources - Data sources that the AI can read from
* 3. Prompts - Template prompts that can be used with variables
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ErrorCode,
ListPromptsRequestSchema,
ListResourcesRequestSchema,
ListToolsRequestSchema,
McpError,
ReadResourceRequestSchema,
GetPromptRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
/**
* What is MCP?
*
* Model Context Protocol (MCP) is a protocol that allows AI assistants
* to connect to external data sources and tools. It provides three main
* capabilities:
*
* 1. TOOLS: Functions the AI can call to perform actions
* - Like calling APIs, running calculations, or modifying data
*
* 2. RESOURCES: Data sources the AI can read from
* - Like files, databases, or web content
*
* 3. PROMPTS: Template prompts with variables
* - Reusable prompt templates that can be customized
*/
class LearningMCPServer {
constructor() {
this.server = new Server(
{
name: 'learning-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
resources: {},
prompts: {},
},
}
);
this.setupToolHandlers();
this.setupResourceHandlers();
this.setupPromptHandlers();
}
/**
* TOOLS SECTION
* Tools are functions that the AI can call to perform actions
*/
setupToolHandlers() {
// List all available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'calculate',
description: 'Perform basic mathematical calculations',
inputSchema: {
type: 'object',
properties: {
expression: {
type: 'string',
description: 'Mathematical expression to evaluate (e.g., "2 + 3 * 4")',
},
},
required: ['expression'],
},
},
{
name: 'get_weather',
description: 'Get weather information for a city (mock data)',
inputSchema: {
type: 'object',
properties: {
city: {
type: 'string',
description: 'Name of the city',
},
},
required: ['city'],
},
},
{
name: 'generate_uuid',
description: 'Generate a unique identifier',
inputSchema: {
type: 'object',
properties: {},
},
},
],
};
});
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'calculate':
return this.handleCalculate(args.expression);
case 'get_weather':
return this.handleGetWeather(args.city);
case 'generate_uuid':
return this.handleGenerateUUID();
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${name}`
);
}
} catch (error) {
throw new McpError(
ErrorCode.InternalError,
`Tool execution failed: ${error.message}`
);
}
});
}
/**
* Tool implementation: Calculate mathematical expressions
*/
handleCalculate(expression) {
try {
// Simple expression evaluator (in production, use a proper math library)
const result = Function(`"use strict"; return (${expression})`)();
return {
content: [
{
type: 'text',
text: `Calculation: ${expression} = ${result}`,
},
],
};
} catch (error) {
throw new Error(`Invalid mathematical expression: ${expression}`);
}
}
/**
* Tool implementation: Get weather (mock data)
*/
handleGetWeather(city) {
// Mock weather data - in real implementation, you'd call a weather API
const mockWeatherData = {
'New York': { temp: 22, condition: 'Sunny', humidity: 65 },
'London': { temp: 15, condition: 'Cloudy', humidity: 80 },
'Tokyo': { temp: 28, condition: 'Partly Cloudy', humidity: 70 },
'Sydney': { temp: 25, condition: 'Clear', humidity: 60 },
};
const weather = mockWeatherData[city] || {
temp: Math.floor(Math.random() * 30) + 5,
condition: 'Variable',
humidity: Math.floor(Math.random() * 40) + 40,
};
return {
content: [
{
type: 'text',
text: `Weather in ${city}:
Temperature: ${weather.temp}°C
Condition: ${weather.condition}
Humidity: ${weather.humidity}%`,
},
],
};
}
/**
* Tool implementation: Generate UUID
*/
handleGenerateUUID() {
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
return {
content: [
{
type: 'text',
text: `Generated UUID: ${uuid}`,
},
],
};
}
/**
* RESOURCES SECTION
* Resources are data sources that the AI can read from
*/
setupResourceHandlers() {
// List all available resources
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: 'file://project-info',
name: 'Project Information',
description: 'Information about this MCP server project',
mimeType: 'text/plain',
},
{
uri: 'file://sample-data',
name: 'Sample Data',
description: 'Sample JSON data for demonstration',
mimeType: 'application/json',
},
{
uri: 'file://system-status',
name: 'System Status',
description: 'Current system status and statistics',
mimeType: 'text/plain',
},
],
};
});
// Handle resource reading
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
switch (uri) {
case 'file://project-info':
return {
contents: [
{
uri: uri,
mimeType: 'text/plain',
text: `MCP Learning Server
====================
This is an educational MCP (Model Context Protocol) server built with JavaScript.
Features:
- Mathematical calculations
- Weather information (mock data)
- UUID generation
- Sample data resources
- System status monitoring
Created: ${new Date().toISOString()}
Version: 1.0.0`,
},
],
};
case 'file://sample-data':
return {
contents: [
{
uri: uri,
mimeType: 'application/json',
text: JSON.stringify({
users: [
{ id: 1, name: 'Alice', role: 'developer' },
{ id: 2, name: 'Bob', role: 'designer' },
{ id: 3, name: 'Charlie', role: 'manager' },
],
products: [
{ id: 101, name: 'Widget A', price: 29.99 },
{ id: 102, name: 'Widget B', price: 49.99 },
{ id: 103, name: 'Widget C', price: 79.99 },
],
stats: {
totalUsers: 3,
totalProducts: 3,
lastUpdated: new Date().toISOString(),
},
}, null, 2),
},
],
};
case 'file://system-status':
return {
contents: [
{
uri: uri,
mimeType: 'text/plain',
text: `System Status Report
===================
Server: Running
Uptime: ${process.uptime()} seconds
Memory Usage: ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB
Node.js Version: ${process.version}
Platform: ${process.platform}
Architecture: ${process.arch}
Last Updated: ${new Date().toLocaleString()}`,
},
],
};
default:
throw new McpError(
ErrorCode.InvalidRequest,
`Unknown resource: ${uri}`
);
}
});
}
/**
* PROMPTS SECTION
* Prompts are template prompts that can be used with variables
*/
setupPromptHandlers() {
// List all available prompts
this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: [
{
name: 'code-review',
description: 'Generate a code review for a given piece of code',
arguments: [
{
name: 'code',
description: 'The code to review',
required: true,
},
{
name: 'language',
description: 'Programming language of the code',
required: true,
},
],
},
{
name: 'explain-concept',
description: 'Explain a technical concept in simple terms',
arguments: [
{
name: 'concept',
description: 'The technical concept to explain',
required: true,
},
{
name: 'audience',
description: 'Target audience (e.g., beginner, intermediate, expert)',
required: false,
},
],
},
{
name: 'project-documentation',
description: 'Generate documentation for a project',
arguments: [
{
name: 'project_name',
description: 'Name of the project',
required: true,
},
{
name: 'description',
description: 'Brief description of the project',
required: true,
},
],
},
],
};
});
// Handle prompt requests
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case 'code-review':
return {
description: 'Code review prompt',
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Please review the following ${args.language} code and provide feedback on:
1. Code quality and best practices
2. Potential bugs or issues
3. Performance considerations
4. Suggestions for improvement
Code to review:
\`\`\`${args.language}
${args.code}
\`\`\`
Please provide a detailed review with specific recommendations.`,
},
},
],
};
case 'explain-concept':
const audience = args.audience || 'general';
return {
description: 'Technical concept explanation prompt',
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Please explain the concept of "${args.concept}" in a way that a ${audience} audience can understand.
Please include:
1. A clear definition
2. Why it's important
3. Real-world examples or analogies
4. Common use cases
5. Related concepts they should know about
Make the explanation engaging and easy to follow.`,
},
},
],
};
case 'project-documentation':
return {
description: 'Project documentation generation prompt',
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Create comprehensive documentation for the project "${args.project_name}".
Project Description: ${args.description}
Please generate documentation that includes:
1. Project overview and purpose
2. Installation instructions
3. Usage examples
4. API reference (if applicable)
5. Configuration options
6. Contributing guidelines
7. License information
Format the documentation in Markdown and make it professional and user-friendly.`,
},
},
],
};
default:
throw new McpError(
ErrorCode.InvalidRequest,
`Unknown prompt: ${name}`
);
}
});
}
/**
* Run the MCP server
*/
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('MCP Learning Server running on stdio');
}
}
// Start the server
const server = new LearningMCPServer();
server.run().catch(console.error);