Content
# 🚀 Complete Tutorial for MCP Development: Building an AI Prompt Optimization Server from Scratch
> This tutorial will guide you through building a complete MCP (Model Context Protocol) server from scratch, implementing AI prompt optimization functionality. With detailed code comments and principle explanations, you will learn how to create, publish, and use an MCP server.
## 📖 Table of Contents
1. [Introduction to MCP Concepts](#introduction-to-mcp-concepts)
2. [Project Architecture Analysis](#project-architecture-analysis)
3. [Source Code Explanation](#source-code-explanation)
4. [Studio Mode and Tool Interaction](#studio-mode-and-tool-interaction)
5. [Publishing and Packaging](#publishing-and-packaging)
6. [Using in Cursor](#using-in-cursor)
7. [Complete Example](#complete-example)
## 🎯 Introduction to MCP Concepts
### What is MCP?
Model Context Protocol (MCP) is an open standard for connecting AI assistants with external data sources and tools. It allows AI assistants to:
- Access external data sources
- Invoke tools and functions
- Integrate with various services
- Extend AI capabilities
### Core Components of MCP
1. **MCP Server** - The server that provides tools and resources
2. **MCP Client** - The client that uses tools and resources (e.g., Cursor)
3. **Transport Layer** - Communication protocols (stdio, HTTP, etc.)
4. **Tool System** - Callable function interfaces
## 🏗️ Project Architecture Analysis
```
prompt-format-mcp/
├── src/
│ ├── index.ts # 📍 Entry file - Server startup and environment configuration
│ ├── server.ts # 🔧 Core server - MCP server implementation and tool registration
│ ├── api/
│ │ └── siliconflow.ts # 🌐 API client - Third-party AI service integration
│ ├── types/
│ │ └── index.ts # 📝 Type definitions - TypeScript type system
│ └── utils/
│ └── helpers.ts # 🛠️ Utility functions - Common functionalities like logging, retry, etc.
├── package.json # 📦 Dependency configuration
├── tsconfig.json # ⚙️ TypeScript compilation configuration
└── README.md # 📖 Documentation for this tutorial
```
### Design Philosophy
- **Modular Design** - Each file has a single responsibility, making it easy to maintain
- **Type Safety** - Using TypeScript to ensure code quality
- **Error Handling** - Comprehensive retry mechanisms and error handling
- **Standardization** - Adhering to MCP protocol standards
## 📚 Source Code Explanation
Below, we will analyze each file in detail, explaining the purpose and principles behind each line of code.
### 1. Entry File (src/index.ts)
```typescript
#!/usr/bin/env node
// 📍 Shebang line: Informs the operating system to use the node interpreter to execute this script
// This allows the file to be run as an executable
// 🔧 Import core modules
import { PromptFormatMcpServer } from "./server.js"; // Import our MCP server class
import { config } from "dotenv"; // Import environment variable configuration tool
import { resolve } from "path"; // Import path resolution tool
import { Logger } from "./utils/helpers.js"; // Import logging utility
// 🌐 Load environment variables
// Attempt to load environment variables from the .env file (if it exists)
// In the MCP context, environment variables are typically passed by the client (e.g., Cursor) through configuration
try {
config({ path: resolve(process.cwd(), ".env") }); // Load .env file from the current working directory
} catch (error) {
// Silently ignore the error if the .env file does not exist
// Because in the MCP environment, environment variables are provided by client configuration
}
// 🔒 Validate environment variables
// Check if the required API key exists
if (!process.env.SILICONFLOW_API_KEY) {
// If the API key does not exist, output detailed error information and solutions
console.error("Error: Please set the environment variable SILICONFLOW_API_KEY");
console.error("In the MCP server configuration, ensure this variable is set in the env field");
process.exit(1); // Exit the program with an error status
}
/**
* 🚀 Start server function
* This is the main entry point of the program, responsible for creating and starting the MCP server
*/
export async function startServer(): Promise<void> {
try {
// Create an MCP server instance
const server = new PromptFormatMcpServer();
// Output startup log
Logger.log("Starting Prompt Format MCP server...");
// Start the server in stdio transport mode
// The stdio mode is the standard transport method of the MCP protocol, communicating with the client via standard input/output
await server.startStdio();
} catch (error) {
// If startup fails, log the error and exit
Logger.error("Server startup failed:", error);
process.exit(1);
}
}
// 🛡️ Global exception handling
// Capture unhandled exceptions to prevent the program from crashing unexpectedly
process.on('uncaughtException', (error) => {
Logger.error('Uncaught exception:', error);
process.exit(1); // Exit on exception
});
// 🎯 Program entry point
// Call the start function to begin running the server
startServer();
```
**Key Points Analysis:**
- **Shebang Line**: Allows the file to be executed directly
- **Environment Variable Handling**: Supports both .env file and client configuration
- **Error Handling**: Comprehensive exception capture and process signal handling
- **Startup Process**: Concise server startup logic
### 2. Core Server (src/server.ts)
This is the core file of the MCP server, showcasing the complete implementation of the MCP server:
```typescript
// 🔧 Import MCP core modules
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; // Core class for MCP server
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; // stdio transport layer
import { z } from "zod"; // Parameter validation and type definition library
/**
* 🎯 Prompt Format MCP Server Class
* This is the core class of the entire MCP server, responsible for:
* 1. Creating and configuring the MCP server instance
* 2. Registering and managing tools
* 3. Handling client requests
* 4. Managing communication with AI APIs
*/
export class PromptFormatMcpServer {
private server: McpServer; // MCP server instance
private apiClient: SiliconFlowClient; // AI API client instance
constructor() {
// 🔧 Create MCP server
// This is the core of the MCP protocol, defining the basic information and capabilities of the server
this.server = new McpServer({
name: "prompt-format-mcp", // Server name, used by the client to identify the server
version: "1.0.11", // Server version number
}, {
capabilities: {
tools: {}, // Declare that this server supports tool functionality
// Other possible capabilities:
// resources: {}, // Resource functionality
// prompts: {}, // Prompt template functionality
// logging: {}, // Logging functionality
},
});
// 📝 Register tools
this.registerTools();
}
/**
* 🛠️ Tool registration method
* Here we register all MCP tools, each with specific functionality
*/
private registerTools() {
// 🔧 First tool: optimize-prompt
this.server.tool(
"optimize-prompt", // Tool name, used by the client to call
"Optimize prompt for better AI model performance", // Tool description
{
// 📋 Parameter schema definition
// Use zod library to define parameter types and validation rules
content: z.string().describe("Prompt content to optimize")
},
// 🎯 Tool handler function
async ({ content }) => {
// 🔄 Use retry mechanism to call AI API
const optimized = await retry(
() => this.apiClient.optimizePrompt(content),
3, // Maximum retry count
1000 // Delay between retries (milliseconds)
);
// 🎯 Return in MCP standard response format
return {
content: [
{
type: "text", // Content type: text
text: optimized // Optimized content
}
]
};
}
);
}
/**
* 🚀 Start stdio server
* This is the standard startup method for the MCP server
*/
async startStdio(): Promise<void> {
// 🔌 Create stdio transport layer
const transport = new StdioServerTransport();
// 🔗 Connect server and transport layer
await this.server.connect(transport);
Logger.log("MCP Server started on stdio");
}
}
```
**Key Concept Analysis:**
#### MCP Server Creation
```typescript
const server = new McpServer({
name: "prompt-format-mcp", // Server identifier
version: "1.0.11", // Version number
}, {
capabilities: {
tools: {}, // Declare supported functionalities
},
});
```
#### Tool Registration Mechanism
```typescript
server.tool(
"tool-name", // Tool name
"Tool description", // Tool description
parameterSchema, // Parameter validation schema
handlerFunction // Handler function
);
```
#### Studio Mode Communication
- **stdio Transport**: Communicates with the client via standard input/output
- **JSON-RPC Protocol**: Uses JSON-RPC 2.0 protocol format
- **Asynchronous Handling**: Supports asynchronous tool calls
### 3. API Client (src/api/siliconflow.ts)
Demonstrates how to encapsulate a third-party AI API:
```typescript
export class SiliconFlowClient {
private apiKey: string; // API key
private baseUrl: string; // Base URL for the API
private modelName: string; // AI model name
/**
* 🔄 Core method to call the API
* Includes complete error handling and retry logic
*/
private async callAPI(request: SiliconFlowRequest): Promise<SiliconFlowResponse> {
const maxRetries = 3;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
// 🌐 Send HTTP request
const response = await axios.post(
`${this.baseUrl}/chat/completions`,
request,
{
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
timeout: 90000, // 90 seconds timeout
validateStatus: (status) => status < 500 // Custom status code validation
}
);
return response.data;
} catch (error) {
// 🔍 Error classification handling
if (error.code === 'ECONNABORTED') {
// Timeout error - retry
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 2000 * attempt));
continue;
}
}
// Other error handling...
}
}
}
}
```
**API Design Points:**
- **Retry Mechanism**: Automatically retries failed requests
- **Error Classification**: Distinguishes between different types of errors
- **Timeout Handling**: Sets reasonable timeout durations
- **Status Code Validation**: Custom HTTP status code handling
### 4. Type Definitions (src/types/index.ts)
```typescript
/**
* 🤖 SiliconFlow API response type
* Complies with OpenAI-compatible API response format
*/
export interface SiliconFlowResponse {
choices: Array<{
message: {
content: string; // Content generated by AI
role: string; // Message role
};
finish_reason: string; // Reason for finishing
index: number; // Choice index
}>;
usage: {
completion_tokens: number; // Number of tokens generated
prompt_tokens: number; // Number of input tokens
total_tokens: number; // Total number of tokens
};
}
```
### 5. Utility Functions (src/utils/helpers.ts)
```typescript
/**
* 🔄 Retry function
* Provides an automatic retry mechanism to improve API call reliability
*/
export async function retry<T>(
fn: () => Promise<T>, // Function to retry
maxAttempts: number = 3, // Maximum retry count
delayMs: number = 1000 // Base delay time
): Promise<T> {
let lastError: Error;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
if (attempt < maxAttempts) {
// Incremental delay: 1s for the first, 2s for the second, 3s for the third
await delay(delayMs * attempt);
}
}
}
throw lastError!;
}
```
**Utility Function Design:**
- **Generic Support**: Supports return values of any type
- **Incremental Delay**: Avoids putting pressure on the server with frequent retries
- **Error Preservation**: Retains the last error information
## 🔧 Studio Mode and Tool Interaction
### MCP Server Creation Process
1. **Initialize Server**
```typescript
const server = new McpServer({
name: "prompt-format-mcp", // Server name
version: "1.0.11", // Version number
}, {
capabilities: {
tools: {}, // Declare support for tool functionality
},
});
```
2. **Register Tools**
```typescript
server.tool(
"tool-name", // Tool name
"Tool description", // Tool description
schema, // Parameter schema
handler // Handler function
);
```
3. **Start Server**
```typescript
const transport = new StdioServerTransport();
await server.connect(transport);
```
### Tool Coordination Mechanism
Our project implements two coordinating tools:
1. **optimize-prompt** - Optimize prompts
2. **confirm-and-continue** - Confirm and continue the conversation
The coordination flow of these two tools:
```
User Input → optimize-prompt → Return Optimized Result → User Confirmation → confirm-and-continue → Trigger AI to Continue Conversation
```
### Client Processing Flow
1. **Client Initiates Request**
```json
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "optimize-prompt",
"arguments": {
"content": "User's prompt"
}
}
}
```
2. **Server Processes and Returns**
```json
{
"jsonrpc": "2.0",
"result": {
"content": [
{
"type": "text",
"text": "Optimized result"
}
]
}
}
```
## 📦 Publishing and Packaging
### 1. Build the Project
```bash
# Compile TypeScript to JavaScript
npm run build
# Compiled files are in the dist/ directory
```
### 2. Configure package.json
```json
{
"name": "prompt-format-mcp",
"version": "1.0.11",
"type": "module", // Use ES modules
"main": "dist/index.js", // Entry file
"bin": {
"prompt-format-mcp": "dist/index.js" // Command line tool
},
"files": [
"dist", // Only include compiled files
"README.md"
]
}
```
### 3. Publish to npm
```bash
# Log in to npm account
npm login
# Publish package
npm publish
```
### 4. Version Management
```bash
# Update version number and publish
npm version patch # Patch version 1.0.11 → 1.0.12
npm version minor # Minor version 1.0.11 → 1.1.0
npm version major # Major version 1.0.11 → 2.0.0
```
## 🎯 Using in Cursor
### 1. Configure MCP Server
Create or edit `~/.cursor/mcp.json` in Cursor:
```json
{
"mcpServers": {
"prompt-format-mcp": {
"command": "npx",
"args": ["-y", "prompt-format-mcp@latest", "--stdio"],
"env": {
"SILICONFLOW_API_KEY": "your-api-key-here"
}
}
}
}
```
### 2. Configuration Explanation
- **command**: Use npx to run without local installation
- **args**:
- `-y`: Automatically confirm installation
- `--stdio`: Use stdio transport mode
- **env**: Environment variable configuration
### 3. Restart Cursor
After configuration, restart Cursor, and the MCP server will start automatically.
### 4. Usage Example
```
# Optimize prompt
@prompt-format-mcp optimize-prompt I want to create a website
# Confirm and continue (used after the optimization result)
@prompt-format-mcp confirm-and-continue This is the final prompt I confirm...
```
## 🎮 Complete Example
### Develop Your Own MCP Server
1. **Create Project Structure**
```bash
mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk axios zod
npm install -D typescript @types/node tsx
```
2. **Create Basic Files**
```typescript
// src/server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
export class MyMcpServer {
private server: McpServer;
constructor() {
this.server = new McpServer({
name: "my-mcp-server",
version: "1.0.0",
}, {
capabilities: {
tools: {},
},
});
this.registerTools();
}
private registerTools() {
// Register your tools
this.server.tool(
"hello-world",
"Say hello to the world",
{
name: z.string().describe("Name to greet")
},
async ({ name }) => {
return {
content: [
{
type: "text",
text: `Hello, ${name}! Welcome to MCP!`
}
]
};
}
);
}
async startStdio(): Promise<void> {
const transport = new StdioServerTransport();
await this.server.connect(transport);
}
}
```
3. **Create Entry File**
```typescript
// src/index.ts
#!/usr/bin/env node
import { MyMcpServer } from "./server.js";
async function main() {
const server = new MyMcpServer();
await server.startStdio();
}
main().catch(console.error);
```
4. **Configure TypeScript**
```json
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "node",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
```
5. **Build and Test**
```bash
# Build
npx tsc
# Test
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | node dist/index.js
```
## 🎉 Summary
Through this tutorial, you have learned:
1. **Basic Concepts of MCP** - Understanding the MCP protocol and architecture
2. **Server Development** - Creating an MCP server and tool registration
3. **Tool Coordination** - Implementing coordination and interaction between tools
4. **Client Integration** - Configuring and using the MCP server in Cursor
5. **Publishing Process** - Publishing the MCP server to npm
The core of MCP development lies in:
- Understanding the stdio communication protocol
- Correctly registering and implementing tools
- Handling errors and exceptions
- Providing a good user experience
Now you can start building your own MCP server! 🚀
## 🔗 Related Resources
- [Official MCP Documentation](https://modelcontextprotocol.io)
- [MCP SDK Documentation](https://github.com/modelcontextprotocol/typescript-sdk)
- [Cursor MCP Integration Guide](https://cursor.com/docs/mcp)
---
**Author**: MCP Development Tutorial
**Version**: 1.0.11
**Last Updated**: December 2024
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
servers
Model Context Protocol Servers
servers
Model Context Protocol Servers
Agent-Reach
Give your AI agent eyes to see the entire internet. Read & search Twitter,...