fix(mcp): intégration réelle passerelle MCP Portainer (image+auth) (v0.34.0)
Image portainer-mcp 0.6.0 (inexistant) -> 2.42.6. Passerelle HTTP :17717/mcp attend Bearer (secret passerelle) + X-Portainer-API-Key (clé API restreinte chlova) : ajout config.portainerApiKey + McpServerConfig.extraHeaders, backend envoie les deux. socket-proxy supprimé (plus de socket monté). Compose prod, .env.example, deploy.md à jour. Typecheck + 78 tests verts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016w5jRe87MGdd6AMvXQcHNi
This commit is contained in:
@@ -35,9 +35,14 @@ const schema = z.object({
|
||||
mcpN8nUrl: z.string().url(),
|
||||
mcpN8nAuthToken: nonEmpty, // SECRET
|
||||
|
||||
// MCP Portainer
|
||||
// MCP Portainer (passerelle HTTP portainer/portainer-mcp)
|
||||
mcpPortainerUrl: z.string().url(),
|
||||
// Secret de PASSERELLE (front-gate) partagé avec le serveur MCP : envoyé en
|
||||
// `Authorization: Bearer`. N'autorise QUE l'accès à la passerelle.
|
||||
portainerMcpAuthToken: nonEmpty, // SECRET
|
||||
// Clé API Portainer de l'utilisateur `chlova` (restreinte) : envoyée en header
|
||||
// `X-Portainer-API-Key`. C'est ELLE qui cloisonne ce que CHLOVA peut voir/faire.
|
||||
portainerApiKey: nonEmpty, // SECRET
|
||||
// Verrou Phase 1 : lecture seule. `false` est refusé tant que la Phase 2
|
||||
// n'est pas explicitement activée (voir assertReadOnlyPhase()).
|
||||
portainerReadOnly: z
|
||||
@@ -99,6 +104,7 @@ const SECRET_KEYS = new Set<keyof Config>([
|
||||
"ollamaApiKey",
|
||||
"mcpN8nAuthToken",
|
||||
"portainerMcpAuthToken",
|
||||
"portainerApiKey",
|
||||
"telegramBotToken",
|
||||
"alertWebhookUrl",
|
||||
"adminPasswordHash",
|
||||
@@ -118,6 +124,7 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): Config {
|
||||
mcpN8nAuthToken: env.MCP_N8N_AUTH_TOKEN,
|
||||
mcpPortainerUrl: env.MCP_PORTAINER_URL,
|
||||
portainerMcpAuthToken: env.PORTAINER_MCP_AUTH_TOKEN,
|
||||
portainerApiKey: env.PORTAINER_API_KEY,
|
||||
portainerReadOnly: env.PORTAINER_READ_ONLY,
|
||||
telegramBotToken: env.TELEGRAM_BOT_TOKEN,
|
||||
telegramAllowedUserIds: env.TELEGRAM_ALLOWED_USER_IDS,
|
||||
|
||||
@@ -49,7 +49,9 @@ async function main(): Promise<void> {
|
||||
await registry.connect({
|
||||
name: "portainer",
|
||||
url: cfg.mcpPortainerUrl,
|
||||
authToken: cfg.portainerMcpAuthToken,
|
||||
authToken: cfg.portainerMcpAuthToken, // secret de passerelle (Bearer)
|
||||
// Clé API Portainer restreinte de `chlova` : cloisonne l'accès réel.
|
||||
extraHeaders: { "X-Portainer-API-Key": cfg.portainerApiKey },
|
||||
});
|
||||
|
||||
// ── Outils + Guard, selon la phase ──────────────────────────────────────
|
||||
|
||||
@@ -20,6 +20,8 @@ export interface McpServerConfig {
|
||||
name: string;
|
||||
url: string;
|
||||
authToken: string;
|
||||
/** Headers additionnels (ex. X-Portainer-API-Key pour la passerelle Portainer). */
|
||||
extraHeaders?: Record<string, string>;
|
||||
}
|
||||
|
||||
interface ConnectedServer {
|
||||
@@ -36,7 +38,10 @@ export class McpRegistry {
|
||||
async connect(cfg: McpServerConfig): Promise<void> {
|
||||
const transport = new StreamableHTTPClientTransport(new URL(cfg.url), {
|
||||
requestInit: {
|
||||
headers: { authorization: `Bearer ${cfg.authToken}` },
|
||||
headers: {
|
||||
authorization: `Bearer ${cfg.authToken}`,
|
||||
...cfg.extraHeaders,
|
||||
},
|
||||
},
|
||||
});
|
||||
const client = new Client({ name: `chlova-${cfg.name}`, version: "0.1.0" });
|
||||
|
||||
@@ -12,8 +12,9 @@ const fullEnv = (): NodeJS.ProcessEnv => ({
|
||||
OLLAMA_MODEL: "qwen3:cloud",
|
||||
MCP_N8N_URL: "http://mcp-n8n:3000",
|
||||
MCP_N8N_AUTH_TOKEN: "secret-n8n",
|
||||
MCP_PORTAINER_URL: "http://mcp-portainer:3000",
|
||||
PORTAINER_MCP_AUTH_TOKEN: "secret-portainer",
|
||||
MCP_PORTAINER_URL: "http://mcp-portainer:17717/mcp",
|
||||
PORTAINER_MCP_AUTH_TOKEN: "secret-gate",
|
||||
PORTAINER_API_KEY: "secret-portainer-apikey",
|
||||
PORTAINER_READ_ONLY: "true",
|
||||
TELEGRAM_BOT_TOKEN: "secret-tg",
|
||||
TELEGRAM_ALLOWED_USER_IDS: "111, 222",
|
||||
|
||||
Reference in New Issue
Block a user