@insforge/mcp 1.1.0 → 1.1.3-dev.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/chunk-DR4IN3DB.js +1391 -0
- package/dist/http-server.js +191 -0
- package/dist/index.js +15 -1314
- package/package.json +6 -3
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
registerInsforgeTools
|
|
4
|
+
} from "./chunk-DR4IN3DB.js";
|
|
5
|
+
|
|
6
|
+
// src/http/server.ts
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
9
|
+
import { program } from "commander";
|
|
10
|
+
import express from "express";
|
|
11
|
+
import { randomUUID } from "crypto";
|
|
12
|
+
program.option("--port <number>", "Port to run HTTP server on", "3000");
|
|
13
|
+
program.parse(process.argv);
|
|
14
|
+
var options = program.opts();
|
|
15
|
+
var { port } = options;
|
|
16
|
+
var PORT = parseInt(port) || 3e3;
|
|
17
|
+
var transports = /* @__PURE__ */ new Map();
|
|
18
|
+
var servers = /* @__PURE__ */ new Map();
|
|
19
|
+
var app = express();
|
|
20
|
+
app.use(express.json({ limit: "10mb" }));
|
|
21
|
+
app.use((req, res, next) => {
|
|
22
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
23
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
24
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization, X-Base-URL, Mcp-Session-Id, Last-Event-ID");
|
|
25
|
+
res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id");
|
|
26
|
+
if (req.method === "OPTIONS") {
|
|
27
|
+
return res.sendStatus(200);
|
|
28
|
+
}
|
|
29
|
+
next();
|
|
30
|
+
});
|
|
31
|
+
app.get("/health", (_req, res) => {
|
|
32
|
+
res.json({
|
|
33
|
+
status: "ok",
|
|
34
|
+
server: "insforge-mcp-streamable",
|
|
35
|
+
version: "1.0.0",
|
|
36
|
+
protocol: "Streamable HTTP",
|
|
37
|
+
sessions: transports.size,
|
|
38
|
+
authentication: "per-request via headers",
|
|
39
|
+
requiredHeaders: {
|
|
40
|
+
"Authorization": "Bearer <API_KEY>",
|
|
41
|
+
"X-Base-URL": "<BACKEND_URL> (e.g. http://localhost:7130)"
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
function isInitializeRequest(body) {
|
|
46
|
+
if (!body) return false;
|
|
47
|
+
if (body.method === "initialize") {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
if (Array.isArray(body)) {
|
|
51
|
+
return body.some((req) => req.method === "initialize");
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
app.post("/mcp", async (req, res) => {
|
|
56
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
57
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] POST /mcp - Session: ${sessionId || "none"}`);
|
|
58
|
+
const authHeader = req.headers["authorization"];
|
|
59
|
+
let apiKey;
|
|
60
|
+
if (authHeader?.startsWith("Bearer ")) {
|
|
61
|
+
apiKey = authHeader.substring(7);
|
|
62
|
+
}
|
|
63
|
+
const apiBaseUrl = req.headers["x-base-url"];
|
|
64
|
+
let transport;
|
|
65
|
+
let mcpServer;
|
|
66
|
+
if (sessionId && transports.has(sessionId)) {
|
|
67
|
+
transport = transports.get(sessionId);
|
|
68
|
+
console.log("Using existing transport for session:", sessionId);
|
|
69
|
+
} else if (isInitializeRequest(req.body)) {
|
|
70
|
+
if (!apiKey) {
|
|
71
|
+
return res.status(401).json({
|
|
72
|
+
error: "Missing required Authorization header. Expected: Authorization: Bearer <API_KEY>"
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (!apiBaseUrl) {
|
|
76
|
+
return res.status(400).json({
|
|
77
|
+
error: "Missing required X-Base-URL header. Expected: X-Base-URL: <BACKEND_URL>"
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
transport = new StreamableHTTPServerTransport({
|
|
81
|
+
sessionIdGenerator: () => randomUUID(),
|
|
82
|
+
onsessioninitialized: (sessionId2) => {
|
|
83
|
+
console.log(`Session initialized: ${sessionId2}`);
|
|
84
|
+
transports.set(sessionId2, transport);
|
|
85
|
+
if (mcpServer) {
|
|
86
|
+
servers.set(sessionId2, mcpServer);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
mcpServer = new McpServer({
|
|
91
|
+
name: "insforge-mcp",
|
|
92
|
+
version: "1.0.0"
|
|
93
|
+
});
|
|
94
|
+
registerInsforgeTools(mcpServer, {
|
|
95
|
+
apiKey,
|
|
96
|
+
apiBaseUrl
|
|
97
|
+
});
|
|
98
|
+
console.log("Connecting server to transport...");
|
|
99
|
+
await mcpServer.connect(transport);
|
|
100
|
+
console.log("Server connected successfully");
|
|
101
|
+
} else {
|
|
102
|
+
return res.status(400).json({
|
|
103
|
+
error: "Session required. Send initialize request first or provide Mcp-Session-Id header."
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
console.log("Handling request with transport...");
|
|
107
|
+
await transport.handleRequest(req, res, req.body);
|
|
108
|
+
console.log("Request handled");
|
|
109
|
+
});
|
|
110
|
+
app.get("/mcp", async (req, res) => {
|
|
111
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
112
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] GET /mcp - Session: ${sessionId || "none"}`);
|
|
113
|
+
if (!sessionId || !transports.has(sessionId)) {
|
|
114
|
+
return res.status(404).json({
|
|
115
|
+
error: "Session not found. Initialize first with POST request."
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
const transport = transports.get(sessionId);
|
|
119
|
+
await transport.handleRequest(req, res, req.body);
|
|
120
|
+
});
|
|
121
|
+
app.delete("/mcp", async (req, res) => {
|
|
122
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
123
|
+
console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] DELETE /mcp - Session: ${sessionId || "none"}`);
|
|
124
|
+
if (!sessionId || !transports.has(sessionId)) {
|
|
125
|
+
return res.status(404).json({
|
|
126
|
+
error: "Session not found."
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
const transport = transports.get(sessionId);
|
|
130
|
+
const server2 = servers.get(sessionId);
|
|
131
|
+
await transport.handleRequest(req, res, req.body);
|
|
132
|
+
if (server2) {
|
|
133
|
+
await server2.close();
|
|
134
|
+
servers.delete(sessionId);
|
|
135
|
+
}
|
|
136
|
+
transports.delete(sessionId);
|
|
137
|
+
console.log(`Session ${sessionId} closed`);
|
|
138
|
+
});
|
|
139
|
+
var server = app.listen(PORT, "127.0.0.1", () => {
|
|
140
|
+
console.log(`
|
|
141
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
142
|
+
\u2551 Insforge MCP Streamable HTTP Server \u2551
|
|
143
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
144
|
+
|
|
145
|
+
\u{1F680} Server: http://127.0.0.1:${PORT}
|
|
146
|
+
\u{1F517} Endpoint: http://127.0.0.1:${PORT}/mcp
|
|
147
|
+
\u{1F49A} Health: http://127.0.0.1:${PORT}/health
|
|
148
|
+
|
|
149
|
+
\u{1F4CB} Protocol: Streamable HTTP (2024-11-05+ spec)
|
|
150
|
+
\u{1F510} Required Headers (per-request):
|
|
151
|
+
\u2022 Authorization: Bearer <API_KEY>
|
|
152
|
+
\u2022 X-Base-URL: <BACKEND_URL>
|
|
153
|
+
|
|
154
|
+
\u{1F4DD} Client Configuration Example:
|
|
155
|
+
{
|
|
156
|
+
"mcpServers": {
|
|
157
|
+
"insforge": {
|
|
158
|
+
"url": "http://127.0.0.1:${PORT}/mcp",
|
|
159
|
+
"headers": {
|
|
160
|
+
"Authorization": "Bearer YOUR_API_KEY",
|
|
161
|
+
"X-Base-URL": "http://localhost:7130"
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
\u{1F504} Session Management: Automatic (stateful)
|
|
168
|
+
\u{1F6E1}\uFE0F Security: Binding to localhost only (127.0.0.1)
|
|
169
|
+
`);
|
|
170
|
+
});
|
|
171
|
+
process.on("SIGINT", async () => {
|
|
172
|
+
console.log("\n\u{1F6D1} Shutting down server...");
|
|
173
|
+
for (const [sessionId, server2] of servers.entries()) {
|
|
174
|
+
try {
|
|
175
|
+
console.log(`Closing session: ${sessionId}`);
|
|
176
|
+
await server2.close();
|
|
177
|
+
const transport = transports.get(sessionId);
|
|
178
|
+
if (transport) {
|
|
179
|
+
await transport.close();
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.error(`Error closing session ${sessionId}:`, error);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
servers.clear();
|
|
186
|
+
transports.clear();
|
|
187
|
+
server.close(() => {
|
|
188
|
+
console.log("\u2705 Server shutdown complete");
|
|
189
|
+
process.exit(0);
|
|
190
|
+
});
|
|
191
|
+
});
|