Skip to content

Ajouter des outils MCP

Ce guide explique comment exposer les endpoints API d'une application NENE2 en tant qu'outils MCP, permettant aux assistants IA (Claude, Cursor, etc.) d'appeler l'API via le Model Context Protocol.

Prérequis : Vous avez une application NENE2 fonctionnelle avec au moins une route et un fichier docs/openapi/openapi.yaml. Sinon, commencez par Ajouter une route personnalisée.


Vue d'ensemble

NENE2 fournit un serveur MCP local (LocalMcpServer) qui traduit les messages JSON-RPC MCP en appels HTTP vers l'API. Le catalogue d'outils (docs/mcp/tools.json) déclare quels endpoints exposer en tant qu'outils MCP et leur niveau de sécurité.

Assistant IA → JSON-RPC (stdio) → LocalMcpServer → HTTP → Application NENE2

Le catalogue est validé par rapport à la spec OpenAPI avec composer mcp.


1. Ajouter le script de validation

Ajoutez dans composer.json :

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

Installez la dépendance de développement :

bash
composer require --dev symfony/yaml

2. Créer le catalogue d'outils

Créez docs/mcp/tools.json. Chaque entrée dans tools correspond à un endpoint API.

json
{
  "version": 1,
  "source": "docs/openapi/openapi.yaml",
  "tools": [
    {
      "name": "listNotes",
      "title": "Liste des notes",
      "description": "Retourne toutes les notes depuis la base de données.",
      "safety": "read",
      "source": {
        "type": "openapi",
        "operationId": "listNotes",
        "method": "GET",
        "path": "/notes"
      },
      "inputSchema": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "limit":  { "type": "integer", "description": "Nombre maximum de résultats à retourner." },
          "offset": { "type": "integer", "description": "Nombre de résultats à ignorer." }
        }
      },
      "responseSchemaRef": "#/components/schemas/NoteListResponse"
    }
  ]
}

Champs d'un outil

ChampRequisDescription
nameOuiIdentifiant camelCase unique
titleOuiLibellé lisible par l'humain
descriptionOuiTexte expliquant l'objectif à l'assistant IA
safetyOuiread / write / admin / destructive
source.operationIdOuiDoit correspondre à l'operationId dans la spec OpenAPI
source.methodOuiMéthode HTTP (casse indifférente, stockée en majuscules)
source.pathOuiChemin URL avec paramètres au format {param}
inputSchemaOuiJSON Schema des arguments de l'outil
responseSchemaRefNon$ref vers un schema de composant OpenAPI, ou null

Niveaux de sécurité

NiveauSignification
readPeut être appelé sans effets de bord (requêtes GET)
writeCrée ou modifie des données (POST / PUT / PATCH)
adminActions d'administration — à utiliser avec précaution
destructiveSupprime définitivement des données — confirmation explicite requise

Commencez avec uniquement des outils read, ajoutez les outils write une fois l'authentification en place.


3. Valider le catalogue

bash
composer mcp

Le validateur vérifie :

  • L'operationId de chaque outil existe dans la spec OpenAPI
  • Le chemin de chaque outil correspond à la définition de chemin OpenAPI
  • Le champ safety est l'une des quatre valeurs autorisées
  • responseSchemaRef (si non null) se résout vers un schema de composant existant

Corrigez toutes les erreurs avant de démarrer le serveur MCP.


4. Ajouter des outils en écriture

json
{
  "name": "createNote",
  "title": "Créer une note",
  "description": "Crée une nouvelle note.",
  "safety": "write",
  "source": {
    "type": "openapi",
    "operationId": "createNote",
    "method": "POST",
    "path": "/notes"
  },
  "inputSchema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["title", "content"],
    "properties": {
      "title":   { "type": "string", "description": "Titre de la note." },
      "content": { "type": "string", "description": "Corps de la note." }
    }
  },
  "responseSchemaRef": null
}

5. Protéger les outils en écriture avec JWT

LocalMcpServer vérifie un en-tête Authorization: Bearer <token> pour chaque appel d'outil write, admin ou destructive. Configurez la variable d'environnement :

dotenv
NENE2_LOCAL_JWT_SECRET=your-local-secret

Sans cette variable, les appels d'outils en écriture retournent une erreur MCP sans transmettre la requête.

Protégez également les endpoints correspondants côté application avec 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. Démarrer le serveur 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

Le serveur lit depuis stdin et écrit sur stdout via le transport stdio MCP.


7. Configurer 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"]
    }
  }
}

Redémarrez Claude et les outils déclarés dans le catalogue apparaîtront comme actions disponibles.


8. Tester la couche MCP

Testez LocalMcpToolCatalog directement. Aucun serveur HTTP n'est requis :

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']);
}

Étapes suivantes

Publié sous licence MIT.