@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
package/README.en.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
4
 
5
- [日本語](README.md) | **English**
5
+ [日本語](https://github.com/esaio/esa-mcp-server#readme) | **English**
6
6
 
7
7
  Official Model Context Protocol (MCP) server for esa.io - STDIO transport version.
8
8
 
@@ -13,6 +13,7 @@ This MCP server provides seamless integration between AI assistants and [esa.io]
13
13
  ## Available Tools
14
14
 
15
15
  ### Team Management
16
+
16
17
  - `esa_get_teams` - Get user's accessible esa teams
17
18
  - `esa_get_team_stats` - Get team statistics (members, posts, comments, stars, watches, active users)
18
19
  - `esa_get_team_tags` - Get all tags used in team posts with count
@@ -26,11 +27,13 @@ This MCP server provides seamless integration between AI assistants and [esa.io]
26
27
  - `esa_update_post` - Update existing post (title, content, tags, category, WIP status)
27
28
 
28
29
  ### Post Actions
30
+
29
31
  - `esa_archive_post` - Archive a post by moving to Archived/ category
30
32
  - `esa_ship_post` - Ship a post (mark as complete by setting wip to false)
31
33
  - `esa_duplicate_post` - Prepare a post for duplication (retrieve name and body_md)
32
34
 
33
35
  ### Comment Management
36
+
34
37
  - `esa_get_comment` - Get a specific comment by ID
35
38
  - `esa_create_comment` - Create a new comment on a post
36
39
  - `esa_update_comment` - Update an existing comment
@@ -39,6 +42,7 @@ This MCP server provides seamless integration between AI assistants and [esa.io]
39
42
  - `esa_get_team_comments` - Get team comments with pagination
40
43
 
41
44
  ### Category Management
45
+
42
46
  - `esa_get_categories` - Get categories and subcategories for a specific path
43
47
  - `esa_get_top_categories` - Get all top-level categories for a team
44
48
  - `esa_get_all_category_paths` - Get all category paths in a team (with post counts, supports filtering)
@@ -46,11 +50,13 @@ This MCP server provides seamless integration between AI assistants and [esa.io]
46
50
  - Filter examples: `prefix: "dev"` for development-related, `match: "api"` for API-related categories
47
51
 
48
52
  ### Attachments
53
+
49
54
  - `esa_get_attachment` - Retrieve an attachment file from esa posts and comments
50
55
  - Returns base64-encoded data for supported images (JPEG, PNG, GIF, WebP) under 30MB
51
56
  - Returns signed URLs (valid for 5 minutes) for other file types, larger images, or when forceSignedUrl is specified
52
57
 
53
58
  ### Help & Documentation
59
+
54
60
  - `esa_get_search_options_help` - Get esa search syntax documentation
55
61
  - `esa_get_markdown_syntax_help` - Get esa Markdown syntax documentation
56
62
  - `esa_search_help` - Search esa documentation for features and terminology
@@ -74,8 +80,8 @@ Add to your MCP client configuration file:
74
80
  ### Required Environment Variables
75
81
 
76
82
  - ESA_ACCESS_TOKEN: Access Token
77
- - Required scopes: `read write` or `admin:comment read:post write:post read:category read:tag read:team read:member`
78
- - [PAT v2](https://docs.esa.io/posts/559) is recommended.
83
+ - Required scopes: `read write` or `admin:comment read:post write:post read:category read:tag read:attachment read:team read:member`
84
+ - [PAT v2](https://docs.esa.io/posts/559) is recommended.
79
85
  - LANG: Language for UI
80
86
 
81
87
  ### Claude Desktop Example
@@ -115,9 +121,7 @@ Add to `claude_desktop_config.json`:
115
121
  "mcpServers": {
116
122
  "esa": {
117
123
  "command": "/Users/your-username/.nodenv/shims/npx",
118
- "args": [
119
- "@esaio/esa-mcp-server"
120
- ],
124
+ "args": ["@esaio/esa-mcp-server"],
121
125
  "env": {
122
126
  "ESA_ACCESS_TOKEN": "your_personal_access_token",
123
127
  "LANG": "en"
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
4
 
5
- **日本語** | [English](README.en.md)
5
+ **日本語** | [English](https://github.com/esaio/esa-mcp-server/blob/main/README.en.md)
6
6
 
7
- esa.io の公式 MCP(Model Context Protocol)サーバー(STDIO Transport版)
7
+ esa.io の公式 MCP(Model Context Protocol)サーバー(STDIO Transport 版)
8
8
 
9
9
  ## 概要
10
10
 
@@ -13,6 +13,7 @@ AI アシスタントと情報共有サービス [esa](https://esa.io) をつな
13
13
  ## 使えるツール
14
14
 
15
15
  ### チーム管理
16
+
16
17
  - `esa_get_teams` - 所属している esa チームの一覧
17
18
  - `esa_get_team_stats` - チームの統計情報(メンバー数、記事数、コメント数など)
18
19
  - `esa_get_team_tags` - チーム内で使われているタグと使用回数
@@ -21,16 +22,18 @@ AI アシスタントと情報共有サービス [esa](https://esa.io) をつな
21
22
  ### 記事管理
22
23
 
23
24
  - `esa_search_posts` - 記事を検索
24
- - `esa_get_post` - 記事IDから記事を取得
25
+ - `esa_get_post` - 記事 ID から記事を取得
25
26
  - `esa_create_post` - 新しい記事を作成(タグ、カテゴリー、WIP ステータス付き)
26
27
  - `esa_update_post` - 記事を更新(タイトル、本文、タグ、カテゴリー、WIP ステータス)
27
28
 
28
29
  ### 記事の操作
30
+
29
31
  - `esa_archive_post` - 記事をアーカイブ(Archived/ カテゴリーへ移動)
30
32
  - `esa_ship_post` - 記事を Ship It!(WIP を外して公開)
31
33
  - `esa_duplicate_post` - 記事を複製するための準備(タイトルと本文を取得)
32
34
 
33
35
  ### コメント管理
36
+
34
37
  - `esa_get_comment` - コメント ID からコメントを取得
35
38
  - `esa_create_comment` - 記事にコメントを追加
36
39
  - `esa_update_comment` - コメントを編集
@@ -39,18 +42,21 @@ AI アシスタントと情報共有サービス [esa](https://esa.io) をつな
39
42
  - `esa_get_team_comments` - チーム全体のコメント一覧(ページング対応)
40
43
 
41
44
  ### カテゴリー管理
45
+
42
46
  - `esa_get_categories` - 指定パス配下のカテゴリー一覧
43
47
  - `esa_get_top_categories` - トップレベルのカテゴリー一覧
44
48
  - `esa_get_all_category_paths` - チーム内の全カテゴリーパス一覧(記事数付き、フィルタリング対応)
45
49
  - カテゴリ構造の把握、整理、統合の計画に最適
46
- - フィルター例: `prefix: "dev"` で開発関連、`match: "api"` でAPI関連を検索
50
+ - フィルター例: `prefix: "dev"` で開発関連、`match: "api"` で API 関連を検索
47
51
 
48
52
  ### 添付ファイル
53
+
49
54
  - `esa_get_attachment` - 記事やコメントの添付ファイルを取得
50
- - サポート形式(JPEG, PNG, GIF, WebP)で30MB以下の画像はbase64エンコードで返却
51
- - その他のファイル、大きな画像、またはforceSignedUrl指定時は署名付きURL(有効期限5分)を返却
55
+ - サポート形式(JPEG, PNG, GIF, WebP)で 30MB 以下の画像は base64 エンコードで返却
56
+ - その他のファイル、大きな画像、または forceSignedUrl 指定時は署名付き URL(有効期限 5 分)を返却
52
57
 
53
58
  ### ヘルプとドキュメント
59
+
54
60
  - `esa_get_search_options_help` - esa の検索構文ヘルプ
55
61
  - `esa_get_markdown_syntax_help` - esa の Markdown 記法ヘルプ
56
62
  - `esa_search_help` - esa のドキュメントから機能や用語を検索
@@ -64,7 +70,7 @@ AI アシスタントと情報共有サービス [esa](https://esa.io) をつな
64
70
  ## プロンプト
65
71
 
66
72
  - `esa_summarize_post` - esa の記事を要約
67
- - 入力: チーム名と記事ID
73
+ - 入力: チーム名と記事 ID
68
74
  - 出力: 記事の構造化された要約
69
75
 
70
76
  ## MCP クライアントの設定
@@ -74,8 +80,8 @@ MCP クライアントの設定ファイルに以下を追加します:
74
80
  ### 用意する環境変数
75
81
 
76
82
  - ESA_ACCESS_TOKEN: アクセストークン
77
- - 必要なスコープ: `read write` または `admin:comment read:post write:post read:category read:tag read:team read:member`
78
- - [PAT v2](https://docs.esa.io/posts/559)を推奨します。
83
+ - 必要なスコープ: `read write` または `admin:comment read:post write:post read:category read:tag read:attachment read:team read:member`
84
+ - [PAT v2](https://docs.esa.io/posts/559)を推奨します。
79
85
  - LANG: UI の言語設定
80
86
 
81
87
  ### Claude Desktop の例
@@ -115,9 +121,7 @@ MCP クライアントの設定ファイルに以下を追加します:
115
121
  "mcpServers": {
116
122
  "esa": {
117
123
  "command": "/Users/your-username/.nodenv/shims/npx",
118
- "args": [
119
- "@esaio/esa-mcp-server"
120
- ],
124
+ "args": ["@esaio/esa-mcp-server"],
121
125
  "env": {
122
126
  "ESA_ACCESS_TOKEN": "your_personal_access_token",
123
127
  "LANG": "ja"
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{McpServer as e,ResourceTemplate as t}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as n}from"@modelcontextprotocol/sdk/server/stdio.js";import r from"i18next";import i from"openapi-fetch";import{z as a}from"zod";var o=`0.1.0`;const s={esa:{apiAccessToken:process.env.ESA_ACCESS_TOKEN||``,apiBaseUrl:process.env.ESA_API_BASE_URL||`https://api.esa.io`},server:{name:`esa-mcp-server`,version:o,description:`Official MCP server for esa.io`}};function c(){if(!s.esa.apiAccessToken)throw Error(`ESA_ACCESS_TOKEN environment variable is required`)}var l={prompts:{summarize_post:{title:`Summarize esa post`,description:`Summarize an esa post in various formats (bullet points, paragraph, or keywords)`,args:{team_name:`The name of the esa team`,post_number:`The post number to summarize`,format:`Summary format (bullet/paragraph/keywords)`}}}},ee={prompts:{summarize_post:{title:`esaの記事の要約`,description:`esa記事を指定した形式で要約します(bullet: 箇条書き, paragraph: 文章, keywords: キーワード)`,args:{team_name:`esaチーム名`,post_number:`要約する記事番号`,format:`要約形式 (bullet/paragraph/keywords)`}}}};async function te(){let e=process.env.LC_ALL?.split(/[-_.]/)[0]||process.env.LC_MESSAGES?.split(/[-_.]/)[0]||process.env.LANG?.split(/[-_.]/)[0]||process.env.LANGUAGE?.split(/[-_.]/)[0]||`en`;return await r.init({lng:e,fallbackLng:`en`,resources:{ja:{translation:ee},en:{translation:l}},interpolation:{escapeValue:!1}}),r}function u(e,t){return r.t(e,t)}function d(e){return{async onRequest({request:t}){return t.headers.set(`Authorization`,`Bearer ${e}`),t},async onResponse({response:e}){let t=e.headers.get(`x-ratelimit-limit`),n=e.headers.get(`x-ratelimit-remaining`);return t&&n&&console.error(`Rate limit: ${n}/${t}`),e},async onError({error:e}){console.error(`Network Error:`,e)}}}const ne=o;function re(e){return{async onRequest({request:t}){return t.headers.set(`User-Agent`,`esa-mcp-server/${e} (official)`),t}}}function ie(e,t=`https://api.esa.io`){let n=i({baseUrl:t});return n.use(re(ne)),n.use(d(e)),n}async function f(e,t,...n){let r;if(`apiAccessToken`in e&&`apiBaseUrl`in e)r=ie(e.apiAccessToken,e.apiBaseUrl);else throw Error(`Unsupported context type. Only StdioContext is currently supported.`);return t(r,...n)}var p=class extends Error{constructor(){super(`Missing required parameter 'teamName'. Use esa_get_teams to list available teams, then retry with teamName specified.`),this.name=`MissingTeamNameError`}};function m(e){return e instanceof Error?`Error: ${e.message}`:typeof e==`number`&&e!==null?`Error: API Response(status: ${e})`:typeof e==`object`&&e?`Error: ${JSON.stringify(e,null,2)}`:`Error: ${String(e)}`}function h(e){return{content:[{type:`text`,text:JSON.stringify(e,null,2)}]}}function g(e,t){return{contents:[{uri:t,mimeType:`application/json`,text:JSON.stringify(e,null,2)}]}}function _(e){return{messages:[{role:`user`,content:{type:`text`,text:e}}]}}function v(e){return{content:[{type:`text`,text:m(e)}]}}function y(e,t){return{contents:[{uri:t,mimeType:`application/json`,text:m(e)}]}}function b(e){return{messages:[{role:`user`,content:{type:`text`,text:m(e)}}]}}const ae=()=>a.object({teamName:a.string().describe(u(`prompts.summarize_post.args.team_name`)),postNumber:a.string().describe(u(`prompts.summarize_post.args.post_number`)),format:a.enum([`bullet`,`paragraph`,`keywords`]).optional().describe(u(`prompts.summarize_post.args.format`))});async function oe(e,t){let{teamName:n,postNumber:r,format:i=`bullet`}=t;if(!n)throw new p;let a=Number.parseInt(r,10);if(Number.isNaN(a)||a<=0)return b(`Post number must be a positive integer`);try{let{data:t,error:r,response:o}=await e.GET(`/v1/teams/{team_name}/posts/{post_number}`,{params:{path:{team_name:n,post_number:a}}});if(r||!o.ok)return b(r||o.status);let s=t,c=`Please summarize the following post:
2
+ import{McpServer as e,ResourceTemplate as t}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as n}from"@modelcontextprotocol/sdk/server/stdio.js";import r from"i18next";import i from"openapi-fetch";import{z as a}from"zod";var o=`0.2.2`;const s={esa:{apiAccessToken:process.env.ESA_ACCESS_TOKEN||``,apiBaseUrl:process.env.ESA_API_BASE_URL||`https://api.esa.io`},server:{name:`esa-mcp-server`,version:o,description:`Official MCP server for esa.io`}};function c(){if(!s.esa.apiAccessToken)throw Error(`ESA_ACCESS_TOKEN environment variable is required`)}var l={prompts:{summarize_post:{title:`Summarize esa post`,description:`Summarize an esa post in various formats (bullet points, paragraph, or keywords)`,args:{team_name:`The name of the esa team`,post_number:`The post number to summarize`,format:`Summary format (bullet/paragraph/keywords)`}}}},ee={prompts:{summarize_post:{title:`esaの記事の要約`,description:`esa記事を指定した形式で要約します(bullet: 箇条書き, paragraph: 文章, keywords: キーワード)`,args:{team_name:`esaチーム名`,post_number:`要約する記事番号`,format:`要約形式 (bullet/paragraph/keywords)`}}}};async function te(){let e=process.env.LC_ALL?.split(/[-_.]/)[0]||process.env.LC_MESSAGES?.split(/[-_.]/)[0]||process.env.LANG?.split(/[-_.]/)[0]||process.env.LANGUAGE?.split(/[-_.]/)[0]||`en`;return await r.init({lng:e,fallbackLng:`en`,resources:{ja:{translation:ee},en:{translation:l}},interpolation:{escapeValue:!1}}),r}function u(e,t){return r.t(e,t)}function d(e){return{async onRequest({request:t}){return t.headers.set(`Authorization`,`Bearer ${e}`),t},async onResponse({response:e}){let t=e.headers.get(`x-ratelimit-limit`),n=e.headers.get(`x-ratelimit-remaining`);return t&&n&&console.error(`Rate limit: ${n}/${t}`),e},async onError({error:e}){console.error(`Network Error:`,e)}}}const ne=o;function re(e){return{async onRequest({request:t}){return t.headers.set(`User-Agent`,`esa-mcp-server/${e} (official)`),t}}}function ie(e,t=`https://api.esa.io`){let n=i({baseUrl:t});return n.use(re(ne)),n.use(d(e)),n}async function f(e,t,...n){let r;if(`apiAccessToken`in e&&`apiBaseUrl`in e)r=ie(e.apiAccessToken,e.apiBaseUrl);else throw Error(`Unsupported context type. Only StdioContext is currently supported.`);return t(r,...n)}var p=class extends Error{constructor(){super(`Missing required parameter 'teamName'. Use esa_get_teams to list available teams, then retry with teamName specified.`),this.name=`MissingTeamNameError`}};function m(e){return e instanceof Error?`Error: ${e.message}`:typeof e==`number`&&e!==null?`Error: API Response(status: ${e})`:typeof e==`object`&&e?`Error: ${JSON.stringify(e,null,2)}`:`Error: ${String(e)}`}function h(e){return{content:[{type:`text`,text:JSON.stringify(e,null,2)}]}}function g(e,t){return{contents:[{uri:t,mimeType:`application/json`,text:JSON.stringify(e,null,2)}]}}function _(e){return{messages:[{role:`user`,content:{type:`text`,text:e}}]}}function v(e){return{content:[{type:`text`,text:m(e)}]}}function y(e,t){return{contents:[{uri:t,mimeType:`application/json`,text:m(e)}]}}function b(e){return{messages:[{role:`user`,content:{type:`text`,text:m(e)}}]}}const ae=()=>a.object({teamName:a.string().describe(u(`prompts.summarize_post.args.team_name`)),postNumber:a.string().describe(u(`prompts.summarize_post.args.post_number`)),format:a.enum([`bullet`,`paragraph`,`keywords`]).optional().describe(u(`prompts.summarize_post.args.format`))});async function oe(e,t){let{teamName:n,postNumber:r,format:i=`bullet`}=t;if(!n)throw new p;let a=Number.parseInt(r,10);if(Number.isNaN(a)||a<=0)return b(`Post number must be a positive integer`);try{let{data:t,error:r,response:o}=await e.GET(`/v1/teams/{team_name}/posts/{post_number}`,{params:{path:{team_name:n,post_number:a}}});if(r||!o.ok)return b(r||o.status);let s=t,c=`Please summarize the following post:
3
3
 
4
4
  `;switch(c+=`Title: ${s.name}\n`,c+=`URL: ${s.url}\n`,c+=`Author: ${s.created_by.name}\n`,c+=`Created: ${s.created_at}\n`,c+=`Updated: ${s.updated_at}\n`,s.category&&(c+=`Category: ${s.category}\n`),s.tags&&s.tags.length>0&&(c+=`Tags: ${s.tags.join(`, `)}\n`),c+=`
5
5
  ---
package/package.json CHANGED
@@ -1,12 +1,30 @@
1
1
  {
2
2
  "name": "@esaio/esa-mcp-server",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Official MCP server for esa.io - STDIO transport version",
5
5
  "main": "bin/index.js",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "esa-mcp-server": "./bin/index.js"
9
9
  },
10
+ "files": [
11
+ "bin",
12
+ "README.md",
13
+ "README.en.md",
14
+ "LICENSE"
15
+ ],
16
+ "publishConfig": {
17
+ "registry": "https://registry.npmjs.org/",
18
+ "access": "public"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/esaio/esa-mcp-server.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/esaio/esa-mcp-server/issues"
26
+ },
27
+ "homepage": "https://github.com/esaio/esa-mcp-server#readme",
10
28
  "scripts": {
11
29
  "build": "tsdown",
12
30
  "test": "vitest",
@@ -15,7 +33,8 @@
15
33
  "lint": "biome check . --error-on-warnings",
16
34
  "lint:fix": "biome check --write .",
17
35
  "type-check": "tsc --noEmit",
18
- "update-esa-api": "openapi-typescript ../esa/api/openapi.yaml --output src/generated/api-types.ts && npm run lint:fix"
36
+ "update-esa-api": "openapi-typescript ../esa/api/openapi.yaml --output src/generated/api-types.ts && npm run lint:fix",
37
+ "prepublishOnly": "npm run build && npm run test:run && npm run lint && npm run type-check"
19
38
  },
20
39
  "keywords": [
21
40
  "mcp",
@@ -38,7 +57,7 @@
38
57
  "@types/node": "^20.19.15",
39
58
  "@vitest/coverage-v8": "^4.0.5",
40
59
  "openapi-typescript": "^7.9.1",
41
- "tsdown": "^0.15.1",
60
+ "tsdown": "^0.16.0",
42
61
  "tsx": "^4.20.5",
43
62
  "typescript": "^5.9.2",
44
63
  "vitest": "^4.0.5"
package/.dockerignore DELETED
@@ -1,36 +0,0 @@
1
- .git
2
- .github
3
-
4
- .env
5
- .env.*
6
- .envrc
7
-
8
- .vscode
9
- .idea
10
- *.swp
11
- *.swo
12
- *~
13
- .DS_Store
14
-
15
- *.log
16
- npm-debug.log*
17
-
18
- tmp
19
- temp
20
- .tmp
21
-
22
- node_modules
23
- .node-version
24
-
25
- README.md
26
- LICENSE
27
-
28
- coverage
29
-
30
- .claude
31
- CLAUDE.md
32
- Dockerfile
33
-
34
- bin
35
- build
36
- dist
@@ -1,23 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: "github-actions"
4
- directory: "/"
5
- schedule:
6
- interval: daily
7
- - package-ecosystem: npm
8
- directory: "/"
9
- schedule:
10
- interval: daily
11
- time: "03:00"
12
- timezone: Asia/Tokyo
13
- open-pull-requests-limit: 10
14
- groups:
15
- vitest:
16
- patterns:
17
- - "vitest"
18
- - "@vitest/*"
19
- ignore:
20
- - dependency-name: "zod"
21
- update-types: ["version-update:semver-major"]
22
- - dependency-name: "@types/node"
23
- update-types: ["version-update:semver-major"]
@@ -1,120 +0,0 @@
1
- name: Docker Build and Push
2
-
3
- on:
4
- push:
5
- tags: [ 'v*' ]
6
-
7
- env:
8
- REGISTRY: ghcr.io
9
- IMAGE_NAME: ${{ github.repository }}
10
-
11
- jobs:
12
- build:
13
- strategy:
14
- fail-fast: true
15
- matrix:
16
- include:
17
- - platform: linux/amd64
18
- runner: ubuntu-latest
19
- - platform: linux/arm64
20
- runner: ubuntu-24.04-arm
21
- runs-on: ${{ matrix.runner }}
22
- permissions:
23
- contents: read
24
- packages: write
25
-
26
- steps:
27
- - name: Prepare
28
- run: |
29
- platform=${{ matrix.platform }}
30
- echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
31
-
32
- - name: Checkout repository
33
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # https://github.com/actions/checkout/tree/v5
34
-
35
- - name: Log in to Container Registry
36
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # https://github.com/docker/login-action/tree/v3
37
- with:
38
- registry: ${{ env.REGISTRY }}
39
- username: ${{ github.actor }}
40
- password: ${{ secrets.GITHUB_TOKEN }}
41
-
42
- - name: Extract metadata
43
- id: meta
44
- uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # https://github.com/docker/metadata-action/tree/v5
45
- with:
46
- images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
47
-
48
- - name: Set up Docker Buildx
49
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # https://github.com/docker/setup-buildx-action/tree/v3
50
-
51
- - name: Build and push by digest
52
- id: build
53
- uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # https://github.com/docker/build-push-action/tree/v6
54
- with:
55
- context: .
56
- platforms: ${{ matrix.platform }}
57
- labels: ${{ steps.meta.outputs.labels }}
58
- outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
59
- cache-from: type=gha
60
- cache-to: type=gha,mode=max
61
-
62
- - name: Export digest
63
- run: |
64
- mkdir -p /tmp/digests
65
- digest="${{ steps.build.outputs.digest }}"
66
- touch "/tmp/digests/${digest#sha256:}"
67
-
68
- - name: Upload digest
69
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # https://github.com/actions/upload-artifact/tree/v4
70
- with:
71
- name: digests-${{ env.PLATFORM_PAIR }}
72
- path: /tmp/digests/*
73
- if-no-files-found: error
74
- retention-days: 1
75
-
76
- merge:
77
- runs-on: ubuntu-latest
78
- needs:
79
- - build
80
- permissions:
81
- contents: read
82
- packages: write
83
-
84
- steps:
85
- - name: Download digests
86
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # https://github.com/actions/download-artifact/tree/v5
87
- with:
88
- path: /tmp/digests
89
- pattern: digests-*
90
- merge-multiple: true
91
-
92
- - name: Log in to Container Registry
93
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # https://github.com/docker/login-action/tree/v3
94
- with:
95
- registry: ${{ env.REGISTRY }}
96
- username: ${{ github.actor }}
97
- password: ${{ secrets.GITHUB_TOKEN }}
98
-
99
- - name: Set up Docker Buildx
100
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # https://github.com/docker/setup-buildx-action/tree/v3
101
-
102
- - name: Extract metadata
103
- id: meta
104
- uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # https://github.com/docker/metadata-action/tree/v5
105
- with:
106
- images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
107
- tags: |
108
- type=semver,pattern={{version}}
109
- type=semver,pattern={{major}}.{{minor}}
110
- type=semver,pattern={{major}}
111
-
112
- - name: Create manifest list and push
113
- working-directory: /tmp/digests
114
- run: |
115
- docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
116
- $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
117
-
118
- - name: Inspect image
119
- run: |
120
- docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
@@ -1,41 +0,0 @@
1
- name: CI
2
- on:
3
- push:
4
- branches:
5
- - main
6
- pull_request:
7
- types: [opened, synchronize, reopened]
8
-
9
- env:
10
- NODE_VERSION: 20.19.4
11
-
12
- jobs:
13
- test:
14
- runs-on: ubuntu-latest
15
- steps:
16
- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # https://github.com/actions/checkout/tree/v5
17
- - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # https://github.com/actions/setup-node/tree/v5
18
- with:
19
- node-version: ${{ env.NODE_VERSION }}
20
- cache: "npm"
21
- - name: Install dependencies
22
- run: npm ci
23
- - name: Run linting
24
- run: |
25
- output=$(npm run lint 2>&1)
26
- echo "$output"
27
- if echo "$output" | grep -q "schema version does not match"; then
28
- echo "::error::Biome schema version mismatch detected. Please run 'npx biome migrate --write' to update biome.json"
29
- exit 1
30
- fi
31
- - name: Run type checking
32
- run: npm run type-check
33
- - name: Run tests
34
- run: npm run test:coverage
35
- - name: Upload coverage reports
36
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # https://github.com/actions/upload-artifact/tree/v4
37
- with:
38
- name: coverage
39
- path: coverage/
40
- - name: Build
41
- run: npm run build
package/CLAUDE.md DELETED
@@ -1,94 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Build and Development Commands
6
-
7
- ```bash
8
- # Install dependencies
9
- npm install
10
-
11
- # Build the project
12
- npm run build
13
-
14
- # Run tests
15
- npm test # Watch mode
16
- npm run test:run # Single run
17
- npm run test:coverage # With coverage report
18
-
19
- # Linting
20
- npm run lint # Check for linting issues
21
- npm run lint:fix # Auto-fix linting and formatting issues
22
-
23
- # Type checking
24
- npm run type-check # Check TypeScript types without building
25
- ```
26
-
27
- ## Architecture Overview
28
-
29
- This is an MCP (Model Context Protocol) server implementation for esa.io, using STDIO transport for communication. The architecture follows a simple structure:
30
-
31
- ### Core Components
32
-
33
- 1. **MCP Server Setup** (`src/index.ts`): Entry point that initializes the MCP server with STDIO transport. Handles transport lifecycle events and graceful error handling.
34
-
35
- 2. **Configuration** (`src/config/index.ts`): Centralized configuration management that reads from environment variables. Required env vars:
36
- - `ESA_ACCESS_TOKEN`: Required for esa.io API authentication
37
- - `ESA_API_BASE_URL`: Optional API base URL (defaults to https://api.esa.io)
38
-
39
- ### Key Technical Details
40
-
41
- - **Module System**: ES modules (type: "module" in package.json)
42
- - **TypeScript**: Strict mode enabled with comprehensive type checking
43
- - **Code Style**: Enforced by Biome (2 spaces, double quotes, semicolons, trailing commas)
44
- - **Node Version**: Requires Node.js >= 20.19.4
45
-
46
- ### MCP Server Pattern
47
-
48
- The server follows the standard MCP pattern:
49
- 1. Validate configuration on startup
50
- 2. Create McpServer instance with name and version
51
- 3. Initialize StdioServerTransport for STDIO communication
52
- 4. Handle transport lifecycle (onclose, onerror)
53
- 5. Connect server to transport
54
-
55
- When extending functionality, new tools and resources should be registered with the server instance before connecting to transport.
56
-
57
- ## Testing Guidelines
58
-
59
- ### Test Writing Principles
60
-
61
- When writing tests, follow these principles for maintainable and readable test code:
62
-
63
- 1. **Import Order Optimization**
64
- - Type imports first (`import type`)
65
- - External dependencies next
66
- - Internal modules last
67
- - Group related imports together
68
-
69
- 2. **Mock Creation Patterns**
70
- - Use `as unknown as` pattern for type casting to keep mocks minimal and focused
71
- - Create helper functions for complex mock objects (e.g., `createMockConfig`, `createMockServer`)
72
- - Extract repetitive mock setup into shared helper functions (e.g., `setupServerMocks`)
73
- - Return mock instances from helper functions for assertion access
74
-
75
- 3. **Mock Documentation**
76
- - Add concise comments explaining the purpose of each mock
77
- - Focus on WHY the mock is needed, not WHAT it does
78
- - Keep comments brief and directly above the mock definition
79
-
80
- 4. **Module Mocking with vi.doMock**
81
- - Use `vi.doMock` for replacing entire modules during import resolution
82
- - Always call `vi.doMock` before the module import
83
- - Use dynamic imports (`await import()`) after setting up module mocks
84
- - Remember that `vi.doMock` is fundamentally different from `vi.fn()` - it intercepts module loading
85
-
86
- 5. **Test Structure**
87
- - Keep each test focused on a single behavior
88
- - Use descriptive test names that explain the expected outcome
89
- - Group related tests with `describe` blocks
90
- - Clean up mocks in `beforeEach`/`afterEach` hooks
91
-
92
- ### Example Test Pattern
93
-
94
- - READ src/__tests__/index.test.ts
package/Dockerfile DELETED
@@ -1,34 +0,0 @@
1
- # syntax=docker/dockerfile:1
2
-
3
- FROM node:alpine AS base
4
-
5
- WORKDIR /app
6
-
7
- FROM base AS deps
8
-
9
- RUN --mount=type=bind,source=package.json,target=package.json \
10
- --mount=type=bind,source=package-lock.json,target=package-lock.json \
11
- --mount=type=cache,target=/root/.npm \
12
- npm ci --only=production
13
-
14
- FROM base AS build
15
-
16
- RUN --mount=type=bind,source=package.json,target=package.json \
17
- --mount=type=bind,source=package-lock.json,target=package-lock.json \
18
- --mount=type=cache,target=/root/.npm \
19
- npm ci
20
-
21
- COPY . .
22
-
23
- RUN npm run build
24
-
25
- FROM base AS production
26
-
27
- ENV NODE_ENV=production
28
-
29
- USER node
30
-
31
- COPY --from=deps /app/node_modules ./node_modules
32
- COPY --from=build /app/bin ./bin
33
-
34
- ENTRYPOINT ["node", "bin/index.js"]
package/biome.json DELETED
@@ -1,57 +0,0 @@
1
- {
2
- "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
3
- "assist": {
4
- "actions": {
5
- "source": {
6
- "organizeImports": "on"
7
- }
8
- }
9
- },
10
- "linter": {
11
- "enabled": true,
12
- "rules": {
13
- "recommended": true,
14
- "complexity": {
15
- "noForEach": "off"
16
- },
17
- "suspicious": {
18
- "noExplicitAny": "error",
19
- "noConsole": "off"
20
- },
21
- "style": {
22
- "useFilenamingConvention": {
23
- "level": "error",
24
- "options": {
25
- "filenameCases": ["kebab-case"]
26
- }
27
- }
28
- }
29
- }
30
- },
31
- "formatter": {
32
- "enabled": true,
33
- "formatWithErrors": false,
34
- "indentStyle": "space",
35
- "indentWidth": 2,
36
- "lineWidth": 80,
37
- "lineEnding": "lf"
38
- },
39
- "javascript": {
40
- "formatter": {
41
- "quoteStyle": "double",
42
- "semicolons": "always",
43
- "trailingCommas": "all"
44
- }
45
- },
46
- "files": {
47
- "includes": [
48
- "**",
49
- "!**/node_modules",
50
- "!**/bin",
51
- "!**/coverage",
52
- "!**/*.config.js",
53
- "!**/*.config.ts",
54
- "!.claude"
55
- ]
56
- }
57
- }