MCP Server Builder Guide
Bottom line: Build a Model Context Protocol server in under 30 minutes using FastMCP or TypeScript, validate it with our checklist, and submit it to the VePrompts directory.
What is an MCP server?
An MCP server is a lightweight adapter that exposes three kinds of capabilities to AI clients:
- Tools — functions the model can call to perform actions, like running a query or sending a message.
- Resources — read-only data the client can pull into context, like a file, a config, or an API response.
- Prompts — reusable templates that help users accomplish common tasks with your server.
MCP clients such as Claude Desktop, Cursor, Cline, and VS Code can discover these capabilities automatically and use them during a conversation.
Step 1: Choose your stack
The fastest way to start is with FastMCP for Python or the official TypeScript SDK for Node.js. Choose stdio for local tools and SSE for remote services.
FastMCP (Python)
Best for rapid prototyping and Python-heavy backends.
TypeScript SDK
Best for Node.js teams and type-safe schemas with Zod.
Step 2: FastMCP template
Install FastMCP and create a file named server.py:
from fastmcp import FastMCP
mcp = FastMCP("weather")
@mcp.tool()
async def get_forecast(city: str, days: int = 3) -> str:
"""Get a short weather forecast for a city.
Args:
city: The city name, e.g. 'San Francisco'.
days: Number of forecast days (1-7).
"""
# Replace with a real weather API call
return f"{city}: sunny, 22°C for the next {days} days."
@mcp.resource("weather://cities")
def list_cities() -> str:
"""Return a list of supported cities."""
return "San Francisco\nNew York\nLondon\nTokyo"
@mcp.prompt()
def forecast_prompt(city: str) -> str:
"""Prompt template for asking about a city forecast."""
return f"What is the weather forecast for {city}?"
if __name__ == "__main__":
mcp.run()Run it locally with Claude Desktop by adding the server to your claude_desktop_config.json.
Step 3: TypeScript SDK template
Create a project with this package.json:
{
"name": "mcp-weather-server",
"version": "1.0.0",
"description": "A simple MCP weather server",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"zod": "^3.23.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.5.0"
}
}Then implement src/index.ts:
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ReadResourceRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
const ForecastArgs = z.object({
city: z.string().min(1),
days: z.number().int().min(1).max(7).default(3)
});
const server = new Server(
{ name: 'weather-server', version: '1.0.0' },
{ capabilities: { tools: {}, resources: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'get_forecast',
description: 'Get a short weather forecast for a city',
inputSchema: {
type: 'object',
properties: {
city: { type: 'string', description: 'City name' },
days: { type: 'number', description: 'Number of days (1-7)', default: 3 }
},
required: ['city']
}
}
]
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { city, days } = ForecastArgs.parse(request.params.arguments);
const text = `${city}: sunny, 22°C for the next ${days} days.`;
return { content: [{ type: 'text', text }] };
});
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
resources: [
{ uri: 'weather://cities', name: 'Supported cities', mimeType: 'text/plain' }
]
}));
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
if (request.params.uri !== 'weather://cities') {
throw new Error(`Unknown resource: ${request.params.uri}`);
}
return {
contents: [{ uri: 'weather://cities', text: 'San Francisco\nNew York\nLondon\nTokyo' }]
};
});
const transport = new StdioServerTransport();
await server.connect(transport);Step 4: Test with a client
Install your server in Claude Desktop, Cursor, or Cline. Open the tool list and verify that:
- The server starts without errors.
- Tools appear with correct names and descriptions.
- Arguments are validated when you call a tool.
- Resources can be read and return the expected data.
- Errors are surfaced clearly in the client.
Step 5: Validate before publishing
Use this checklist to make sure your server is ready for the VePrompts directory.
- Implements at least one tool, resource, or prompt Required
- Has a clear name and description Required
- Uses stdio or SSE transport correctly Required
- Validates tool arguments with schemas Required
- Handles errors gracefully and returns clear messages Required
- Includes a README with install instructions Required
- Provides example prompts or usage
- Supports authentication if accessing paid/private APIs
- Has a license file
- Tested with Claude Desktop, Cursor, or Cline Required
Step 6: Submit to the directory
Once your server passes validation, submit it to the VePrompts MCP directory. Include the name, description, repository URL, install command, category, and supported clients.
Ready to share your server?
Add it to the directory so thousands of developers can discover and install it.
Submit your MCP serverPrefer GitHub? Open a pull request with your server metadata.
Best practices
- Keep tools focused. One tool should do one thing well. Avoid tools with many optional branches.
- Write good descriptions. The model uses descriptions to decide when to call a tool. Be specific.
- Return structured data. JSON or Markdown tables are easier for the model to reason over than plain paragraphs.
- Validate inputs early. Use Zod or Pydantic to reject bad arguments before they reach your business logic.
- Handle errors gracefully. Return user-friendly error messages instead of stack traces.
- Document install steps. Assume the user has never configured an MCP server before.