Initial commit

This commit is contained in:
CHE LIANG ZHAO
2026-01-16 18:21:32 +08:00
commit 6e8a93c8e9
27 changed files with 2441 additions and 0 deletions

53
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,53 @@
# MCP Demo Server - Copilot Instructions
## 项目概述
这是一个 Model Context Protocol (MCP) 服务器示例项目,使用 TypeScript 编写。
## 技术栈
- **语言**: TypeScript
- **运行时**: Node.js
- **MCP SDK**: @modelcontextprotocol/sdk
- **Schema 验证**: Zod
## 项目结构
- `src/index.ts` - MCP 服务器主入口文件
- `build/` - 编译后的 JavaScript 文件
- `.vscode/mcp.json` - VS Code MCP 服务器配置
## 开发指南
### 添加新工具
使用 `server.registerTool()` 方法注册新工具:
```typescript
server.registerTool(
"tool_name",
{
description: "工具描述",
inputSchema: {
// 使用 Zod schema 定义参数
},
},
async (params) => {
// 实现工具逻辑
return {
content: [{ type: "text", text: "结果" }],
};
}
);
```
### 常用命令
- `npm run build` - 编译 TypeScript
- `npm start` - 运行 MCP 服务器
- `npm run dev` - 开发模式(监听文件变化)
## MCP 参考文档
- SDK 文档: https://github.com/modelcontextprotocol/typescript-sdk
- 协议规范: https://modelcontextprotocol.io/

32
.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
# Dependencies
node_modules/
# Build outputs
typescript/build/
# Go binaries
golang/*.exe
golang/*.dll
golang/*.so
golang/*.dylib
# IDE
.idea/
*.swp
*.swo
.DS_Store
# Logs
*.log
npm-debug.log*
# Environment
.env
.env.local
.env.*.local
# Temporary files
*.tmp
*.temp
*.db

13
.vscode/mcp.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"servers": {
"mcp-demo-ts": {
"type": "stdio",
"command": "node",
"args": ["typescript/build/index.js"]
},
"mcp-demo-go": {
"type": "stdio",
"command": "${workspaceFolder}/golang/mcp-demo-go.exe"
}
}
}

157
README.md Normal file
View File

@@ -0,0 +1,157 @@
# MCP Demo Server
一个使用 TypeScript 构建的 Model Context Protocol (MCP) 服务器示例,展示了如何创建和注册各种工具。
## 功能特性
此 MCP 服务器提供以下示例工具:
| 工具名称 | 描述 |
|---------|------|
| `echo` | 回显传入的消息 |
| `calculator` | 执行基本数学运算(加、减、乘、除) |
| `get_current_time` | 获取当前日期和时间(支持时区) |
| `random_number` | 在指定范围内生成随机数 |
| `string_utils` | 字符串处理工具(大小写转换、反转、计数等) |
## 快速开始
### 安装依赖
```bash
npm install
```
### 编译项目
```bash
npm run build
```
### 运行服务器
```bash
npm start
```
## 在 VS Code 中使用
项目已配置好 `.vscode/mcp.json`,可以直接在 VS Code 中使用此 MCP 服务器:
1. 确保已编译项目 (`npm run build`)
2. 在 VS Code 中打开此项目
3. MCP 服务器会自动被识别并可用
## 配置示例
### Claude Desktop 配置
将以下内容添加到 Claude Desktop 的配置文件中:
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"mcp-demo": {
"command": "node",
"args": ["D:\\Projects\\McpDemo\\build\\index.js"]
}
}
}
```
## 工具使用示例
### Echo 工具
```
输入: { "message": "Hello, MCP!" }
输出: "Echo: Hello, MCP!"
```
### Calculator 工具
```
输入: { "operation": "add", "a": 10, "b": 5 }
输出: "10 add 5 = 15"
```
### Get Current Time 工具
```
输入: { "timezone": "Asia/Shanghai" }
输出: "Current time: 1/16/2026, 2:30:00 PM"
```
### Random Number 工具
```
输入: { "min": 1, "max": 100 }
输出: "Random number between 1 and 100: 42"
```
### String Utils 工具
```
输入: { "operation": "uppercase", "text": "hello world" }
输出: "HELLO WORLD"
```
## 项目结构
```
McpDemo/
├── src/
│ └── index.ts # MCP 服务器主文件
├── build/ # 编译输出目录
├── .vscode/
│ └── mcp.json # VS Code MCP 配置
├── package.json
├── tsconfig.json
└── README.md
```
## 开发指南
### 添加新工具
`src/index.ts` 中使用 `server.registerTool()` 添加新工具:
```typescript
server.registerTool(
"tool_name",
{
description: "工具描述",
inputSchema: {
param1: z.string().describe("参数1描述"),
param2: z.number().describe("参数2描述"),
},
},
async ({ param1, param2 }) => {
// 工具逻辑
return {
content: [
{
type: "text",
text: "结果",
},
],
};
}
);
```
### 开发模式
使用 watch 模式进行开发:
```bash
npm run dev
```
## 相关资源
- [MCP 官方文档](https://modelcontextprotocol.io/)
- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
- [MCP 服务器示例](https://github.com/modelcontextprotocol/servers)
## 许可证
MIT

BIN
demo.db Normal file

Binary file not shown.

BIN
golang/demo.db Normal file

Binary file not shown.

14
golang/go.mod Normal file
View File

@@ -0,0 +1,14 @@
module mcp-demo-go
go 1.23.0
require (
github.com/mattn/go-sqlite3 v1.14.24
github.com/modelcontextprotocol/go-sdk v1.2.0
)
require (
github.com/google/jsonschema-go v0.3.0 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
)

16
golang/go.sum Normal file
View File

@@ -0,0 +1,16 @@
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q=
github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/modelcontextprotocol/go-sdk v1.2.0 h1:Y23co09300CEk8iZ/tMxIX1dVmKZkzoSBZOpJwUnc/s=
github.com/modelcontextprotocol/go-sdk v1.2.0/go.mod h1:6fM3LCm3yV7pAs8isnKLn07oKtB0MP9LHd3DfAcKw10=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=

293
golang/main.go Normal file
View File

@@ -0,0 +1,293 @@
package main
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"strings"
_ "github.com/mattn/go-sqlite3"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
var db *sql.DB
// ==================== 数据库初始化 ====================
func initDB() error {
// 获取可执行文件所在目录
execPath, err := os.Executable()
if err != nil {
execPath = "."
}
dbPath := filepath.Join(filepath.Dir(execPath), "demo.db")
// 如果数据库不存在,使用当前目录
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
dbPath = "demo.db"
}
db, err = sql.Open("sqlite3", dbPath)
if err != nil {
return err
}
// 创建示例表
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE,
age INTEGER,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
price REAL,
stock INTEGER DEFAULT 0
);
`)
if err != nil {
return err
}
// 插入示例数据(如果表为空)
var count int
db.QueryRow("SELECT COUNT(*) FROM users").Scan(&count)
if count == 0 {
_, err = db.Exec(`
INSERT INTO users (name, email, age) VALUES
('张三', 'zhangsan@example.com', 28),
('李四', 'lisi@example.com', 32),
('王五', 'wangwu@example.com', 25);
INSERT INTO products (name, price, stock) VALUES
('iPhone 15', 6999.00, 100),
('MacBook Pro', 14999.00, 50),
('AirPods Pro', 1899.00, 200);
`)
if err != nil {
return err
}
}
return nil
}
// ==================== 工具输入结构 ====================
// ListTablesInput - 列出所有表
type ListTablesInput struct{}
// QueryInput - 执行 SQL 查询
type QueryInput struct {
SQL string `json:"sql" mcp:"SQL query to execute (SELECT only for safety)"`
}
// GetUserInput - 获取用户
type GetUserInput struct {
ID int `json:"id" mcp:"User ID to retrieve"`
}
// AddUserInput - 添加用户
type AddUserInput struct {
Name string `json:"name" mcp:"User name"`
Email string `json:"email" mcp:"User email address"`
Age int `json:"age" mcp:"User age"`
}
// DeleteUserInput - 删除用户
type DeleteUserInput struct {
ID int `json:"id" mcp:"User ID to delete"`
}
// ==================== 辅助函数 ====================
func textResult(text string) (*mcp.CallToolResult, any, error) {
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: text},
},
}, nil, nil
}
func errorResult(text string) (*mcp.CallToolResult, any, error) {
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: text},
},
IsError: true,
}, nil, nil
}
// ==================== 工具实现 ====================
// ListTables - 列出数据库中的所有表
func ListTables(ctx context.Context, req *mcp.CallToolRequest, input ListTablesInput) (*mcp.CallToolResult, any, error) {
rows, err := db.Query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")
if err != nil {
return errorResult(fmt.Sprintf("Failed to list tables: %v", err))
}
defer rows.Close()
var tables []string
for rows.Next() {
var name string
rows.Scan(&name)
tables = append(tables, name)
}
result, _ := json.MarshalIndent(tables, "", " ")
return textResult(fmt.Sprintf("Tables in database:\n%s", string(result)))
}
// QueryDB - 执行 SQL 查询
func QueryDB(ctx context.Context, req *mcp.CallToolRequest, input QueryInput) (*mcp.CallToolResult, any, error) {
// 安全检查:只允许 SELECT 查询
sqlUpper := strings.ToUpper(strings.TrimSpace(input.SQL))
if !strings.HasPrefix(sqlUpper, "SELECT") {
return errorResult("Only SELECT queries are allowed for safety")
}
rows, err := db.Query(input.SQL)
if err != nil {
return errorResult(fmt.Sprintf("Query failed: %v", err))
}
defer rows.Close()
// 获取列名
columns, _ := rows.Columns()
// 读取所有行
var results []map[string]interface{}
for rows.Next() {
values := make([]interface{}, len(columns))
valuePtrs := make([]interface{}, len(columns))
for i := range values {
valuePtrs[i] = &values[i]
}
rows.Scan(valuePtrs...)
row := make(map[string]interface{})
for i, col := range columns {
row[col] = values[i]
}
results = append(results, row)
}
jsonResult, _ := json.MarshalIndent(results, "", " ")
return textResult(fmt.Sprintf("Query results (%d rows):\n%s", len(results), string(jsonResult)))
}
// GetUser - 获取单个用户
func GetUser(ctx context.Context, req *mcp.CallToolRequest, input GetUserInput) (*mcp.CallToolResult, any, error) {
var id int
var name, email string
var age int
var createdAt string
err := db.QueryRow("SELECT id, name, email, age, created_at FROM users WHERE id = ?", input.ID).
Scan(&id, &name, &email, &age, &createdAt)
if err == sql.ErrNoRows {
return errorResult(fmt.Sprintf("User with ID %d not found", input.ID))
}
if err != nil {
return errorResult(fmt.Sprintf("Query failed: %v", err))
}
user := map[string]interface{}{
"id": id,
"name": name,
"email": email,
"age": age,
"created_at": createdAt,
}
jsonResult, _ := json.MarshalIndent(user, "", " ")
return textResult(string(jsonResult))
}
// AddUser - 添加用户
func AddUser(ctx context.Context, req *mcp.CallToolRequest, input AddUserInput) (*mcp.CallToolResult, any, error) {
result, err := db.Exec("INSERT INTO users (name, email, age) VALUES (?, ?, ?)",
input.Name, input.Email, input.Age)
if err != nil {
return errorResult(fmt.Sprintf("Failed to add user: %v", err))
}
id, _ := result.LastInsertId()
return textResult(fmt.Sprintf("User added successfully with ID: %d", id))
}
// DeleteUser - 删除用户
func DeleteUser(ctx context.Context, req *mcp.CallToolRequest, input DeleteUserInput) (*mcp.CallToolResult, any, error) {
result, err := db.Exec("DELETE FROM users WHERE id = ?", input.ID)
if err != nil {
return errorResult(fmt.Sprintf("Failed to delete user: %v", err))
}
affected, _ := result.RowsAffected()
if affected == 0 {
return errorResult(fmt.Sprintf("User with ID %d not found", input.ID))
}
return textResult(fmt.Sprintf("User with ID %d deleted successfully", input.ID))
}
// ==================== 主函数 ====================
func main() {
// 初始化数据库
if err := initDB(); err != nil {
log.Fatalf("Failed to initialize database: %v", err)
}
defer db.Close()
// 创建 MCP Server
server := mcp.NewServer(
&mcp.Implementation{
Name: "mcp-demo-go",
Version: "1.0.0",
},
nil,
)
// 注册工具
mcp.AddTool(server, &mcp.Tool{
Name: "list_tables",
Description: "List all tables in the SQLite database",
}, ListTables)
mcp.AddTool(server, &mcp.Tool{
Name: "query_db",
Description: "Execute a SELECT SQL query on the database",
}, QueryDB)
mcp.AddTool(server, &mcp.Tool{
Name: "get_user",
Description: "Get a user by ID from the users table",
}, GetUser)
mcp.AddTool(server, &mcp.Tool{
Name: "add_user",
Description: "Add a new user to the database",
}, AddUser)
mcp.AddTool(server, &mcp.Tool{
Name: "delete_user",
Description: "Delete a user from the database by ID",
}, DeleteUser)
// 运行服务器
log.Println("MCP Demo Go Server (SQLite) is running...")
if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
log.Fatalf("Server error: %v", err)
}
}

1162
typescript/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

32
typescript/package.json Normal file
View File

@@ -0,0 +1,32 @@
{
"name": "mcp-demo-server",
"version": "1.0.0",
"description": "A demo MCP server with example tools",
"type": "module",
"bin": {
"mcp-demo": "./build/index.js"
},
"scripts": {
"build": "tsc",
"start": "node build/index.js",
"dev": "tsc --watch"
},
"files": [
"build"
],
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.0",
"zod": "^3.24.0"
},
"devDependencies": {
"@types/node": "^22.10.0",
"typescript": "^5.7.0"
},
"keywords": [
"mcp",
"model-context-protocol",
"ai",
"tools"
],
"license": "MIT"
}

29
typescript/src/index.ts Normal file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/env node
/**
* MCP Demo Server - TypeScript
*
* A demonstration MCP server with modular architecture:
* - Tools: echo, calculator, time, random, string utilities
* - Resources: skills capabilities, tool info
* - Prompts: list-skills, process-text, math-helper
*/
import { server } from "./server.js";
import { registerAllTools } from "./tools/index.js";
import { registerAllResources } from "./resources/index.js";
import { registerAllPrompts } from "./prompts/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// Register all modules
registerAllTools();
registerAllResources();
registerAllPrompts();
// Start the server
async function main() {
await server.connect(new StdioServerTransport());
console.error("MCP Demo Server (TypeScript) running on stdio");
}
main().catch(console.error);

View File

@@ -0,0 +1,15 @@
import { registerListSkillsPrompt } from "./list-skills.js";
import { registerProcessTextPrompt } from "./process-text.js";
import { registerMathHelperPrompt } from "./math-helper.js";
export function registerAllPrompts() {
registerListSkillsPrompt();
registerProcessTextPrompt();
registerMathHelperPrompt();
}
export {
registerListSkillsPrompt,
registerProcessTextPrompt,
registerMathHelperPrompt,
};

View File

@@ -0,0 +1,74 @@
import { server } from "../server.js";
import { z } from "zod";
export function registerListSkillsPrompt() {
server.registerPrompt(
"list-skills",
{
description:
"List all available skills and capabilities of this MCP server",
argsSchema: {
format: z
.enum(["brief", "detailed"])
.optional()
.describe("Output format: 'brief' or 'detailed'"),
},
},
async ({ format }) => {
const isDetailed = format === "detailed";
const briefContent = `This MCP server provides 5 tools:
1. echo - Echo messages
2. calculator - Math operations (+, -, *, /)
3. get_current_time - Current time with timezone support
4. random_number - Random number generation
5. string_utils - String manipulation`;
const detailedContent = `# MCP Demo Server - Complete Skills Guide
## Tools Overview
### 1. echo
Echo back any message you send.
\`\`\`json
{"message": "Hello!"}
\`\`\`
### 2. calculator
Perform basic math: add, subtract, multiply, divide.
\`\`\`json
{"operation": "add", "a": 10, "b": 5}
\`\`\`
### 3. get_current_time
Get current time, optionally in a specific timezone.
\`\`\`json
{"timezone": "Asia/Shanghai"}
\`\`\`
### 4. random_number
Generate random numbers in a range.
\`\`\`json
{"min": 1, "max": 100}
\`\`\`
### 5. string_utils
String operations: uppercase, lowercase, reverse, length, word_count.
\`\`\`json
{"operation": "uppercase", "text": "hello world"}
\`\`\``;
return {
messages: [
{
role: "user",
content: {
type: "text",
text: isDetailed ? detailedContent : briefContent,
},
},
],
};
}
);
}

View File

@@ -0,0 +1,35 @@
import { server } from "../server.js";
import { z } from "zod";
export function registerMathHelperPrompt() {
server.registerPrompt(
"math-helper",
{
description: "Generate a prompt for mathematical calculations",
argsSchema: {
problem: z.string().describe("The math problem to solve"),
},
},
async ({ problem }) => {
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `Please help solve this math problem: ${problem}
Use the calculator tool to perform the calculations. Show your work step by step.
Available operations:
- add (a + b)
- subtract (a - b)
- multiply (a * b)
- divide (a / b)`,
},
},
],
};
}
);
}

View File

@@ -0,0 +1,59 @@
import { server } from "../server.js";
import { z } from "zod";
export function registerProcessTextPrompt() {
server.registerPrompt(
"process-text",
{
description: "Generate a prompt for text processing tasks",
argsSchema: {
text: z.string().describe("The text to process"),
task: z
.enum(["analyze", "transform", "summarize"])
.optional()
.describe("Processing task: 'analyze', 'transform', or 'summarize'"),
},
},
async ({ text, task }) => {
const taskType = task || "analyze";
const prompts: Record<string, string> = {
analyze: `Please analyze the following text and provide insights:
Text: "${text}"
Use the string_utils tool to:
1. Get the length of the text
2. Count the words
3. Show the text in uppercase and lowercase`,
transform: `Please transform the following text:
Text: "${text}"
Use the string_utils tool to:
1. Convert to UPPERCASE
2. Convert to lowercase
3. Reverse the text`,
summarize: `Please provide a summary of the following text:
Text: "${text}"
First, use string_utils to get basic stats (length, word_count), then provide your analysis.`,
};
return {
messages: [
{
role: "user",
content: {
type: "text",
text: prompts[taskType] || prompts["analyze"],
},
},
],
};
}
);
}

View File

@@ -0,0 +1,9 @@
import { registerSkillsResource } from "./skills.js";
import { registerToolInfoResource, AVAILABLE_TOOLS } from "./tool-info.js";
export function registerAllResources() {
registerSkillsResource();
registerToolInfoResource();
}
export { registerSkillsResource, registerToolInfoResource, AVAILABLE_TOOLS };

View File

@@ -0,0 +1,56 @@
import { server } from "../server.js";
const SKILLS_CONTENT = `# MCP Demo Server Skills
## Available Tools
### 1. echo
- **Description**: Echo back the provided message
- **Use Case**: Testing connectivity, message relay
### 2. calculator
- **Description**: Perform basic mathematical operations (add, subtract, multiply, divide)
- **Use Case**: Mathematical calculations
### 3. get_current_time
- **Description**: Get the current date and time with optional timezone
- **Use Case**: Time queries, timezone conversions
### 4. random_number
- **Description**: Generate a random number within a specified range
- **Use Case**: Random selection, gaming, testing
### 5. string_utils
- **Description**: Perform string operations (uppercase, lowercase, reverse, length, word_count)
- **Use Case**: Text processing, string manipulation
## Capabilities Summary
- Basic I/O operations
- Mathematical calculations
- Time and date handling
- Random number generation
- String manipulation
## How to Use
Call any tool with its required parameters. Use the \`list-skills\` prompt for interactive guidance.
`;
export function registerSkillsResource() {
server.registerResource(
"skills-capabilities",
"mcp-demo://skills/capabilities",
{
description: "Complete list of server capabilities and skills",
mimeType: "text/markdown",
},
async () => ({
contents: [
{
uri: "mcp-demo://skills/capabilities",
mimeType: "text/markdown",
text: SKILLS_CONTENT,
},
],
})
);
}

View File

@@ -0,0 +1,123 @@
import { server } from "../server.js";
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
interface ToolMetadata {
name: string;
description: string;
parameters: Record<string, string>;
examples: string[];
}
const TOOL_METADATA: Record<string, ToolMetadata> = {
echo: {
name: "echo",
description: "Echo back the provided message",
parameters: {
message: "string - The message to echo back",
},
examples: ['{"message": "Hello, World!"}'],
},
calculator: {
name: "calculator",
description: "Perform basic mathematical operations",
parameters: {
operation: "enum - add, subtract, multiply, divide",
a: "number - First operand",
b: "number - Second operand",
},
examples: [
'{"operation": "add", "a": 10, "b": 5}',
'{"operation": "multiply", "a": 3, "b": 7}',
],
},
get_current_time: {
name: "get_current_time",
description: "Get the current date and time",
parameters: {
timezone: "string (optional) - Timezone like 'Asia/Shanghai'",
},
examples: ["{}", '{"timezone": "America/New_York"}'],
},
random_number: {
name: "random_number",
description: "Generate a random number within a range",
parameters: {
min: "number - Minimum value (inclusive)",
max: "number - Maximum value (inclusive)",
},
examples: ['{"min": 1, "max": 100}'],
},
string_utils: {
name: "string_utils",
description: "Perform string operations",
parameters: {
operation: "enum - uppercase, lowercase, reverse, length, word_count",
text: "string - The text to process",
},
examples: [
'{"operation": "uppercase", "text": "hello"}',
'{"operation": "word_count", "text": "Hello World"}',
],
},
};
export const AVAILABLE_TOOLS = Object.keys(TOOL_METADATA);
export function registerToolInfoResource() {
server.registerResource(
"tool-info",
new ResourceTemplate("mcp-demo://tools/{toolName}", {
list: async () => ({
resources: AVAILABLE_TOOLS.map((name) => ({
uri: `mcp-demo://tools/${name}`,
name: `Tool: ${name}`,
description: TOOL_METADATA[name].description,
mimeType: "application/json",
})),
}),
complete: {
toolName: async (value: string) =>
AVAILABLE_TOOLS.filter((name) =>
name.toLowerCase().startsWith(value.toLowerCase())
),
},
}),
{
description: "Detailed information about a specific tool",
mimeType: "application/json",
},
async (uri: URL) => {
const toolName = uri.pathname.split("/").pop() || "";
const metadata = TOOL_METADATA[toolName];
if (!metadata) {
return {
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify(
{
error: `Tool '${toolName}' not found`,
availableTools: AVAILABLE_TOOLS,
},
null,
2
),
},
],
};
}
return {
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify(metadata, null, 2),
},
],
};
}
);
}

7
typescript/src/server.ts Normal file
View File

@@ -0,0 +1,7 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
// 创建 MCP Server 实例
export const server = new McpServer({
name: "mcp-demo-server",
version: "1.0.0",
});

View File

@@ -0,0 +1,56 @@
import { server } from "../server.js";
import { z } from "zod";
export function registerCalculatorTool() {
server.registerTool(
"calculator",
{
description: "Perform basic mathematical operations",
inputSchema: {
operation: z
.enum(["add", "subtract", "multiply", "divide"])
.describe("The operation to perform"),
a: z.number().describe("First operand"),
b: z.number().describe("Second operand"),
},
},
async ({ operation, a, b }) => {
let result: number;
switch (operation) {
case "add":
result = a + b;
break;
case "subtract":
result = a - b;
break;
case "multiply":
result = a * b;
break;
case "divide":
if (b === 0) {
return {
content: [
{
type: "text",
text: "Error: Division by zero is not allowed",
},
],
isError: true,
};
}
result = a / b;
break;
}
return {
content: [
{
type: "text",
text: `${a} ${operation} ${b} = ${result}`,
},
],
};
}
);
}

View File

@@ -0,0 +1,24 @@
import { server } from "../server.js";
import { z } from "zod";
export function registerEchoTool() {
server.registerTool(
"echo",
{
description: "Echo back the provided message",
inputSchema: {
message: z.string().describe("The message to echo back"),
},
},
async ({ message }) => {
return {
content: [
{
type: "text",
text: `Echo: ${message}`,
},
],
};
}
);
}

View File

@@ -0,0 +1,21 @@
import { registerEchoTool } from "./echo.js";
import { registerCalculatorTool } from "./calculator.js";
import { registerTimeTool } from "./time.js";
import { registerRandomTool } from "./random.js";
import { registerStringTool } from "./string.js";
export function registerAllTools() {
registerEchoTool();
registerCalculatorTool();
registerTimeTool();
registerRandomTool();
registerStringTool();
}
export {
registerEchoTool,
registerCalculatorTool,
registerTimeTool,
registerRandomTool,
registerStringTool,
};

View File

@@ -0,0 +1,39 @@
import { server } from "../server.js";
import { z } from "zod";
export function registerRandomTool() {
server.registerTool(
"random_number",
{
description: "Generate a random number within a specified range",
inputSchema: {
min: z.number().describe("Minimum value (inclusive)"),
max: z.number().describe("Maximum value (inclusive)"),
},
},
async ({ min, max }) => {
if (min > max) {
return {
content: [
{
type: "text",
text: "Error: min must be less than or equal to max",
},
],
isError: true,
};
}
const randomNum = Math.floor(Math.random() * (max - min + 1)) + min;
return {
content: [
{
type: "text",
text: `Random number between ${min} and ${max}: ${randomNum}`,
},
],
};
}
);
}

View File

@@ -0,0 +1,51 @@
import { server } from "../server.js";
import { z } from "zod";
export function registerStringTool() {
server.registerTool(
"string_utils",
{
description: "Perform various string operations",
inputSchema: {
operation: z
.enum(["uppercase", "lowercase", "reverse", "length", "word_count"])
.describe("The string operation to perform"),
text: z.string().describe("The text to process"),
},
},
async ({ operation, text }) => {
let result: string;
switch (operation) {
case "uppercase":
result = text.toUpperCase();
break;
case "lowercase":
result = text.toLowerCase();
break;
case "reverse":
result = text.split("").reverse().join("");
break;
case "length":
result = `Length: ${text.length} characters`;
break;
case "word_count":
const words = text
.trim()
.split(/\s+/)
.filter((w) => w.length > 0);
result = `Word count: ${words.length}`;
break;
}
return {
content: [
{
type: "text",
text: result,
},
],
};
}
);
}

View File

@@ -0,0 +1,48 @@
import { server } from "../server.js";
import { z } from "zod";
export function registerTimeTool() {
server.registerTool(
"get_current_time",
{
description: "Get the current date and time",
inputSchema: {
timezone: z
.string()
.optional()
.describe("Timezone (e.g., 'Asia/Shanghai', 'America/New_York')"),
},
},
async ({ timezone }) => {
const now = new Date();
let timeString: string;
if (timezone) {
try {
timeString = now.toLocaleString("en-US", { timeZone: timezone });
} catch {
return {
content: [
{
type: "text",
text: `Error: Invalid timezone '${timezone}'`,
},
],
isError: true,
};
}
} else {
timeString = now.toISOString();
}
return {
content: [
{
type: "text",
text: `Current time: ${timeString}`,
},
],
};
}
);
}

23
typescript/tsconfig.json Normal file
View File

@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"build"
]
}