@dalcontak/blogger-mcp-server 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/.github/workflows/publish.yml +34 -0
- package/AGENTS.md +155 -0
- package/Dockerfile +64 -0
- package/README.md +169 -0
- package/RELEASE.md +125 -0
- package/dist/bloggerService.d.ts +121 -0
- package/dist/bloggerService.js +323 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.js +32 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +304 -0
- package/dist/mcp-sdk-mock.d.ts +57 -0
- package/dist/mcp-sdk-mock.js +227 -0
- package/dist/server.d.ts +16 -0
- package/dist/server.js +448 -0
- package/dist/types.d.ts +94 -0
- package/dist/types.js +2 -0
- package/dist/ui-manager.d.ts +22 -0
- package/dist/ui-manager.js +110 -0
- package/jest.config.js +7 -0
- package/package.json +43 -0
- package/public/index.html +201 -0
- package/public/main.js +271 -0
- package/public/styles.css +155 -0
- package/src/bloggerService.test.ts +398 -0
- package/src/bloggerService.ts +351 -0
- package/src/config.test.ts +121 -0
- package/src/config.ts +33 -0
- package/src/index.ts +349 -0
- package/src/server.ts +443 -0
- package/src/types.ts +113 -0
- package/src/ui-manager.ts +128 -0
- package/start-dev.sh +64 -0
- package/start-prod.sh +53 -0
- package/tsconfig.json +15 -0
- package/vercel.json +24 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createToolDefinitions = createToolDefinitions;
|
|
4
|
+
exports.initMCPServer = initMCPServer;
|
|
5
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
/**
|
|
8
|
+
* Creates the tool definitions for the Blogger MCP server
|
|
9
|
+
* @param bloggerService Blogger service to interact with the API
|
|
10
|
+
* @returns Array of tool definitions
|
|
11
|
+
*/
|
|
12
|
+
function createToolDefinitions(bloggerService) {
|
|
13
|
+
return [
|
|
14
|
+
{
|
|
15
|
+
name: 'list_blogs',
|
|
16
|
+
description: 'Lists all accessible blogs',
|
|
17
|
+
args: zod_1.z.object({}),
|
|
18
|
+
handler: async (_args, _extra) => {
|
|
19
|
+
try {
|
|
20
|
+
const blogs = await bloggerService.listBlogs();
|
|
21
|
+
return {
|
|
22
|
+
content: [
|
|
23
|
+
{
|
|
24
|
+
type: 'text',
|
|
25
|
+
text: JSON.stringify({ blogs }, null, 2)
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error('Error fetching blogs:', error);
|
|
32
|
+
return {
|
|
33
|
+
content: [
|
|
34
|
+
{
|
|
35
|
+
type: 'text',
|
|
36
|
+
text: `Error fetching blogs: ${error}`
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
isError: true
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'get_blog',
|
|
46
|
+
description: 'Retrieves details of a specific blog',
|
|
47
|
+
args: zod_1.z.object({
|
|
48
|
+
blogId: zod_1.z.string().describe('Blog ID')
|
|
49
|
+
}),
|
|
50
|
+
handler: async (args, _extra) => {
|
|
51
|
+
try {
|
|
52
|
+
const blog = await bloggerService.getBlog(args.blogId);
|
|
53
|
+
return {
|
|
54
|
+
content: [
|
|
55
|
+
{
|
|
56
|
+
type: 'text',
|
|
57
|
+
text: JSON.stringify({ blog }, null, 2)
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
console.error(`Error fetching blog ${args.blogId}:`, error);
|
|
64
|
+
return {
|
|
65
|
+
content: [
|
|
66
|
+
{
|
|
67
|
+
type: 'text',
|
|
68
|
+
text: `Error fetching blog: ${error}`
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
isError: true
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'get_blog_by_url',
|
|
78
|
+
description: 'Retrieves a blog by its URL (useful for discovering blog ID)',
|
|
79
|
+
args: zod_1.z.object({
|
|
80
|
+
url: zod_1.z.string().describe('Blog URL')
|
|
81
|
+
}),
|
|
82
|
+
handler: async (args, _extra) => {
|
|
83
|
+
try {
|
|
84
|
+
const blog = await bloggerService.getBlogByUrl(args.url);
|
|
85
|
+
return {
|
|
86
|
+
content: [
|
|
87
|
+
{
|
|
88
|
+
type: 'text',
|
|
89
|
+
text: JSON.stringify({ blog }, null, 2)
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error(`Error fetching blog by URL ${args.url}:`, error);
|
|
96
|
+
return {
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: 'text',
|
|
100
|
+
text: `Error fetching blog by URL: ${error}`
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
isError: true
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'create_blog',
|
|
110
|
+
description: 'Creates a new blog (not supported by the Blogger API)',
|
|
111
|
+
args: zod_1.z.object({
|
|
112
|
+
name: zod_1.z.string().describe('Blog name'),
|
|
113
|
+
description: zod_1.z.string().optional().describe('Blog description')
|
|
114
|
+
}),
|
|
115
|
+
handler: async (_args, _extra) => {
|
|
116
|
+
return {
|
|
117
|
+
content: [
|
|
118
|
+
{
|
|
119
|
+
type: 'text',
|
|
120
|
+
text: 'Blog creation is not supported by the Blogger API. Please create a blog via the Blogger web interface.'
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
isError: true
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'list_posts',
|
|
129
|
+
description: 'Lists all posts from a blog',
|
|
130
|
+
args: zod_1.z.object({
|
|
131
|
+
blogId: zod_1.z.string().describe('Blog ID'),
|
|
132
|
+
maxResults: zod_1.z.number().optional().describe('Maximum number of results to return')
|
|
133
|
+
}),
|
|
134
|
+
handler: async (args, _extra) => {
|
|
135
|
+
try {
|
|
136
|
+
const posts = await bloggerService.listPosts(args.blogId, args.maxResults);
|
|
137
|
+
return {
|
|
138
|
+
content: [
|
|
139
|
+
{
|
|
140
|
+
type: 'text',
|
|
141
|
+
text: JSON.stringify({ posts }, null, 2)
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error(`Error fetching posts for blog ${args.blogId}:`, error);
|
|
148
|
+
return {
|
|
149
|
+
content: [
|
|
150
|
+
{
|
|
151
|
+
type: 'text',
|
|
152
|
+
text: `Error fetching posts: ${error}`
|
|
153
|
+
}
|
|
154
|
+
],
|
|
155
|
+
isError: true
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'search_posts',
|
|
162
|
+
description: 'Searches posts in a blog',
|
|
163
|
+
args: zod_1.z.object({
|
|
164
|
+
blogId: zod_1.z.string().describe('Blog ID'),
|
|
165
|
+
query: zod_1.z.string().describe('Search term'),
|
|
166
|
+
maxResults: zod_1.z.number().optional().describe('Maximum number of results to return')
|
|
167
|
+
}),
|
|
168
|
+
handler: async (args, _extra) => {
|
|
169
|
+
try {
|
|
170
|
+
const posts = await bloggerService.searchPosts(args.blogId, args.query, args.maxResults);
|
|
171
|
+
return {
|
|
172
|
+
content: [
|
|
173
|
+
{
|
|
174
|
+
type: 'text',
|
|
175
|
+
text: JSON.stringify({ posts }, null, 2)
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.error(`Error searching posts in blog ${args.blogId}:`, error);
|
|
182
|
+
return {
|
|
183
|
+
content: [
|
|
184
|
+
{
|
|
185
|
+
type: 'text',
|
|
186
|
+
text: `Error searching posts: ${error}`
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
isError: true
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'get_post',
|
|
196
|
+
description: 'Retrieves details of a specific post',
|
|
197
|
+
args: zod_1.z.object({
|
|
198
|
+
blogId: zod_1.z.string().describe('Blog ID'),
|
|
199
|
+
postId: zod_1.z.string().describe('Post ID')
|
|
200
|
+
}),
|
|
201
|
+
handler: async (args, _extra) => {
|
|
202
|
+
try {
|
|
203
|
+
const post = await bloggerService.getPost(args.blogId, args.postId);
|
|
204
|
+
return {
|
|
205
|
+
content: [
|
|
206
|
+
{
|
|
207
|
+
type: 'text',
|
|
208
|
+
text: JSON.stringify({ post }, null, 2)
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
console.error(`Error fetching post ${args.postId}:`, error);
|
|
215
|
+
return {
|
|
216
|
+
content: [
|
|
217
|
+
{
|
|
218
|
+
type: 'text',
|
|
219
|
+
text: `Error fetching post: ${error}`
|
|
220
|
+
}
|
|
221
|
+
],
|
|
222
|
+
isError: true
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: 'create_post',
|
|
229
|
+
description: 'Creates a new post in a blog',
|
|
230
|
+
args: zod_1.z.object({
|
|
231
|
+
blogId: zod_1.z.string().describe('Blog ID'),
|
|
232
|
+
title: zod_1.z.string().describe('Post title'),
|
|
233
|
+
content: zod_1.z.string().describe('Post content'),
|
|
234
|
+
labels: zod_1.z.array(zod_1.z.string()).optional().describe('Labels to associate with the post')
|
|
235
|
+
}),
|
|
236
|
+
handler: async (args, _extra) => {
|
|
237
|
+
try {
|
|
238
|
+
const post = await bloggerService.createPost(args.blogId, {
|
|
239
|
+
title: args.title,
|
|
240
|
+
content: args.content,
|
|
241
|
+
labels: args.labels
|
|
242
|
+
});
|
|
243
|
+
return {
|
|
244
|
+
content: [
|
|
245
|
+
{
|
|
246
|
+
type: 'text',
|
|
247
|
+
text: JSON.stringify({ post }, null, 2)
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
console.error(`Error creating post in blog ${args.blogId}:`, error);
|
|
254
|
+
return {
|
|
255
|
+
content: [
|
|
256
|
+
{
|
|
257
|
+
type: 'text',
|
|
258
|
+
text: `Error creating post: ${error}`
|
|
259
|
+
}
|
|
260
|
+
],
|
|
261
|
+
isError: true
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: 'update_post',
|
|
268
|
+
description: 'Updates an existing post',
|
|
269
|
+
args: zod_1.z.object({
|
|
270
|
+
blogId: zod_1.z.string().describe('Blog ID'),
|
|
271
|
+
postId: zod_1.z.string().describe('Post ID'),
|
|
272
|
+
title: zod_1.z.string().optional().describe('New post title'),
|
|
273
|
+
content: zod_1.z.string().optional().describe('New post content'),
|
|
274
|
+
labels: zod_1.z.array(zod_1.z.string()).optional().describe('New labels to associate with the post')
|
|
275
|
+
}),
|
|
276
|
+
handler: async (args, _extra) => {
|
|
277
|
+
try {
|
|
278
|
+
const post = await bloggerService.updatePost(args.blogId, args.postId, {
|
|
279
|
+
title: args.title,
|
|
280
|
+
content: args.content,
|
|
281
|
+
labels: args.labels
|
|
282
|
+
});
|
|
283
|
+
return {
|
|
284
|
+
content: [
|
|
285
|
+
{
|
|
286
|
+
type: 'text',
|
|
287
|
+
text: JSON.stringify({ post }, null, 2)
|
|
288
|
+
}
|
|
289
|
+
]
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
console.error(`Error updating post ${args.postId}:`, error);
|
|
294
|
+
return {
|
|
295
|
+
content: [
|
|
296
|
+
{
|
|
297
|
+
type: 'text',
|
|
298
|
+
text: `Error updating post: ${error}`
|
|
299
|
+
}
|
|
300
|
+
],
|
|
301
|
+
isError: true
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: 'delete_post',
|
|
308
|
+
description: 'Deletes a post',
|
|
309
|
+
args: zod_1.z.object({
|
|
310
|
+
blogId: zod_1.z.string().describe('Blog ID'),
|
|
311
|
+
postId: zod_1.z.string().describe('Post ID')
|
|
312
|
+
}),
|
|
313
|
+
handler: async (args, _extra) => {
|
|
314
|
+
try {
|
|
315
|
+
await bloggerService.deletePost(args.blogId, args.postId);
|
|
316
|
+
return {
|
|
317
|
+
content: [
|
|
318
|
+
{
|
|
319
|
+
type: 'text',
|
|
320
|
+
text: JSON.stringify({ success: true }, null, 2)
|
|
321
|
+
}
|
|
322
|
+
]
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
console.error(`Error deleting post ${args.postId}:`, error);
|
|
327
|
+
return {
|
|
328
|
+
content: [
|
|
329
|
+
{
|
|
330
|
+
type: 'text',
|
|
331
|
+
text: `Error deleting post: ${error}`
|
|
332
|
+
}
|
|
333
|
+
],
|
|
334
|
+
isError: true
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: 'list_labels',
|
|
341
|
+
description: 'Lists all labels from a blog',
|
|
342
|
+
args: zod_1.z.object({
|
|
343
|
+
blogId: zod_1.z.string().describe('Blog ID')
|
|
344
|
+
}),
|
|
345
|
+
handler: async (args, _extra) => {
|
|
346
|
+
try {
|
|
347
|
+
const labels = await bloggerService.listLabels(args.blogId);
|
|
348
|
+
return {
|
|
349
|
+
content: [
|
|
350
|
+
{
|
|
351
|
+
type: 'text',
|
|
352
|
+
text: JSON.stringify({ labels }, null, 2)
|
|
353
|
+
}
|
|
354
|
+
]
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
console.error(`Error fetching labels for blog ${args.blogId}:`, error);
|
|
359
|
+
return {
|
|
360
|
+
content: [
|
|
361
|
+
{
|
|
362
|
+
type: 'text',
|
|
363
|
+
text: `Error fetching labels: ${error}`
|
|
364
|
+
}
|
|
365
|
+
],
|
|
366
|
+
isError: true
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: 'get_label',
|
|
373
|
+
description: 'Retrieves details of a specific label',
|
|
374
|
+
args: zod_1.z.object({
|
|
375
|
+
blogId: zod_1.z.string().describe('Blog ID'),
|
|
376
|
+
labelName: zod_1.z.string().describe('Label name')
|
|
377
|
+
}),
|
|
378
|
+
handler: async (args, _extra) => {
|
|
379
|
+
try {
|
|
380
|
+
const label = await bloggerService.getLabel(args.blogId, args.labelName);
|
|
381
|
+
return {
|
|
382
|
+
content: [
|
|
383
|
+
{
|
|
384
|
+
type: 'text',
|
|
385
|
+
text: JSON.stringify({ label }, null, 2)
|
|
386
|
+
}
|
|
387
|
+
]
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
console.error(`Error fetching label ${args.labelName}:`, error);
|
|
392
|
+
return {
|
|
393
|
+
content: [
|
|
394
|
+
{
|
|
395
|
+
type: 'text',
|
|
396
|
+
text: `Error fetching label: ${error}`
|
|
397
|
+
}
|
|
398
|
+
],
|
|
399
|
+
isError: true
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
];
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Initializes the MCP server with all Blogger tools
|
|
408
|
+
* @param bloggerService Blogger service to interact with the API
|
|
409
|
+
* @param config Server configuration
|
|
410
|
+
* @returns MCP server instance
|
|
411
|
+
*/
|
|
412
|
+
function initMCPServer(bloggerService, config) {
|
|
413
|
+
// Create a new MCP server instance with server information
|
|
414
|
+
const server = new mcp_js_1.McpServer({
|
|
415
|
+
name: "Blogger MCP Server",
|
|
416
|
+
version: "1.0.4",
|
|
417
|
+
vendor: "mcproadev"
|
|
418
|
+
});
|
|
419
|
+
// Get all tool definitions
|
|
420
|
+
const toolDefinitions = createToolDefinitions(bloggerService);
|
|
421
|
+
// Register each tool with the MCP server
|
|
422
|
+
for (const tool of toolDefinitions) {
|
|
423
|
+
// We can't directly pass the schema object if it's already a Zod object in our definition,
|
|
424
|
+
// The MCP SDK expects the shape, not the Zod object itself for the 'args' parameter in server.tool()
|
|
425
|
+
// However, looking at the previous code:
|
|
426
|
+
// server.tool('name', 'desc', { param: z.string() }, handler)
|
|
427
|
+
// The previous code passed an object with Zod schemas as values.
|
|
428
|
+
// Our ToolDefinition.args is a z.ZodType<any>, which is likely a z.object({...}).
|
|
429
|
+
// We need to extract the shape from the z.object to pass it to server.tool if we want to match the signature.
|
|
430
|
+
// Actually, looking at the SDK, server.tool takes:
|
|
431
|
+
// name: string, description: string, args: ToolArgs, handler: ToolCallback
|
|
432
|
+
// where ToolArgs is Record<string, ZodType<any>>
|
|
433
|
+
// So my ToolDefinition.args should probably be Record<string, ZodType<any>> instead of z.ZodType<any>
|
|
434
|
+
// to make it easier to spread.
|
|
435
|
+
// Let's adjust the implementation in the loop.
|
|
436
|
+
// Since I defined args as z.ZodType<any> (which is z.object({...})), I can cast it or access .shape if it's a ZodObject.
|
|
437
|
+
if (tool.args instanceof zod_1.z.ZodObject) {
|
|
438
|
+
server.tool(tool.name, tool.description, tool.args.shape, tool.handler);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
// Fallback for empty objects or other schemas if we had them (list_blogs has empty object)
|
|
442
|
+
// If it's not a ZodObject, we might have issues if the SDK expects a shape map.
|
|
443
|
+
// list_blogs used {} which is compatible with Record<string, ZodType>
|
|
444
|
+
server.tool(tool.name, tool.description, {}, tool.handler);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return server;
|
|
448
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Types used in the MCP server for Blogger
|
|
4
|
+
*/
|
|
5
|
+
export interface ToolDefinition {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
args: z.ZodType<any>;
|
|
9
|
+
handler: (args: any, extra?: any) => Promise<any>;
|
|
10
|
+
}
|
|
11
|
+
export interface BloggerBlog {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
url: string;
|
|
16
|
+
status?: string;
|
|
17
|
+
posts?: BloggerPost[];
|
|
18
|
+
labels?: BloggerLabel[];
|
|
19
|
+
}
|
|
20
|
+
export interface BloggerPost {
|
|
21
|
+
id: string;
|
|
22
|
+
blogId: string;
|
|
23
|
+
title: string;
|
|
24
|
+
content: string;
|
|
25
|
+
url?: string;
|
|
26
|
+
published?: string;
|
|
27
|
+
updated?: string;
|
|
28
|
+
author?: {
|
|
29
|
+
id: string;
|
|
30
|
+
displayName: string;
|
|
31
|
+
url: string;
|
|
32
|
+
image?: {
|
|
33
|
+
url: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
labels?: string[];
|
|
37
|
+
}
|
|
38
|
+
export interface BloggerLabel {
|
|
39
|
+
id?: string;
|
|
40
|
+
name: string;
|
|
41
|
+
}
|
|
42
|
+
export interface SearchParams {
|
|
43
|
+
query: string;
|
|
44
|
+
maxResults?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface PaginationParams {
|
|
47
|
+
pageToken?: string;
|
|
48
|
+
maxResults?: number;
|
|
49
|
+
}
|
|
50
|
+
export type ServerMode = {
|
|
51
|
+
type: 'stdio';
|
|
52
|
+
} | {
|
|
53
|
+
type: 'http';
|
|
54
|
+
host: string;
|
|
55
|
+
port: number;
|
|
56
|
+
};
|
|
57
|
+
export interface OAuth2Config {
|
|
58
|
+
clientId?: string;
|
|
59
|
+
clientSecret?: string;
|
|
60
|
+
refreshToken?: string;
|
|
61
|
+
}
|
|
62
|
+
export interface ServerConfig {
|
|
63
|
+
mode: ServerMode;
|
|
64
|
+
blogger: {
|
|
65
|
+
apiKey?: string;
|
|
66
|
+
maxResults: number;
|
|
67
|
+
timeout: number;
|
|
68
|
+
};
|
|
69
|
+
oauth2: OAuth2Config;
|
|
70
|
+
logging: {
|
|
71
|
+
level: string;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export interface ServerStatus {
|
|
75
|
+
running: boolean;
|
|
76
|
+
mode: string;
|
|
77
|
+
startTime?: Date;
|
|
78
|
+
connections: number;
|
|
79
|
+
tools: string[];
|
|
80
|
+
}
|
|
81
|
+
export interface ClientConnection {
|
|
82
|
+
id: string;
|
|
83
|
+
ip?: string;
|
|
84
|
+
connectedAt: Date;
|
|
85
|
+
lastActivity: Date;
|
|
86
|
+
requestCount: number;
|
|
87
|
+
}
|
|
88
|
+
export interface ServerStats {
|
|
89
|
+
totalRequests: number;
|
|
90
|
+
successfulRequests: number;
|
|
91
|
+
failedRequests: number;
|
|
92
|
+
averageResponseTime: number;
|
|
93
|
+
toolUsage: Record<string, number>;
|
|
94
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ServerStatus, ClientConnection, ServerStats } from './types';
|
|
2
|
+
export interface UIManager {
|
|
3
|
+
start(port: number): Promise<void>;
|
|
4
|
+
stop(): Promise<void>;
|
|
5
|
+
updateStatus(status: ServerStatus): void;
|
|
6
|
+
updateConnections(connections: ClientConnection[]): void;
|
|
7
|
+
updateStats(stats: ServerStats): void;
|
|
8
|
+
}
|
|
9
|
+
export declare class WebUIManager implements UIManager {
|
|
10
|
+
private app;
|
|
11
|
+
private server;
|
|
12
|
+
private io;
|
|
13
|
+
private status;
|
|
14
|
+
private connections;
|
|
15
|
+
private stats;
|
|
16
|
+
constructor();
|
|
17
|
+
start(port: number): Promise<void>;
|
|
18
|
+
stop(): Promise<void>;
|
|
19
|
+
updateStatus(status: ServerStatus): void;
|
|
20
|
+
updateConnections(connections: ClientConnection[]): void;
|
|
21
|
+
updateStats(stats: ServerStats): void;
|
|
22
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WebUIManager = void 0;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const http_1 = require("http");
|
|
10
|
+
const socket_io_1 = require("socket.io");
|
|
11
|
+
// UI manager implementation
|
|
12
|
+
class WebUIManager {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.server = null;
|
|
15
|
+
this.io = null;
|
|
16
|
+
this.status = {
|
|
17
|
+
running: false,
|
|
18
|
+
mode: 'stopped',
|
|
19
|
+
connections: 0,
|
|
20
|
+
tools: []
|
|
21
|
+
};
|
|
22
|
+
this.connections = [];
|
|
23
|
+
this.stats = {
|
|
24
|
+
totalRequests: 0,
|
|
25
|
+
successfulRequests: 0,
|
|
26
|
+
failedRequests: 0,
|
|
27
|
+
averageResponseTime: 0,
|
|
28
|
+
toolUsage: {}
|
|
29
|
+
};
|
|
30
|
+
this.app = (0, express_1.default)();
|
|
31
|
+
// Express configuration
|
|
32
|
+
this.app.use(express_1.default.json());
|
|
33
|
+
this.app.use(express_1.default.static(path_1.default.join(__dirname, '../public')));
|
|
34
|
+
// API routes
|
|
35
|
+
this.app.get('/api/status', (req, res) => {
|
|
36
|
+
res.json(this.status);
|
|
37
|
+
});
|
|
38
|
+
this.app.get('/api/connections', (req, res) => {
|
|
39
|
+
res.json(this.connections);
|
|
40
|
+
});
|
|
41
|
+
this.app.get('/api/stats', (req, res) => {
|
|
42
|
+
res.json(this.stats);
|
|
43
|
+
});
|
|
44
|
+
// Main route for the UI - wildcard route fix
|
|
45
|
+
this.app.get('/', (req, res) => {
|
|
46
|
+
res.sendFile(path_1.default.join(__dirname, '../public/index.html'));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async start(port) {
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
this.server = new http_1.Server(this.app);
|
|
52
|
+
this.io = new socket_io_1.Server(this.server);
|
|
53
|
+
// Socket.IO configuration
|
|
54
|
+
this.io.on('connection', (socket) => {
|
|
55
|
+
console.log('New UI connection:', socket.id);
|
|
56
|
+
// Send initial data
|
|
57
|
+
socket.emit('status', this.status);
|
|
58
|
+
socket.emit('connections', this.connections);
|
|
59
|
+
socket.emit('stats', this.stats);
|
|
60
|
+
// Handle user actions
|
|
61
|
+
socket.on('restart-server', () => {
|
|
62
|
+
console.log('Server restart request received');
|
|
63
|
+
// Restart logic to be implemented
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
this.server.listen(port, () => {
|
|
67
|
+
console.log(`Web UI started on port ${port}`);
|
|
68
|
+
resolve();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async stop() {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
if (this.server) {
|
|
75
|
+
this.server.close((err) => {
|
|
76
|
+
if (err) {
|
|
77
|
+
reject(err);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.server = null;
|
|
81
|
+
this.io = null;
|
|
82
|
+
resolve();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
resolve();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
updateStatus(status) {
|
|
92
|
+
this.status = status;
|
|
93
|
+
if (this.io) {
|
|
94
|
+
this.io.emit('status', status);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
updateConnections(connections) {
|
|
98
|
+
this.connections = connections;
|
|
99
|
+
if (this.io) {
|
|
100
|
+
this.io.emit('connections', connections);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
updateStats(stats) {
|
|
104
|
+
this.stats = stats;
|
|
105
|
+
if (this.io) {
|
|
106
|
+
this.io.emit('stats', stats);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.WebUIManager = WebUIManager;
|