@esaio/esa-mcp-server 0.2.1 → 0.2.3
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/README.en.md +1 -1
- package/README.md +1 -1
- package/bin/index.js +1 -1
- package/package.json +20 -2
- package/.dockerignore +0 -36
- package/.github/dependabot.yml +0 -23
- package/.github/workflows/docker-publish.yml +0 -120
- package/.github/workflows/main.yml +0 -41
- package/CLAUDE.md +0 -94
- package/Dockerfile +0 -34
- package/biome.json +0 -57
- package/src/__tests__/fixtures/mock-comment.ts +0 -90
- package/src/__tests__/fixtures/mock-post.ts +0 -79
- package/src/__tests__/index.test.ts +0 -216
- package/src/api_client/__tests__/index.test.ts +0 -149
- package/src/api_client/__tests__/middleware.test.ts +0 -120
- package/src/api_client/__tests__/with-context.test.ts +0 -98
- package/src/api_client/index.ts +0 -29
- package/src/api_client/middleware.ts +0 -21
- package/src/api_client/with-context.ts +0 -26
- package/src/config/__tests__/index.test.ts +0 -65
- package/src/config/index.ts +0 -20
- package/src/context/mcp-context.ts +0 -1
- package/src/context/stdio-context.ts +0 -6
- package/src/errors/missing-team-name-error.ts +0 -8
- package/src/formatters/__tests__/mcp-response.test.ts +0 -106
- package/src/formatters/mcp-response.ts +0 -95
- package/src/generated/api-types.ts +0 -2968
- package/src/i18n/__tests__/index.test.ts +0 -53
- package/src/i18n/index.ts +0 -39
- package/src/index.ts +0 -47
- package/src/locales/en.json +0 -13
- package/src/locales/ja.json +0 -13
- package/src/prompts/__tests__/index.test.ts +0 -48
- package/src/prompts/__tests__/summarize-post.test.ts +0 -291
- package/src/prompts/index.ts +0 -21
- package/src/prompts/summarize-post.ts +0 -94
- package/src/resources/__tests__/index.test.ts +0 -50
- package/src/resources/__tests__/recent-posts-list.test.ts +0 -92
- package/src/resources/__tests__/recent-posts.test.ts +0 -270
- package/src/resources/index.ts +0 -33
- package/src/resources/recent-posts-list.ts +0 -22
- package/src/resources/recent-posts.ts +0 -45
- package/src/schemas/team-name-schema.ts +0 -19
- package/src/tools/__tests__/attachments.test.ts +0 -460
- package/src/tools/__tests__/categories.test.ts +0 -402
- package/src/tools/__tests__/comments.test.ts +0 -970
- package/src/tools/__tests__/helps.test.ts +0 -222
- package/src/tools/__tests__/index.test.ts +0 -48
- package/src/tools/__tests__/post-actions.test.ts +0 -445
- package/src/tools/__tests__/posts.test.ts +0 -917
- package/src/tools/__tests__/search.test.ts +0 -339
- package/src/tools/__tests__/teams.test.ts +0 -615
- package/src/tools/attachments.ts +0 -167
- package/src/tools/categories.ts +0 -153
- package/src/tools/comments.ts +0 -258
- package/src/tools/helps.ts +0 -50
- package/src/tools/index.ts +0 -351
- package/src/tools/post-actions.ts +0 -132
- package/src/tools/posts.ts +0 -179
- package/src/tools/search.ts +0 -98
- package/src/tools/teams.ts +0 -157
- package/src/transformers/__tests__/category-transformer.test.ts +0 -161
- package/src/transformers/__tests__/comment-transformer.test.ts +0 -129
- package/src/transformers/__tests__/post-name-normalizer.test.ts +0 -53
- package/src/transformers/__tests__/post-transformer.test.ts +0 -70
- package/src/transformers/__tests__/query-normalizer.test.ts +0 -98
- package/src/transformers/__tests__/team-name-normalizer.test.ts +0 -21
- package/src/transformers/category-transformer.ts +0 -36
- package/src/transformers/comment-transformer.ts +0 -34
- package/src/transformers/post-name-normalizer.ts +0 -30
- package/src/transformers/post-transformer.ts +0 -38
- package/src/transformers/query-normalizer.ts +0 -36
- package/src/transformers/team-name-normalizer.ts +0 -7
- package/tsconfig.build.json +0 -4
- package/tsconfig.json +0 -30
- package/tsdown.config.ts +0 -13
- package/vitest.config.ts +0 -24
package/src/tools/index.ts
DELETED
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import type { z } from "zod";
|
|
3
|
-
import { withContext } from "../api_client/with-context.js";
|
|
4
|
-
import type { MCPContext } from "../context/mcp-context.js";
|
|
5
|
-
import { getAttachment, getAttachmentSchema } from "./attachments.js";
|
|
6
|
-
import {
|
|
7
|
-
getAllCategoryPaths,
|
|
8
|
-
getAllCategoryPathsSchema,
|
|
9
|
-
getCategories,
|
|
10
|
-
getCategoriesSchema,
|
|
11
|
-
getTopCategories,
|
|
12
|
-
getTopCategoriesSchema,
|
|
13
|
-
} from "./categories.js";
|
|
14
|
-
import {
|
|
15
|
-
createComment,
|
|
16
|
-
createCommentSchema,
|
|
17
|
-
deleteComment,
|
|
18
|
-
deleteCommentSchema,
|
|
19
|
-
getComment,
|
|
20
|
-
getCommentSchema,
|
|
21
|
-
getPostComments,
|
|
22
|
-
getPostCommentsSchema,
|
|
23
|
-
getTeamComments,
|
|
24
|
-
getTeamCommentsSchema,
|
|
25
|
-
updateComment,
|
|
26
|
-
updateCommentSchema,
|
|
27
|
-
} from "./comments.js";
|
|
28
|
-
import {
|
|
29
|
-
getMarkdownSyntaxHelp,
|
|
30
|
-
getSearchOptionsHelp,
|
|
31
|
-
searchHelp,
|
|
32
|
-
searchHelpSchema,
|
|
33
|
-
} from "./helps.js";
|
|
34
|
-
import {
|
|
35
|
-
archivePost,
|
|
36
|
-
archivePostSchema,
|
|
37
|
-
duplicatePost,
|
|
38
|
-
duplicatePostSchema,
|
|
39
|
-
shipPost,
|
|
40
|
-
shipPostSchema,
|
|
41
|
-
} from "./post-actions.js";
|
|
42
|
-
import {
|
|
43
|
-
createPost,
|
|
44
|
-
createPostSchema,
|
|
45
|
-
getPost,
|
|
46
|
-
getPostSchema,
|
|
47
|
-
updatePost,
|
|
48
|
-
updatePostSchema,
|
|
49
|
-
} from "./posts.js";
|
|
50
|
-
import { searchPosts, searchPostsSchema } from "./search.js";
|
|
51
|
-
import {
|
|
52
|
-
getTeamMembers,
|
|
53
|
-
getTeamMembersSchema,
|
|
54
|
-
getTeamStats,
|
|
55
|
-
getTeamStatsSchema,
|
|
56
|
-
getTeams,
|
|
57
|
-
getTeamsSchema,
|
|
58
|
-
getTeamTags,
|
|
59
|
-
getTeamTagsSchema,
|
|
60
|
-
} from "./teams.js";
|
|
61
|
-
|
|
62
|
-
export function setupTools(server: McpServer, context: MCPContext): void {
|
|
63
|
-
console.error("Setting up MCP tools...");
|
|
64
|
-
|
|
65
|
-
server.registerTool(
|
|
66
|
-
"esa_get_teams",
|
|
67
|
-
{
|
|
68
|
-
title: "Get user's accessible esa teams",
|
|
69
|
-
description: "Retrieves a list of esa teams that the user has access to.",
|
|
70
|
-
inputSchema: getTeamsSchema.shape,
|
|
71
|
-
},
|
|
72
|
-
async (params: z.infer<typeof getTeamsSchema>) =>
|
|
73
|
-
withContext(context, getTeams, params),
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
server.registerTool(
|
|
77
|
-
"esa_get_team_stats",
|
|
78
|
-
{
|
|
79
|
-
title: "Get team statistics",
|
|
80
|
-
description:
|
|
81
|
-
"Retrieves team statistics including member count, posts count (total/WIP/shipped), comments, stars, watches, and daily/weekly/monthly active users",
|
|
82
|
-
inputSchema: getTeamStatsSchema.shape,
|
|
83
|
-
},
|
|
84
|
-
async (params: z.infer<typeof getTeamStatsSchema>) =>
|
|
85
|
-
withContext(context, getTeamStats, params),
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
server.registerTool(
|
|
89
|
-
"esa_get_team_tags",
|
|
90
|
-
{
|
|
91
|
-
title: "Get team tags",
|
|
92
|
-
description:
|
|
93
|
-
"Retrieves all tags used in posts within a team, along with the count of posts for each tag",
|
|
94
|
-
inputSchema: getTeamTagsSchema.shape,
|
|
95
|
-
},
|
|
96
|
-
async (params: z.infer<typeof getTeamTagsSchema>) =>
|
|
97
|
-
withContext(context, getTeamTags, params),
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
server.registerTool(
|
|
101
|
-
"esa_get_team_members",
|
|
102
|
-
{
|
|
103
|
-
title: "Get team members",
|
|
104
|
-
description:
|
|
105
|
-
"Retrieves all members of a team with their roles and profile information",
|
|
106
|
-
inputSchema: getTeamMembersSchema.shape,
|
|
107
|
-
},
|
|
108
|
-
async (params: z.infer<typeof getTeamMembersSchema>) =>
|
|
109
|
-
withContext(context, getTeamMembers, params),
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
server.registerTool(
|
|
113
|
-
"esa_get_post",
|
|
114
|
-
{
|
|
115
|
-
title: "Get a specific esa post",
|
|
116
|
-
description:
|
|
117
|
-
"Retrieves a specific post from an esa team by post number, with optional comments included.",
|
|
118
|
-
inputSchema: getPostSchema.shape,
|
|
119
|
-
},
|
|
120
|
-
async (params: z.infer<typeof getPostSchema>) =>
|
|
121
|
-
withContext(context, getPost, params),
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
server.registerTool(
|
|
125
|
-
"esa_search_posts",
|
|
126
|
-
{
|
|
127
|
-
title: "Search Posts",
|
|
128
|
-
description: "Search for posts in esa.io",
|
|
129
|
-
inputSchema: searchPostsSchema.shape,
|
|
130
|
-
},
|
|
131
|
-
async (params: z.infer<typeof searchPostsSchema>) =>
|
|
132
|
-
withContext(context, searchPosts, params),
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
server.registerTool(
|
|
136
|
-
"esa_create_post",
|
|
137
|
-
{
|
|
138
|
-
title: "Create a new esa post",
|
|
139
|
-
description:
|
|
140
|
-
"Creates a new post in an esa team with optional tags, category, and WIP status.",
|
|
141
|
-
inputSchema: createPostSchema.shape,
|
|
142
|
-
},
|
|
143
|
-
async (params: z.infer<typeof createPostSchema>) =>
|
|
144
|
-
withContext(context, createPost, params),
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
server.registerTool(
|
|
148
|
-
"esa_update_post",
|
|
149
|
-
{
|
|
150
|
-
title: "Update an existing esa post",
|
|
151
|
-
description:
|
|
152
|
-
"Updates an existing post in an esa team by post number. You can update the title, content, tags, category, and WIP status. To ship a post (mark as complete), set wip to false - this is preferred over using esa_ship_post when updating other fields simultaneously.",
|
|
153
|
-
inputSchema: updatePostSchema.shape,
|
|
154
|
-
},
|
|
155
|
-
async (params: z.infer<typeof updatePostSchema>) =>
|
|
156
|
-
withContext(context, updatePost, params),
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
server.registerTool(
|
|
160
|
-
"esa_get_comment",
|
|
161
|
-
{
|
|
162
|
-
title: "Get a specific comment",
|
|
163
|
-
description:
|
|
164
|
-
"Retrieves a specific comment by comment ID, with optional stargazers included.",
|
|
165
|
-
inputSchema: getCommentSchema.shape,
|
|
166
|
-
},
|
|
167
|
-
async (params: z.infer<typeof getCommentSchema>) =>
|
|
168
|
-
withContext(context, getComment, params),
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
server.registerTool(
|
|
172
|
-
"esa_create_comment",
|
|
173
|
-
{
|
|
174
|
-
title: "Create a new comment on a post",
|
|
175
|
-
description: "Creates a new comment on an existing post in an esa team.",
|
|
176
|
-
inputSchema: createCommentSchema.shape,
|
|
177
|
-
},
|
|
178
|
-
async (params: z.infer<typeof createCommentSchema>) =>
|
|
179
|
-
withContext(context, createComment, params),
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
server.registerTool(
|
|
183
|
-
"esa_update_comment",
|
|
184
|
-
{
|
|
185
|
-
title: "Update an existing comment",
|
|
186
|
-
description: "Updates an existing comment in an esa team by comment ID.",
|
|
187
|
-
inputSchema: updateCommentSchema.shape,
|
|
188
|
-
},
|
|
189
|
-
async (params: z.infer<typeof updateCommentSchema>) =>
|
|
190
|
-
withContext(context, updateComment, params),
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
server.registerTool(
|
|
194
|
-
"esa_delete_comment",
|
|
195
|
-
{
|
|
196
|
-
title: "Delete a comment",
|
|
197
|
-
description: "Deletes a comment from an esa team by comment ID.",
|
|
198
|
-
inputSchema: deleteCommentSchema.shape,
|
|
199
|
-
},
|
|
200
|
-
async (params: z.infer<typeof deleteCommentSchema>) =>
|
|
201
|
-
withContext(context, deleteComment, params),
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
server.registerTool(
|
|
205
|
-
"esa_get_post_comments",
|
|
206
|
-
{
|
|
207
|
-
title: "Get comments for a specific post",
|
|
208
|
-
description:
|
|
209
|
-
"Retrieves a list of comments for a specific post with pagination support.",
|
|
210
|
-
inputSchema: getPostCommentsSchema.shape,
|
|
211
|
-
},
|
|
212
|
-
async (params: z.infer<typeof getPostCommentsSchema>) =>
|
|
213
|
-
withContext(context, getPostComments, params),
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
server.registerTool(
|
|
217
|
-
"esa_get_team_comments",
|
|
218
|
-
{
|
|
219
|
-
title: "Get team comments",
|
|
220
|
-
description:
|
|
221
|
-
"Retrieves a list of comments in a team with pagination support.",
|
|
222
|
-
inputSchema: getTeamCommentsSchema.shape,
|
|
223
|
-
},
|
|
224
|
-
async (params: z.infer<typeof getTeamCommentsSchema>) =>
|
|
225
|
-
withContext(context, getTeamComments, params),
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
server.registerTool(
|
|
229
|
-
"esa_get_categories",
|
|
230
|
-
{
|
|
231
|
-
title: "Get categories for a specific path",
|
|
232
|
-
description:
|
|
233
|
-
"Retrieves category information and subcategories for a specific category path, with optional posts and parent categories included",
|
|
234
|
-
inputSchema: getCategoriesSchema.shape,
|
|
235
|
-
},
|
|
236
|
-
async (params: z.infer<typeof getCategoriesSchema>) =>
|
|
237
|
-
withContext(context, getCategories, params),
|
|
238
|
-
);
|
|
239
|
-
|
|
240
|
-
server.registerTool(
|
|
241
|
-
"esa_get_top_categories",
|
|
242
|
-
{
|
|
243
|
-
title: "Get top-level categories",
|
|
244
|
-
description: "Retrieves all top-level categories for a team",
|
|
245
|
-
inputSchema: getTopCategoriesSchema.shape,
|
|
246
|
-
},
|
|
247
|
-
async (params: z.infer<typeof getTopCategoriesSchema>) =>
|
|
248
|
-
withContext(context, getTopCategories, params),
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
server.registerTool(
|
|
252
|
-
"esa_get_all_category_paths",
|
|
253
|
-
{
|
|
254
|
-
title: "Get all category paths for organization and structure review",
|
|
255
|
-
description:
|
|
256
|
-
"Retrieves all category paths in a team at once to understand the overall category structure. Perfect for category organization, cleanup, migration planning, or finding similar categories. Returns a simple list of paths with post counts, sorted in lexicographic order. Supports filtering (prefix/suffix/match/exact_match) to find categories by pattern. No pagination - gets all categories in one call.",
|
|
257
|
-
inputSchema: getAllCategoryPathsSchema.shape,
|
|
258
|
-
},
|
|
259
|
-
async (params: z.infer<typeof getAllCategoryPathsSchema>) =>
|
|
260
|
-
withContext(context, getAllCategoryPaths, params),
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
server.registerTool(
|
|
264
|
-
"esa_archive_post",
|
|
265
|
-
{
|
|
266
|
-
title: "Archive a post",
|
|
267
|
-
description:
|
|
268
|
-
"Archives a post by moving it to the Archived/ category. If the post is in 'dev/docs', it becomes 'Archived/dev/docs'. Posts without category go to 'Archived'.",
|
|
269
|
-
inputSchema: archivePostSchema.shape,
|
|
270
|
-
},
|
|
271
|
-
async (params: z.infer<typeof archivePostSchema>) =>
|
|
272
|
-
withContext(context, archivePost, params),
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
server.registerTool(
|
|
276
|
-
"esa_ship_post",
|
|
277
|
-
{
|
|
278
|
-
title: "Ship a post",
|
|
279
|
-
description:
|
|
280
|
-
"Ships a post by setting wip to false. This marks the post as complete and ready to be published. Use this only when you need to ship without making other changes - if you're also updating title, content, or other fields, use esa_update_post with wip: false instead.",
|
|
281
|
-
inputSchema: shipPostSchema.shape,
|
|
282
|
-
},
|
|
283
|
-
async (params: z.infer<typeof shipPostSchema>) =>
|
|
284
|
-
withContext(context, shipPost, params),
|
|
285
|
-
);
|
|
286
|
-
|
|
287
|
-
server.registerTool(
|
|
288
|
-
"esa_duplicate_post",
|
|
289
|
-
{
|
|
290
|
-
title: "Prepare a post for duplication",
|
|
291
|
-
description:
|
|
292
|
-
"Prepares a post for duplication by retrieving its name and body_md content. Returns the name and body_md that can be used with esa_create_post to create a duplicate of the original post.",
|
|
293
|
-
inputSchema: duplicatePostSchema.shape,
|
|
294
|
-
},
|
|
295
|
-
async (params: z.infer<typeof duplicatePostSchema>) =>
|
|
296
|
-
withContext(context, duplicatePost, params),
|
|
297
|
-
);
|
|
298
|
-
|
|
299
|
-
server.registerTool(
|
|
300
|
-
"esa_get_search_options_help",
|
|
301
|
-
{
|
|
302
|
-
title: "Get esa search options documentation",
|
|
303
|
-
description: `Get esa search syntax documentation when you need to construct complex
|
|
304
|
-
search queries. Use this BEFORE esa_search_posts if you're unsure how to
|
|
305
|
-
translate user's search requirements into proper esa query syntax (e.g., date
|
|
306
|
-
ranges, tag filters, category searches, advanced operators).`,
|
|
307
|
-
inputSchema: {},
|
|
308
|
-
},
|
|
309
|
-
async (params: Record<string, never>) =>
|
|
310
|
-
withContext(context, getSearchOptionsHelp, params),
|
|
311
|
-
);
|
|
312
|
-
|
|
313
|
-
server.registerTool(
|
|
314
|
-
"esa_get_markdown_syntax_help",
|
|
315
|
-
{
|
|
316
|
-
title: "Get esa Markdown syntax documentation",
|
|
317
|
-
description: `Get esa Markdown and formatting documentation when unsure about syntax.
|
|
318
|
-
Use this BEFORE using any tools with *_md parameters (like esa_create_post,
|
|
319
|
-
esa_update_post, esa_create_comment, esa_update_comment) if you need
|
|
320
|
-
clarification on Markdown syntax, esa-specific extensions, or formatting options.`,
|
|
321
|
-
inputSchema: {},
|
|
322
|
-
},
|
|
323
|
-
async (params: Record<string, never>) =>
|
|
324
|
-
withContext(context, getMarkdownSyntaxHelp, params),
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
server.registerTool(
|
|
328
|
-
"esa_search_help",
|
|
329
|
-
{
|
|
330
|
-
title: "Search esa documentation and help",
|
|
331
|
-
description: `Search esa documentation for features, terminology, and specifications.
|
|
332
|
-
Use this when users mention esa-specific terms, ask about esa functionality,
|
|
333
|
-
or request help with esa workflows that you're not familiar with.`,
|
|
334
|
-
inputSchema: searchHelpSchema.shape,
|
|
335
|
-
},
|
|
336
|
-
async (params: z.infer<typeof searchHelpSchema>) =>
|
|
337
|
-
withContext(context, searchHelp, params),
|
|
338
|
-
);
|
|
339
|
-
|
|
340
|
-
server.registerTool(
|
|
341
|
-
"esa_get_attachment",
|
|
342
|
-
{
|
|
343
|
-
title: "Get attachment file from esa",
|
|
344
|
-
description:
|
|
345
|
-
"Retrieves an attachment file from esa with signed URLs. For supported images (JPEG, PNG, GIF, WebP) under 30MB, returns base64-encoded data. For other file types, larger images, or when forceSignedUrl is true, returns signed URLs.",
|
|
346
|
-
inputSchema: getAttachmentSchema.shape,
|
|
347
|
-
},
|
|
348
|
-
async (params: z.infer<typeof getAttachmentSchema>) =>
|
|
349
|
-
withContext(context, getAttachment, params),
|
|
350
|
-
);
|
|
351
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import type { createEsaClient } from "../api_client/index.js";
|
|
3
|
-
import { MissingTeamNameError } from "../errors/missing-team-name-error.js";
|
|
4
|
-
import {
|
|
5
|
-
formatToolError,
|
|
6
|
-
formatToolResponse,
|
|
7
|
-
} from "../formatters/mcp-response.js";
|
|
8
|
-
import type { components } from "../generated/api-types.js";
|
|
9
|
-
import { createSchemaWithTeamName } from "../schemas/team-name-schema.js";
|
|
10
|
-
import { normalizeTeamName } from "../transformers/team-name-normalizer.js";
|
|
11
|
-
import { createPost, updatePost } from "./posts.js";
|
|
12
|
-
|
|
13
|
-
export const archivePostSchema = createSchemaWithTeamName({
|
|
14
|
-
postNumber: z.number().describe("The post number to archive"),
|
|
15
|
-
message: z.string().optional().describe("Archive message for the post"),
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
export async function archivePost(
|
|
19
|
-
client: ReturnType<typeof createEsaClient>,
|
|
20
|
-
args: z.infer<typeof archivePostSchema>,
|
|
21
|
-
) {
|
|
22
|
-
try {
|
|
23
|
-
if (!args.teamName) {
|
|
24
|
-
throw new MissingTeamNameError();
|
|
25
|
-
}
|
|
26
|
-
const { data, error, response } = await client.GET(
|
|
27
|
-
"/v1/teams/{team_name}/posts/{post_number}",
|
|
28
|
-
{
|
|
29
|
-
params: {
|
|
30
|
-
path: { team_name: args.teamName, post_number: args.postNumber },
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
if (error || !response.ok) {
|
|
36
|
-
return formatToolError(error || response.status);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const post: components["schemas"]["Post"] = data;
|
|
40
|
-
const currentCategory = post.category || "";
|
|
41
|
-
|
|
42
|
-
if (currentCategory.startsWith("Archived/")) {
|
|
43
|
-
return formatToolResponse({
|
|
44
|
-
message: "Post is already archived",
|
|
45
|
-
category: currentCategory,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const archivedCategory =
|
|
50
|
-
currentCategory === "" ? "Archived" : `Archived/${currentCategory}`;
|
|
51
|
-
|
|
52
|
-
return await updatePost(client, {
|
|
53
|
-
teamName: args.teamName,
|
|
54
|
-
postNumber: args.postNumber,
|
|
55
|
-
category: archivedCategory,
|
|
56
|
-
message: args.message || "Archive post",
|
|
57
|
-
});
|
|
58
|
-
} catch (error) {
|
|
59
|
-
return formatToolError(error);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export const shipPostSchema = createSchemaWithTeamName({
|
|
64
|
-
postNumber: z.number().describe("The post number to ship"),
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
export async function shipPost(
|
|
68
|
-
client: ReturnType<typeof createEsaClient>,
|
|
69
|
-
args: z.infer<typeof shipPostSchema>,
|
|
70
|
-
) {
|
|
71
|
-
try {
|
|
72
|
-
if (!args.teamName) {
|
|
73
|
-
throw new MissingTeamNameError();
|
|
74
|
-
}
|
|
75
|
-
return await updatePost(client, {
|
|
76
|
-
teamName: args.teamName,
|
|
77
|
-
postNumber: args.postNumber,
|
|
78
|
-
wip: false,
|
|
79
|
-
message: "Ship It!",
|
|
80
|
-
});
|
|
81
|
-
} catch (error) {
|
|
82
|
-
return formatToolError(error);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export const duplicatePostSchema = createSchemaWithTeamName({
|
|
87
|
-
postNumber: z
|
|
88
|
-
.number()
|
|
89
|
-
.describe("The source post number to prepare for duplication"),
|
|
90
|
-
targetTeamName: z
|
|
91
|
-
.string()
|
|
92
|
-
.optional()
|
|
93
|
-
.describe("The name of the esa team")
|
|
94
|
-
.transform((val) => (val ? normalizeTeamName(val) : undefined)),
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
export async function duplicatePost(
|
|
98
|
-
client: ReturnType<typeof createEsaClient>,
|
|
99
|
-
args: z.infer<typeof duplicatePostSchema>,
|
|
100
|
-
) {
|
|
101
|
-
try {
|
|
102
|
-
if (!args.teamName) {
|
|
103
|
-
throw new MissingTeamNameError();
|
|
104
|
-
}
|
|
105
|
-
const { data, error, response } = await client.GET(
|
|
106
|
-
"/v1/teams/{team_name}/posts/new",
|
|
107
|
-
{
|
|
108
|
-
params: {
|
|
109
|
-
path: { team_name: args.teamName },
|
|
110
|
-
query: {
|
|
111
|
-
parent_post_id: args.postNumber,
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
if (error || !response.ok) {
|
|
118
|
-
return formatToolError(error || response.status);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const postNew = data.post as components["schemas"]["PostNew"];
|
|
122
|
-
|
|
123
|
-
return createPost(client, {
|
|
124
|
-
teamName: args.targetTeamName || args.teamName,
|
|
125
|
-
name: postNew.name,
|
|
126
|
-
bodyMd: postNew.body_md,
|
|
127
|
-
wip: true,
|
|
128
|
-
});
|
|
129
|
-
} catch (error) {
|
|
130
|
-
return formatToolError(error);
|
|
131
|
-
}
|
|
132
|
-
}
|
package/src/tools/posts.ts
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import type { createEsaClient } from "../api_client/index.js";
|
|
3
|
-
import { MissingTeamNameError } from "../errors/missing-team-name-error.js";
|
|
4
|
-
import {
|
|
5
|
-
formatToolError,
|
|
6
|
-
formatToolResponse,
|
|
7
|
-
} from "../formatters/mcp-response.js";
|
|
8
|
-
import type { components } from "../generated/api-types.js";
|
|
9
|
-
import { createSchemaWithTeamName } from "../schemas/team-name-schema.js";
|
|
10
|
-
import { normalizePostName } from "../transformers/post-name-normalizer.js";
|
|
11
|
-
import { transformPost } from "../transformers/post-transformer.js";
|
|
12
|
-
|
|
13
|
-
export const getPostSchema = createSchemaWithTeamName({
|
|
14
|
-
postNumber: z.number().describe("The post number to retrieve"),
|
|
15
|
-
include: z
|
|
16
|
-
.enum(["comments"])
|
|
17
|
-
.optional()
|
|
18
|
-
.describe("Specify 'comments' to include comments in the response"),
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
export async function getPost(
|
|
22
|
-
client: ReturnType<typeof createEsaClient>,
|
|
23
|
-
args: z.infer<typeof getPostSchema>,
|
|
24
|
-
) {
|
|
25
|
-
try {
|
|
26
|
-
if (!args.teamName) {
|
|
27
|
-
throw new MissingTeamNameError();
|
|
28
|
-
}
|
|
29
|
-
const { data, error, response } = await client.GET(
|
|
30
|
-
"/v1/teams/{team_name}/posts/{post_number}",
|
|
31
|
-
{
|
|
32
|
-
params: {
|
|
33
|
-
path: { team_name: args.teamName, post_number: args.postNumber },
|
|
34
|
-
query: {
|
|
35
|
-
include: args.include,
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
if (error || !response.ok) {
|
|
42
|
-
return formatToolError(error || response.status);
|
|
43
|
-
}
|
|
44
|
-
const post: components["schemas"]["Post"] = data;
|
|
45
|
-
const transformed = transformPost(post);
|
|
46
|
-
|
|
47
|
-
return formatToolResponse(transformed);
|
|
48
|
-
} catch (error) {
|
|
49
|
-
return formatToolError(error);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export const createPostSchema = createSchemaWithTeamName({
|
|
54
|
-
name: z.string().describe("The post name (title)"),
|
|
55
|
-
bodyMd: z.string().optional().describe("The post content in Markdown format"),
|
|
56
|
-
tags: z.array(z.string()).optional().describe("Tags for the post"),
|
|
57
|
-
category: z.string().optional().describe("Category path (e.g., 'dev/docs')"),
|
|
58
|
-
wip: z
|
|
59
|
-
.boolean()
|
|
60
|
-
.default(true)
|
|
61
|
-
.describe(
|
|
62
|
-
"Whether the post is Work In Progress. Set to false to ship it (mark as complete and ready to be published)",
|
|
63
|
-
),
|
|
64
|
-
message: z.string().optional().describe("Update message for the post"),
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
export async function createPost(
|
|
68
|
-
client: ReturnType<typeof createEsaClient>,
|
|
69
|
-
args: z.infer<typeof createPostSchema>,
|
|
70
|
-
) {
|
|
71
|
-
try {
|
|
72
|
-
if (!args.teamName) {
|
|
73
|
-
throw new MissingTeamNameError();
|
|
74
|
-
}
|
|
75
|
-
const { name, category } = normalizePostName(args.name, args.category);
|
|
76
|
-
|
|
77
|
-
const { data, error, response } = await client.POST(
|
|
78
|
-
"/v1/teams/{team_name}/posts",
|
|
79
|
-
{
|
|
80
|
-
params: {
|
|
81
|
-
path: { team_name: args.teamName },
|
|
82
|
-
},
|
|
83
|
-
body: {
|
|
84
|
-
post: {
|
|
85
|
-
name: name,
|
|
86
|
-
body_md: args.bodyMd,
|
|
87
|
-
tags: args.tags,
|
|
88
|
-
category: category,
|
|
89
|
-
wip: args.wip,
|
|
90
|
-
message: args.message,
|
|
91
|
-
} as components["schemas"]["PostCreateInput"],
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
if (error || !response.ok) {
|
|
97
|
-
return formatToolError(error || response.status);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const post: components["schemas"]["Post"] = data;
|
|
101
|
-
const transformed = transformPost(post);
|
|
102
|
-
|
|
103
|
-
return formatToolResponse(transformed);
|
|
104
|
-
} catch (error) {
|
|
105
|
-
return formatToolError(error);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export const updatePostSchema = createSchemaWithTeamName({
|
|
110
|
-
postNumber: z.number().describe("The post number to update"),
|
|
111
|
-
name: z.string().optional().describe("The post name (title)"),
|
|
112
|
-
bodyMd: z.string().optional().describe("The post content in Markdown format"),
|
|
113
|
-
tags: z.array(z.string()).optional().describe("Tags for the post"),
|
|
114
|
-
category: z.string().optional().describe("Category path (e.g., 'dev/docs')"),
|
|
115
|
-
wip: z
|
|
116
|
-
.boolean()
|
|
117
|
-
.optional()
|
|
118
|
-
.describe(
|
|
119
|
-
"Whether the post is Work In Progress. Set to false to ship it (mark as complete and ready to be published)",
|
|
120
|
-
),
|
|
121
|
-
message: z.string().optional().describe("Update message for the post"),
|
|
122
|
-
originalRevision: z
|
|
123
|
-
.object({
|
|
124
|
-
bodyMd: z.string(),
|
|
125
|
-
number: z.number(),
|
|
126
|
-
user: z.string(),
|
|
127
|
-
})
|
|
128
|
-
.optional()
|
|
129
|
-
.describe("Original revision to check for conflicts"),
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
export async function updatePost(
|
|
133
|
-
client: ReturnType<typeof createEsaClient>,
|
|
134
|
-
args: z.infer<typeof updatePostSchema>,
|
|
135
|
-
) {
|
|
136
|
-
try {
|
|
137
|
-
if (!args.teamName) {
|
|
138
|
-
throw new MissingTeamNameError();
|
|
139
|
-
}
|
|
140
|
-
const { name, category } = normalizePostName(args.name, args.category);
|
|
141
|
-
|
|
142
|
-
const { data, error, response } = await client.PATCH(
|
|
143
|
-
"/v1/teams/{team_name}/posts/{post_number}",
|
|
144
|
-
{
|
|
145
|
-
params: {
|
|
146
|
-
path: { team_name: args.teamName, post_number: args.postNumber },
|
|
147
|
-
},
|
|
148
|
-
body: {
|
|
149
|
-
post: {
|
|
150
|
-
name: name,
|
|
151
|
-
body_md: args.bodyMd,
|
|
152
|
-
tags: args.tags,
|
|
153
|
-
category: category,
|
|
154
|
-
wip: args.wip,
|
|
155
|
-
message: args.message,
|
|
156
|
-
original_revision: args.originalRevision
|
|
157
|
-
? {
|
|
158
|
-
body_md: args.originalRevision.bodyMd,
|
|
159
|
-
number: args.originalRevision.number,
|
|
160
|
-
user: args.originalRevision.user,
|
|
161
|
-
}
|
|
162
|
-
: undefined,
|
|
163
|
-
} as components["schemas"]["PostUpdateInput"],
|
|
164
|
-
},
|
|
165
|
-
},
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
if (error || !response.ok) {
|
|
169
|
-
return formatToolError(error || response.status);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const post: components["schemas"]["Post"] = data;
|
|
173
|
-
const transformed = transformPost(post);
|
|
174
|
-
|
|
175
|
-
return formatToolResponse(transformed);
|
|
176
|
-
} catch (error) {
|
|
177
|
-
return formatToolError(error);
|
|
178
|
-
}
|
|
179
|
-
}
|