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