This project is a comprehensive Model Context Protocol (MCP) Server built with Next.js 15, designed to demonstrate how to create, deploy, and integrate MCP tools that can be consumed by AI assistants and language models. The MCP enables AI models to access external tools, data sources, and APIs in a standardized way.
The Model Context Protocol (MCP) is an open standard that enables secure connections between AI applications and external data sources and tools. It provides:
- Standardized Communication: A unified way for AI models to interact with external resources
- Security: Controlled access to tools and data with proper authentication
- Extensibility: Easy to add new tools and capabilities
- Interoperability: Works across different AI platforms and models
mcp-app/
โโโ app/
โ โโโ api/mcp/
โ โ โโโ route.js # MCP server endpoint
โ โโโ layout.tsx # Root layout component
โ โโโ page.tsx # Main application page
โ โโโ globals.css # Global styles
โโโ package.json # Project dependencies
โโโ next.config.ts # Next.js configuration
โโโ tsconfig.json # TypeScript configuration
โโโ README.md # This file
- MCP Handler (
app/api/mcp/route.js): The main MCP server implementation - Next.js App Router: Modern routing and API handling
- TypeScript Support: Type-safe development environment
- Tailwind CSS: Utility-first CSS framework
The current MCP server includes:
- Function:
time_now - Description: Returns current time in Istanbul (Europe/Istanbul timezone)
- Parameters: None
- Returns: Formatted time string in Turkish locale
server.tool(
"time_now",
"Returns the current time in Istanbul (Europe/Istanbul timezone)",
{},
async () => {
const now = new Date();
const options = {
timeZone: "Europe/Istanbul",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
day: "2-digit",
month: "2-digit",
year: "numeric",
};
const timeString = now.toLocaleString("tr-TR", options);
return {
content: [
{ type: "text", text: `ลu an ฤฐstanbul saatiyle: ${timeString}` },
],
};
}
);- Node.js 18.17 or later
- pnpm, npm, yarn, or bun package manager
- Basic understanding of JavaScript/TypeScript
- Familiarity with Next.js (optional but helpful)
-
Clone the repository:
git clone <repository-url> cd mcp-app
-
Install dependencies:
pnpm install # or npm install # or yarn install # or bun install
-
Run the development server:
pnpm dev # or npm run dev # or yarn dev # or bun dev
-
Access the application:
- Frontend: http://localhost:3000
- MCP Endpoint: http://localhost:3000/api/mcp
You can test the MCP server using various methods:
-
Direct API Call:
curl -X POST http://localhost:3000/api/mcp \ -H "Content-Type: application/json" \ -d '{"method": "tools/call", "params": {"name": "time_now", "arguments": {}}}'
-
AI Assistant Integration: Connect your MCP server to compatible AI assistants
An MCP server consists of:
- Tools: Functions that the AI can call
- Resources: Data sources the AI can access
- Prompts: Pre-defined prompts for specific tasks
server.tool(
"tool_name", // Unique identifier
"Tool description", // What the tool does
{
// Input schema (JSON Schema)
type: "object",
properties: {
param1: {
type: "string",
description: "Parameter description",
},
},
required: ["param1"],
},
async (args) => {
// Implementation function
// Your tool logic here
return {
content: [
{
type: "text",
text: "Tool response",
},
],
};
}
);server.tool(
"get_weather",
"Get current weather for a city",
{
type: "object",
properties: {
city: {
type: "string",
description: "City name",
},
country: {
type: "string",
description: "Country code (optional)",
default: "TR",
},
},
required: ["city"],
},
async (args) => {
const { city, country = "TR" } = args;
// Replace with actual weather API call
const weatherData = await fetchWeatherData(city, country);
return {
content: [
{
type: "text",
text: `Weather in ${city}: ${weatherData.temperature}ยฐC, ${weatherData.description}`,
},
],
};
}
);server.tool(
"query_database",
"Execute a safe database query",
{
type: "object",
properties: {
table: {
type: "string",
description: "Table name to query",
},
filters: {
type: "object",
description: "Query filters",
},
},
required: ["table"],
},
async (args) => {
const { table, filters = {} } = args;
// Implement safe database querying
const results = await executeQuery(table, filters);
return {
content: [
{
type: "text",
text: `Query results: ${JSON.stringify(results, null, 2)}`,
},
],
};
}
);server.tool(
"read_file",
"Read content of a file",
{
type: "object",
properties: {
filepath: {
type: "string",
description: "Path to the file to read",
},
encoding: {
type: "string",
description: "File encoding",
default: "utf8",
},
},
required: ["filepath"],
},
async (args) => {
const { filepath, encoding = "utf8" } = args;
try {
const fs = require("fs").promises;
const content = await fs.readFile(filepath, encoding);
return {
content: [
{
type: "text",
text: content,
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error reading file: ${error.message}`,
},
],
};
}
}
);Resources provide access to data sources:
server.resource(
"config://database",
"Database configuration",
"application/json",
async () => {
return {
contents: [
{
type: "text",
text: JSON.stringify({
host: "localhost",
port: 5432,
database: "myapp",
}),
},
],
};
}
);Pre-defined prompts for specific tasks:
server.prompt(
"analyze_code",
"Analyze code quality and suggest improvements",
{
type: "object",
properties: {
code: {
type: "string",
description: "Code to analyze",
},
language: {
type: "string",
description: "Programming language",
},
},
required: ["code", "language"],
},
async (args) => {
const { code, language } = args;
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `Analyze this ${language} code and provide suggestions:\n\n${code}`,
},
},
],
};
}
);- Input Validation: Always validate and sanitize inputs
- Access Control: Implement proper authentication and authorization
- Rate Limiting: Prevent abuse with rate limiting
- Error Handling: Don't expose sensitive information in errors
- Sandboxing: Isolate dangerous operations
// Example of secure input validation
server.tool(
"secure_tool",
"A secure tool with proper validation",
schema,
async (args) => {
// Validate inputs
if (!args.param || typeof args.param !== "string") {
throw new Error("Invalid input parameter");
}
// Sanitize inputs
const sanitizedParam = sanitizeInput(args.param);
// Check permissions
if (!hasPermission(sanitizedParam)) {
throw new Error("Access denied");
}
// Proceed with operation
return await safeOperation(sanitizedParam);
}
);// test/mcp.test.js
import { createMcpHandler } from "mcp-handler";
describe("MCP Tools", () => {
test("time_now returns valid time", async () => {
// Test implementation
const result = await timeNowTool();
expect(result.content[0].text).toMatch(/ลu an ฤฐstanbul saatiyle:/);
});
});# Test MCP server endpoints
curl -X POST http://localhost:3000/api/mcp \
-H "Content-Type: application/json" \
-d '{"method": "tools/list"}'- @modelcontextprotocol/sdk: Official MCP SDK for building MCP servers
- mcp-handler: Simplified wrapper for creating MCP servers in Next.js
- next: React framework for production-ready web applications
- react & react-dom: React library for building user interfaces
- typescript: Type-safe JavaScript development
- eslint: Code linting and quality checking
- tailwindcss: Utility-first CSS framework
- @types/*: TypeScript type definitions
The project includes specific version overrides for zod to ensure compatibility:
"overrides": {
"mcp-handler": {
"zod": "3.25.76"
},
"@modelcontextprotocol/sdk": {
"zod": "3.25.76"
}
}- Connect your repository to Vercel
- Configure environment variables if needed
- Deploy: Vercel automatically deploys on push to main branch
-
Build the application:
pnpm build
-
Start production server:
pnpm start
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]- Configure MCP in Claude Desktop:
{ "mcpServers": { "my-mcp-server": { "command": "node", "args": ["path/to/your/mcp/server.js"] } } }
Use the MCP SDK to integrate with any AI system:
import { MCPClient } from "@modelcontextprotocol/sdk";
const client = new MCPClient();
await client.connect("http://localhost:3000/api/mcp");
// List available tools
const tools = await client.listTools();
// Call a tool
const result = await client.callTool("time_now", {});