@mgsoftwarebv/mg-dashboard-mcp 2.3.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +120 -14
- package/dist/index.js.map +1 -1
- package/package.json +46 -46
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import {
|
|
4
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
5
|
+
import { ListToolsRequestSchema, CallToolRequestSchema, isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { createServer } from 'http';
|
|
7
|
+
import { randomUUID, createHash, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
|
|
5
8
|
import { createClient } from '@supabase/supabase-js';
|
|
6
|
-
import { createHash, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
|
|
7
9
|
import { Client } from 'ssh2';
|
|
8
10
|
|
|
9
11
|
// src/trigger-tools.ts
|
|
@@ -1213,6 +1215,8 @@ var supabaseKey = getArg("supabase-key") || process.env.SUPABASE_SERVICE_ROLE_KE
|
|
|
1213
1215
|
var encryptionKey = getArg("encryption-key") || process.env.ENCRYPTION_KEY;
|
|
1214
1216
|
var mijnhostApiKey = getArg("mijnhost-api-key") || process.env.MIJNHOST_API_KEY;
|
|
1215
1217
|
var agentWorkspaceId = getArg("workspace-id") || process.env.AGENT_WORKSPACE_ID || null;
|
|
1218
|
+
var httpMode = args.includes("--http");
|
|
1219
|
+
var httpPort = Number(getArg("port")) || 3100;
|
|
1216
1220
|
if (!apiKey) {
|
|
1217
1221
|
console.error("API key is required. Use --api-key=dk_xxx or set MG_DASHBOARD_API_KEY");
|
|
1218
1222
|
process.exit(1);
|
|
@@ -2474,11 +2478,8 @@ var TOOLS = [
|
|
|
2474
2478
|
// ----- Agent Reporting -----
|
|
2475
2479
|
...AGENT_TOOLS
|
|
2476
2480
|
];
|
|
2477
|
-
var
|
|
2478
|
-
|
|
2479
|
-
{ capabilities: { tools: {} } }
|
|
2480
|
-
);
|
|
2481
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
2481
|
+
var MCP_VERSION = "2.3.1";
|
|
2482
|
+
async function handleListTools() {
|
|
2482
2483
|
if (!authContext) return { tools: TOOLS };
|
|
2483
2484
|
const accessible = TOOLS.filter((tool) => {
|
|
2484
2485
|
const requiredModule = TOOL_MODULE_MAP[tool.name];
|
|
@@ -2486,8 +2487,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
2486
2487
|
return authContext.permissions.modules[requiredModule] === true;
|
|
2487
2488
|
});
|
|
2488
2489
|
return { tools: accessible };
|
|
2489
|
-
}
|
|
2490
|
-
|
|
2490
|
+
}
|
|
2491
|
+
async function handleCallTool(request) {
|
|
2491
2492
|
if (!authContext) {
|
|
2492
2493
|
return { content: [{ type: "text", text: "Error: not authenticated" }] };
|
|
2493
2494
|
}
|
|
@@ -2935,7 +2936,17 @@ ${lines.join("\n")}` }] };
|
|
|
2935
2936
|
const message = err instanceof Error ? err.message : String(err);
|
|
2936
2937
|
return { content: [{ type: "text", text: `Error: ${message}` }] };
|
|
2937
2938
|
}
|
|
2938
|
-
}
|
|
2939
|
+
}
|
|
2940
|
+
function createMcpServer() {
|
|
2941
|
+
const s = new Server(
|
|
2942
|
+
{ name: "mg-dashboard-mcp", version: MCP_VERSION },
|
|
2943
|
+
{ capabilities: { tools: {} } }
|
|
2944
|
+
);
|
|
2945
|
+
s.setRequestHandler(ListToolsRequestSchema, handleListTools);
|
|
2946
|
+
s.setRequestHandler(CallToolRequestSchema, handleCallTool);
|
|
2947
|
+
return s;
|
|
2948
|
+
}
|
|
2949
|
+
var server = createMcpServer();
|
|
2939
2950
|
async function main() {
|
|
2940
2951
|
console.error("Starting MG Dashboard MCP Server...");
|
|
2941
2952
|
authContext = await validateApiKey(apiKey);
|
|
@@ -2943,10 +2954,105 @@ async function main() {
|
|
|
2943
2954
|
console.error("API key validation failed");
|
|
2944
2955
|
process.exit(1);
|
|
2945
2956
|
}
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2957
|
+
const toolNames = TOOLS.map((t) => t.name).join(", ");
|
|
2958
|
+
if (httpMode) {
|
|
2959
|
+
console.error(`API key validated. Starting Streamable HTTP transport on port ${httpPort}...`);
|
|
2960
|
+
const transports = /* @__PURE__ */ new Map();
|
|
2961
|
+
const REST_TOOL_MAP = {
|
|
2962
|
+
"/api/report-coverage": "agent-report-coverage",
|
|
2963
|
+
"/api/report-finding": "agent-report-finding",
|
|
2964
|
+
"/api/save-documentation": "agent-save-documentation",
|
|
2965
|
+
"/api/list-findings": "agent-list-findings",
|
|
2966
|
+
"/api/get-documentation": "agent-get-documentation"
|
|
2967
|
+
};
|
|
2968
|
+
const httpServer = createServer(async (req, res) => {
|
|
2969
|
+
const url = new URL(req.url ?? "/", `http://localhost:${httpPort}`);
|
|
2970
|
+
const restToolName = REST_TOOL_MAP[url.pathname];
|
|
2971
|
+
if (restToolName && req.method === "POST") {
|
|
2972
|
+
const chunks = [];
|
|
2973
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
2974
|
+
let toolArgs;
|
|
2975
|
+
try {
|
|
2976
|
+
toolArgs = JSON.parse(Buffer.concat(chunks).toString());
|
|
2977
|
+
} catch {
|
|
2978
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2979
|
+
res.end(JSON.stringify({ error: "Invalid JSON" }));
|
|
2980
|
+
return;
|
|
2981
|
+
}
|
|
2982
|
+
try {
|
|
2983
|
+
const result = await handleAgentTool(restToolName, toolArgs, { supabase, workspaceId: agentWorkspaceId });
|
|
2984
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2985
|
+
res.end(JSON.stringify({ ok: true, result }));
|
|
2986
|
+
} catch (err) {
|
|
2987
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
2988
|
+
res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
|
|
2989
|
+
}
|
|
2990
|
+
return;
|
|
2991
|
+
}
|
|
2992
|
+
if (url.pathname === "/api/tools" && req.method === "GET") {
|
|
2993
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2994
|
+
res.end(JSON.stringify({ tools: Object.keys(REST_TOOL_MAP) }));
|
|
2995
|
+
return;
|
|
2996
|
+
}
|
|
2997
|
+
if (url.pathname !== "/mcp") {
|
|
2998
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
2999
|
+
res.end("Not found");
|
|
3000
|
+
return;
|
|
3001
|
+
}
|
|
3002
|
+
if (req.method === "OPTIONS") {
|
|
3003
|
+
res.writeHead(204, {
|
|
3004
|
+
"Access-Control-Allow-Origin": "*",
|
|
3005
|
+
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
3006
|
+
"Access-Control-Allow-Headers": "Content-Type, mcp-session-id",
|
|
3007
|
+
"Access-Control-Expose-Headers": "mcp-session-id"
|
|
3008
|
+
});
|
|
3009
|
+
res.end();
|
|
3010
|
+
return;
|
|
3011
|
+
}
|
|
3012
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
3013
|
+
res.setHeader("Access-Control-Expose-Headers", "mcp-session-id");
|
|
3014
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
3015
|
+
let body;
|
|
3016
|
+
if (req.method === "POST") {
|
|
3017
|
+
const chunks = [];
|
|
3018
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
3019
|
+
try {
|
|
3020
|
+
body = JSON.parse(Buffer.concat(chunks).toString());
|
|
3021
|
+
} catch {
|
|
3022
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
3023
|
+
res.end("Invalid JSON");
|
|
3024
|
+
return;
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
if (sessionId && transports.has(sessionId)) {
|
|
3028
|
+
await transports.get(sessionId).handleRequest(req, res, body);
|
|
3029
|
+
return;
|
|
3030
|
+
}
|
|
3031
|
+
if (req.method === "POST" && body && (Array.isArray(body) ? body.some(isInitializeRequest) : isInitializeRequest(body))) {
|
|
3032
|
+
const transport = new StreamableHTTPServerTransport({
|
|
3033
|
+
sessionIdGenerator: () => randomUUID()
|
|
3034
|
+
});
|
|
3035
|
+
transport.onclose = () => {
|
|
3036
|
+
if (transport.sessionId) transports.delete(transport.sessionId);
|
|
3037
|
+
};
|
|
3038
|
+
const sessionServer = createMcpServer();
|
|
3039
|
+
await sessionServer.connect(transport);
|
|
3040
|
+
if (transport.sessionId) transports.set(transport.sessionId, transport);
|
|
3041
|
+
await transport.handleRequest(req, res, body);
|
|
3042
|
+
return;
|
|
3043
|
+
}
|
|
3044
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
3045
|
+
res.end("Bad request \u2014 missing or invalid session");
|
|
3046
|
+
});
|
|
3047
|
+
httpServer.listen(httpPort, () => {
|
|
3048
|
+
console.error(`MCP HTTP server ready on port ${httpPort}. Tools: ${toolNames}`);
|
|
3049
|
+
});
|
|
3050
|
+
} else {
|
|
3051
|
+
console.error("API key validated. Starting stdio transport...");
|
|
3052
|
+
const transport = new StdioServerTransport();
|
|
3053
|
+
await server.connect(transport);
|
|
3054
|
+
console.error(`MCP Server ready. Tools: ${toolNames}`);
|
|
3055
|
+
}
|
|
2950
3056
|
}
|
|
2951
3057
|
main().catch((err) => {
|
|
2952
3058
|
console.error("Fatal error:", err);
|