flowbite-mcp 1.0.0
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/LICENSE.md +21 -0
- package/README.md +324 -0
- package/build/index.js +975 -0
- package/build/server-runner.js +228 -0
- package/data/components/accordion.md +860 -0
- package/data/components/alerts.md +739 -0
- package/data/components/avatar.md +178 -0
- package/data/components/badge.md +420 -0
- package/data/components/banner.md +145 -0
- package/data/components/bottom-navigation.md +513 -0
- package/data/components/breadcrumb.md +273 -0
- package/data/components/button-group.md +410 -0
- package/data/components/buttons.md +405 -0
- package/data/components/card.md +711 -0
- package/data/components/carousel.md +858 -0
- package/data/components/chat-bubble.md +1063 -0
- package/data/components/clipboard.md +1029 -0
- package/data/components/datepicker.md +673 -0
- package/data/components/device-mockups.md +152 -0
- package/data/components/drawer.md +1353 -0
- package/data/components/dropdowns.md +1925 -0
- package/data/components/footer.md +299 -0
- package/data/components/forms.md +371 -0
- package/data/components/gallery.md +322 -0
- package/data/components/indicators.md +262 -0
- package/data/components/jumbotron.md +213 -0
- package/data/components/kbd.md +217 -0
- package/data/components/list-group.md +365 -0
- package/data/components/mega-menu.md +558 -0
- package/data/components/modal.md +1309 -0
- package/data/components/navbar.md +1053 -0
- package/data/components/pagination.md +472 -0
- package/data/components/popover.md +826 -0
- package/data/components/progress.md +95 -0
- package/data/components/qr-code.md +280 -0
- package/data/components/rating.md +323 -0
- package/data/components/sidebar.md +1067 -0
- package/data/components/skeleton.md +221 -0
- package/data/components/speed-dial.md +1270 -0
- package/data/components/spinner.md +222 -0
- package/data/components/stepper.md +271 -0
- package/data/components/tables.md +3127 -0
- package/data/components/tabs.md +808 -0
- package/data/components/timeline.md +304 -0
- package/data/components/toast.md +341 -0
- package/data/components/tooltips.md +524 -0
- package/data/components/typography.md +269 -0
- package/data/components/video.md +95 -0
- package/data/forms/checkbox.md +375 -0
- package/data/forms/file-input.md +98 -0
- package/data/forms/floating-label.md +185 -0
- package/data/forms/input-field.md +222 -0
- package/data/forms/number-input.md +1099 -0
- package/data/forms/phone-input.md +577 -0
- package/data/forms/radio.md +315 -0
- package/data/forms/range.md +83 -0
- package/data/forms/search-input.md +280 -0
- package/data/forms/select.md +259 -0
- package/data/forms/textarea.md +155 -0
- package/data/forms/timepicker.md +732 -0
- package/data/forms/toggle.md +176 -0
- package/data/plugins/charts.md +2683 -0
- package/data/plugins/datatables.md +1922 -0
- package/data/plugins/datepicker.md +5 -0
- package/data/plugins/wysiwyg.md +2377 -0
- package/data/quickstart.md +169 -0
- package/data/theme.md +231 -0
- package/data/toc.md +79 -0
- package/data/typography/blockquote.md +182 -0
- package/data/typography/headings.md +174 -0
- package/data/typography/hr.md +74 -0
- package/data/typography/images.md +168 -0
- package/data/typography/links.md +118 -0
- package/data/typography/lists.md +387 -0
- package/data/typography/paragraphs.md +186 -0
- package/data/typography/text.md +249 -0
- package/package.json +71 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5
|
+
import { InMemoryEventStore } from "@modelcontextprotocol/sdk/examples/shared/inMemoryEventStore.js";
|
|
6
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
const PORT = process.env.PORT || 3000;
|
|
8
|
+
export const ExpressHttpStreamableMcpServer = (options, setupCb) => {
|
|
9
|
+
const server = new McpServer({
|
|
10
|
+
name: options.name,
|
|
11
|
+
version: "1.0.0",
|
|
12
|
+
}, {
|
|
13
|
+
capabilities: {
|
|
14
|
+
logging: {},
|
|
15
|
+
tools: {
|
|
16
|
+
listChanged: false
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
setupCb(server);
|
|
21
|
+
const app = express();
|
|
22
|
+
app.use(express.json());
|
|
23
|
+
// Map to store transports by session ID
|
|
24
|
+
const transports = {};
|
|
25
|
+
// Handle POST requests for client-to-server communication
|
|
26
|
+
app.post('/mcp', async (req, res) => {
|
|
27
|
+
console.log(`Request received: ${req.method} ${req.url}`, { body: req.body });
|
|
28
|
+
// Capture response data for logging
|
|
29
|
+
const originalJson = res.json;
|
|
30
|
+
res.json = function (body) {
|
|
31
|
+
console.log(`Response being sent:`, JSON.stringify(body, null, 2));
|
|
32
|
+
return originalJson.call(this, body);
|
|
33
|
+
};
|
|
34
|
+
try {
|
|
35
|
+
// Check for existing session ID
|
|
36
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
37
|
+
let transport;
|
|
38
|
+
if (sessionId && transports[sessionId]) {
|
|
39
|
+
// Reuse existing transport
|
|
40
|
+
console.log(`Reusing session: ${sessionId}`);
|
|
41
|
+
transport = transports[sessionId];
|
|
42
|
+
}
|
|
43
|
+
else if (!sessionId && isInitializeRequest(req.body)) {
|
|
44
|
+
console.log(`New session request: ${req.body.method}`);
|
|
45
|
+
// New initialization request
|
|
46
|
+
const eventStore = new InMemoryEventStore();
|
|
47
|
+
transport = new StreamableHTTPServerTransport({
|
|
48
|
+
sessionIdGenerator: () => randomUUID(),
|
|
49
|
+
enableJsonResponse: true,
|
|
50
|
+
eventStore, // Enable resumability
|
|
51
|
+
onsessioninitialized: (sessionId) => {
|
|
52
|
+
// Store the transport by session ID
|
|
53
|
+
console.log(`Session initialized: ${sessionId}`);
|
|
54
|
+
transports[sessionId] = transport;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
// Clean up transport when closed
|
|
58
|
+
transport.onclose = () => {
|
|
59
|
+
const sid = transport.sessionId;
|
|
60
|
+
if (sid && transports[sid]) {
|
|
61
|
+
console.log(`Transport closed for session ${sid}, removing from transports map`);
|
|
62
|
+
delete transports[sid];
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
// Connect to the MCP server BEFORE handling the request
|
|
66
|
+
console.log(`Connecting transport to MCP server...`);
|
|
67
|
+
await server.connect(transport);
|
|
68
|
+
console.log(`Transport connected to MCP server successfully`);
|
|
69
|
+
console.log(`Handling initialization request...`);
|
|
70
|
+
await transport.handleRequest(req, res, req.body);
|
|
71
|
+
console.log(`Initialization request handled, response sent`);
|
|
72
|
+
return; // Already handled
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.error('Invalid request: No valid session ID or initialization request');
|
|
76
|
+
// Invalid request
|
|
77
|
+
res.status(400).json({
|
|
78
|
+
jsonrpc: '2.0',
|
|
79
|
+
error: {
|
|
80
|
+
code: -32000,
|
|
81
|
+
message: 'Bad Request: No valid session ID provided',
|
|
82
|
+
},
|
|
83
|
+
id: null,
|
|
84
|
+
});
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
console.log(`Handling request for session: ${transport.sessionId}`);
|
|
88
|
+
console.log(`Request body:`, JSON.stringify(req.body, null, 2));
|
|
89
|
+
// Handle the request with existing transport
|
|
90
|
+
console.log(`Calling transport.handleRequest...`);
|
|
91
|
+
const startTime = Date.now();
|
|
92
|
+
await transport.handleRequest(req, res, req.body);
|
|
93
|
+
const duration = Date.now() - startTime;
|
|
94
|
+
console.log(`Request handling completed in ${duration}ms for session: ${transport.sessionId}`);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
console.error('Error handling MCP request:', error);
|
|
98
|
+
if (!res.headersSent) {
|
|
99
|
+
res.status(500).json({
|
|
100
|
+
jsonrpc: '2.0',
|
|
101
|
+
error: {
|
|
102
|
+
code: -32603,
|
|
103
|
+
message: 'Internal server error',
|
|
104
|
+
},
|
|
105
|
+
id: null,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
// Handle GET requests for server-to-client notifications via HTTP Streaming
|
|
111
|
+
app.get('/mcp', async (req, res) => {
|
|
112
|
+
console.log(`GET Request received: ${req.method} ${req.url}`);
|
|
113
|
+
try {
|
|
114
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
115
|
+
if (!sessionId || !transports[sessionId]) {
|
|
116
|
+
console.log(`Invalid session ID in GET request: ${sessionId}`);
|
|
117
|
+
res.status(400).send('Invalid or missing session ID');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// Check for Last-Event-ID header for resumability
|
|
121
|
+
const lastEventId = req.headers['last-event-id'];
|
|
122
|
+
if (lastEventId) {
|
|
123
|
+
console.log(`Client reconnecting with Last-Event-ID: ${lastEventId}`);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
console.log(`Establishing new HTTP stream for session ${sessionId}`);
|
|
127
|
+
}
|
|
128
|
+
const transport = transports[sessionId];
|
|
129
|
+
// Set up connection close monitoring
|
|
130
|
+
res.on('close', () => {
|
|
131
|
+
console.log(`HTTP stream closed for session ${sessionId}`);
|
|
132
|
+
});
|
|
133
|
+
console.log(`Starting HTTP transport.handleRequest for session ${sessionId}...`);
|
|
134
|
+
const startTime = Date.now();
|
|
135
|
+
await transport.handleRequest(req, res);
|
|
136
|
+
const duration = Date.now() - startTime;
|
|
137
|
+
console.log(`HTTP stream setup completed in ${duration}ms for session: ${sessionId}`);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.error('Error handling GET request:', error);
|
|
141
|
+
if (!res.headersSent) {
|
|
142
|
+
res.status(500).send('Internal server error');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
// Handle DELETE requests for session termination
|
|
147
|
+
app.delete('/mcp', async (req, res) => {
|
|
148
|
+
console.log(`DELETE Request received: ${req.method} ${req.url}`);
|
|
149
|
+
try {
|
|
150
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
151
|
+
if (!sessionId || !transports[sessionId]) {
|
|
152
|
+
console.log(`Invalid session ID in DELETE request: ${sessionId}`);
|
|
153
|
+
res.status(400).send('Invalid or missing session ID');
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
console.log(`Received session termination request for session ${sessionId}`);
|
|
157
|
+
const transport = transports[sessionId];
|
|
158
|
+
// Capture response for logging
|
|
159
|
+
const originalSend = res.send;
|
|
160
|
+
res.send = function (body) {
|
|
161
|
+
console.log(`DELETE response being sent:`, body);
|
|
162
|
+
return originalSend.call(this, body);
|
|
163
|
+
};
|
|
164
|
+
console.log(`Processing session termination...`);
|
|
165
|
+
const startTime = Date.now();
|
|
166
|
+
await transport.handleRequest(req, res);
|
|
167
|
+
const duration = Date.now() - startTime;
|
|
168
|
+
console.log(`Session termination completed in ${duration}ms for session: ${sessionId}`);
|
|
169
|
+
// Check if transport was actually closed
|
|
170
|
+
setTimeout(() => {
|
|
171
|
+
if (transports[sessionId]) {
|
|
172
|
+
console.log(`Note: Transport for session ${sessionId} still exists after DELETE request`);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
console.log(`Transport for session ${sessionId} successfully removed after DELETE request`);
|
|
176
|
+
}
|
|
177
|
+
}, 100);
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
console.error('Error handling DELETE request:', error);
|
|
181
|
+
if (!res.headersSent) {
|
|
182
|
+
res.status(500).send('Error processing session termination');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
const express_server = app.listen(PORT, () => {
|
|
187
|
+
console.log(`MCP Streamable HTTP Server listening on port ${PORT}`);
|
|
188
|
+
});
|
|
189
|
+
// Add server event listeners for better visibility
|
|
190
|
+
express_server.on('connect', (transport) => {
|
|
191
|
+
console.log(`[Server] Transport connected: ${transport}`);
|
|
192
|
+
});
|
|
193
|
+
express_server.on('disconnect', (transport) => {
|
|
194
|
+
console.log(`[Server] Transport disconnected: ${transport.sessionId}`);
|
|
195
|
+
});
|
|
196
|
+
express_server.on('request', (request, transport) => {
|
|
197
|
+
console.log(`[Server] Received request: ${request.method} from transport: ${transport}`);
|
|
198
|
+
});
|
|
199
|
+
express_server.on('response', (response, transport) => {
|
|
200
|
+
console.log(`[Server] Sending response for id: ${response.id} to transport: ${transport.sessionId}`);
|
|
201
|
+
});
|
|
202
|
+
express_server.on('notification', (notification, transport) => {
|
|
203
|
+
console.log(`[Server] Sending notification: ${notification.method} to transport: ${transport.sessionId}`);
|
|
204
|
+
});
|
|
205
|
+
express_server.on('error', (error, transport) => {
|
|
206
|
+
console.error(`[Server] Error with transport ${transport?.sessionId || 'unknown'}:`, error);
|
|
207
|
+
});
|
|
208
|
+
// Handle server shutdown
|
|
209
|
+
process.on('SIGINT', async () => {
|
|
210
|
+
console.log('Shutting down server...');
|
|
211
|
+
// Close all active transports to properly clean up resources
|
|
212
|
+
for (const sessionId in transports) {
|
|
213
|
+
try {
|
|
214
|
+
console.log(`Closing transport for session ${sessionId}`);
|
|
215
|
+
await transports[sessionId].close();
|
|
216
|
+
delete transports[sessionId];
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
console.error(`Error closing transport for session ${sessionId}:`, error);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
await express_server.close();
|
|
223
|
+
await server.close();
|
|
224
|
+
console.log('Server shutdown complete');
|
|
225
|
+
process.exit(0);
|
|
226
|
+
});
|
|
227
|
+
return { process, server, express_server };
|
|
228
|
+
};
|