diff --git a/MyIA.AI.Notebooks/GenAI/SemanticKernel/08-SemanticKernel-MCP.ipynb b/MyIA.AI.Notebooks/GenAI/SemanticKernel/08-SemanticKernel-MCP.ipynb index a74e9a7d5f..dc918d6e70 100644 --- a/MyIA.AI.Notebooks/GenAI/SemanticKernel/08-SemanticKernel-MCP.ipynb +++ b/MyIA.AI.Notebooks/GenAI/SemanticKernel/08-SemanticKernel-MCP.ipynb @@ -1,1968 +1 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "bd9f46f9", - "metadata": { - "tags": [] - }, - "source": [ - "# SK-8-MCP : Model Context Protocol et Integration\n", - "\n", - "**Navigation** : [<< 07-MultiModal](07-SemanticKernel-MultiModal.ipynb) | [Index](README.md)\n", - "\n", - "***\n", - "\n", - "## Objectifs d'apprentissage\n", - "\n", - "A la fin de ce notebook, vous saurez :\n", - "1. Comprendre le **Model Context Protocol (MCP)**\n", - "2. Identifier les **serveurs MCP** disponibles\n", - "3. Integrer des **outils MCP** comme plugins SK\n", - "4. Créer un **bridge Agent Framework ↔ MCP**\n", - "5. Concevoir un **serveur MCP personnalise**\n", - "\n", - "### Prerequis\n", - "\n", - "- Python 3.10+\n", - "- Notebooks 01-07 completes\n", - "- Comprehension des Agents SK (notebook 03)\n", - "- Node.js installe (pour certains serveurs MCP)\n", - "\n", - "### Duree estimee : 45 minutes\n", - "\n", - "***\n", - "\n", - "## Sommaire\n", - "\n", - "| Section | Contenu | Concepts cles |\n", - "|---------|---------|---------------|\n", - "| 1 | Introduction | Qu'est-ce que MCP ? |\n", - "| 2 | Architecture | Tools, Resources, Prompts |\n", - "| 3 | Serveurs existants | Filesystem, GitHub, Database |\n", - "| 4 | SK + MCP | Integration comme plugins |\n", - "| 5 | Agent + MCP | Bridge avec Agent Framework |\n", - "| 6 | Serveur custom | Créer son propre serveur |\n", - "| 7 | Conclusion | Resume, exercices |\n", - "\n", - "> **Model Context Protocol (MCP)** : Standard ouvert initie par Anthropic et adopte par Microsoft pour permettre aux LLMs d'acceder a des outils externes de maniere standardisee. C'est l'evolution des plugins vers l'interoperabilite." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "de07375f", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "MCP SDK installe\n" - ] - } - ], - "source": [ - "# Installation\n", - "\n", - "import os\n", - "from dotenv import load_dotenv\n", - "\n", - "load_dotenv()\n", - "print(\"MCP SDK installe\")" - ] - }, - { - "cell_type": "markdown", - "id": "1b072c98", - "metadata": { - "tags": [] - }, - "source": [ - "## 1. Introduction au Model Context Protocol\n", - "\n", - "### Pourquoi MCP ?\n", - "\n", - "Avant MCP, chaque framework avait son propre système de plugins :\n", - "\n", - "| Framework | Système de plugins | Problème |\n", - "|-----------|-------------------|----------|\n", - "| Semantic Kernel | KernelFunction | Non portable |\n", - "| LangChain | Tools | Non portable |\n", - "| AutoGen | Tools | Non portable |\n", - "| OpenAI | Function Calling | Spécifique OpenAI |\n", - "\n", - "**MCP resout ce problème** en definissant un standard :\n", - "\n", - "```\n", - "┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐\n", - "│ Semantic Kernel │ │ LangChain │ │ AutoGen │\n", - "│ │ │ │ │ │\n", - "│ MCP Client │ │ MCP Client │ │ MCP Client │\n", - "└────────┬────────┘ └────────┬────────┘ └────────┬────────┘\n", - " │ │ │\n", - " └───────────────────────┼───────────────────────┘\n", - " │\n", - " ↓\n", - " ┌─────────────────────────┐\n", - " │ MCP Servers │\n", - " │ │\n", - " │ - Filesystem │\n", - " │ - GitHub │\n", - " │ - Database │\n", - " │ - Custom... │\n", - " └─────────────────────────┘\n", - "```\n", - "\n", - "### Acteurs principaux\n", - "\n", - "| Acteur | Rôle | Exemple |\n", - "|--------|------|---------|\n", - "| **Client** | Consomme les outils | Claude Code, SK |\n", - "| **Server** | Fournit les outils | mcp-server-filesystem |\n", - "| **Transport** | Communication | stdio, HTTP, SSE |" - ] - }, - { - "cell_type": "markdown", - "id": "mcp5k08c", - "metadata": { - "tags": [] - }, - "source": [ - "Le diagramme ci-dessous rend cette architecture **MCP** sous forme de graphe : plusieurs\n", - "frameworks clients consomment un même ensemble de serveurs d'outils standardises.\n", - "\n", - "```mermaid\n", - "flowchart TD\n", - " SK[\"Semantic Kernel
MCP Client\"] --> SRV\n", - " LC[\"LangChain
MCP Client\"] --> SRV\n", - " AG[\"AutoGen
MCP Client\"] --> SRV\n", - " SRV[\"MCP Servers
Filesystem / GitHub / Database / Custom...\"]\n", - " classDef client fill:#cfe2ff,stroke:#084298,color:#052c65\n", - " classDef server fill:#fff3cd,stroke:#b8860b,color:#5c4400\n", - " class SK,LC,AG client\n", - " class SRV server\n", - "```\n", - "\n", - "> **Lecture.** Le **Model Context Protocol** standardise la facon dont un agent accede a\n", - "> des outils externes : n'importe quel **client** conforme (Semantic Kernel, LangChain,\n", - "> AutoGen...) peut consommer n'importe quel **serveur MCP** (filesystem, GitHub, base de\n", - "> données, serveurs maison) sans integration sur-mesure. C'est l'equivalent, pour les\n", - "> outils d'agents, de ce que USB a ete pour les peripheriques : un connecteur commun qui\n", - "> decouple les clients des fournisseurs de capacites.\n" - ] - }, - { - "cell_type": "markdown", - "id": "63080987", - "metadata": { - "tags": [] - }, - "source": [ - "## 2. Architecture MCP\n", - "\n", - "MCP définit trois types de primitives :\n", - "\n", - "### 2.1 Tools (Outils)\n", - "\n", - "Fonctions executables par le LLM :\n", - "\n", - "```json\n", - "{\n", - " \"name\": \"read_file\",\n", - " \"description\": \"Read the contents of a file\",\n", - " \"inputSchema\": {\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"path\": { \"type\": \"string\" }\n", - " },\n", - " \"required\": [\"path\"]\n", - " }\n", - "}\n", - "```\n", - "\n", - "### 2.2 Resources (Ressources)\n", - "\n", - "Données accessibles en lecture :\n", - "\n", - "```json\n", - "{\n", - " \"uri\": \"file:///path/to/doc.md\",\n", - " \"name\": \"Documentation\",\n", - " \"mimeType\": \"text/markdown\"\n", - "}\n", - "```\n", - "\n", - "### 2.3 Prompts\n", - "\n", - "Templates de prompts reutilisables :\n", - "\n", - "```json\n", - "{\n", - " \"name\": \"summarize\",\n", - " \"description\": \"Summarize a document\",\n", - " \"arguments\": [\n", - " { \"name\": \"content\", \"required\": true }\n", - " ]\n", - "}\n", - "```\n", - "\n", - "### Tableau comparatif\n", - "\n", - "| Primitive | Action | Equivalent SK |\n", - "|-----------|--------|---------------|\n", - "| **Tool** | Execute une fonction | KernelFunction |\n", - "| **Resource** | Lit des données | Pas d'equivalent direct |\n", - "| **Prompt** | Fournit un template | PromptTemplate |" - ] - }, - { - "cell_type": "markdown", - "id": "9c33e97c", - "metadata": { - "tags": [] - }, - "source": [ - "## 3. Serveurs MCP existants\n", - "\n", - "De nombreux serveurs MCP sont disponibles :" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "90023b20", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Serveurs MCP populaires:\n", - "============================================================\n", - "\n", - "FILESYSTEM\n", - " Package: @anthropic/mcp-server-filesystem\n", - " Description: Lecture/ecriture de fichiers\n", - " Tools: read_file, write_file, list_directory\n", - "\n", - "GITHUB\n", - " Package: @anthropic/mcp-server-github\n", - " Description: Operations GitHub (repos, issues, PRs)\n", - " Tools: search_repos, get_issue, create_pr\n", - "\n", - "POSTGRES\n", - " Package: @anthropic/mcp-server-postgres\n", - " Description: Requetes SQL PostgreSQL\n", - " Tools: query, execute, describe_table\n", - "\n", - "JUPYTER\n", - " Package: mcp-jupyter\n", - " Description: Execution de notebooks Jupyter\n", - " Tools: execute_notebook, read_cells, manage_kernel\n", - "\n", - "PUPPETEER\n", - " Package: @anthropic/mcp-server-puppeteer\n", - " Description: Automatisation web (scraping, screenshots)\n", - " Tools: navigate, screenshot, click\n" - ] - } - ], - "source": [ - "# Liste des serveurs MCP populaires\n", - "mcp_servers = [\n", - " {\n", - " \"name\": \"filesystem\",\n", - " \"package\": \"@anthropic/mcp-server-filesystem\",\n", - " \"description\": \"Lecture/ecriture de fichiers\",\n", - " \"tools\": [\"read_file\", \"write_file\", \"list_directory\"]\n", - " },\n", - " {\n", - " \"name\": \"github\",\n", - " \"package\": \"@anthropic/mcp-server-github\",\n", - " \"description\": \"Operations GitHub (repos, issues, PRs)\",\n", - " \"tools\": [\"search_repos\", \"get_issue\", \"create_pr\"]\n", - " },\n", - " {\n", - " \"name\": \"postgres\",\n", - " \"package\": \"@anthropic/mcp-server-postgres\",\n", - " \"description\": \"Requetes SQL PostgreSQL\",\n", - " \"tools\": [\"query\", \"execute\", \"describe_table\"]\n", - " },\n", - " {\n", - " \"name\": \"jupyter\",\n", - " \"package\": \"mcp-jupyter\",\n", - " \"description\": \"Execution de notebooks Jupyter\",\n", - " \"tools\": [\"execute_notebook\", \"read_cells\", \"manage_kernel\"]\n", - " },\n", - " {\n", - " \"name\": \"puppeteer\",\n", - " \"package\": \"@anthropic/mcp-server-puppeteer\",\n", - " \"description\": \"Automatisation web (scraping, screenshots)\",\n", - " \"tools\": [\"navigate\", \"screenshot\", \"click\"]\n", - " }\n", - "]\n", - "\n", - "print(\"Serveurs MCP populaires:\")\n", - "print(\"=\" * 60)\n", - "for server in mcp_servers:\n", - " print(f\"\\n{server['name'].upper()}\")\n", - " print(f\" Package: {server['package']}\")\n", - " print(f\" Description: {server['description']}\")\n", - " print(f\" Tools: {', '.join(server['tools'])}\")" - ] - }, - { - "cell_type": "markdown", - "id": "9accb0c5", - "metadata": { - "tags": [] - }, - "source": [ - "### Exemple guidé 1 : Analyseur de capacites MCP\n", - "\n", - "*Contribution étudiante de Gabriel COMBE (@GabrielC-star) et Rémi LESANNE, PR #18553, intégrée comme exemple guidé.*\n", - "\n", - "Cet exemple classe les cinq serveurs MCP listés plus haut par domaine d'expertise (`storage`, `devops`, `web`, `data`). La fonction `categorize_mcp_servers()` parcourt la liste, reconnaît le domaine de chaque serveur grâce aux mots-clés de sa description, compte ses outils et repère le serveur le mieux pourvu. Elle retourne un dict avec les catégories, le total d'outils et le nom du serveur en ayant le plus." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "44789c95", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "storage: ['filesystem']\n", - "devops: ['github']\n", - "web: ['puppeteer']\n", - "data: ['postgres']\n", - "Total outils: 15\n" - ] - } - ], - "source": [ - "# Exemple guide 1 : Analyseur de capacites MCP\n", - "# Contribution etudiante de Gabriel COMBE et Remi LESANNE (PR #18553)\n", - "def categorize_mcp_servers(servers: list) -> dict:\n", - " \"\"\"\n", - " Classifier les serveurs MCP par domaine d'expertise.\n", - " \n", - " Args:\n", - " servers: Liste de dicts avec les cles: name, description, tools\n", - " \n", - " Returns:\n", - " dict avec les cles: categories (dict domaine -> list serveurs),\n", - " total_tools (int), server_with_most_tools (str)\n", - " \"\"\"\n", - " categories = {\n", - " \"storage\": [],\n", - " \"devops\": [],\n", - " \"web\": [],\n", - " \"data\": []\n", - " }\n", - "\n", - " total_tools = 0\n", - " server_with_most_tools = None\n", - " max_tools = 0\n", - "\n", - " for server in servers:\n", - " name = server[\"name\"]\n", - " description = server[\"description\"].lower()\n", - " tools = server[\"tools\"]\n", - "\n", - " nb_tools = len(tools)\n", - " total_tools += nb_tools\n", - "\n", - " if nb_tools > max_tools:\n", - " max_tools = nb_tools\n", - " server_with_most_tools = name\n", - " \n", - " if \"fichier\" in description:\n", - " categories[\"storage\"].append(server)\n", - "\n", - " elif \"github\" in description:\n", - " categories[\"devops\"].append(server)\n", - "\n", - " elif \"scraping\" in description:\n", - " categories[\"web\"].append(server)\n", - "\n", - " elif \"sql\" in description:\n", - " categories[\"data\"].append(server)\n", - "\n", - "\n", - " return {\n", - " \"categories\": categories,\n", - " \"total_tools\": total_tools,\n", - " \"server_with_most_tools\": server_with_most_tools\n", - " }\n", - " \n", - " \n", - "# Test :\n", - "result = categorize_mcp_servers(mcp_servers)\n", - "for category, srvs in result['categories'].items():\n", - " print(f\"{category}: {[s['name'] for s in srvs]}\")\n", - "print(f\"Total outils: {result['total_tools']}\")" - ] - }, - { - "cell_type": "markdown", - "id": "mcp-eg1-lecture", - "metadata": {}, - "source": [ - "### Lecture du résultat — exemple guidé 1 (capacités MCP)\n", - "\n", - "- Les quatre domaines attendus sont remplis : `storage: ['filesystem']`, `devops: ['github']`, `web: ['puppeteer']`, `data: ['postgres']`.\n", - "- `mcp_servers` compte 5 serveurs, mais un seul manque à l'appel : `jupyter` n'apparaît dans aucune catégorie. Sa description (« Execution de notebooks Jupyter ») ne contient aucun des mots-clés testés (`fichier`, `github`, `scraping`, `sql`) — le classement par mots-clés l'ignore **silencieusement**, sans erreur ni avertissement.\n", - "- `Total outils: 15` compte pourtant les 5 serveurs (3 outils chacun), `jupyter` compris : le total et les catégories ne décrivent pas le même périmètre.\n", - "- `server_with_most_tools` est calculé — la paire gagnante serait `filesystem`, premier ex æquo à 3 outils — mais jamais imprimé : la valeur retourne dans le dict sans apparaître dans la sortie." - ] - }, - { - "cell_type": "markdown", - "id": "mcp-ex1-routage-md", - "metadata": { - "tags": [] - }, - "source": [ - "### Exercice 1 : Router une tache vers le bon serveur et son outil\n", - "\n", - "L'exemple guidé 1 classe les serveurs par domaine, mais il ne dit pas *quel outil* appeler pour une tâche donnée, et surtout il **ignore silencieusement** tout serveur que ses mots-clés ne reconnaissent pas. Un vrai routeur doit savoir répondre « aucun serveur ne sait faire ça ».\n", - "\n", - "**Objectif** : écrire `choisir_outil(tache, servers)` qui, pour une tâche en langage naturel, note chaque paire (serveur, outil) selon les mots de la tâche, et retourne la meilleure paire — ou `None` quand aucun score n'est positif.\n", - "\n", - "**Étapes** :\n", - "1. `# Étape 1` : découper la tâche en mots (minuscules), en ajoutant au besoin des synonymes (par exemple « capture d'ecran » → `screenshot`)\n", - "2. `# Étape 2` : pour chaque serveur de `mcp_servers` et chacun de ses outils, calculer un score : +1 si un mot de la tâche correspond au nom du serveur, à sa description ou au nom de l'outil\n", - "3. `# Étape 3` : retourner la paire `(serveur, outil)` au meilleur score si ce score est strictement positif, sinon `None`\n", - "\n", - "**Indices** :\n", - "- `# Indice` : « ouvrir une issue pour signaler un bug » contient le mot *issue* — un outil de `github` s'appelle `get_issue`\n", - "- `# Indice` : « lister les tables de la base » parle de tables — `describe_table` est un outil de `postgres`\n", - "- `# Indice` : « envoyer un SMS » ne correspond à aucun serveur — la fonction doit retourner `None`, sans lever d'erreur\n", - "\n", - "**Critère de réussite** : les quatre tests affichent `('github', 'get_issue')`, `('puppeteer', 'screenshot')`, `('postgres', 'describe_table')` puis `None`." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "mcp-ex1-routage-code", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Exercice a completer\n" - ] - } - ], - "source": [ - "# Exercice 1 : Router une tache vers le bon serveur et son outil\n", - "# TODO etudiant : completer choisir_outil, puis decommenter les tests\n", - "\n", - "def choisir_outil(tache: str, servers: list):\n", - " \"\"\"\n", - " Choisir la paire (serveur, outil) la mieux adaptee a une tache.\n", - "\n", - " Args:\n", - " tache: la tache en langage naturel\n", - " servers: la liste des serveurs (meme forme que mcp_servers)\n", - "\n", - " Returns:\n", - " tuple (nom du serveur, nom de l'outil) si un score est positif,\n", - " None sinon\n", - " \"\"\"\n", - " # Etape 1 : mots de la tache en minuscules (+ synonymes si besoin)\n", - " mots = None # TODO etudiant\n", - "\n", - " meilleur_score = 0\n", - " meilleure_paire = None\n", - "\n", - " # Etape 2 : scorer chaque paire (serveur, outil)\n", - " # for server in servers:\n", - " # texte = server[\"name\"] + \" \" + server[\"description\"]\n", - " # for tool in server[\"tools\"]:\n", - " # score = 0 # TODO etudiant : +1 par mot present dans texte ou tool\n", - " # if score > meilleur_score:\n", - " # meilleur_score = score\n", - " # meilleure_paire = (server[\"name\"], tool)\n", - "\n", - " # Etape 3 : retourner la meilleure paire, ou None si aucun score positif\n", - " return None # TODO etudiant\n", - "\n", - "\n", - "# Tests\n", - "taches = [\n", - " \"ouvrir une issue pour signaler un bug\",\n", - " \"faire une capture d'ecran d'une page web\",\n", - " \"lister les tables de la base\",\n", - " \"envoyer un SMS\",\n", - "]\n", - "# for tache in taches:\n", - "# print(f\"{tache} -> {choisir_outil(tache, mcp_servers)}\")\n", - "\n", - "print(\"Exercice a completer\")" - ] - }, - { - "cell_type": "markdown", - "id": "y02s0tymzf", - "metadata": { - "tags": [] - }, - "source": [ - "### Interprétation : Écosystème des serveurs MCP\n", - "\n", - "**Sortie obtenue** : Liste de 5 serveurs MCP représentatifs de l'écosystème\n", - "\n", - "| Serveur | Domaine | Maturité | Cas d'usage typique |\n", - "|---------|---------|----------|---------------------|\n", - "| **filesystem** | Système | Production | Manipulation de fichiers locaux |\n", - "| **github** | DevOps | Production | Automatisation CI/CD, gestion issues |\n", - "| **postgres** | Base de données | Production | Requêtes SQL, analytics |\n", - "| **jupyter** | Data Science | Expérimental | Notebooks interactifs, reproductibilité |\n", - "| **puppeteer** | Web scraping | Production | Tests E2E, captures d'écran |\n", - "\n", - "**Points clés** :\n", - "\n", - "1. **Diversité** : MCP couvre tous les domaines (filesystem, cloud, databases, web)\n", - "2. **Standards vs Custom** : Les serveurs Anthropic (@anthropic/*) sont les références\n", - "3. **Packages npm** : La majorité utilise Node.js, quelques-uns Python (uvx)\n", - "4. **Granularité** : Chaque serveur expose 3-10 outils spécialisés\n", - "\n", - "**Note technique** : L'installation nécessite Node.js 18+ pour les serveurs npm, ou Python 3.10+ avec `uvx` pour les serveurs Python.\n", - "\n", - "**Évolution** : De nouveaux serveurs apparaissent régulièrement sur [modelcontextprotocol.io/servers](https://modelcontextprotocol.io/servers)\n" - ] - }, - { - "cell_type": "markdown", - "id": "dcf4c1c8", - "metadata": { - "tags": [] - }, - "source": [ - "### Installation d'un serveur MCP\n", - "\n", - "```bash\n", - "# Via npm (Node.js requis)\n", - "npx -y @anthropic/mcp-server-filesystem --root /path/to/files\n", - "\n", - "# Via uvx (Python)\n", - "uvx mcp-server-sqlite --db-path database.db\n", - "```\n", - "\n", - "### Configuration dans Claude Code\n", - "\n", - "Les serveurs MCP se configurent dans `~/.claude.json` :\n", - "\n", - "```json\n", - "{\n", - " \"mcpServers\": {\n", - " \"filesystem\": {\n", - " \"command\": \"npx\",\n", - " \"args\": [\"-y\", \"@anthropic/mcp-server-filesystem\", \"--root\", \"/path\"]\n", - " }\n", - " }\n", - "}\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "e6160224", - "metadata": { - "tags": [] - }, - "source": [ - "## 4. Integration SK + MCP\n", - "\n", - "Semantic Kernel peut consommer des serveurs MCP comme plugins." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "1f015b46", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Plugin MCP Filesystem ajoute au Kernel\n", - "\n", - "Fonctions disponibles:\n", - " - list_directory: Liste les fichiers d'un repertoire\n", - " - read_file: Lit le contenu d'un fichier\n", - " - write_file: Ecrit du contenu dans un fichier\n" - ] - } - ], - "source": [ - "# Exemple conceptuel : MCP Server comme plugin SK\n", - "# Note: L'integration native SK+MCP est en cours de developpement\n", - "\n", - "from semantic_kernel import Kernel\n", - "from semantic_kernel.functions import kernel_function\n", - "from typing import Annotated\n", - "import subprocess\n", - "import json\n", - "\n", - "class MCPFilesystemPlugin:\n", - " \"\"\"\n", - " Wrapper qui expose un serveur MCP comme plugin SK.\n", - " Pattern: MCP tools -> KernelFunctions\n", - " \"\"\"\n", - " \n", - " def __init__(self, root_path: str):\n", - " self.root_path = root_path\n", - " \n", - " @kernel_function(description=\"Lit le contenu d'un fichier\")\n", - " def read_file(self, path: Annotated[str, \"Chemin du fichier\"]) -> str:\n", - " \"\"\"Wrapper pour l'outil MCP read_file.\"\"\"\n", - " full_path = os.path.join(self.root_path, path)\n", - " try:\n", - " with open(full_path, 'r', encoding='utf-8') as f:\n", - " return f.read()\n", - " except Exception as e:\n", - " return f\"Erreur: {e}\"\n", - " \n", - " @kernel_function(description=\"Liste les fichiers d'un repertoire\")\n", - " def list_directory(self, path: Annotated[str, \"Chemin du repertoire\"] = \".\") -> str:\n", - " \"\"\"Wrapper pour l'outil MCP list_directory.\"\"\"\n", - " full_path = os.path.join(self.root_path, path)\n", - " try:\n", - " files = os.listdir(full_path)\n", - " return json.dumps(files, indent=2)\n", - " except Exception as e:\n", - " return f\"Erreur: {e}\"\n", - " \n", - " @kernel_function(description=\"Ecrit du contenu dans un fichier\")\n", - " def write_file(self, \n", - " path: Annotated[str, \"Chemin du fichier\"],\n", - " content: Annotated[str, \"Contenu a ecrire\"]) -> str:\n", - " \"\"\"Wrapper pour l'outil MCP write_file.\"\"\"\n", - " full_path = os.path.join(self.root_path, path)\n", - " try:\n", - " with open(full_path, 'w', encoding='utf-8') as f:\n", - " f.write(content)\n", - " return f\"Fichier ecrit: {full_path}\"\n", - " except Exception as e:\n", - " return f\"Erreur: {e}\"\n", - "\n", - "# Utilisation\n", - "kernel = Kernel()\n", - "kernel.add_plugin(MCPFilesystemPlugin(root_path=\".\"), plugin_name=\"filesystem\")\n", - "\n", - "print(\"Plugin MCP Filesystem ajoute au Kernel\")\n", - "print(\"\\nFonctions disponibles:\")\n", - "for func in kernel.get_plugin(\"filesystem\").functions.values():\n", - " print(f\" - {func.name}: {func.description}\")" - ] - }, - { - "cell_type": "markdown", - "id": "x0z5k215bqn", - "metadata": { - "tags": [] - }, - "source": [ - "### Interprétation : Bridge MCP → Semantic Kernel\n", - "\n", - "**Sortie obtenue** : Plugin SK exposant 3 fonctions (read_file, list_directory, write_file)\n", - "\n", - "| Fonction SK | Outil MCP source | Type de transformation |\n", - "|-------------|------------------|------------------------|\n", - "| `read_file()` | `read_file` | Wrapper direct |\n", - "| `list_directory()` | `list_directory` | Wrapper avec JSON serialization |\n", - "| `write_file()` | `write_file` | Wrapper direct |\n", - "\n", - "**Architecture du bridge** :\n", - "\n", - "```\n", - "MCP Server (Node.js) Python Bridge Semantic Kernel\n", - "┌─────────────────┐ ┌──────────────┐ ┌───────────────┐\n", - "│ @anthropic/ │ │ MCP │ │ Kernel │\n", - "│ mcp-server- │ stdio │ Filesystem │ @kernel │ Plugin │\n", - "│ filesystem │ ──────> │ Plugin │ ──────> │ │\n", - "│ │ │ │ _function│ Functions │\n", - "│ - read_file │ │ - read_file │ │ disponibles │\n", - "│ - list_dir │ │ - list_dir │ │ │\n", - "└─────────────────┘ └──────────────┘ └───────────────┘\n", - "```\n", - "\n", - "**Points clés** :\n", - "\n", - "1. **Pattern Adapter** : Le plugin SK adapte le protocole MCP (stdio) en KernelFunctions\n", - "2. **Annotations Semantic Kernel** : `@kernel_function` + `Annotated[str, \"description\"]`\n", - "3. **Gestion d'erreurs** : Les exceptions MCP sont capturées et retournées comme strings\n", - "4. **Path resolution** : Le bridge gère les chemins relatifs via `root_path`\n", - "\n", - "**Limitations actuelles** :\n", - "\n", - "| Limitation | Impact | Contournement |\n", - "|------------|--------|---------------|\n", - "| Pas d'intégration native SK | Code wrapper manuel requis | Bridge custom comme ci-dessus |\n", - "| Communication stdio | Latence légère | Acceptable pour la plupart des usages |\n", - "| Pas de Resources/Prompts | Seulement les Tools MCP exposés | Enrichir le plugin au besoin |\n", - "\n", - "**Roadmap** : Microsoft travaille sur une intégration native MCP dans SK (Q2 2026)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "faf9dd9c", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Fichiers dans le repertoire courant:\n", - "[\n", - " \"01-SemanticKernel-Intro.ipynb\",\n", - " \"02-SemanticKernel-Advanced.ipynb\",\n", - " \"03-SemanticKernel-Agents.ipynb\",\n", - " \"04-SemanticKernel-Filters-Observability.ipynb\",\n", - " \"05-SemanticKernel-VectorStores.ipynb\",\n", - " \"06-SemanticKernel-ProcessFramework.ipynb\",\n", - " \"07-SemanticKernel-MultiModal.ipynb\",\n", - " \"08-SemanticKernel-MCP.ipynb\",\n", - " \"09-SemanticKernel-Building-CLR.ipynb\",\n", - " \"10-SemanticKernel-NotebookMaker.ipynb\",\n", - " \"10a-SemanticKernel-NotebookMaker-batch.ipynb\",\n", - " \"10b-SemanticKernel-NotebookMaker-batch-parameterized.ipynb\",\n", - " \"11-SemanticKernel-A2A.ipynb\",\n", - " \"aspire-otel\",\n", - " \"AutoGenNotebookUpdater.cs\",\n", - " \"AutoInvokeSKAgentsNotebookUpdater.cs\",\n", - " \"Cr\\u00e9ateur de mail personnalis\\u00e9.ipynb\",\n", - " \"DisplayLogger.cs\",\n", - " \"DisplayLoggerProvider.cs\",\n", - " \"fort-boyard-csharp.ipynb\",\n", - " \"fort-boyard-python.ipynb\",\n", - " \"GuessingGame.cs\",\n", - " \"MANIFEST.md\",\n", - " \"Notebook-Generated.ipynb\",\n", - " \"Notebook-Template.ipynb\",\n", - " \"NotebookExecutor.cs\",\n", - " \"NotebookPlannerUpdater.cs\",\n", - " \"NotebookUpdaterBase.cs\",\n", - " \"prompt_template_samples\",\n", - " \"README.md\",\n", - " \"Resources\",\n", - " \"semantic-fleet\",\n", - " \"Semantic-kernel-AutoInteractive.ipynb\",\n", - " \"START_HERE.txt\",\n", - " \"Workbook-Template-Python.ipynb\",\n", - " \"Workbook-Template.ipynb\",\n", - " \"WorkbookInteractionBase.cs\",\n", - " \"WorkbookUpdateInteraction.cs\",\n", - " \"WorkbookValidation.cs\"\n", - "]\n" - ] - } - ], - "source": [ - "# Test du plugin\n", - "from semantic_kernel.functions import KernelArguments\n", - "\n", - "# Lister les fichiers du repertoire courant\n", - "list_func = kernel.get_function(\"filesystem\", \"list_directory\")\n", - "result = await kernel.invoke(list_func, KernelArguments(path=\".\"))\n", - "\n", - "print(\"Fichiers dans le repertoire courant:\")\n", - "print(result)" - ] - }, - { - "cell_type": "markdown", - "id": "a4498146", - "metadata": { - "tags": [] - }, - "source": [ - "### Exemple guidé 2 : Plugin MCP avec validation d'entrees\n", - "\n", - "*Contribution étudiante de Gabriel COMBE (@GabrielC-star) et Rémi LESANNE, PR #18553, intégrée comme exemple guidé.*\n", - "\n", - "Cet exemple durcit la lecture de fichiers : le plugin `ValidatedFilesystemPlugin` ne rend un fichier qu'après trois contrôles — le chemin résolu reste dans le répertoire autorisé (via `os.path.realpath` puis `os.path.commonpath`), le fichier existe, et sa taille reste sous `max_file_size`. Toute tentative de sortie du répertoire autorisé (attaque par *path traversal* telle que `../../etc/passwd`) lève une `ValueError` avant la moindre lecture." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "91032d36", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "# SemanticKernel - Microsoft Semantic Kernel\n", - "\n", - "