Skip to content

Adicionar ferramentas MCP

Este guia explica como expor os endpoints de API de uma aplicação NENE2 como ferramentas MCP, permitindo que assistentes de IA (Claude, Cursor, etc.) chamem a API via o Model Context Protocol.

Pré-requisito: Você tem uma aplicação NENE2 funcionando com pelo menos uma rota e um arquivo docs/openapi/openapi.yaml. Caso contrário, comece por Adicionar uma rota personalizada.


Visão geral

NENE2 fornece um servidor MCP local (LocalMcpServer) que traduz mensagens JSON-RPC MCP em chamadas HTTP para a API. O catálogo de ferramentas (docs/mcp/tools.json) declara quais endpoints expor como ferramentas MCP e o nível de segurança de cada uma.

Assistente IA → JSON-RPC (stdio) → LocalMcpServer → HTTP → Aplicação NENE2

O catálogo é validado em relação à spec OpenAPI com composer mcp.


1. Adicionar o script de validação

Adicione em composer.json:

json
{
  "require-dev": {
    "symfony/yaml": "^7.0"
  },
  "scripts": {
    "mcp": "php vendor/hideyukimori/nene2/tools/validate-mcp-tools.php --root=."
  }
}

Instale a dependência de desenvolvimento:

bash
composer require --dev symfony/yaml

2. Criar o catálogo de ferramentas

Crie docs/mcp/tools.json. Cada entrada em tools corresponde a um endpoint da API.

json
{
  "version": 1,
  "source": "docs/openapi/openapi.yaml",
  "tools": [
    {
      "name": "listNotes",
      "title": "Lista de notas",
      "description": "Retorna todas as notas do banco de dados.",
      "safety": "read",
      "source": {
        "type": "openapi",
        "operationId": "listNotes",
        "method": "GET",
        "path": "/notes"
      },
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "limit":  { "type": "integer", "description": "Número máximo de itens a retornar." },
          "offset": { "type": "integer", "description": "Número de itens a ignorar." }
        }
      },
      "responseSchemaRef": "#/components/schemas/NoteListResponse"
    }
  ]
}

Campos de uma ferramenta

CampoObrigatórioDescrição
nameSimIdentificador camelCase único
titleSimRótulo legível por humanos
descriptionSimTexto que explica o propósito ao assistente de IA
safetySimread / write / admin / destructive
source.operationIdSimDeve corresponder ao operationId na spec OpenAPI
source.methodSimMétodo HTTP (maiúsculas/minúsculas indiferente, armazenado em maiúsculas)
source.pathSimCaminho URL com parâmetros no formato {param}
inputSchemaSimJSON Schema dos argumentos da ferramenta
responseSchemaRefNão$ref para um schema de componente OpenAPI, ou null

Níveis de segurança

NívelSignificado
readPode ser chamado sem efeitos colaterais (requisições GET)
writeCria ou modifica dados (POST / PUT / PATCH)
adminAções administrativas — usar com cautela
destructiveExclui dados permanentemente — confirmação explícita necessária

Comece apenas com ferramentas read, adicione ferramentas write quando a autenticação estiver configurada.


bash
composer mcp

O validador verifica:

  • O operationId de cada ferramenta existe na spec OpenAPI
  • O caminho de cada ferramenta corresponde à definição de caminho OpenAPI
  • O campo safety é um dos quatro valores permitidos
  • responseSchemaRef (se não nulo) resolve para um schema de componente existente

Corrija todos os erros antes de iniciar o servidor MCP.


4. Adicionar ferramentas de escrita

json
{
  "name": "createNote",
  "title": "Criar nota",
  "description": "Cria uma nova nota.",
  "safety": "write",
  "source": {
    "type": "openapi",
    "operationId": "createNote",
    "method": "POST",
    "path": "/notes"
  },
  "inputSchema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["title", "content"],
    "properties": {
      "title":   { "type": "string", "description": "Título da nota." },
      "content": { "type": "string", "description": "Conteúdo da nota." }
    }
  },
  "responseSchemaRef": null
}

5. Proteger ferramentas de escrita com JWT

LocalMcpServer verifica um cabeçalho Authorization: Bearer <token> para cada chamada de ferramenta write, admin ou destructive. Configure a variável de ambiente:

dotenv
NENE2_LOCAL_JWT_SECRET=your-local-secret

Sem essa variável, chamadas de ferramentas de escrita retornam um erro MCP sem encaminhar a requisição.

Proteja também os endpoints correspondentes no lado da aplicação com BearerTokenMiddleware:

php
use Nene2\Auth\BearerTokenMiddleware;
use Nene2\Auth\LocalBearerTokenVerifier;

$secret = getenv('NENE2_LOCAL_JWT_SECRET') ?: null;

$authMiddleware = $secret !== null
    ? new BearerTokenMiddleware(
        $problemDetails,
        new LocalBearerTokenVerifier($secret),
        excludedPaths: ['/notes'],
        protectedPathPrefixes: ['/notes/'],
    )
    : null;

6. Iniciar o servidor MCP

bash
NENE2_LOCAL_API_BASE_URL=http://localhost:8200 \
NENE2_LOCAL_JWT_SECRET=your-local-secret \
php vendor/hideyukimori/nene2/tools/local-mcp-server.php

O servidor lê de stdin e escreve em stdout via o transporte stdio do MCP.


7. Configurar Claude Code ou Claude Desktop

Claude Code (~/.claude/claude_code_config.json)

json
{
  "mcpServers": {
    "my-app": {
      "command": "/path/to/my-app/mcp-server.sh"
    }
  }
}

Claude Desktop (claude_desktop_config.json)

json
{
  "mcpServers": {
    "my-app": {
      "command": "bash",
      "args": ["/path/to/my-app/mcp-server.sh"]
    }
  }
}

Reinicie o Claude e as ferramentas declaradas no catálogo aparecerão como ações disponíveis.


8. Testar a camada MCP

Teste LocalMcpToolCatalog diretamente. Nenhum servidor HTTP é necessário:

php
use Nene2\Mcp\LocalMcpToolCatalog;

public function testListNotesToolIsPresent(): void
{
    $catalog = new LocalMcpToolCatalog(dirname(__DIR__) . '/docs/mcp/tools.json');

    $tool = $catalog->find('listNotes');

    self::assertNotNull($tool);
    self::assertSame('read', $tool['safety']);
    self::assertSame('GET', $tool['source']['method']);
    self::assertSame('/notes', $tool['source']['path']);
}

Próximos passos

Publicado sob a licença MIT.