Les outils MCP pour le système de fichiers et Git donnent aux agents IA un accès direct à votre code source, ce qui est puissant mais dangereux sans garde-fous. Ce tutoriel construit un agent sécurisé capable de lire des fichiers, rechercher dans les bases de code et gérer les opérations Git tout en imposant le sandboxing des chemins, des valeurs par défaut en lecture seule et des listes d'opérations autorisées. Le grounding par recherche garantit que tout code généré s'appuie sur la documentation actuelle plutôt que sur des données d'entraînement obsolètes.
Prérequis
- Claude Code installé
- Node.js 18+ installé
- Un dépôt Git avec lequel travailler
- Une clé API Scavio pour le grounding par recherche
Parcours
Étape 1: Configurer les MCP système de fichiers et Git avec sandboxing
Mettez en place les serveurs MCP avec des chemins restreints. Le serveur système de fichiers n'accède qu'à un répertoire autorisé ; le serveur Git n'opère que sur des dépôts spécifiés.
import json
from pathlib import Path
mcp_config = {
'mcpServers': {
'filesystem': {
'command': 'npx',
'args': ['-y', '@anthropic/mcp-filesystem',
'--allowed-dir', '/home/user/projects',
'--read-only'],
'env': {}
},
'git': {
'command': 'npx',
'args': ['-y', '@anthropic/mcp-git',
'--repo-path', '/home/user/projects/my-repo',
'--allow-operations', 'status,log,diff,branch'],
'env': {}
}
}
}
Path('.mcp.json').write_text(json.dumps(mcp_config, indent=2))
print('Filesystem MCP: read-only, sandboxed to /home/user/projects')
print('Git MCP: status, log, diff, branch only (no push, no force)')Étape 2: Construire un middleware de validation des chemins
Ajoutez une couche de validation qui rejette toute tentative de traversée de chemin avant qu'elle n'atteigne le serveur MCP du système de fichiers.
import os
ALLOWED_ROOT = '/home/user/projects'
BLOCKED_PATTERNS = ['.env', '.ssh', 'credentials', 'secrets', '.git/config']
def validate_path(requested_path: str) -> dict:
resolved = os.path.realpath(requested_path)
# Check sandbox
if not resolved.startswith(ALLOWED_ROOT):
return {'allowed': False, 'reason': f'Path escapes sandbox: {resolved}'}
# Check blocked patterns
for pattern in BLOCKED_PATTERNS:
if pattern in resolved.lower():
return {'allowed': False, 'reason': f'Blocked pattern: {pattern}'}
# Check symlink attacks
if os.path.islink(requested_path):
target = os.readlink(requested_path)
if not os.path.realpath(target).startswith(ALLOWED_ROOT):
return {'allowed': False, 'reason': f'Symlink escapes sandbox'}
return {'allowed': True, 'resolved': resolved}
# Test cases
test_paths = [
'/home/user/projects/src/main.py', # allowed
'/home/user/projects/../.ssh/id_rsa', # blocked
'/home/user/projects/.env', # blocked
'/etc/passwd', # blocked
]
for p in test_paths:
result = validate_path(p)
status = 'ALLOWED' if result['allowed'] else f'BLOCKED ({result["reason"]})'
print(f' {p}: {status}')Étape 3: Ajouter une assistance de code fondée sur la recherche
Lorsque l'agent doit générer ou modifier du code, recherchez d'abord la documentation actuelle pour éviter d'halluciner des API obsolètes.
import requests
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
def ground_code_generation(language: str, task: str) -> str:
"""Search for current docs before generating code."""
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': f'{language} {task} documentation 2026',
'country_code': 'us', 'num_results': 3})
results = resp.json().get('organic_results', [])
context = '\n'.join(f'- {r["title"]}: {r.get("snippet", "")}' for r in results)
return context
# Example: before generating Next.js code, check current API
context = ground_code_generation('Next.js', 'app router server components')
print('Grounding context for code generation:')
print(context)
print(f'\nCost: $0.005')Exemple Python
import os, json, requests
from pathlib import Path
ALLOWED_ROOT = '/home/user/projects'
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
def validate_path(path):
resolved = os.path.realpath(path)
if not resolved.startswith(ALLOWED_ROOT):
return False
blocked = ['.env', '.ssh', 'credentials', 'secrets']
return not any(b in resolved.lower() for b in blocked)
def search_docs(query):
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us', 'num_results': 3})
return [r.get('snippet', '') for r in resp.json().get('organic_results', [])]
print(validate_path('/home/user/projects/src/main.py')) # True
print(validate_path('/etc/passwd')) # False
print(search_docs('python pathlib best practices 2026')[0][:80])Exemple JavaScript
const ALLOWED_ROOT = '/home/user/projects';
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
const path = require('path');
function validatePath(p) {
const resolved = path.resolve(p);
if (!resolved.startsWith(ALLOWED_ROOT)) return false;
const blocked = ['.env', '.ssh', 'credentials'];
return !blocked.some(b => resolved.toLowerCase().includes(b));
}
async function searchDocs(query) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, country_code: 'us', num_results: 3 })
});
return (await resp.json()).organic_results?.map(r => r.snippet) || [];
}
console.log(validatePath('/home/user/projects/src/main.py')); // true
console.log(validatePath('/etc/passwd')); // falseSortie attendue
/home/user/projects/src/main.py: ALLOWED
/home/user/projects/../.ssh/id_rsa: BLOCKED (Path escapes sandbox)
/home/user/projects/.env: BLOCKED (Blocked pattern: .env)
/etc/passwd: BLOCKED (Path escapes sandbox)
Grounding context for code generation:
- Next.js App Router Docs: Server Components run on the server...
- Vercel Blog: Next.js 16 App Router updates...
Cost: $0.005