Projeto final: Crie um servidor MCP pronto para produção que integre múltiplos provedores, ferramentas e sistemas do mundo real
Parabéns por chegar ao projeto final! Agora você aplicará tudo o que aprendeu para construir um servidor MCP pronto para produção que demonstra padrões de integração do mundo real e melhores práticas.
Seu projeto final é um Servidor de Inteligência de Workspace - um servidor MCP que fornece às aplicações de IA acesso abrangente a ambientes de desenvolvimento. Este servidor integrará múltiplas fontes de dados e fornecerá ferramentas poderosas para gerenciamento de projetos, fluxos de trabalho de desenvolvimento e colaboração em equipe.
🔧 Arquitetura Multi-Provedor: Sistema de arquivos, Git, banco de dados, API e provedores de configuração 🛠️ Conjunto Abrangente de Ferramentas: Operações de arquivo, gerenciamento de projetos, comunicação e ferramentas de desenvolvimento ⚡ Pronto para Produção: Logging, monitoramento, tratamento de erros e desligamento gracioso 🔒 Seguro por Design: Validação de entrada, controles de acesso e logging de auditoria 📊 Observável: Verificações de saúde, métricas e monitoramento de performance 🧪 Bem Testado: Testes unitários, testes de integração e testes end-to-end
<CodeExample
title="Estrutura Completa do Projeto"
language="text"
code={mcp-workspace-server/ ├── src/ │ ├── index.ts # Entrada principal do servidor │ ├── config/ │ │ ├── index.ts # Gerenciamento de configuração │ │ └── schema.ts # Validação de config │ ├── providers/ │ │ ├── index.ts # Registro de provedores │ │ ├── filesystem.ts # Provedor de sistema de arquivos │ │ ├── database.ts # Provedor de banco de dados │ │ ├── api.ts # Provedor de API externa │ │ └── git.ts # Provedor de repositório Git │ ├── tools/ │ │ ├── index.ts # Registro de ferramentas │ │ ├── file-ops.ts # Operações de arquivo │ │ ├── project-mgmt.ts # Gerenciamento de projeto │ │ ├── communication.ts # Ferramentas Email/Slack │ │ └── development.ts # Ferramentas dev (git, npm, etc.) │ ├── middleware/ │ │ ├── auth.ts # Autenticação │ │ ├── rate-limit.ts # Limitação de taxa │ │ └── logging.ts # Logging de requisições │ ├── utils/ │ │ ├── validation.ts # Validação de entrada │ │ ├── security.ts # Helpers de segurança │ │ └── errors.ts # Classes de erro │ └── types/ │ ├── config.ts # Tipos de configuração │ ├── providers.ts # Interfaces de provedor │ └── tools.ts # Interfaces de ferramenta ├── tests/ │ ├── unit/ # Testes unitários │ ├── integration/ # Testes de integração │ └── fixtures/ # Dados de teste ├── docs/ │ ├── README.md # Documentação principal │ ├── API.md # Referência da API │ └── DEPLOYMENT.md # Guia de implantação ├── config/ │ ├── development.json # Config de desenvolvimento │ ├── production.json # Config de produção │ └── test.json # Config de teste ├── scripts/ │ ├── build.sh # Script de build │ ├── test.sh # Script de teste │ └── deploy.sh # Script de implantação ├── .github/ │ └── workflows/ # Workflows CI/CD ├── package.json ├── tsconfig.json ├── jest.config.js ├── .eslintrc.js └── .gitignore
}
/>
Seu servidor seguirá estes princípios arquiteturais:
Filosofia de Design: Construa para produção desde o primeiro dia. Cada componente deve incluir tratamento de erros, logging, validação e testes.
Organização de servidor MCP pronto para produção
Module content not available.
Module content not available.
Seu servidor precisa de um conjunto robusto de ferramentas que cubra todo o espectro de tarefas de desenvolvimento e gerenciamento de projetos.
<CodeExample title="Implementação do Registro de Ferramentas" 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> { // Limitação de taxa if (!this.rateLimiter.allow(name)) { throw new Error(`Limite de taxa excedido para ferramenta: ${name}`); }
// Encontrar ferramenta
const tool = this.tools.get(name);
if (!tool) {
throw new Error(\`Ferramenta não encontrada: \${name}\`);
}
// Validar argumentos
const validation = this.validateArguments(tool, args);
if (!validation.valid) {
throw new Error(\`Argumentos inválidos: \${validation.errors.join(', ')}\`);
}
// Executar com monitoramento
const startTime = Date.now();
try {
const result = await tool.execute(args);
// Registrar execução
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="Conjunto de Ferramentas de Desenvolvimento" language="typescript" code={`// src/tools/development.ts export class DevelopmentTools implements ToolProvider { getName() { return 'development'; }
getTools(): ToolDefinition[] { return [ { name: 'git_commit', description: 'Criar um commit git com mudanças preparadas', inputSchema: { type: 'object', properties: { message: { type: 'string', description: 'Mensagem de commit' }, addAll: { type: 'boolean', default: false, description: 'Adicionar todas as mudanças antes de commitar' } }, required: ['message'] } }, { name: 'npm_install', description: 'Instalar pacotes npm', inputSchema: { type: 'object', properties: { packages: { type: 'array', items: { type: 'string' }, description: 'Nomes dos pacotes para instalar' }, dev: { type: 'boolean', default: false, description: 'Instalar como dependências de desenvolvimento' }, global: { type: 'boolean', default: false, description: 'Instalar globalmente' } }, required: ['packages'] } }, { name: 'run_tests', description: 'Executar testes do projeto', inputSchema: { type: 'object', properties: { pattern: { type: 'string', description: 'Padrão de arquivo de teste' }, watch: { type: 'boolean', default: false, description: 'Executar em modo watch' }, coverage: { type: 'boolean', default: false, description: 'Gerar relatório de cobertura' } } } }, { name: 'build_project', description: 'Construir o projeto', inputSchema: { type: 'object', properties: { mode: { type: 'string', enum: ['development', 'production'], default: 'development', description: 'Modo de build' }, clean: { type: 'boolean', default: false, description: 'Limpar antes do 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(`Ferramenta de desenvolvimento desconhecida: ${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: \`Commit git bem-sucedido:\\n\${results.join('\\n')}\`
}
]
};
} catch (error) {
return {
content: [
{
type: 'text',
text: \`Commit git falhou: \${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 minutos
});
return {
content: [
{
type: 'text',
text: \`Pacotes instalados com sucesso:\\n\${result.stdout}\`
}
]
};
} catch (error) {
return {
content: [
{
type: 'text',
text: \`npm install falhou: \${error.message}\`
}
],
isError: true
};
}
} }`} />
<CodeExample title="Ferramentas de Comunicação" language="typescript" code={`// src/tools/communication.ts export class CommunicationTools implements ToolProvider { getName() { return 'communication'; }
getTools(): ToolDefinition[] { const tools = [ { name: 'send_email', description: 'Enviar uma notificação por e-mail', inputSchema: { type: 'object', properties: { to: { type: 'string', description: 'Endereço de e-mail do destinatário' }, subject: { type: 'string', description: 'Assunto do e-mail' }, body: { type: 'string', description: 'Conteúdo do corpo do e-mail' }, priority: { type: 'string', enum: ['low', 'normal', 'high'], default: 'normal', description: 'Prioridade do e-mail' } }, required: ['to', 'subject', 'body'] } } ];
// Adicionar ferramentas Slack se token estiver configurado
if (this.config.slackToken) {
tools.push({
name: 'slack_message',
description: 'Enviar mensagem para canal Slack',
inputSchema: {
type: 'object',
properties: {
channel: { type: 'string', description: 'Nome ou ID do canal Slack' },
message: { type: 'string', description: 'Texto da mensagem' },
urgent: { type: 'boolean', default: false, description: 'Marcar como urgente' },
threadId: { type: 'string', description: 'Responder a thread (opcional)' }
},
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(`Ferramenta de comunicação desconhecida: ${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: '🚨 MENSAGEM URGENTE 🚨' }] }) }) });
const result = await response.json();
if (!result.ok) {
throw new Error(result.error || 'Erro da API Slack');
}
return {
content: [
{
type: 'text',
text: \`Mensagem enviada para \${args.channel} com sucesso\`
}
]
};
} catch (error) {
return {
content: [
{
type: 'text',
text: \`Falha ao enviar mensagem Slack: \${error.message}\`
}
],
isError: true
};
}
} }`} />
Gerenciamento e execução centralizados de ferramentas
Um servidor de produção requer testes abrangentes e processos adequados de implantação.
<CodeExample title="Conjunto Abrangente de Testes" language="typescript" code={`// tests/integration/server.test.ts
describe('Integração do Servidor MCP Workspace', () => { let server: MCPWorkspaceServer; let transport: TestTransport;
beforeEach(async () => { // Configurar ambiente de teste process.env.NODE_ENV = 'test';
server = new MCPWorkspaceServer();
transport = new TestTransport();
await server.start(transport);
});
afterEach(async () => { await server.shutdown(); });
describe('Recursos', () => { test('deve listar todos os recursos disponíveis', 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('deve ler recursos do sistema de arquivos', async () => {
const response = await transport.request('resources/read', {
uri: 'file://test-file.txt'
});
expect(response.contents).toBeDefined();
expect(response.contents[0].text).toContain('conteúdo de teste');
});
test('deve lidar com URIs de recurso inválidos', async () => {
await expect(
transport.request('resources/read', { uri: 'invalid://uri' })
).rejects.toThrow('Nenhum provedor encontrado');
});
});
describe('Ferramentas', () => { test('deve listar todas as ferramentas disponíveis', async () => { const response = await transport.request('tools/list', {});
expect(response.tools).toBeDefined();
expect(Array.isArray(response.tools)).toBe(true);
// Verificar ferramentas esperadas
const toolNames = response.tools.map(t => t.name);
expect(toolNames).toContain('read_file');
expect(toolNames).toContain('write_file');
});
test('deve executar operações de arquivo', async () => {
const response = await transport.request('tools/call', {
name: 'write_file',
arguments: {
path: 'test-output.txt',
content: 'Olá, MCP!'
}
});
expect(response.isError).toBeFalsy();
expect(response.content[0].text).toContain('Escrito com sucesso');
});
test('deve validar parâmetros de ferramentas', async () => {
await expect(
transport.request('tools/call', {
name: 'write_file',
arguments: {
// Faltando parâmetro obrigatório 'content'
path: 'test.txt'
}
})
).rejects.toThrow('Argumentos inválidos');
});
});
describe('Saúde e Monitoramento', () => { test('deve fornecer status de saúde', async () => { const response = await transport.request('health', {});
expect(response.status).toBe('healthy');
expect(response.providers).toBeGreaterThan(0);
expect(response.tools).toBeGreaterThan(0);
});
}); });`} />
<CodeExample title="Configuração de Implantação de Produção" 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="Workflow GitHub Actions" language="yaml" code={`# .github/workflows/ci.yml name: Pipeline CI/CD
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: Usar Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Instalar dependências
run: npm ci
- name: Executar linting
run: npm run lint
- name: Executar verificação de tipos
run: npm run type-check
- name: Executar testes
run: npm run test:coverage
- name: Upload relatórios de cobertura
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: Construir imagem Docker
run: docker build -t mcp-workspace-server .
- name: Executar scan de segurança
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: Implantar em produção
run: echo "Implantar no ambiente de produção"`}
/>
Testes unitários e de integração para servidor MCP
Docker e configuração de implantação