@esaio/esa-mcp-server 0.2.0 → 0.2.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/README.en.md +10 -6
- package/README.md +16 -12
- package/bin/{index.js → index.mjs} +1 -1
- package/package.json +22 -3
- 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
|
@@ -1,339 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
createExpectedTransformed,
|
|
4
|
-
createMockPost,
|
|
5
|
-
createNullBodyPost,
|
|
6
|
-
createWipPost,
|
|
7
|
-
} from "../../__tests__/fixtures/mock-post.js";
|
|
8
|
-
import type { createEsaClient } from "../../api_client/index.js";
|
|
9
|
-
import { searchPosts } from "../search.js";
|
|
10
|
-
|
|
11
|
-
describe("searchPosts", () => {
|
|
12
|
-
const mockClient = {
|
|
13
|
-
GET: vi.fn(),
|
|
14
|
-
} as unknown as ReturnType<typeof createEsaClient> & {
|
|
15
|
-
GET: ReturnType<typeof vi.fn>;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
vi.clearAllMocks();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("should return posts successfully", async () => {
|
|
23
|
-
const mockPost1 = createMockPost();
|
|
24
|
-
const mockPost2 = createWipPost({
|
|
25
|
-
number: 456,
|
|
26
|
-
name: "wip-post.md",
|
|
27
|
-
full_name: "dev/wip-post.md",
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
mockClient.GET.mockResolvedValue({
|
|
31
|
-
data: {
|
|
32
|
-
posts: [mockPost1, mockPost2],
|
|
33
|
-
prev_page: null,
|
|
34
|
-
next_page: 2,
|
|
35
|
-
total_count: 42,
|
|
36
|
-
page: 1,
|
|
37
|
-
per_page: 20,
|
|
38
|
-
max_per_page: 100,
|
|
39
|
-
},
|
|
40
|
-
error: undefined,
|
|
41
|
-
response: {
|
|
42
|
-
ok: true,
|
|
43
|
-
status: 200,
|
|
44
|
-
} as Response,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
const result = await searchPosts(mockClient, {
|
|
48
|
-
query: "test",
|
|
49
|
-
teamName: "test-team",
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
expect(mockClient.GET).toHaveBeenCalledWith("/v1/teams/{team_name}/posts", {
|
|
53
|
-
params: {
|
|
54
|
-
path: { team_name: "test-team" },
|
|
55
|
-
query: {
|
|
56
|
-
q: "test",
|
|
57
|
-
sort: undefined,
|
|
58
|
-
order: undefined,
|
|
59
|
-
page: undefined,
|
|
60
|
-
per_page: undefined,
|
|
61
|
-
include: undefined,
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const expectedResponse = [
|
|
67
|
-
createExpectedTransformed(mockPost1),
|
|
68
|
-
createExpectedTransformed(mockPost2),
|
|
69
|
-
];
|
|
70
|
-
|
|
71
|
-
expect(result).toEqual({
|
|
72
|
-
content: [
|
|
73
|
-
{
|
|
74
|
-
type: "text",
|
|
75
|
-
text: JSON.stringify(expectedResponse, null, 2),
|
|
76
|
-
},
|
|
77
|
-
],
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("should handle search with all parameters", async () => {
|
|
82
|
-
const mockPost = createMockPost();
|
|
83
|
-
|
|
84
|
-
mockClient.GET.mockResolvedValue({
|
|
85
|
-
data: {
|
|
86
|
-
posts: [mockPost],
|
|
87
|
-
prev_page: 1,
|
|
88
|
-
next_page: 3,
|
|
89
|
-
total_count: 100,
|
|
90
|
-
page: 2,
|
|
91
|
-
per_page: 10,
|
|
92
|
-
max_per_page: 100,
|
|
93
|
-
},
|
|
94
|
-
error: undefined,
|
|
95
|
-
response: {
|
|
96
|
-
ok: true,
|
|
97
|
-
status: 200,
|
|
98
|
-
} as Response,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const result = await searchPosts(mockClient, {
|
|
102
|
-
query: "tag:release",
|
|
103
|
-
teamName: "test-team",
|
|
104
|
-
sort: "updated",
|
|
105
|
-
order: "desc",
|
|
106
|
-
page: 2,
|
|
107
|
-
perPage: 10,
|
|
108
|
-
include: "comments",
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
expect(mockClient.GET).toHaveBeenCalledWith("/v1/teams/{team_name}/posts", {
|
|
112
|
-
params: {
|
|
113
|
-
path: { team_name: "test-team" },
|
|
114
|
-
query: {
|
|
115
|
-
q: "tag:release",
|
|
116
|
-
sort: "updated",
|
|
117
|
-
order: "desc",
|
|
118
|
-
page: 2,
|
|
119
|
-
per_page: 10,
|
|
120
|
-
include: "comments",
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
const expectedResponse = [createExpectedTransformed(mockPost)];
|
|
126
|
-
expect(result).toEqual({
|
|
127
|
-
content: [
|
|
128
|
-
{
|
|
129
|
-
type: "text",
|
|
130
|
-
text: JSON.stringify(expectedResponse, null, 2),
|
|
131
|
-
},
|
|
132
|
-
],
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("should handle empty search results", async () => {
|
|
137
|
-
mockClient.GET.mockResolvedValue({
|
|
138
|
-
data: {
|
|
139
|
-
posts: [],
|
|
140
|
-
prev_page: null,
|
|
141
|
-
next_page: null,
|
|
142
|
-
total_count: 0,
|
|
143
|
-
page: 1,
|
|
144
|
-
per_page: 20,
|
|
145
|
-
max_per_page: 100,
|
|
146
|
-
},
|
|
147
|
-
error: undefined,
|
|
148
|
-
response: {
|
|
149
|
-
ok: true,
|
|
150
|
-
status: 200,
|
|
151
|
-
} as Response,
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
const result = await searchPosts(mockClient, {
|
|
155
|
-
query: "nonexistent",
|
|
156
|
-
teamName: "test-team",
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
expect(result).toEqual({
|
|
160
|
-
content: [
|
|
161
|
-
{
|
|
162
|
-
type: "text",
|
|
163
|
-
text: JSON.stringify([], null, 2),
|
|
164
|
-
},
|
|
165
|
-
],
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it("should handle multiple posts with various states", async () => {
|
|
170
|
-
const shippedPost = createMockPost();
|
|
171
|
-
const wipPost = createWipPost();
|
|
172
|
-
const nullBodyPost = createNullBodyPost();
|
|
173
|
-
|
|
174
|
-
mockClient.GET.mockResolvedValue({
|
|
175
|
-
data: {
|
|
176
|
-
posts: [shippedPost, wipPost, nullBodyPost],
|
|
177
|
-
prev_page: null,
|
|
178
|
-
next_page: null,
|
|
179
|
-
total_count: 3,
|
|
180
|
-
page: 1,
|
|
181
|
-
per_page: 20,
|
|
182
|
-
max_per_page: 100,
|
|
183
|
-
},
|
|
184
|
-
error: undefined,
|
|
185
|
-
response: {
|
|
186
|
-
ok: true,
|
|
187
|
-
status: 200,
|
|
188
|
-
} as Response,
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
const result = await searchPosts(mockClient, {
|
|
192
|
-
query: "",
|
|
193
|
-
teamName: "test-team",
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
const parsedResult = JSON.parse(result.content[0].text as string);
|
|
197
|
-
expect(parsedResult).toHaveLength(3);
|
|
198
|
-
expect(parsedResult[0].wip).toBe("Shipped");
|
|
199
|
-
expect(parsedResult[1].wip).toBe("WIP");
|
|
200
|
-
expect(parsedResult[2].body_md).toBe(undefined);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("should handle API errors", async () => {
|
|
204
|
-
const mockError = {
|
|
205
|
-
error: "unauthorized",
|
|
206
|
-
message: "Invalid access token",
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
mockClient.GET.mockResolvedValue({
|
|
210
|
-
data: undefined,
|
|
211
|
-
error: mockError,
|
|
212
|
-
response: {
|
|
213
|
-
ok: false,
|
|
214
|
-
status: 401,
|
|
215
|
-
} as Response,
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
const result = await searchPosts(mockClient, {
|
|
219
|
-
query: "test",
|
|
220
|
-
teamName: "test-team",
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
expect(result).toEqual({
|
|
224
|
-
content: [
|
|
225
|
-
{
|
|
226
|
-
type: "text",
|
|
227
|
-
text: `Error: ${JSON.stringify(mockError, null, 2)}`,
|
|
228
|
-
},
|
|
229
|
-
],
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it("should handle network errors", async () => {
|
|
234
|
-
const networkError = new Error("Network connection failed");
|
|
235
|
-
|
|
236
|
-
mockClient.GET.mockRejectedValue(networkError);
|
|
237
|
-
|
|
238
|
-
const result = await searchPosts(mockClient, {
|
|
239
|
-
query: "test",
|
|
240
|
-
teamName: "test-team",
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
expect(result).toEqual({
|
|
244
|
-
content: [
|
|
245
|
-
{
|
|
246
|
-
type: "text",
|
|
247
|
-
text: "Error: Network connection failed",
|
|
248
|
-
},
|
|
249
|
-
],
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it("should handle non-Error exceptions", async () => {
|
|
254
|
-
mockClient.GET.mockRejectedValue("Unexpected error");
|
|
255
|
-
|
|
256
|
-
const result = await searchPosts(mockClient, {
|
|
257
|
-
query: "test",
|
|
258
|
-
teamName: "test-team",
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
expect(result).toEqual({
|
|
262
|
-
content: [
|
|
263
|
-
{
|
|
264
|
-
type: "text",
|
|
265
|
-
text: "Error: Unexpected error",
|
|
266
|
-
},
|
|
267
|
-
],
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it("should throw MissingTeamNameError when teamName is empty", async () => {
|
|
272
|
-
const result = await searchPosts(mockClient, {
|
|
273
|
-
query: "test",
|
|
274
|
-
teamName: "",
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
expect(result).toEqual({
|
|
278
|
-
content: [
|
|
279
|
-
{
|
|
280
|
-
type: "text",
|
|
281
|
-
text: "Error: Missing required parameter 'teamName'. Use esa_get_teams to list available teams, then retry with teamName specified.",
|
|
282
|
-
},
|
|
283
|
-
],
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
expect(mockClient.GET).not.toHaveBeenCalled();
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
it("should handle complex search queries", async () => {
|
|
290
|
-
const mockPost = createMockPost();
|
|
291
|
-
|
|
292
|
-
mockClient.GET.mockResolvedValue({
|
|
293
|
-
data: {
|
|
294
|
-
posts: [mockPost],
|
|
295
|
-
prev_page: null,
|
|
296
|
-
next_page: null,
|
|
297
|
-
total_count: 1,
|
|
298
|
-
page: 1,
|
|
299
|
-
per_page: 20,
|
|
300
|
-
max_per_page: 100,
|
|
301
|
-
},
|
|
302
|
-
error: undefined,
|
|
303
|
-
response: {
|
|
304
|
-
ok: true,
|
|
305
|
-
status: 200,
|
|
306
|
-
} as Response,
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
const result = await searchPosts(mockClient, {
|
|
310
|
-
query: "category:dev wip:false keyword:API",
|
|
311
|
-
teamName: "test-team",
|
|
312
|
-
sort: "best_match",
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
expect(mockClient.GET).toHaveBeenCalledWith("/v1/teams/{team_name}/posts", {
|
|
316
|
-
params: {
|
|
317
|
-
path: { team_name: "test-team" },
|
|
318
|
-
query: {
|
|
319
|
-
q: "category:dev wip:false keyword:API",
|
|
320
|
-
sort: "best_match",
|
|
321
|
-
order: undefined,
|
|
322
|
-
page: undefined,
|
|
323
|
-
per_page: undefined,
|
|
324
|
-
include: undefined,
|
|
325
|
-
},
|
|
326
|
-
},
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
const expectedResponse = [createExpectedTransformed(mockPost)];
|
|
330
|
-
expect(result).toEqual({
|
|
331
|
-
content: [
|
|
332
|
-
{
|
|
333
|
-
type: "text",
|
|
334
|
-
text: JSON.stringify(expectedResponse, null, 2),
|
|
335
|
-
},
|
|
336
|
-
],
|
|
337
|
-
});
|
|
338
|
-
});
|
|
339
|
-
});
|