1Panel MCP Server
by ruibaby
Verified
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import dotenv from "dotenv";
import express from "express";
import fs from "fs-extra";
import path from "path";
import { z } from "zod";
import OnePanelAPI from "./1panel-api.mjs";
dotenv.config();
const app = express();
const PORT = process.env.PORT || 3000;
const mcpServer = new McpServer(
{
name: "1panel-deploy",
version: "1.0.0",
},
{
capabilities: {
tools: ["deploy_website"],
},
}
);
mcpServer.tool(
"deploy_website",
"Deploy website to 1Panel server",
{
domain: z.string().describe("Website domain"),
buildDir: z
.string()
.optional()
.describe(
"Build directory name (e.g. /Users/username/project/dist, /Users/username/project/build)"
),
},
async (params) => {
try {
const { buildDir, domain } = params;
const buildDirPath = path.resolve(buildDir);
if (!fs.existsSync(buildDirPath)) {
return {
content: [
{
type: "text",
text: `Build directory ${buildDir} does not exist`,
},
],
isError: true,
};
}
const deployResult = await deployTo1Panel(buildDirPath, domain);
return {
content: [
{
type: "text",
text: `
Successfully deployed to 1Panel!
Domain: ${deployResult.domain}
URL: ${deployResult.url}
Upload statistics:
- Total files: ${deployResult.uploadStats.totalFiles}
- Successfully uploaded: ${deployResult.uploadStats.successCount}
- Failed to upload: ${deployResult.uploadStats.failCount}
`,
},
],
};
} catch (error) {
console.error("Deployment failed:", error);
return {
content: [
{
type: "text",
text: `Deployment failed: ${
error.message || "An internal error occurred during deployment"
}`,
},
],
isError: true,
};
}
}
);
let activeTransport = null;
app.get("/sse", async (req, res) => {
const transport = new SSEServerTransport("/messages", res);
activeTransport = transport;
await mcpServer.connect(transport);
req.on("close", () => {
console.log("Client disconnected");
if (activeTransport === transport) {
activeTransport = null;
}
});
});
app.post("/messages", async (req, res) => {
try {
if (activeTransport) {
await activeTransport.handlePostMessage(req, res);
} else {
res.status(400).json({ error: "No active transport connection" });
}
} catch (error) {
console.error("Error processing message:", error);
res.status(500).json({ error: "Failed to process message" });
}
});
app.listen(PORT, () => {
console.log(`MCP server running on port ${PORT}`);
console.log(`MCP SSE endpoint: http://localhost:${PORT}/sse`);
console.log(`MCP message endpoint: http://localhost:${PORT}/messages`);
});
async function deployTo1Panel(buildDirPath, domain = "") {
try {
console.log(`Start deploying directory ${buildDirPath} to 1Panel`);
const onePanelAPI = new OnePanelAPI({
baseURL: process.env.ONEPANEL_BASE_URL,
apiKey: process.env.ONEPANEL_API_KEY,
languageCode: process.env.ONEPANEL_LANGUAGE || "zh",
});
const siteConfig = {
domain: domain,
};
let website = await onePanelAPI.getWebsiteDetail(domain);
if (!website) {
website = await onePanelAPI.createWebsite(siteConfig);
console.log(`Create website: domain: ${domain}`);
} else {
console.log(`Website already exists: domain: ${domain}`);
}
console.log("Upload files to website");
const uploadResult = await onePanelAPI.uploadStaticFiles(
domain,
buildDirPath
);
return {
domain: domain,
url: `http://${domain}`,
status: "success",
uploadStats: {
totalFiles: uploadResult.totalFiles,
successCount: uploadResult.successCount,
failCount: uploadResult.failCount,
},
};
} catch (error) {
console.error("Error deploying to 1Panel:", error);
throw new Error(`1Panel deployment failed: ${error.message}`);
}
}