@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.
Files changed (78) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +1 -1
  3. package/bin/index.js +1 -1
  4. package/package.json +20 -2
  5. package/.dockerignore +0 -36
  6. package/.github/dependabot.yml +0 -23
  7. package/.github/workflows/docker-publish.yml +0 -120
  8. package/.github/workflows/main.yml +0 -41
  9. package/CLAUDE.md +0 -94
  10. package/Dockerfile +0 -34
  11. package/biome.json +0 -57
  12. package/src/__tests__/fixtures/mock-comment.ts +0 -90
  13. package/src/__tests__/fixtures/mock-post.ts +0 -79
  14. package/src/__tests__/index.test.ts +0 -216
  15. package/src/api_client/__tests__/index.test.ts +0 -149
  16. package/src/api_client/__tests__/middleware.test.ts +0 -120
  17. package/src/api_client/__tests__/with-context.test.ts +0 -98
  18. package/src/api_client/index.ts +0 -29
  19. package/src/api_client/middleware.ts +0 -21
  20. package/src/api_client/with-context.ts +0 -26
  21. package/src/config/__tests__/index.test.ts +0 -65
  22. package/src/config/index.ts +0 -20
  23. package/src/context/mcp-context.ts +0 -1
  24. package/src/context/stdio-context.ts +0 -6
  25. package/src/errors/missing-team-name-error.ts +0 -8
  26. package/src/formatters/__tests__/mcp-response.test.ts +0 -106
  27. package/src/formatters/mcp-response.ts +0 -95
  28. package/src/generated/api-types.ts +0 -2968
  29. package/src/i18n/__tests__/index.test.ts +0 -53
  30. package/src/i18n/index.ts +0 -39
  31. package/src/index.ts +0 -47
  32. package/src/locales/en.json +0 -13
  33. package/src/locales/ja.json +0 -13
  34. package/src/prompts/__tests__/index.test.ts +0 -48
  35. package/src/prompts/__tests__/summarize-post.test.ts +0 -291
  36. package/src/prompts/index.ts +0 -21
  37. package/src/prompts/summarize-post.ts +0 -94
  38. package/src/resources/__tests__/index.test.ts +0 -50
  39. package/src/resources/__tests__/recent-posts-list.test.ts +0 -92
  40. package/src/resources/__tests__/recent-posts.test.ts +0 -270
  41. package/src/resources/index.ts +0 -33
  42. package/src/resources/recent-posts-list.ts +0 -22
  43. package/src/resources/recent-posts.ts +0 -45
  44. package/src/schemas/team-name-schema.ts +0 -19
  45. package/src/tools/__tests__/attachments.test.ts +0 -460
  46. package/src/tools/__tests__/categories.test.ts +0 -402
  47. package/src/tools/__tests__/comments.test.ts +0 -970
  48. package/src/tools/__tests__/helps.test.ts +0 -222
  49. package/src/tools/__tests__/index.test.ts +0 -48
  50. package/src/tools/__tests__/post-actions.test.ts +0 -445
  51. package/src/tools/__tests__/posts.test.ts +0 -917
  52. package/src/tools/__tests__/search.test.ts +0 -339
  53. package/src/tools/__tests__/teams.test.ts +0 -615
  54. package/src/tools/attachments.ts +0 -167
  55. package/src/tools/categories.ts +0 -153
  56. package/src/tools/comments.ts +0 -258
  57. package/src/tools/helps.ts +0 -50
  58. package/src/tools/index.ts +0 -351
  59. package/src/tools/post-actions.ts +0 -132
  60. package/src/tools/posts.ts +0 -179
  61. package/src/tools/search.ts +0 -98
  62. package/src/tools/teams.ts +0 -157
  63. package/src/transformers/__tests__/category-transformer.test.ts +0 -161
  64. package/src/transformers/__tests__/comment-transformer.test.ts +0 -129
  65. package/src/transformers/__tests__/post-name-normalizer.test.ts +0 -53
  66. package/src/transformers/__tests__/post-transformer.test.ts +0 -70
  67. package/src/transformers/__tests__/query-normalizer.test.ts +0 -98
  68. package/src/transformers/__tests__/team-name-normalizer.test.ts +0 -21
  69. package/src/transformers/category-transformer.ts +0 -36
  70. package/src/transformers/comment-transformer.ts +0 -34
  71. package/src/transformers/post-name-normalizer.ts +0 -30
  72. package/src/transformers/post-transformer.ts +0 -38
  73. package/src/transformers/query-normalizer.ts +0 -36
  74. package/src/transformers/team-name-normalizer.ts +0 -7
  75. package/tsconfig.build.json +0 -4
  76. package/tsconfig.json +0 -30
  77. package/tsdown.config.ts +0 -13
  78. package/vitest.config.ts +0 -24
@@ -1,216 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import type { MockInstance } from "vitest";
4
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
-
6
- describe("MCP Server", () => {
7
- let consoleErrorSpy: MockInstance<typeof console.error>;
8
- let processExitSpy: MockInstance<typeof process.exit>;
9
-
10
- const createMockConfig = (behavior?: { shouldThrow?: boolean }) => ({
11
- config: {
12
- server: {
13
- name: "test-server",
14
- version: "1.0.0",
15
- },
16
- esa: {
17
- apiAccessToken: "test-token",
18
- apiBaseUrl: "https://api.esa.example.com",
19
- },
20
- },
21
- validateConfig: vi.fn().mockImplementation(() => {
22
- if (behavior?.shouldThrow) {
23
- throw new Error("Invalid config");
24
- }
25
- }),
26
- });
27
-
28
- const createMockServer = (behavior?: { shouldFailConnect?: boolean }) => {
29
- const mockConnect = behavior?.shouldFailConnect
30
- ? vi.fn().mockRejectedValue(new Error("Connection failed"))
31
- : vi.fn().mockResolvedValue(undefined);
32
-
33
- return {
34
- connect: mockConnect,
35
- } as unknown as McpServer;
36
- };
37
-
38
- const createMockTransport = () =>
39
- ({
40
- onclose: undefined as (() => void) | undefined,
41
- onerror: undefined as ((error: Error) => void) | undefined,
42
- }) as unknown as StdioServerTransport;
43
-
44
- const setupServerMocks = (
45
- mockConfig: ReturnType<typeof createMockConfig>,
46
- mockServer: ReturnType<typeof createMockServer>,
47
- mockTransport: ReturnType<typeof createMockTransport>,
48
- setupFunctions?: {
49
- setupTools?: ReturnType<typeof vi.fn>;
50
- setupResources?: ReturnType<typeof vi.fn>;
51
- setupPrompts?: ReturnType<typeof vi.fn>;
52
- initI18n?: ReturnType<typeof vi.fn>;
53
- },
54
- ) => {
55
- // Use function keyword for constructor mocks (required in vitest v4)
56
- // biome-ignore lint/complexity/useArrowFunction: Constructor mocks in vitest v4 require function keyword, not arrow functions
57
- const MockMcpServer = vi.fn(function (..._args: unknown[]) {
58
- return mockServer;
59
- });
60
- // biome-ignore lint/complexity/useArrowFunction: Constructor mocks in vitest v4 require function keyword, not arrow functions
61
- const MockStdioServerTransport = vi.fn(function (..._args: unknown[]) {
62
- return mockTransport;
63
- });
64
-
65
- vi.doMock("../config/index.js", () => mockConfig);
66
- vi.doMock("@modelcontextprotocol/sdk/server/mcp.js", () => ({
67
- McpServer: MockMcpServer,
68
- }));
69
- vi.doMock("@modelcontextprotocol/sdk/server/stdio.js", () => ({
70
- StdioServerTransport: MockStdioServerTransport,
71
- }));
72
- vi.doMock("../tools/index.js", () => ({
73
- setupTools: setupFunctions?.setupTools ?? vi.fn(),
74
- }));
75
- vi.doMock("../resources/index.js", () => ({
76
- setupResources: setupFunctions?.setupResources ?? vi.fn(),
77
- }));
78
- vi.doMock("../prompts/index.js", () => ({
79
- setupPrompts: setupFunctions?.setupPrompts ?? vi.fn(),
80
- }));
81
- vi.doMock("../i18n/index.js", () => ({
82
- initI18n:
83
- setupFunctions?.initI18n ?? vi.fn().mockResolvedValue(undefined),
84
- }));
85
-
86
- return { MockMcpServer, MockStdioServerTransport };
87
- };
88
-
89
- beforeEach(() => {
90
- vi.resetModules();
91
- vi.clearAllMocks();
92
-
93
- consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
94
- processExitSpy = vi
95
- .spyOn(process, "exit")
96
- .mockImplementation(() => undefined as never);
97
- });
98
-
99
- afterEach(() => {
100
- consoleErrorSpy.mockRestore();
101
- processExitSpy.mockRestore();
102
- vi.resetModules();
103
- });
104
-
105
- it("should validate config on startup", async () => {
106
- const mockConfig = createMockConfig();
107
- const mockServer = createMockServer();
108
- const mockTransport = createMockTransport();
109
-
110
- setupServerMocks(mockConfig, mockServer, mockTransport);
111
-
112
- await import("../index.js");
113
-
114
- expect(mockConfig.validateConfig).toHaveBeenCalled();
115
- });
116
-
117
- it("should exit with error when config validation fails", async () => {
118
- const mockConfig = createMockConfig({ shouldThrow: true });
119
-
120
- vi.doMock("../config/index.js", () => mockConfig);
121
-
122
- try {
123
- await import("../index.js");
124
- } catch (_error) {
125
- // Expected to throw due to config validation
126
- }
127
-
128
- expect(consoleErrorSpy).toHaveBeenCalledWith(
129
- "Configuration error:",
130
- expect.any(Error),
131
- );
132
- expect(processExitSpy).toHaveBeenCalledWith(1);
133
- });
134
-
135
- it("should create MCP server and connect", async () => {
136
- const mockConfig = createMockConfig();
137
- const mockServer = createMockServer();
138
- const mockTransport = createMockTransport();
139
-
140
- // Track setup function calls
141
- const mockSetupTools = vi.fn();
142
- const mockSetupResources = vi.fn();
143
- const mockSetupPrompts = vi.fn();
144
-
145
- const { MockMcpServer } = setupServerMocks(
146
- mockConfig,
147
- mockServer,
148
- mockTransport,
149
- {
150
- setupTools: mockSetupTools,
151
- setupResources: mockSetupResources,
152
- setupPrompts: mockSetupPrompts,
153
- },
154
- );
155
-
156
- await import("../index.js");
157
-
158
- expect(MockMcpServer).toHaveBeenCalledWith({
159
- name: "test-server",
160
- version: "1.0.0",
161
- });
162
-
163
- const expectedEsaConfig = {
164
- apiAccessToken: "test-token",
165
- apiBaseUrl: "https://api.esa.example.com",
166
- };
167
-
168
- expect(mockSetupTools).toHaveBeenCalledWith(mockServer, expectedEsaConfig);
169
- expect(mockSetupResources).toHaveBeenCalledWith(
170
- mockServer,
171
- expectedEsaConfig,
172
- );
173
- expect(mockSetupPrompts).toHaveBeenCalledWith(
174
- mockServer,
175
- expectedEsaConfig,
176
- );
177
- expect(mockServer.connect).toHaveBeenCalled();
178
- expect(consoleErrorSpy).toHaveBeenCalledWith("test-server v1.0.0 started");
179
- });
180
-
181
- it("should set up transport error handlers", async () => {
182
- const mockConfig = createMockConfig();
183
- const mockServer = createMockServer();
184
- const mockTransport = createMockTransport();
185
-
186
- setupServerMocks(mockConfig, mockServer, mockTransport);
187
-
188
- await import("../index.js");
189
-
190
- expect(mockTransport.onclose).toBeDefined();
191
- expect(mockTransport.onerror).toBeDefined();
192
-
193
- mockTransport.onclose?.();
194
- expect(consoleErrorSpy).toHaveBeenCalledWith("Transport closed");
195
-
196
- const testError = new Error("Test error");
197
- mockTransport.onerror?.(testError);
198
- expect(consoleErrorSpy).toHaveBeenCalledWith("Transport error:", testError);
199
- });
200
-
201
- it("should handle server startup errors", async () => {
202
- const mockConfig = createMockConfig();
203
- const mockServer = createMockServer({ shouldFailConnect: true });
204
- const mockTransport = createMockTransport();
205
-
206
- setupServerMocks(mockConfig, mockServer, mockTransport);
207
-
208
- await import("../index.js");
209
-
210
- expect(consoleErrorSpy).toHaveBeenCalledWith(
211
- "Server startup error:",
212
- expect.any(Error),
213
- );
214
- expect(processExitSpy).toHaveBeenCalledWith(1);
215
- });
216
- });
@@ -1,149 +0,0 @@
1
- import createClient from "openapi-fetch";
2
- import { beforeEach, describe, expect, it, vi } from "vitest";
3
- import packageJson from "../../../package.json" with { type: "json" };
4
- import type { paths } from "../../generated/api-types.js";
5
- import { createEsaClient } from "../index.js";
6
-
7
- // Mock openapi-fetch to test client configuration without actual HTTP calls
8
- vi.mock("openapi-fetch", () => ({
9
- default: vi.fn(),
10
- }));
11
-
12
- describe("createEsaClient", () => {
13
- // Create mock client with use method to verify middleware registration
14
- const createMockClient = () =>
15
- ({
16
- use: vi.fn(),
17
- }) as unknown as ReturnType<typeof createClient<paths>>;
18
-
19
- // Setup test environment with configurable parameters for different test scenarios
20
- const setupTest = (overrides?: {
21
- apiBaseUrl?: string;
22
- apiAccessToken?: string;
23
- }) => {
24
- const config = {
25
- apiBaseUrl: overrides?.apiBaseUrl ?? "https://api.esa.example.com",
26
- apiAccessToken: overrides?.apiAccessToken ?? "test-token",
27
- };
28
-
29
- const mockClient = createMockClient();
30
- const mockCreateClient = vi.mocked(createClient);
31
- mockCreateClient.mockReturnValue(mockClient);
32
-
33
- return { config, mockClient, mockCreateClient };
34
- };
35
-
36
- beforeEach(() => {
37
- vi.clearAllMocks();
38
- });
39
-
40
- it("should create client with correct baseUrl", () => {
41
- const { config, mockCreateClient } = setupTest();
42
-
43
- createEsaClient(config.apiAccessToken, config.apiBaseUrl);
44
-
45
- expect(mockCreateClient).toHaveBeenCalledWith({
46
- baseUrl: config.apiBaseUrl,
47
- });
48
- });
49
-
50
- it("should add User-Agent and Auth middleware to client", () => {
51
- const { config, mockClient } = setupTest();
52
-
53
- createEsaClient(config.apiAccessToken, config.apiBaseUrl);
54
-
55
- expect(mockClient.use).toHaveBeenCalledTimes(2);
56
- expect(mockClient.use).toHaveBeenNthCalledWith(
57
- 1,
58
- expect.objectContaining({
59
- onRequest: expect.any(Function),
60
- }),
61
- );
62
- expect(mockClient.use).toHaveBeenNthCalledWith(
63
- 2,
64
- expect.objectContaining({
65
- onRequest: expect.any(Function),
66
- }),
67
- );
68
- });
69
-
70
- it("should return the configured client", () => {
71
- const { config, mockClient } = setupTest();
72
-
73
- const result = createEsaClient(config.apiAccessToken, config.apiBaseUrl);
74
-
75
- expect(result).toBe(mockClient);
76
- });
77
-
78
- it("should set User-Agent header in middleware", async () => {
79
- const { config, mockClient } = setupTest();
80
-
81
- createEsaClient(config.apiAccessToken, config.apiBaseUrl);
82
-
83
- // Get the User-Agent middleware function (first one registered)
84
- const useMock = mockClient.use as ReturnType<typeof vi.fn>;
85
- const userAgentMiddleware = useMock.mock.calls[0][0];
86
-
87
- // Create mock request to verify User-Agent header is set
88
- const mockRequest = {
89
- headers: {
90
- set: vi.fn(),
91
- },
92
- };
93
-
94
- await userAgentMiddleware.onRequest({ request: mockRequest });
95
-
96
- expect(mockRequest.headers.set).toHaveBeenCalledWith(
97
- "User-Agent",
98
- `esa-mcp-server/${packageJson.version} (official)`,
99
- );
100
- });
101
-
102
- it("should set Authorization header in middleware", async () => {
103
- const { config, mockClient } = setupTest();
104
-
105
- createEsaClient(config.apiAccessToken, config.apiBaseUrl);
106
-
107
- // Get the Auth middleware function (second one registered)
108
- const useMock = mockClient.use as ReturnType<typeof vi.fn>;
109
- const authMiddleware = useMock.mock.calls[1][0];
110
-
111
- // Create mock request to verify Authorization header is set
112
- const mockRequest = {
113
- headers: {
114
- set: vi.fn(),
115
- },
116
- };
117
-
118
- await authMiddleware.onRequest({ request: mockRequest });
119
-
120
- expect(mockRequest.headers.set).toHaveBeenCalledWith(
121
- "Authorization",
122
- `Bearer ${config.apiAccessToken}`,
123
- );
124
- });
125
-
126
- it("should create separate client instances for different tokens", () => {
127
- const { config, mockCreateClient, mockClient } = setupTest();
128
- const token1 = "token-1";
129
- const token2 = "token-2";
130
-
131
- createEsaClient(token1, config.apiBaseUrl);
132
- createEsaClient(token2, config.apiBaseUrl);
133
-
134
- expect(mockCreateClient).toHaveBeenCalledTimes(2);
135
- expect(mockClient.use).toHaveBeenCalledTimes(4); // 2 middlewares × 2 clients
136
- });
137
-
138
- it("should create separate client instances for different base URLs", () => {
139
- const { config, mockCreateClient } = setupTest();
140
- const url1 = "https://api.esa.example.com";
141
- const url2 = "https://api.example.com";
142
-
143
- createEsaClient(config.apiAccessToken, url1);
144
- createEsaClient(config.apiAccessToken, url2);
145
-
146
- expect(mockCreateClient).toHaveBeenCalledWith({ baseUrl: url1 });
147
- expect(mockCreateClient).toHaveBeenCalledWith({ baseUrl: url2 });
148
- });
149
- });
@@ -1,120 +0,0 @@
1
- import type { MockInstance } from "vitest";
2
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
- import { createAuthMiddleware } from "../middleware.js";
4
-
5
- describe("createAuthMiddleware", () => {
6
- // Mock console.error to verify rate limit and error logging without console output
7
- let consoleErrorSpy: MockInstance<typeof console.error>;
8
-
9
- // Create test request for authorization header testing
10
- const createTestRequest = (url = "https://api.esa.example.com/test") =>
11
- new Request(url);
12
-
13
- // Create test response with optional rate limit headers
14
- const createTestResponse = (rateLimitHeaders?: {
15
- limit?: string;
16
- remaining?: string;
17
- }) => {
18
- const headers: Record<string, string> = {};
19
- if (rateLimitHeaders?.limit) {
20
- headers["x-ratelimit-limit"] = rateLimitHeaders.limit;
21
- }
22
- if (rateLimitHeaders?.remaining) {
23
- headers["x-ratelimit-remaining"] = rateLimitHeaders.remaining;
24
- }
25
- return new Response(null, { headers });
26
- };
27
-
28
- // Helper to call middleware methods with minimal required context using as unknown as pattern
29
- const callOnRequest = (
30
- middleware: ReturnType<typeof createAuthMiddleware>,
31
- request: Request,
32
- ) =>
33
- middleware.onRequest?.({
34
- request,
35
- schemaPath: "/test",
36
- params: {},
37
- id: "test-id",
38
- options: { baseUrl: "https://api.esa.example.com" },
39
- } as unknown as Parameters<NonNullable<typeof middleware.onRequest>>[0]);
40
-
41
- const callOnResponse = (
42
- middleware: ReturnType<typeof createAuthMiddleware>,
43
- response: Response,
44
- ) =>
45
- middleware.onResponse?.({
46
- request: createTestRequest(),
47
- response,
48
- schemaPath: "/test",
49
- params: {},
50
- id: "test-id",
51
- options: { baseUrl: "https://api.esa.example.com" },
52
- } as unknown as Parameters<NonNullable<typeof middleware.onResponse>>[0]);
53
-
54
- const callOnError = (
55
- middleware: ReturnType<typeof createAuthMiddleware>,
56
- error: Error,
57
- ) =>
58
- middleware.onError?.({
59
- request: createTestRequest(),
60
- error,
61
- schemaPath: "/test",
62
- params: {},
63
- id: "test-id",
64
- options: { baseUrl: "https://api.esa.example.com" },
65
- } as unknown as Parameters<NonNullable<typeof middleware.onError>>[0]);
66
-
67
- beforeEach(() => {
68
- consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
69
- });
70
-
71
- afterEach(() => {
72
- consoleErrorSpy.mockRestore();
73
- });
74
-
75
- it("should add authorization header", async () => {
76
- const middleware = createAuthMiddleware("test-token");
77
- const request = createTestRequest();
78
-
79
- expect(middleware.onRequest).toBeDefined();
80
- expect(request.headers.get("Authorization")).toBeNull();
81
-
82
- await callOnRequest(middleware, request);
83
-
84
- expect(request.headers.get("Authorization")).toBe("Bearer test-token");
85
- });
86
-
87
- it("should log rate limit info when present", async () => {
88
- const middleware = createAuthMiddleware("test-token");
89
- const response = createTestResponse({ limit: "75", remaining: "50" });
90
-
91
- await callOnResponse(middleware, response);
92
- expect(consoleErrorSpy).toHaveBeenCalledWith("Rate limit: 50/75");
93
- });
94
-
95
- it("should handle response without rate limit headers", async () => {
96
- const middleware = createAuthMiddleware("test-token");
97
- const response = createTestResponse();
98
-
99
- await callOnResponse(middleware, response);
100
- expect(consoleErrorSpy).not.toHaveBeenCalled();
101
- });
102
-
103
- it("should log network errors", async () => {
104
- const middleware = createAuthMiddleware("test-token");
105
- const error = new Error("Network failed");
106
-
107
- await callOnError(middleware, error);
108
- expect(consoleErrorSpy).toHaveBeenCalledWith("Network Error:", error);
109
- });
110
-
111
- it("should return the modified request", async () => {
112
- const middleware = createAuthMiddleware("test-token");
113
- const request = createTestRequest();
114
-
115
- const result = await callOnRequest(middleware, request);
116
-
117
- expect(result).toBe(request);
118
- expect(request.headers.get("Authorization")).toBe("Bearer test-token");
119
- });
120
- });
@@ -1,98 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import { createEsaClient } from "../index.js";
3
- import { withContext } from "../with-context.js";
4
-
5
- // Mock createEsaClient to test context handling without real API client creation
6
- vi.mock("../index.js", () => ({
7
- createEsaClient: vi.fn(),
8
- }));
9
-
10
- describe("withContext", () => {
11
- const mockCreateEsaClient = vi.mocked(createEsaClient);
12
-
13
- // Mock client instance to verify it's passed to handlers correctly
14
- const mockClient: Partial<ReturnType<typeof createEsaClient>> = {};
15
-
16
- // Mock handler function to verify arguments and return values
17
- const mockHandler = vi.fn();
18
-
19
- // Create test context with default values and optional overrides
20
- const createTestContext = (overrides?: {
21
- apiAccessToken?: string;
22
- apiBaseUrl?: string;
23
- }) => ({
24
- apiAccessToken: overrides?.apiAccessToken ?? "test-token",
25
- apiBaseUrl: overrides?.apiBaseUrl ?? "https://api.esa.example.com",
26
- });
27
-
28
- beforeEach(() => {
29
- vi.clearAllMocks();
30
- mockCreateEsaClient.mockReturnValue(
31
- mockClient as ReturnType<typeof createEsaClient>,
32
- );
33
- });
34
-
35
- it("should create client and call handler for StdioContext", async () => {
36
- const context = createTestContext();
37
- const expectedResult = { success: true };
38
- mockHandler.mockResolvedValue(expectedResult);
39
-
40
- const result = await withContext(context, mockHandler, "arg1", "arg2");
41
-
42
- expect(mockCreateEsaClient).toHaveBeenCalledWith(
43
- context.apiAccessToken,
44
- context.apiBaseUrl,
45
- );
46
- expect(mockHandler).toHaveBeenCalledWith(mockClient, "arg1", "arg2");
47
- expect(result).toBe(expectedResult);
48
- });
49
-
50
- it("should work with different apiBaseUrl", async () => {
51
- const context = createTestContext({
52
- apiBaseUrl: "https://api.example.com",
53
- });
54
-
55
- await withContext(context, mockHandler);
56
-
57
- expect(mockCreateEsaClient).toHaveBeenCalledWith(
58
- context.apiAccessToken,
59
- context.apiBaseUrl,
60
- );
61
- });
62
-
63
- it("should throw error for unsupported context type", async () => {
64
- // Create invalid context missing required apiAccessToken property
65
- const invalidContext = {
66
- someOtherProperty: "value",
67
- };
68
-
69
- await expect(
70
- withContext(
71
- invalidContext as unknown as Parameters<typeof withContext>[0],
72
- mockHandler,
73
- ),
74
- ).rejects.toThrow(
75
- "Unsupported context type. Only StdioContext is currently supported.",
76
- );
77
- });
78
-
79
- it("should handle handler errors correctly", async () => {
80
- const context = createTestContext();
81
- const handlerError = new Error("Handler failed");
82
- mockHandler.mockRejectedValue(handlerError);
83
-
84
- await expect(withContext(context, mockHandler)).rejects.toThrow(
85
- "Handler failed",
86
- );
87
- });
88
-
89
- it("should pass through handler arguments correctly", async () => {
90
- const context = createTestContext();
91
- const args = [{ page: 1 }, { per_page: 20 }, "extra"];
92
- mockHandler.mockResolvedValue("success");
93
-
94
- await withContext(context, mockHandler, ...args);
95
-
96
- expect(mockHandler).toHaveBeenCalledWith(mockClient, ...args);
97
- });
98
- });
@@ -1,29 +0,0 @@
1
- import createClient from "openapi-fetch";
2
- import packageJson from "../../package.json" with { type: "json" };
3
- import type { paths } from "../generated/api-types.js";
4
- import { createAuthMiddleware } from "./middleware.js";
5
-
6
- const packageVersion = packageJson.version;
7
-
8
- function createUserAgentMiddleware(version: string) {
9
- return {
10
- async onRequest({ request }: { request: Request }) {
11
- request.headers.set("User-Agent", `esa-mcp-server/${version} (official)`);
12
- return request;
13
- },
14
- };
15
- }
16
-
17
- export function createEsaClient(
18
- apiAccessToken: string,
19
- apiBaseUrl: string = "https://api.esa.io",
20
- ) {
21
- const client = createClient<paths>({
22
- baseUrl: apiBaseUrl,
23
- });
24
-
25
- client.use(createUserAgentMiddleware(packageVersion));
26
- client.use(createAuthMiddleware(apiAccessToken));
27
-
28
- return client;
29
- }
@@ -1,21 +0,0 @@
1
- import type { Middleware } from "openapi-fetch";
2
-
3
- export function createAuthMiddleware(apiAccessToken: string): Middleware {
4
- return {
5
- async onRequest({ request }) {
6
- request.headers.set("Authorization", `Bearer ${apiAccessToken}`);
7
- return request;
8
- },
9
- async onResponse({ response }) {
10
- const rateLimit = response.headers.get("x-ratelimit-limit");
11
- const remaining = response.headers.get("x-ratelimit-remaining");
12
- if (rateLimit && remaining) {
13
- console.error(`Rate limit: ${remaining}/${rateLimit}`);
14
- }
15
- return response;
16
- },
17
- async onError({ error }) {
18
- console.error("Network Error:", error);
19
- },
20
- };
21
- }
@@ -1,26 +0,0 @@
1
- import { createEsaClient } from "../api_client/index.js";
2
- import type { MCPContext } from "../context/mcp-context.js";
3
-
4
- export async function withContext<T extends unknown[], R>(
5
- context: MCPContext,
6
- handler: (
7
- client: ReturnType<typeof createEsaClient>,
8
- ...args: T
9
- ) => Promise<R>,
10
- ...args: T
11
- ): Promise<R> {
12
- let client: ReturnType<typeof createEsaClient>;
13
-
14
- if ("apiAccessToken" in context && "apiBaseUrl" in context) {
15
- client = createEsaClient(
16
- context.apiAccessToken as string,
17
- context.apiBaseUrl as string,
18
- );
19
- } else {
20
- throw new Error(
21
- "Unsupported context type. Only StdioContext is currently supported.",
22
- );
23
- }
24
-
25
- return handler(client, ...args);
26
- }