|
|
|
|
@@ -1,474 +0,0 @@
|
|
|
|
|
# Plan de migration détaillé : metoro-io/mcp-golang vers mark3labs/mcp-go
|
|
|
|
|
|
|
|
|
|
Ce document présente un plan de migration détaillé pour remplacer la bibliothèque metoro-io/mcp-golang par mark3labs/mcp-go dans le projet LocalAGI. Le plan est divisé en phases avec des exemples de code concrets pour chaque étape critique.
|
|
|
|
|
|
|
|
|
|
## Phase 1 : Préparation et analyse
|
|
|
|
|
|
|
|
|
|
### Étape 1.1 : Audit du code existant
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Identifier tous les fichiers qui utilisent la bibliothèque metoro-io/mcp-golang
|
|
|
|
|
grep -r "github.com/metoro-io/mcp-golang" --include="*.go" .
|
|
|
|
|
|
|
|
|
|
# Identifier les fonctionnalités spécifiques utilisées
|
|
|
|
|
grep -r "mcp\.Client" --include="*.go" .
|
|
|
|
|
grep -r "transport/http" --include="*.go" .
|
|
|
|
|
grep -r "transport/stdio" --include="*.go" .
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Étape 1.2 : Création d'un environnement de test
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Créer une branche pour la migration
|
|
|
|
|
git checkout -b mcp-migration
|
|
|
|
|
|
|
|
|
|
# Installer la nouvelle bibliothèque
|
|
|
|
|
go get github.com/mark3labs/mcp-go
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Phase 2 : Modification des importations et structures
|
|
|
|
|
|
|
|
|
|
### Étape 2.1 : Mise à jour des importations
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
// Avant
|
|
|
|
|
import (
|
|
|
|
|
mcp "github.com/metoro-io/mcp-golang"
|
|
|
|
|
"github.com/metoro-io/mcp-golang/transport/http"
|
|
|
|
|
stdioTransport "github.com/metoro-io/mcp-golang/transport/stdio"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Après
|
|
|
|
|
import (
|
|
|
|
|
"github.com/mark3labs/mcp-go/mcp"
|
|
|
|
|
"github.com/mark3labs/mcp-go/server"
|
|
|
|
|
)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Étape 2.2 : Adaptation des structures
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
// Avant
|
|
|
|
|
type mcpAction struct {
|
|
|
|
|
mcpClient *mcp.Client
|
|
|
|
|
inputSchema ToolInputSchema
|
|
|
|
|
toolName string
|
|
|
|
|
toolDescription string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Après
|
|
|
|
|
type mcpAction struct {
|
|
|
|
|
// Nous devrons déterminer la structure équivalente dans mark3labs/mcp-go
|
|
|
|
|
// Basé sur la documentation, cela pourrait ressembler à:
|
|
|
|
|
mcpClient *mcp.Client // ou une structure équivalente
|
|
|
|
|
toolDefinition *mcp.Tool
|
|
|
|
|
toolName string
|
|
|
|
|
toolDescription string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Avant
|
|
|
|
|
type ToolInputSchema struct {
|
|
|
|
|
Type string `json:"type"`
|
|
|
|
|
Properties map[string]interface{} `json:"properties,omitempty"`
|
|
|
|
|
Required []string `json:"required,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Après
|
|
|
|
|
// Nous utiliserons directement les structures de schéma fournies par mark3labs/mcp-go
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Phase 3 : Migration des clients et transports
|
|
|
|
|
|
|
|
|
|
### Étape 3.1 : Clients HTTP
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
// Avant
|
|
|
|
|
transport := http.NewHTTPClientTransport("/mcp")
|
|
|
|
|
transport.WithBaseURL(mcpServer.URL)
|
|
|
|
|
if mcpServer.Token != "" {
|
|
|
|
|
transport.WithHeader("Authorization", "Bearer "+mcpServer.Token)
|
|
|
|
|
}
|
|
|
|
|
client := mcp.NewClient(transport)
|
|
|
|
|
|
|
|
|
|
// Après
|
|
|
|
|
// Basé sur les exemples disponibles, nous pourrions avoir besoin de créer un client HTTP personnalisé
|
|
|
|
|
// Voici une approche possible:
|
|
|
|
|
httpClient := &http.Client{}
|
|
|
|
|
// Configurer un client HTTP pour se connecter au serveur MCP
|
|
|
|
|
// Note: L'API exacte dépendra de la façon dont mark3labs/mcp-go implémente les clients
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Étape 3.2 : Clients STDIO
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
// Avant
|
|
|
|
|
client := stdio.NewClient(a.options.mcpBoxURL)
|
|
|
|
|
p, err := client.CreateProcess(a.context,
|
|
|
|
|
mcpStdioServer.Cmd,
|
|
|
|
|
mcpStdioServer.Args,
|
|
|
|
|
mcpStdioServer.Env,
|
|
|
|
|
a.Character.Name)
|
|
|
|
|
read, writer, err := client.GetProcessIO(p.ID)
|
|
|
|
|
transport := stdioTransport.NewStdioServerTransportWithIO(read, writer)
|
|
|
|
|
mcpClient := mcp.NewClient(transport)
|
|
|
|
|
|
|
|
|
|
// Après
|
|
|
|
|
// Basé sur les exemples, mark3labs/mcp-go utilise une approche différente pour STDIO
|
|
|
|
|
// Nous devrons adapter notre code pour utiliser l'API de mark3labs/mcp-go
|
|
|
|
|
// Exemple possible:
|
|
|
|
|
cmd := exec.Command(mcpStdioServer.Cmd, mcpStdioServer.Args...)
|
|
|
|
|
cmd.Env = append(os.Environ(), mcpStdioServer.Env...)
|
|
|
|
|
// Configurer les pipes stdin/stdout
|
|
|
|
|
// Créer un client qui utilise ces pipes
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Phase 4 : Migration des outils et actions
|
|
|
|
|
|
|
|
|
|
### Étape 4.1 : Définition des outils
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
// Avant
|
|
|
|
|
// Les outils sont définis implicitement via les réponses du serveur MCP
|
|
|
|
|
var inputSchema ToolInputSchema
|
|
|
|
|
err = json.Unmarshal(dat, &inputSchema)
|
|
|
|
|
generatedActions = append(generatedActions, &mcpAction{
|
|
|
|
|
mcpClient: client,
|
|
|
|
|
toolName: t.Name,
|
|
|
|
|
inputSchema: inputSchema,
|
|
|
|
|
toolDescription: desc,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Après
|
|
|
|
|
// Avec mark3labs/mcp-go, nous pouvons définir les outils plus explicitement
|
|
|
|
|
// Exemple basé sur la documentation:
|
|
|
|
|
calculatorTool := mcp.NewTool("calculate",
|
|
|
|
|
mcp.WithDescription("Perform basic arithmetic operations"),
|
|
|
|
|
mcp.WithString("operation",
|
|
|
|
|
mcp.Required(),
|
|
|
|
|
mcp.Description("The operation to perform"),
|
|
|
|
|
mcp.Enum("add", "subtract", "multiply", "divide"),
|
|
|
|
|
),
|
|
|
|
|
mcp.WithNumber("x",
|
|
|
|
|
mcp.Required(),
|
|
|
|
|
mcp.Description("First number"),
|
|
|
|
|
),
|
|
|
|
|
mcp.WithNumber("y",
|
|
|
|
|
mcp.Required(),
|
|
|
|
|
mcp.Description("Second number"),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Étape 4.2 : Appel des outils
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
// Avant
|
|
|
|
|
func (m *mcpAction) Run(ctx context.Context, sharedState *types.AgentSharedState, params types.ActionParams) (types.ActionResult, error) {
|
|
|
|
|
resp, err := m.mcpClient.CallTool(ctx, m.toolName, params)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return types.ActionResult{}, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
textResult := ""
|
|
|
|
|
for _, c := range resp.Content {
|
|
|
|
|
switch c.Type {
|
|
|
|
|
case mcp.ContentTypeText:
|
|
|
|
|
textResult += c.TextContent.Text + "\n"
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return types.ActionResult{
|
|
|
|
|
Result: textResult,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Après
|
|
|
|
|
// Basé sur la documentation, l'appel d'outil pourrait ressembler à:
|
|
|
|
|
func (m *mcpAction) Run(ctx context.Context, sharedState *types.AgentSharedState, params types.ActionParams) (types.ActionResult, error) {
|
|
|
|
|
// Convertir params en format attendu par mark3labs/mcp-go
|
|
|
|
|
args := make(map[string]interface{})
|
|
|
|
|
if err := params.Unmarshal(&args); err != nil {
|
|
|
|
|
return types.ActionResult{}, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Créer une requête d'appel d'outil
|
|
|
|
|
request := mcp.CallToolRequest{
|
|
|
|
|
Params: mcp.CallToolParams{
|
|
|
|
|
Name: m.toolName,
|
|
|
|
|
Arguments: args,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Appeler l'outil
|
|
|
|
|
// Note: L'API exacte dépendra de la façon dont mark3labs/mcp-go implémente les appels d'outils
|
|
|
|
|
result, err := m.mcpClient.CallTool(ctx, request)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return types.ActionResult{}, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Traiter le résultat
|
|
|
|
|
textResult := ""
|
|
|
|
|
// Extraire le texte du résultat selon le format de mark3labs/mcp-go
|
|
|
|
|
|
|
|
|
|
return types.ActionResult{
|
|
|
|
|
Result: textResult,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Phase 5 : Gestion des résultats
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
// Avant
|
|
|
|
|
textResult := ""
|
|
|
|
|
for _, c := range resp.Content {
|
|
|
|
|
switch c.Type {
|
|
|
|
|
case mcp.ContentTypeText:
|
|
|
|
|
textResult += c.TextContent.Text + "\n"
|
|
|
|
|
case mcp.ContentTypeImage:
|
|
|
|
|
xlog.Error("Image content not supported yet")
|
|
|
|
|
case mcp.ContentTypeEmbeddedResource:
|
|
|
|
|
xlog.Error("Embedded resource content not supported yet")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Après
|
|
|
|
|
// Basé sur la documentation, le traitement des résultats pourrait ressembler à:
|
|
|
|
|
textResult := ""
|
|
|
|
|
// Supposons que result est de type *mcp.CallToolResult
|
|
|
|
|
if result.Error != nil {
|
|
|
|
|
return types.ActionResult{}, fmt.Errorf("tool error: %s", result.Error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Traiter les différents types de contenu
|
|
|
|
|
for _, content := range result.Content {
|
|
|
|
|
switch content.Type {
|
|
|
|
|
case "text":
|
|
|
|
|
textContent, ok := content.Content.(mcp.TextContent)
|
|
|
|
|
if ok {
|
|
|
|
|
textResult += textContent.Text + "\n"
|
|
|
|
|
}
|
|
|
|
|
case "image":
|
|
|
|
|
xlog.Error("Image content not supported yet")
|
|
|
|
|
case "embedded_resource":
|
|
|
|
|
xlog.Error("Embedded resource content not supported yet")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Phase 6 : Gestion des sessions
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
// Avant
|
|
|
|
|
// La gestion des sessions est limitée dans la bibliothèque actuelle
|
|
|
|
|
|
|
|
|
|
// Après
|
|
|
|
|
// Implémentation d'une gestion de session plus avancée avec mark3labs/mcp-go
|
|
|
|
|
type MCPSession struct {
|
|
|
|
|
id string
|
|
|
|
|
notifChannel chan mcp.JSONRPCNotification
|
|
|
|
|
isInitialized bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Implémenter l'interface ClientSession
|
|
|
|
|
func (s *MCPSession) SessionID() string {
|
|
|
|
|
return s.id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *MCPSession) NotificationChannel() chan<- mcp.JSONRPCNotification {
|
|
|
|
|
return s.notifChannel
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *MCPSession) Initialize() {
|
|
|
|
|
s.isInitialized = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *MCPSession) Initialized() bool {
|
|
|
|
|
return s.isInitialized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Utilisation dans le code
|
|
|
|
|
session := &MCPSession{
|
|
|
|
|
id: mcpServer.SessionId,
|
|
|
|
|
notifChannel: make(chan mcp.JSONRPCNotification, 10),
|
|
|
|
|
}
|
|
|
|
|
if err := mcpServer.RegisterSession(context.Background(), session); err != nil {
|
|
|
|
|
xlog.Error("Failed to register session", "error", err.Error())
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Phase 7 : Adaptation de la fonction initMCPActions
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
// Avant
|
|
|
|
|
func (a *Agent) initMCPActions() error {
|
|
|
|
|
a.mcpActions = nil
|
|
|
|
|
var err error
|
|
|
|
|
generatedActions := types.Actions{}
|
|
|
|
|
|
|
|
|
|
// MCP HTTP Servers
|
|
|
|
|
for _, mcpServer := range a.options.mcpServers {
|
|
|
|
|
transport := http.NewHTTPClientTransport("/mcp")
|
|
|
|
|
transport.WithBaseURL(mcpServer.URL)
|
|
|
|
|
if mcpServer.Token != "" {
|
|
|
|
|
transport.WithHeader("Authorization", "Bearer "+mcpServer.Token)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client := mcp.NewClient(transport)
|
|
|
|
|
actions, err := a.addTools(client)
|
|
|
|
|
if err != nil {
|
|
|
|
|
xlog.Error("Failed to add tools for MCP server", "error", err.Error())
|
|
|
|
|
}
|
|
|
|
|
generatedActions = append(generatedActions, actions...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MCP STDIO Servers
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
a.mcpActions = generatedActions
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Après
|
|
|
|
|
func (a *Agent) initMCPActions() error {
|
|
|
|
|
a.mcpActions = nil
|
|
|
|
|
var err error
|
|
|
|
|
generatedActions := types.Actions{}
|
|
|
|
|
|
|
|
|
|
// MCP HTTP Servers
|
|
|
|
|
for _, mcpServer := range a.options.mcpServers {
|
|
|
|
|
// Créer un client HTTP pour se connecter au serveur MCP
|
|
|
|
|
// Note: L'implémentation exacte dépendra de l'API de mark3labs/mcp-go
|
|
|
|
|
httpClient := &http.Client{}
|
|
|
|
|
// Configurer les headers, l'URL, etc.
|
|
|
|
|
|
|
|
|
|
// Initialiser le client
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
// Lister et ajouter les outils
|
|
|
|
|
actions, err := a.addTools(client, &mcpServer)
|
|
|
|
|
if err != nil {
|
|
|
|
|
xlog.Error("Failed to add tools for MCP server", "error", err.Error())
|
|
|
|
|
}
|
|
|
|
|
generatedActions = append(generatedActions, actions...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MCP STDIO Servers
|
|
|
|
|
// Adapter pour utiliser l'API STDIO de mark3labs/mcp-go
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
a.mcpActions = generatedActions
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Phase 8 : Tests et validation
|
|
|
|
|
|
|
|
|
|
### Étape 8.1 : Tests unitaires
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
// Créer des tests unitaires pour chaque composant migré
|
|
|
|
|
func TestMCPClientInitialization(t *testing.T) {
|
|
|
|
|
// Tester l'initialisation du client avec mark3labs/mcp-go
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestToolDefinition(t *testing.T) {
|
|
|
|
|
// Tester la définition des outils avec mark3labs/mcp-go
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestToolExecution(t *testing.T) {
|
|
|
|
|
// Tester l'exécution des outils avec mark3labs/mcp-go
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Étape 8.2 : Tests d'intégration
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
// Créer des tests d'intégration pour vérifier que tout fonctionne ensemble
|
|
|
|
|
func TestMCPServerIntegration(t *testing.T) {
|
|
|
|
|
// Tester l'intégration complète avec un serveur MCP
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Phase 9 : Déploiement progressif
|
|
|
|
|
|
|
|
|
|
### Étape 9.1 : Déploiement en environnement de test
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Déployer la version migrée en environnement de test
|
|
|
|
|
docker build -t localagi-mcp-migration:test .
|
|
|
|
|
docker run -d --name localagi-test localagi-mcp-migration:test
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Étape 9.2 : Surveillance et correction
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Surveiller les logs pour détecter d'éventuels problèmes
|
|
|
|
|
docker logs -f localagi-test
|
|
|
|
|
|
|
|
|
|
# Corriger les problèmes identifiés
|
|
|
|
|
# ...
|
|
|
|
|
|
|
|
|
|
# Redéployer
|
|
|
|
|
docker build -t localagi-mcp-migration:test-v2 .
|
|
|
|
|
docker run -d --name localagi-test-v2 localagi-mcp-migration:test-v2
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Étape 9.3 : Déploiement en production
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Une fois les tests validés, déployer en production
|
|
|
|
|
docker build -t localagi:latest .
|
|
|
|
|
# Déployer selon votre processus habituel
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Considérations supplémentaires
|
|
|
|
|
|
|
|
|
|
### Compatibilité avec les serveurs MCP existants
|
|
|
|
|
|
|
|
|
|
Il est important de vérifier que mark3labs/mcp-go est compatible avec les serveurs MCP existants auxquels votre application se connecte. Si des différences de protocole existent, des adaptations supplémentaires pourraient être nécessaires.
|
|
|
|
|
|
|
|
|
|
### Documentation
|
|
|
|
|
|
|
|
|
|
Documenter tous les changements effectués et les différences de comportement entre les deux bibliothèques. Cela facilitera la maintenance future et aidera les développeurs à comprendre les choix de migration.
|
|
|
|
|
|
|
|
|
|
### Formation
|
|
|
|
|
|
|
|
|
|
Prévoir une session de formation pour les développeurs afin de les familiariser avec la nouvelle bibliothèque et ses particularités.
|
|
|
|
|
|
|
|
|
|
## Diagramme de séquence de la migration
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
sequenceDiagram
|
|
|
|
|
participant Dev as Développeur
|
|
|
|
|
participant Git as Système de contrôle de version
|
|
|
|
|
participant Test as Environnement de test
|
|
|
|
|
participant Prod as Production
|
|
|
|
|
|
|
|
|
|
Dev->>Git: Créer branche de migration
|
|
|
|
|
Dev->>Git: Modifier importations
|
|
|
|
|
Dev->>Git: Adapter structures
|
|
|
|
|
Dev->>Git: Migrer clients HTTP
|
|
|
|
|
Dev->>Git: Migrer clients STDIO
|
|
|
|
|
Dev->>Git: Migrer définition des outils
|
|
|
|
|
Dev->>Git: Migrer appel des outils
|
|
|
|
|
Dev->>Git: Migrer gestion des résultats
|
|
|
|
|
Dev->>Git: Migrer gestion des sessions
|
|
|
|
|
Dev->>Git: Écrire tests unitaires
|
|
|
|
|
Dev->>Git: Écrire tests d'intégration
|
|
|
|
|
Git->>Test: Déployer en test
|
|
|
|
|
Test->>Dev: Retour d'erreurs
|
|
|
|
|
Dev->>Git: Corrections
|
|
|
|
|
Git->>Test: Redéployer en test
|
|
|
|
|
Test->>Dev: Validation
|
|
|
|
|
Git->>Prod: Déployer en production
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Conclusion
|
|
|
|
|
|
|
|
|
|
Ce plan de migration détaillé devrait vous guider efficacement à travers le processus de remplacement de metoro-io/mcp-golang par mark3labs/mcp-go. La migration nécessitera des modifications significatives du code existant, mais les avantages potentiels en termes de maintenance, de fonctionnalités et de robustesse justifient cet effort.
|
|
|
|
|
|
|
|
|
|
Les principales difficultés résident dans les différences d'API et la façon dont les transports et les outils sont configurés et utilisés. Une approche progressive, avec des tests approfondis à chaque étape, est recommandée pour minimiser les risques.
|
|
|
|
|
|
|
|
|
|
N'hésitez pas à adapter ce plan en fonction des spécificités de votre projet et des découvertes faites pendant la migration.
|