@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.
Files changed (78) hide show
  1. package/README.en.md +10 -6
  2. package/README.md +16 -12
  3. package/bin/{index.js → index.mjs} +1 -1
  4. package/package.json +22 -3
  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,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
- }
@@ -1,65 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
- import packageJson from "../../../package.json" with { type: "json" };
3
-
4
- describe("config", () => {
5
- beforeEach(() => {
6
- vi.resetModules();
7
- vi.stubEnv("ESA_ACCESS_TOKEN", undefined);
8
- vi.stubEnv("ESA_API_BASE_URL", undefined);
9
- });
10
-
11
- afterEach(() => {
12
- vi.unstubAllEnvs();
13
- });
14
-
15
- describe("config object", () => {
16
- it("should have correct default values", async () => {
17
- const { config } = await import("../index.js");
18
-
19
- expect(config.esa.apiBaseUrl).toBe("https://api.esa.io");
20
- expect(config.server.name).toBe("esa-mcp-server");
21
- expect(config.server.version).toBe(packageJson.version);
22
- expect(config.server.description).toBe("Official MCP server for esa.io");
23
- });
24
-
25
- it("should use environment variables when set", async () => {
26
- vi.stubEnv("ESA_ACCESS_TOKEN", "test-token");
27
- vi.stubEnv("ESA_API_BASE_URL", "https://api.esa.localhost");
28
-
29
- const { config } = await import("../index.js");
30
-
31
- expect(config.esa.apiAccessToken).toBe("test-token");
32
- expect(config.esa.apiBaseUrl).toBe("https://api.esa.localhost");
33
- });
34
- });
35
-
36
- describe("validateConfig", () => {
37
- it("should throw error when ESA_ACCESS_TOKEN is not set", async () => {
38
- vi.stubEnv("ESA_ACCESS_TOKEN", undefined);
39
-
40
- const { validateConfig } = await import("../index.js");
41
-
42
- expect(() => validateConfig()).toThrow(
43
- "ESA_ACCESS_TOKEN environment variable is required",
44
- );
45
- });
46
-
47
- it("should not throw error when ESA_ACCESS_TOKEN is set", async () => {
48
- vi.stubEnv("ESA_ACCESS_TOKEN", "valid-token");
49
-
50
- const { validateConfig } = await import("../index.js");
51
-
52
- expect(() => validateConfig()).not.toThrow();
53
- });
54
-
55
- it("should throw error when ESA_ACCESS_TOKEN is empty string", async () => {
56
- vi.stubEnv("ESA_ACCESS_TOKEN", "");
57
-
58
- const { validateConfig } = await import("../index.js");
59
-
60
- expect(() => validateConfig()).toThrow(
61
- "ESA_ACCESS_TOKEN environment variable is required",
62
- );
63
- });
64
- });
65
- });
@@ -1,20 +0,0 @@
1
- import packageJson from "../../package.json" with { type: "json" };
2
- import type { StdioContext } from "../context/stdio-context.js";
3
-
4
- export const config = {
5
- esa: {
6
- apiAccessToken: process.env.ESA_ACCESS_TOKEN || "",
7
- apiBaseUrl: process.env.ESA_API_BASE_URL || "https://api.esa.io",
8
- } satisfies StdioContext,
9
- server: {
10
- name: "esa-mcp-server",
11
- version: packageJson.version,
12
- description: "Official MCP server for esa.io",
13
- },
14
- } as const;
15
-
16
- export function validateConfig(): void {
17
- if (!config.esa.apiAccessToken) {
18
- throw new Error("ESA_ACCESS_TOKEN environment variable is required");
19
- }
20
- }
@@ -1 +0,0 @@
1
- export type MCPContext = Record<string, unknown>;
@@ -1,6 +0,0 @@
1
- import type { MCPContext } from "./mcp-context.js";
2
-
3
- export interface StdioContext extends MCPContext {
4
- apiAccessToken: string;
5
- apiBaseUrl: string;
6
- }
@@ -1,8 +0,0 @@
1
- export class MissingTeamNameError extends Error {
2
- constructor() {
3
- super(
4
- "Missing required parameter 'teamName'. Use esa_get_teams to list available teams, then retry with teamName specified.",
5
- );
6
- this.name = "MissingTeamNameError";
7
- }
8
- }
@@ -1,106 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- formatPromptError,
4
- formatPromptResponse,
5
- formatResourceError,
6
- formatResourceResponse,
7
- formatToolError,
8
- formatToolResponse,
9
- } from "../mcp-response.js";
10
-
11
- describe("response formatters", () => {
12
- describe("formatToolResponse", () => {
13
- it("should format data as JSON text content", () => {
14
- const data = { key: "value", count: 42 };
15
- const result = formatToolResponse(data);
16
-
17
- expect(result.content[0].type).toBe("text");
18
- expect(result.content[0].text).toBe(JSON.stringify(data, null, 2));
19
- });
20
- });
21
-
22
- describe("formatResourceResponse", () => {
23
- it("should format data as JSON resource with URI and mimeType", () => {
24
- const data = { resource: "data" };
25
- const uri = "esa://resource/123";
26
- const result = formatResourceResponse(data, uri);
27
-
28
- expect(result.contents[0]).toEqual({
29
- uri,
30
- mimeType: "application/json",
31
- text: JSON.stringify(data, null, 2),
32
- });
33
- });
34
- });
35
-
36
- describe("formatPromptResponse", () => {
37
- it("should format message as user role content", () => {
38
- const message = "This is a prompt message";
39
- const result = formatPromptResponse(message);
40
-
41
- expect(result.messages[0]).toEqual({
42
- role: "user",
43
- content: {
44
- type: "text",
45
- text: message,
46
- },
47
- });
48
- });
49
- });
50
- });
51
-
52
- describe("error formatters", () => {
53
- it("should format Error instances consistently", () => {
54
- const error = new Error("test error");
55
-
56
- expect(formatToolError(error).content[0].text).toBe("Error: test error");
57
- expect(formatResourceError(error, "uri").contents[0].text).toBe(
58
- "Error: test error",
59
- );
60
- expect(formatPromptError(error).messages[0].content.text).toBe(
61
- "Error: test error",
62
- );
63
- });
64
-
65
- it("should format status codes as API responses", () => {
66
- const status = 404;
67
-
68
- expect(formatToolError(status).content[0].text).toBe(
69
- "Error: API Response(status: 404)",
70
- );
71
- expect(formatResourceError(status, "uri").contents[0].text).toBe(
72
- "Error: API Response(status: 404)",
73
- );
74
- });
75
-
76
- it("should format object errors as JSON strings", () => {
77
- const error = { code: "NOT_FOUND", message: "Resource not found" };
78
- const expectedText = `Error: ${JSON.stringify(error, null, 2)}`;
79
-
80
- expect(formatToolError(error).content[0].text).toBe(expectedText);
81
- expect(formatResourceError(error, "uri").contents[0].text).toBe(
82
- expectedText,
83
- );
84
- expect(formatPromptError(error).messages[0].content.text).toBe(
85
- expectedText,
86
- );
87
- });
88
-
89
- it("should maintain correct structure for each error formatter", () => {
90
- const error = new Error("test");
91
- const uri = "test://uri";
92
-
93
- const toolResult = formatToolError(error);
94
- expect(toolResult).toHaveProperty("content");
95
- expect(Array.isArray(toolResult.content)).toBe(true);
96
-
97
- const resourceResult = formatResourceError(error, uri);
98
- expect(resourceResult).toHaveProperty("contents");
99
- expect(resourceResult.contents[0].uri).toBe(uri);
100
- expect(resourceResult.contents[0].mimeType).toBe("application/json");
101
-
102
- const promptResult = formatPromptError(error);
103
- expect(promptResult).toHaveProperty("messages");
104
- expect(promptResult.messages[0].role).toBe("user");
105
- });
106
- });
@@ -1,95 +0,0 @@
1
- import type {
2
- CallToolResult,
3
- GetPromptResult,
4
- ReadResourceResult,
5
- } from "@modelcontextprotocol/sdk/types.js";
6
-
7
- function formatErrorText(error: unknown): string {
8
- return error instanceof Error
9
- ? `Error: ${error.message}`
10
- : typeof error === "number" && error !== null
11
- ? `Error: API Response(status: ${error})`
12
- : typeof error === "object" && error !== null
13
- ? `Error: ${JSON.stringify(error, null, 2)}`
14
- : `Error: ${String(error)}`;
15
- }
16
-
17
- export function formatToolResponse(data: unknown): CallToolResult {
18
- return {
19
- content: [
20
- {
21
- type: "text",
22
- text: JSON.stringify(data, null, 2),
23
- },
24
- ],
25
- };
26
- }
27
-
28
- export function formatResourceResponse(
29
- data: unknown,
30
- uri: string,
31
- ): ReadResourceResult {
32
- return {
33
- contents: [
34
- {
35
- uri,
36
- mimeType: "application/json",
37
- text: JSON.stringify(data, null, 2),
38
- },
39
- ],
40
- };
41
- }
42
-
43
- export function formatPromptResponse(message: string): GetPromptResult {
44
- return {
45
- messages: [
46
- {
47
- role: "user",
48
- content: {
49
- type: "text",
50
- text: message,
51
- },
52
- },
53
- ],
54
- };
55
- }
56
-
57
- export function formatToolError(error: unknown): CallToolResult {
58
- return {
59
- content: [
60
- {
61
- type: "text",
62
- text: formatErrorText(error),
63
- },
64
- ],
65
- };
66
- }
67
-
68
- export function formatResourceError(
69
- error: unknown,
70
- uri: string,
71
- ): ReadResourceResult {
72
- return {
73
- contents: [
74
- {
75
- uri,
76
- mimeType: "application/json",
77
- text: formatErrorText(error),
78
- },
79
- ],
80
- };
81
- }
82
-
83
- export function formatPromptError(error: unknown): GetPromptResult {
84
- return {
85
- messages: [
86
- {
87
- role: "user",
88
- content: {
89
- type: "text",
90
- text: formatErrorText(error),
91
- },
92
- },
93
- ],
94
- };
95
- }