Capstone project: Create a production-ready MCP server that integrates multiple providers, tools, and real-world systems
Congratulations on reaching the capstone project! You'll now apply everything you've learned to build a production-ready MCP server that demonstrates real-world integration patterns and best practices.
Your capstone project is a Workspace Intelligence Server - an MCP server that provides AI applications with comprehensive access to development environments. This server will integrate multiple data sources and provide powerful tools for project management, development workflows, and team collaboration.
π§ Multi-Provider Architecture: File system, Git, database, API, and configuration providers π οΈ Comprehensive Toolset: File operations, project management, communication, and development tools β‘ Production Ready: Logging, monitoring, error handling, and graceful shutdown π Secure by Design: Input validation, access controls, and audit logging π Observable: Health checks, metrics, and performance monitoring π§ͺ Well Tested: Unit tests, integration tests, and end-to-end testing
<CodeExample
title="Complete Project Structure"
language="text"
code={mcp-workspace-server/ βββ src/ β βββ index.ts # Main server entry β βββ config/ β β βββ index.ts # Configuration management β β βββ schema.ts # Config validation β βββ providers/ β β βββ index.ts # Provider registry β β βββ filesystem.ts # File system provider β β βββ database.ts # Database provider β β βββ api.ts # External API provider β β βββ git.ts # Git repository provider β βββ tools/ β β βββ index.ts # Tool registry β β βββ file-ops.ts # File operations β β βββ project-mgmt.ts # Project management β β βββ communication.ts # Email/Slack tools β β βββ development.ts # Dev tools (git, npm, etc.) β βββ middleware/ β β βββ auth.ts # Authentication β β βββ rate-limit.ts # Rate limiting β β βββ logging.ts # Request logging β βββ utils/ β β βββ validation.ts # Input validation β β βββ security.ts # Security helpers β β βββ errors.ts # Error classes β βββ types/ β βββ config.ts # Configuration types β βββ providers.ts # Provider interfaces β βββ tools.ts # Tool interfaces βββ tests/ β βββ unit/ # Unit tests β βββ integration/ # Integration tests β βββ fixtures/ # Test data βββ docs/ β βββ README.md # Main documentation β βββ API.md # API reference β βββ DEPLOYMENT.md # Deployment guide βββ config/ β βββ development.json # Dev configuration β βββ production.json # Prod configuration β βββ test.json # Test configuration βββ scripts/ β βββ build.sh # Build script β βββ test.sh # Test script β βββ deploy.sh # Deployment script βββ .github/ β βββ workflows/ # CI/CD workflows βββ package.json βββ tsconfig.json βββ jest.config.js βββ .eslintrc.js βββ .gitignore
}
/>
Your server will follow these architectural principles:
Design Philosophy: Build for production from day one. Every component should include error handling, logging, validation, and tests.
Production-ready MCP server organization
Module content not available.
Module content not available.
Your server needs a robust set of tools that cover the full spectrum of development and project management tasks.
<CodeExample title="Tool Registry Implementation" language="typescript" code={`export class ToolRegistry { private tools: Map<string, Tool> = new Map(); private toolProviders: Map<string, ToolProvider> = new Map(); private executionLog: ExecutionLog[] = []; private rateLimiter: RateLimiter;
constructor(private config: any, private logger: any) { this.rateLimiter = new RateLimiter(config.rateLimiting); }
async registerAll() { const providers = [ new FileOperationTools(this.config.filesystem), new ProjectManagementTools(this.config.project), new CommunicationTools(this.config.communication), new DevelopmentTools(this.config.development) ];
for (const provider of providers) {
await this.registerToolProvider(provider);
}
}
async executeTool(name: string, args: any): Promise<ToolResult> { // Rate limiting if (!this.rateLimiter.allow(name)) { throw new Error(`Rate limit exceeded for tool: ${name}`); }
// Find tool
const tool = this.tools.get(name);
if (!tool) {
throw new Error(\`Tool not found: \${name}\`);
}
// Validate arguments
const validation = this.validateArguments(tool, args);
if (!validation.valid) {
throw new Error(\`Invalid arguments: \${validation.errors.join(', ')}\`);
}
// Execute with monitoring
const startTime = Date.now();
try {
const result = await tool.execute(args);
// Log execution
this.logExecution({
tool: name,
success: !result.isError,
duration: Date.now() - startTime,
timestamp: new Date()
});
return result;
} catch (error) {
this.logExecution({
tool: name,
success: false,
duration: Date.now() - startTime,
error: error.message,
timestamp: new Date()
});
throw error;
}
}
getAllTools(): ToolDefinition[] { return Array.from(this.tools.values()).map(tool => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema })); }
getExecutionStats(): ExecutionStats { const recent = this.executionLog.filter( log => Date.now() - log.timestamp.getTime() < 24 * 60 * 60 * 1000 );
return {
totalExecutions: recent.length,
successRate: recent.filter(log => log.success).length / recent.length,
averageDuration: recent.reduce((sum, log) => sum + log.duration, 0) / recent.length,
topTools: this.getTopTools(recent)
};
} }`} />
<CodeExample title="Development Tools Suite" language="typescript" code={`// src/tools/development.ts export class DevelopmentTools implements ToolProvider { getName() { return 'development'; }
getTools(): ToolDefinition[] { return [ { name: 'git_commit', description: 'Create a git commit with staged changes', inputSchema: { type: 'object', properties: { message: { type: 'string', description: 'Commit message' }, addAll: { type: 'boolean', default: false, description: 'Add all changes before committing' } }, required: ['message'] } }, { name: 'npm_install', description: 'Install npm packages', inputSchema: { type: 'object', properties: { packages: { type: 'array', items: { type: 'string' }, description: 'Package names to install' }, dev: { type: 'boolean', default: false, description: 'Install as dev dependencies' }, global: { type: 'boolean', default: false, description: 'Install globally' } }, required: ['packages'] } }, { name: 'run_tests', description: 'Run project tests', inputSchema: { type: 'object', properties: { pattern: { type: 'string', description: 'Test file pattern' }, watch: { type: 'boolean', default: false, description: 'Run in watch mode' }, coverage: { type: 'boolean', default: false, description: 'Generate coverage report' } } } }, { name: 'build_project', description: 'Build the project', inputSchema: { type: 'object', properties: { mode: { type: 'string', enum: ['development', 'production'], default: 'development', description: 'Build mode' }, clean: { type: 'boolean', default: false, description: 'Clean before build' } } } } ]; }
async executeTool(name: string, args: any): Promise<ToolResult> { switch (name) { case 'git_commit': return this.gitCommit(args); case 'npm_install': return this.npmInstall(args); case 'run_tests': return this.runTests(args); case 'build_project': return this.buildProject(args); default: throw new Error(`Unknown development tool: ${name}`); } }
private async gitCommit(args: any): Promise<ToolResult> { try { const commands = [];
if (args.addAll) {
commands.push('git add .');
}
commands.push(\`git commit -m "\${args.message}"\`);
const results = [];
for (const command of commands) {
const result = await execAsync(command, { cwd: this.config.repositoryPath });
results.push(result.stdout);
}
return {
content: [
{
type: 'text',
text: \`Git commit successful:\\n\${results.join('\\n')}\`
}
]
};
} catch (error) {
return {
content: [
{
type: 'text',
text: \`Git commit failed: \${error.message}\`
}
],
isError: true
};
}
}
private async npmInstall(args: any): Promise<ToolResult> { try { const flags = []; if (args.dev) flags.push('--save-dev'); if (args.global) flags.push('--global');
const command = \`npm install \${args.packages.join(' ')} \${flags.join(' ')}\`;
const result = await execAsync(command, {
cwd: this.config.projectPath,
timeout: 300000 // 5 minutes
});
return {
content: [
{
type: 'text',
text: \`Packages installed successfully:\\n\${result.stdout}\`
}
]
};
} catch (error) {
return {
content: [
{
type: 'text',
text: \`npm install failed: \${error.message}\`
}
],
isError: true
};
}
} }`} />
<CodeExample title="Communication Tools" language="typescript" code={`// src/tools/communication.ts export class CommunicationTools implements ToolProvider { getName() { return 'communication'; }
getTools(): ToolDefinition[] { const tools = [ { name: 'send_email', description: 'Send an email notification', inputSchema: { type: 'object', properties: { to: { type: 'string', description: 'Recipient email address' }, subject: { type: 'string', description: 'Email subject' }, body: { type: 'string', description: 'Email body content' }, priority: { type: 'string', enum: ['low', 'normal', 'high'], default: 'normal', description: 'Email priority' } }, required: ['to', 'subject', 'body'] } } ];
// Add Slack tools if token is configured
if (this.config.slackToken) {
tools.push({
name: 'slack_message',
description: 'Send a message to Slack channel',
inputSchema: {
type: 'object',
properties: {
channel: { type: 'string', description: 'Slack channel name or ID' },
message: { type: 'string', description: 'Message text' },
urgent: { type: 'boolean', default: false, description: 'Mark as urgent' },
threadId: { type: 'string', description: 'Reply to thread (optional)' }
},
required: ['channel', 'message']
}
});
}
return tools;
}
async executeTool(name: string, args: any): Promise<ToolResult> { switch (name) { case 'send_email': return this.sendEmail(args); case 'slack_message': return this.sendSlackMessage(args); default: throw new Error(`Unknown communication tool: ${name}`); } }
private async sendSlackMessage(args: any): Promise<ToolResult> { try { const response = await fetch('https://slack.com/api/chat.postMessage', { method: 'POST', headers: { 'Authorization': `Bearer ${this.config.slackToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ channel: args.channel, text: args.message, thread_ts: args.threadId, ...(args.urgent && { attachments: [{ color: 'danger', text: 'π¨ URGENT MESSAGE π¨' }] }) }) });
const result = await response.json();
if (!result.ok) {
throw new Error(result.error || 'Slack API error');
}
return {
content: [
{
type: 'text',
text: \`Message sent to \${args.channel} successfully\`
}
]
};
} catch (error) {
return {
content: [
{
type: 'text',
text: \`Failed to send Slack message: \${error.message}\`
}
],
isError: true
};
}
} }`} />
Centralized tool management and execution
A production server requires comprehensive testing and proper deployment processes.
<CodeExample title="Comprehensive Test Suite" language="typescript" code={`// tests/integration/server.test.ts
describe('MCP Workspace Server Integration', () => { let server: MCPWorkspaceServer; let transport: TestTransport;
beforeEach(async () => { // Set up test environment process.env.NODE_ENV = 'test';
server = new MCPWorkspaceServer();
transport = new TestTransport();
await server.start(transport);
});
afterEach(async () => { await server.shutdown(); });
describe('Resources', () => { test('should list all available resources', async () => { const response = await transport.request('resources/list', {});
expect(response.resources).toBeDefined();
expect(Array.isArray(response.resources)).toBe(true);
expect(response.resources.length).toBeGreaterThan(0);
});
test('should read file system resources', async () => {
const response = await transport.request('resources/read', {
uri: 'file://test-file.txt'
});
expect(response.contents).toBeDefined();
expect(response.contents[0].text).toContain('test content');
});
test('should handle invalid resource URIs', async () => {
await expect(
transport.request('resources/read', { uri: 'invalid://uri' })
).rejects.toThrow('No provider found');
});
});
describe('Tools', () => { test('should list all available tools', async () => { const response = await transport.request('tools/list', {});
expect(response.tools).toBeDefined();
expect(Array.isArray(response.tools)).toBe(true);
// Check for expected tools
const toolNames = response.tools.map(t => t.name);
expect(toolNames).toContain('read_file');
expect(toolNames).toContain('write_file');
});
test('should execute file operations', async () => {
const response = await transport.request('tools/call', {
name: 'write_file',
arguments: {
path: 'test-output.txt',
content: 'Hello, MCP!'
}
});
expect(response.isError).toBeFalsy();
expect(response.content[0].text).toContain('Successfully wrote');
});
test('should validate tool parameters', async () => {
await expect(
transport.request('tools/call', {
name: 'write_file',
arguments: {
// Missing required 'content' parameter
path: 'test.txt'
}
})
).rejects.toThrow('Invalid arguments');
});
});
describe('Health and Monitoring', () => { test('should provide health status', async () => { const response = await transport.request('health', {});
expect(response.status).toBe('healthy');
expect(response.providers).toBeGreaterThan(0);
expect(response.tools).toBeGreaterThan(0);
});
}); });`} />
<CodeExample title="Production Deployment Configuration" language="dockerfile" code={`# Dockerfile FROM node:18-alpine
RUN apk add --no-cache git
WORKDIR /app
COPY package*.json ./ COPY tsconfig.json ./
RUN npm ci --only=production
COPY src/ ./src/ COPY config/ ./config/
RUN npm run build
RUN addgroup -g 1001 -S nodejs RUN adduser -S mcp -u 1001
RUN mkdir -p /workspace && chown mcp:nodejs /workspace
USER mcp
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD node dist/health-check.js
CMD ["npm", "start"]`} />
<CodeExample title="GitHub Actions Workflow" language="yaml" code={`# .github/workflows/ci.yml name: CI/CD Pipeline
on: push: branches: [ main, develop ] pull_request: branches: [ main ]
jobs: test: runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run type checking
run: npm run type-check
- name: Run tests
run: npm run test:coverage
- name: Upload coverage reports
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
build: needs: test runs-on: ubuntu-latest if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Build Docker image
run: docker build -t mcp-workspace-server .
- name: Run security scan
run: docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy image mcp-workspace-server
deploy: needs: [test, build] runs-on: ubuntu-latest if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to production
run: echo "Deploy to production environment"`}
/>
Unit and integration tests for MCP server
Docker and deployment configuration