#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { PortainerClient } from './portainer-client.js';
import { config } from './config.js';
import { createContainerTools } from './tools/container-tools.js';
import { createImageTools } from './tools/image-tools.js';
import { createNetworkTools } from './tools/network-tools.js';
import { createVolumeTools } from './tools/volume-tools.js';
import { createConfigTools } from './tools/config-tools.js';
import { createEndpointTools } from './tools/endpoint-tools.js';
// Config tools are always available (to allow setting up credentials)
const configTools = createConfigTools();
// Function to get all tools dynamically (recreates client if config changed)
function getAllTools() {
const tools: Record<string, any> = { ...configTools };
// Only add Portainer tools if configured
if (config.isConfigured) {
try {
const client = new PortainerClient();
Object.assign(tools, createEndpointTools(client));
Object.assign(tools, createContainerTools(client));
Object.assign(tools, createImageTools(client));
Object.assign(tools, createNetworkTools(client));
Object.assign(tools, createVolumeTools(client));
} catch (error) {
console.error('Warning: Could not create Portainer client:', error);
}
}
return tools;
}
// Create MCP server
const server = new Server(
{
name: 'portainer-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// Handle list tools request
server.setRequestHandler(ListToolsRequestSchema, async () => {
const tools = getAllTools();
return {
tools: Object.entries(tools).map(([name, tool]) => ({
name,
description: tool.description,
inputSchema: tool.inputSchema,
})),
};
});
// Handle tool call request
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const toolName = request.params.name;
const tools = getAllTools();
const tool = tools[toolName];
if (!tool) {
// Check if it's a Portainer tool that requires configuration
if (['list_containers', 'get_container', 'start_container', 'stop_container',
'restart_container', 'remove_container', 'create_container',
'list_images', 'get_image', 'pull_image', 'remove_image',
'list_networks', 'create_network', 'remove_network',
'list_volumes', 'create_volume', 'remove_volume'].includes(toolName)) {
return {
content: [
{
type: 'text',
text: `⚠️ Portainer not configured!\n\nUse the "set_portainer_config" tool first to configure your Portainer connection:\n\nExample:\n- URL: https://portainer.onlitec.com.br\n- API Key: ptr_your_key_here\n- Endpoint ID: 1`,
},
],
isError: true,
};
}
throw new Error(`Unknown tool: ${toolName}`);
}
try {
const result = await tool.handler(request.params.arguments ?? {});
return result;
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
});
// Start server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Portainer MCP Server running on stdio');
}
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});