Chapter 11: Real-World Project Development
Haiyue
32min
Chapter 11: Real-World Project Development
Learning Objectives
- Develop a File System Management MCP Server
- Create a Database Operations MCP Server
- Implement an API Gateway and Proxy Server
- Build a Content Management and Search Server
- Develop Custom Business Logic Servers
1. Project 1: File System Management Server
1.1 Requirements Analysis
interface FileManagerRequirements {
// Tool capabilities
tools: {
listFiles: "List directory contents";
readFile: "Read file contents";
writeFile: "Write file contents";
createDirectory: "Create directory";
deleteFile: "Delete file";
moveFile: "Move file";
copyFile: "Copy file";
searchFiles: "Search files";
getFileInfo: "Get file information";
compressFiles: "Compress files";
extractArchive: "Extract archive";
};
// Resource capabilities
resources: {
fileContent: "File content resource";
directoryTree: "Directory tree resource";
fileMetadata: "File metadata resource";
};
// Security requirements
security: {
sandboxing: "File access sandbox restrictions";
permissions: "File operation permission control";
validation: "Path and content validation";
};
}
1.2 Core Architecture Implementation
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import * as fs from 'fs/promises';
import * as path from 'path';
import * as mime from 'mime-types';
import { z } from 'zod';
interface FileManagerConfig {
baseDirectory: string;
maxFileSize: number;
allowedExtensions: string[];
forbiddenPaths: string[];
enableCompression: boolean;
}
class FileSystemManager {
private config: FileManagerConfig;
private basePath: string;
constructor(config: FileManagerConfig) {
this.config = config;
this.basePath = path.resolve(config.baseDirectory);
this.validateBasePath();
}
private validateBasePath(): void {
if (!fs.stat(this.basePath).catch(() => false)) {
throw new Error(`Base directory does not exist: ${this.basePath}`);
}
}
private validatePath(filePath: string): string {
const resolvedPath = path.resolve(this.basePath, filePath);
// Ensure path is within sandbox
if (!resolvedPath.startsWith(this.basePath)) {
throw new Error("Path is outside of allowed directory");
}
// Check forbidden paths
const relativePath = path.relative(this.basePath, resolvedPath);
for (const forbidden of this.config.forbiddenPaths) {
if (relativePath.startsWith(forbidden)) {
throw new Error(`Access to path is forbidden: ${relativePath}`);
}
}
return resolvedPath;
}
private validateFileExtension(fileName: string): void {
if (this.config.allowedExtensions.length === 0) return;
const ext = path.extname(fileName).toLowerCase();
if (!this.config.allowedExtensions.includes(ext)) {
throw new Error(`File extension not allowed: ${ext}`);
}
}
async listFiles(directoryPath: string = '.'): Promise<FileInfo[]> {
const fullPath = this.validatePath(directoryPath);
const entries = await fs.readdir(fullPath, { withFileTypes: true });
const fileInfos = await Promise.all(
entries.map(async (entry) => {
const entryPath = path.join(fullPath, entry.name);
const stats = await fs.stat(entryPath);
return {
name: entry.name,
path: path.relative(this.basePath, entryPath),
type: entry.isDirectory() ? 'directory' : 'file',
size: stats.size,
modified: stats.mtime,
permissions: this.getPermissions(stats),
mimeType: entry.isFile() ? mime.lookup(entry.name) || 'application/octet-stream' : null
} as FileInfo;
})
);
return fileInfos.sort((a, b) => {
if (a.type !== b.type) {
return a.type === 'directory' ? -1 : 1;
}
return a.name.localeCompare(b.name);
});
}
async readFile(filePath: string, encoding: string = 'utf8'): Promise<string | Buffer> {
const fullPath = this.validatePath(filePath);
this.validateFileExtension(filePath);
const stats = await fs.stat(fullPath);
if (stats.size > this.config.maxFileSize) {
throw new Error(`File too large: ${stats.size} bytes (max: ${this.config.maxFileSize})`);
}
if (encoding === 'binary') {
return await fs.readFile(fullPath);
} else {
return await fs.readFile(fullPath, encoding as BufferEncoding);
}
}
async writeFile(filePath: string, content: string | Buffer, options?: WriteOptions): Promise<void> {
const fullPath = this.validatePath(filePath);
this.validateFileExtension(filePath);
// Check content size
const contentSize = typeof content === 'string' ? Buffer.byteLength(content) : content.length;
if (contentSize > this.config.maxFileSize) {
throw new Error(`Content too large: ${contentSize} bytes (max: ${this.config.maxFileSize})`);
}
// Ensure directory exists
const directory = path.dirname(fullPath);
await fs.mkdir(directory, { recursive: true });
const writeOptions: any = {
encoding: options?.encoding || 'utf8',
mode: options?.mode || 0o644
};
if (options?.backup && await this.fileExists(fullPath)) {
await this.createBackup(fullPath);
}
await fs.writeFile(fullPath, content, writeOptions);
}
async createDirectory(dirPath: string, recursive: boolean = true): Promise<void> {
const fullPath = this.validatePath(dirPath);
await fs.mkdir(fullPath, { recursive });
}
async deleteFile(filePath: string, force: boolean = false): Promise<void> {
const fullPath = this.validatePath(filePath);
const stats = await fs.stat(fullPath);
if (!force && stats.isDirectory()) {
const entries = await fs.readdir(fullPath);
if (entries.length > 0) {
throw new Error("Directory is not empty. Use force=true to delete recursively.");
}
}
if (stats.isDirectory()) {
await fs.rmdir(fullPath, { recursive: force });
} else {
await fs.unlink(fullPath);
}
}
async moveFile(sourcePath: string, targetPath: string): Promise<void> {
const fullSourcePath = this.validatePath(sourcePath);
const fullTargetPath = this.validatePath(targetPath);
// Ensure target directory exists
const targetDir = path.dirname(fullTargetPath);
await fs.mkdir(targetDir, { recursive: true });
await fs.rename(fullSourcePath, fullTargetPath);
}
async copyFile(sourcePath: string, targetPath: string, overwrite: boolean = false): Promise<void> {
const fullSourcePath = this.validatePath(sourcePath);
const fullTargetPath = this.validatePath(targetPath);
if (!overwrite && await this.fileExists(fullTargetPath)) {
throw new Error("Target file already exists. Use overwrite=true to replace.");
}
// Ensure target directory exists
const targetDir = path.dirname(fullTargetPath);
await fs.mkdir(targetDir, { recursive: true });
await fs.copyFile(fullSourcePath, fullTargetPath);
}
async searchFiles(pattern: string, directory: string = '.', options?: SearchOptions): Promise<FileInfo[]> {
const fullPath = this.validatePath(directory);
const results: FileInfo[] = [];
await this.searchRecursive(fullPath, pattern, results, options);
return results;
}
private async searchRecursive(
dirPath: string,
pattern: string,
results: FileInfo[],
options?: SearchOptions
): Promise<void> {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const entryPath = path.join(dirPath, entry.name);
const relativePath = path.relative(this.basePath, entryPath);
if (entry.isDirectory() && (options?.recursive !== false)) {
await this.searchRecursive(entryPath, pattern, results, options);
} else if (entry.isFile()) {
const matches = options?.useRegex ?
new RegExp(pattern, options.caseSensitive ? '' : 'i').test(entry.name) :
entry.name.toLowerCase().includes(pattern.toLowerCase());
if (matches) {
const stats = await fs.stat(entryPath);
results.push({
name: entry.name,
path: relativePath,
type: 'file',
size: stats.size,
modified: stats.mtime,
permissions: this.getPermissions(stats),
mimeType: mime.lookup(entry.name) || 'application/octet-stream'
});
}
}
}
}
private async fileExists(filePath: string): Promise<boolean> {
try {
await fs.stat(filePath);
return true;
} catch {
return false;
}
}
private async createBackup(filePath: string): Promise<void> {
const backupPath = `${filePath}.backup.${Date.now()}`;
await fs.copyFile(filePath, backupPath);
}
private getPermissions(stats: any): string {
const mode = stats.mode;
return {
readable: !!(mode & 0o444),
writable: !!(mode & 0o222),
executable: !!(mode & 0o111)
};
}
}
interface FileInfo {
name: string;
path: string;
type: 'file' | 'directory';
size: number;
modified: Date;
permissions: any;
mimeType: string | null;
}
interface WriteOptions {
encoding?: string;
mode?: number;
backup?: boolean;
}
interface SearchOptions {
recursive?: boolean;
useRegex?: boolean;
caseSensitive?: boolean;
}
1.3 MCP Server Integration
class FileSystemMCPServer {
private server: Server;
private fileManager: FileSystemManager;
constructor(config: FileManagerConfig) {
this.fileManager = new FileSystemManager(config);
this.server = new Server(
{
name: "filesystem-manager",
version: "1.0.0"
},
{
capabilities: {
tools: {},
resources: {},
prompts: {}
}
}
);
this.setupTools();
this.setupResources();
}
private setupTools(): void {
// List files tool
this.server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "list_files",
description: "List files and directories in a given path",
inputSchema: {
type: "object",
properties: {
path: { type: "string", description: "Directory path to list" }
}
}
},
{
name: "read_file",
description: "Read the contents of a file",
inputSchema: {
type: "object",
properties: {
path: { type: "string", description: "File path to read" },
encoding: { type: "string", description: "File encoding", default: "utf8" }
},
required: ["path"]
}
},
{
name: "write_file",
description: "Write content to a file",
inputSchema: {
type: "object",
properties: {
path: { type: "string", description: "File path to write" },
content: { type: "string", description: "Content to write" },
encoding: { type: "string", description: "File encoding", default: "utf8" },
backup: { type: "boolean", description: "Create backup before writing", default: false }
},
required: ["path", "content"]
}
},
{
name: "search_files",
description: "Search for files matching a pattern",
inputSchema: {
type: "object",
properties: {
pattern: { type: "string", description: "Search pattern" },
directory: { type: "string", description: "Directory to search in", default: "." },
recursive: { type: "boolean", description: "Search recursively", default: true },
useRegex: { type: "boolean", description: "Use regular expressions", default: false }
},
required: ["pattern"]
}
}
]
}));
// Tool execution handler
this.server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case "list_files":
const files = await this.fileManager.listFiles(args.path);
return {
content: [{
type: "text",
text: JSON.stringify(files, null, 2)
}]
};
case "read_file":
const content = await this.fileManager.readFile(args.path, args.encoding);
return {
content: [{
type: "text",
text: typeof content === 'string' ? content : content.toString('base64')
}]
};
case "write_file":
await this.fileManager.writeFile(args.path, args.content, {
encoding: args.encoding,
backup: args.backup
});
return {
content: [{
type: "text",
text: `File written successfully: ${args.path}`
}]
};
case "search_files":
const results = await this.fileManager.searchFiles(args.pattern, args.directory, {
recursive: args.recursive,
useRegex: args.useRegex
});
return {
content: [{
type: "text",
text: JSON.stringify(results, null, 2)
}]
};
default:
throw new Error(`Unknown tool: ${name}`);
}
});
}
private setupResources(): void {
this.server.setRequestHandler("resources/list", async () => ({
resources: [
{
uri: "file://tree",
name: "Directory Tree",
description: "Complete directory tree structure",
mimeType: "application/json"
}
]
}));
this.server.setRequestHandler("resources/read", async (request) => {
const { uri } = request.params;
if (uri === "file://tree") {
const tree = await this.buildDirectoryTree('.');
return {
contents: [{
uri,
mimeType: "application/json",
text: JSON.stringify(tree, null, 2)
}]
};
}
throw new Error(`Unknown resource: ${uri}`);
});
}
private async buildDirectoryTree(dirPath: string): Promise<any> {
const files = await this.fileManager.listFiles(dirPath);
const tree: any = { name: path.basename(dirPath) || 'root', type: 'directory', children: [] };
for (const file of files) {
if (file.type === 'directory') {
const subtree = await this.buildDirectoryTree(file.path);
tree.children.push(subtree);
} else {
tree.children.push({
name: file.name,
type: 'file',
size: file.size,
mimeType: file.mimeType
});
}
}
return tree;
}
async start(): Promise<void> {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error("File System MCP Server running");
}
}
// Start server
const config: FileManagerConfig = {
baseDirectory: process.env.FS_BASE_DIR || './sandbox',
maxFileSize: parseInt(process.env.FS_MAX_FILE_SIZE || '10485760'), // 10MB
allowedExtensions: process.env.FS_ALLOWED_EXT?.split(',') || [],
forbiddenPaths: process.env.FS_FORBIDDEN_PATHS?.split(',') || ['.git', 'node_modules'],
enableCompression: process.env.FS_ENABLE_COMPRESSION === 'true'
};
const server = new FileSystemMCPServer(config);
server.start().catch(console.error);
2. Project 2: Database Operations Server
2.1 Database Abstraction Layer
interface DatabaseConfig {
type: 'sqlite' | 'mysql' | 'postgresql';
connection: {
host?: string;
port?: number;
database: string;
username?: string;
password?: string;
filename?: string; // for SQLite
};
pool?: {
min: number;
max: number;
idleTimeoutMillis: number;
};
}
interface QueryResult {
rows: any[];
rowCount: number;
fields: FieldInfo[];
query: string;
duration: number;
}
interface FieldInfo {
name: string;
type: string;
nullable: boolean;
}
abstract class DatabaseAdapter {
protected config: DatabaseConfig;
constructor(config: DatabaseConfig) {
this.config = config;
}
abstract connect(): Promise<void>;
abstract disconnect(): Promise<void>;
abstract query(sql: string, params?: any[]): Promise<QueryResult>;
abstract getSchema(): Promise<SchemaInfo>;
abstract beginTransaction(): Promise<Transaction>;
}
interface SchemaInfo {
tables: TableInfo[];
}
interface TableInfo {
name: string;
columns: ColumnInfo[];
indexes: IndexInfo[];
constraints: ConstraintInfo[];
}
interface ColumnInfo {
name: string;
type: string;
nullable: boolean;
defaultValue: any;
isPrimaryKey: boolean;
}
interface IndexInfo {
name: string;
columns: string[];
unique: boolean;
}
interface ConstraintInfo {
name: string;
type: 'primary_key' | 'foreign_key' | 'unique' | 'check';
columns: string[];
referencedTable?: string;
referencedColumns?: string[];
}
interface Transaction {
query(sql: string, params?: any[]): Promise<QueryResult>;
commit(): Promise<void>;
rollback(): Promise<void>;
}
2.2 SQLite Adapter Implementation
import sqlite3 from 'sqlite3';
import { open, Database } from 'sqlite';
class SQLiteAdapter extends DatabaseAdapter {
private db: Database | null = null;
async connect(): Promise<void> {
this.db = await open({
filename: this.config.connection.filename || ':memory:',
driver: sqlite3.Database
});
}
async disconnect(): Promise<void> {
if (this.db) {
await this.db.close();
this.db = null;
}
}
async query(sql: string, params: any[] = []): Promise<QueryResult> {
if (!this.db) {
throw new Error('Database not connected');
}
const startTime = Date.now();
try {
// Check if it's a SELECT query
const isSelect = sql.trim().toLowerCase().startsWith('select');
if (isSelect) {
const rows = await this.db.all(sql, params);
const duration = Date.now() - startTime;
// Get field information
const fields = rows.length > 0 ?
Object.keys(rows[0]).map(name => ({
name,
type: this.getColumnType(rows[0][name]),
nullable: true // SQLite doesn't provide this info easily
})) : [];
return {
rows,
rowCount: rows.length,
fields,
query: sql,
duration
};
} else {
const result = await this.db.run(sql, params);
const duration = Date.now() - startTime;
return {
rows: [],
rowCount: result.changes || 0,
fields: [],
query: sql,
duration
};
}
} catch (error) {
throw new Error(`Query failed: ${error.message}\nSQL: ${sql}`);
}
}
async getSchema(): Promise<SchemaInfo> {
if (!this.db) {
throw new Error('Database not connected');
}
const tables = await this.db.all(`
SELECT name FROM sqlite_master
WHERE type='table' AND name NOT LIKE 'sqlite_%'
ORDER BY name
`);
const schemaInfo: SchemaInfo = { tables: [] };
for (const table of tables) {
const tableInfo = await this.getTableInfo(table.name);
schemaInfo.tables.push(tableInfo);
}
return schemaInfo;
}
private async getTableInfo(tableName: string): Promise<TableInfo> {
const columns = await this.db!.all(`PRAGMA table_info(${tableName})`);
const indexes = await this.db!.all(`PRAGMA index_list(${tableName})`);
const tableInfo: TableInfo = {
name: tableName,
columns: columns.map(col => ({
name: col.name,
type: col.type,
nullable: !col.notnull,
defaultValue: col.dflt_value,
isPrimaryKey: !!col.pk
})),
indexes: [],
constraints: []
};
// Get index information
for (const index of indexes) {
const indexColumns = await this.db!.all(`PRAGMA index_info(${index.name})`);
tableInfo.indexes.push({
name: index.name,
columns: indexColumns.map(col => col.name),
unique: !!index.unique
});
}
return tableInfo;
}
async beginTransaction(): Promise<Transaction> {
if (!this.db) {
throw new Error('Database not connected');
}
await this.db.exec('BEGIN TRANSACTION');
return new SQLiteTransaction(this.db);
}
private getColumnType(value: any): string {
if (typeof value === 'number') {
return Number.isInteger(value) ? 'INTEGER' : 'REAL';
}
if (typeof value === 'string') return 'TEXT';
if (typeof value === 'boolean') return 'BOOLEAN';
if (value instanceof Date) return 'DATETIME';
if (value === null) return 'NULL';
return 'BLOB';
}
}
class SQLiteTransaction implements Transaction {
constructor(private db: Database) {}
async query(sql: string, params: any[] = []): Promise<QueryResult> {
const startTime = Date.now();
const isSelect = sql.trim().toLowerCase().startsWith('select');
if (isSelect) {
const rows = await this.db.all(sql, params);
return {
rows,
rowCount: rows.length,
fields: [],
query: sql,
duration: Date.now() - startTime
};
} else {
const result = await this.db.run(sql, params);
return {
rows: [],
rowCount: result.changes || 0,
fields: [],
query: sql,
duration: Date.now() - startTime
};
}
}
async commit(): Promise<void> {
await this.db.exec('COMMIT');
}
async rollback(): Promise<void> {
await this.db.exec('ROLLBACK');
}
}
2.3 Database MCP Server
class DatabaseMCPServer {
private server: Server;
private dbAdapter: DatabaseAdapter;
private queryValidator: QueryValidator;
constructor(config: DatabaseConfig) {
this.dbAdapter = this.createAdapter(config);
this.queryValidator = new QueryValidator();
this.server = new Server(
{
name: "database-manager",
version: "1.0.0"
},
{
capabilities: {
tools: {},
resources: {}
}
}
);
this.setupTools();
this.setupResources();
}
private createAdapter(config: DatabaseConfig): DatabaseAdapter {
switch (config.type) {
case 'sqlite':
return new SQLiteAdapter(config);
default:
throw new Error(`Unsupported database type: ${config.type}`);
}
}
private setupTools(): void {
this.server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "execute_query",
description: "Execute a SQL query",
inputSchema: {
type: "object",
properties: {
sql: { type: "string", description: "SQL query to execute" },
params: {
type: "array",
items: { type: "string" },
description: "Parameters for prepared statement"
}
},
required: ["sql"]
}
},
{
name: "get_schema",
description: "Get database schema information",
inputSchema: {
type: "object",
properties: {
table: { type: "string", description: "Specific table name (optional)" }
}
}
},
{
name: "execute_transaction",
description: "Execute multiple queries in a transaction",
inputSchema: {
type: "object",
properties: {
queries: {
type: "array",
items: {
type: "object",
properties: {
sql: { type: "string" },
params: { type: "array", items: { type: "string" } }
}
},
description: "Array of queries to execute"
}
},
required: ["queries"]
}
}
]
}));
this.server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case "execute_query":
return await this.executeQuery(args.sql, args.params);
case "get_schema":
return await this.getSchemaInfo(args.table);
case "execute_transaction":
return await this.executeTransaction(args.queries);
default:
throw new Error(`Unknown tool: ${name}`);
}
});
}
private async executeQuery(sql: string, params: any[] = []): Promise<any> {
// Validate query
const validation = this.queryValidator.validate(sql);
if (!validation.safe) {
throw new Error(`Unsafe query detected: ${validation.reason}`);
}
try {
const result = await this.dbAdapter.query(sql, params);
return {
content: [{
type: "text",
text: JSON.stringify({
query: result.query,
rowCount: result.rowCount,
duration: `${result.duration}ms`,
data: result.rows.slice(0, 100) // Limit returned rows
}, null, 2)
}]
};
} catch (error) {
throw new Error(`Query execution failed: ${error.message}`);
}
}
private async getSchemaInfo(tableName?: string): Promise<any> {
const schema = await this.dbAdapter.getSchema();
if (tableName) {
const table = schema.tables.find(t => t.name === tableName);
if (!table) {
throw new Error(`Table not found: ${tableName}`);
}
return {
content: [{
type: "text",
text: JSON.stringify(table, null, 2)
}]
};
}
return {
content: [{
type: "text",
text: JSON.stringify(schema, null, 2)
}]
};
}
private async executeTransaction(queries: Array<{sql: string, params?: any[]}>): Promise<any> {
const transaction = await this.dbAdapter.beginTransaction();
const results: any[] = [];
try {
for (const queryInfo of queries) {
const validation = this.queryValidator.validate(queryInfo.sql);
if (!validation.safe) {
throw new Error(`Unsafe query in transaction: ${validation.reason}`);
}
const result = await transaction.query(queryInfo.sql, queryInfo.params);
results.push({
query: result.query,
rowCount: result.rowCount,
duration: result.duration
});
}
await transaction.commit();
return {
content: [{
type: "text",
text: JSON.stringify({
status: "committed",
results
}, null, 2)
}]
};
} catch (error) {
await transaction.rollback();
throw new Error(`Transaction failed: ${error.message}`);
}
}
private setupResources(): void {
this.server.setRequestHandler("resources/list", async () => ({
resources: [
{
uri: "db://schema",
name: "Database Schema",
description: "Complete database schema",
mimeType: "application/json"
},
{
uri: "db://tables",
name: "Table List",
description: "List of all tables",
mimeType: "application/json"
}
]
}));
this.server.setRequestHandler("resources/read", async (request) => {
const { uri } = request.params;
switch (uri) {
case "db://schema":
const schema = await this.dbAdapter.getSchema();
return {
contents: [{
uri,
mimeType: "application/json",
text: JSON.stringify(schema, null, 2)
}]
};
case "db://tables":
const tables = (await this.dbAdapter.getSchema()).tables.map(t => ({
name: t.name,
columns: t.columns.length,
indexes: t.indexes.length
}));
return {
contents: [{
uri,
mimeType: "application/json",
text: JSON.stringify(tables, null, 2)
}]
};
default:
throw new Error(`Unknown resource: ${uri}`);
}
});
}
async start(): Promise<void> {
await this.dbAdapter.connect();
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error("Database MCP Server running");
}
async stop(): Promise<void> {
await this.dbAdapter.disconnect();
}
}
class QueryValidator {
private dangerousKeywords = [
'drop', 'truncate', 'delete', 'alter', 'create', 'grant', 'revoke'
];
validate(sql: string): { safe: boolean; reason?: string } {
const normalizedSql = sql.toLowerCase().trim();
// Check for dangerous keywords
for (const keyword of this.dangerousKeywords) {
if (normalizedSql.includes(keyword)) {
return {
safe: false,
reason: `Contains potentially dangerous keyword: ${keyword}`
};
}
}
// Check for SQL comment injection
if (normalizedSql.includes('--') || normalizedSql.includes('/*')) {
return {
safe: false,
reason: "SQL comments not allowed"
};
}
// Check for multiple statements
const statements = sql.split(';').filter(s => s.trim().length > 0);
if (statements.length > 1) {
return {
safe: false,
reason: "Multiple statements not allowed"
};
}
return { safe: true };
}
}
3. Project 3: API Gateway Proxy Server
(Due to length constraints, I’ll include a summary)
The API Gateway implementation includes:
- API configuration and endpoint management
- Rate limiting and caching
- Request routing and load balancing
- Authentication and authorization
- Metrics collection and monitoring
4. Best Practices
4.1 Project Organization Principles
- Modular Design - Clear functional module separation
- Configuration Externalization - All configuration can be specified externally
- Error Handling - Comprehensive error handling with user-friendly messages
- Logging - Detailed operation logging
- Performance Optimization - Reasonable caching and resource management
4.2 Security Considerations
- Input Validation - Strict validation of all user input
- Permission Control - Fine-grained operation permission control
- Sandbox Isolation - Limit file and network access scope
- Sensitive Information - Avoid logging sensitive information
- Audit Logging - Record all critical operations for auditing
4.3 Maintainability
- Code Structure - Clear code organization and naming
- Documentation - Detailed API and configuration documentation
- Test Coverage - Comprehensive unit and integration testing
- Monitoring and Alerting - Real-time monitoring and exception alerting
- Version Management - Standard version release and change management
Summary
Through the hands-on projects in this chapter, we learned:
- Complete implementation of a File System Management Server
- Architectural design of a Database Operations Server
- Core functionality of an API Gateway Proxy Server
- Security and performance considerations in real projects
- Best practices for enterprise-grade MCP Servers
These hands-on projects demonstrate how to apply the theoretical knowledge from previous chapters to specific business scenarios, laying a solid foundation for developing practical MCP Server applications.