@esaio/esa-mcp-server 0.2.1 → 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 +1 -1
- package/README.md +1 -1
- 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
package/README.en.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
4
|
|
|
5
|
-
[日本語](
|
|
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
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](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
7
|
esa.io の公式 MCP(Model Context Protocol)サーバー(STDIO Transport 版)
|
|
8
8
|
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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
|
package/.github/dependabot.yml
DELETED
|
@@ -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
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import type { components } from "../../generated/api-types.js";
|
|
2
|
-
import { transformComment } from "../../transformers/comment-transformer.js";
|
|
3
|
-
|
|
4
|
-
export function createMockComment(
|
|
5
|
-
overrides?: Partial<components["schemas"]["Comment"]>,
|
|
6
|
-
): components["schemas"]["Comment"] {
|
|
7
|
-
return {
|
|
8
|
-
id: 123,
|
|
9
|
-
post_number: 456,
|
|
10
|
-
body_md: "This is a test comment content",
|
|
11
|
-
body_html: "<p>This is a test comment content</p>",
|
|
12
|
-
created_at: "2024-01-01T00:00:00+09:00",
|
|
13
|
-
updated_at: "2024-01-01T01:00:00+09:00",
|
|
14
|
-
url: "https://test-team.esa.example.com/posts/456#comment-123",
|
|
15
|
-
created_by: {
|
|
16
|
-
name: "Test User",
|
|
17
|
-
screen_name: "testuser",
|
|
18
|
-
icon: "https://example.com/icon.png",
|
|
19
|
-
myself: true,
|
|
20
|
-
},
|
|
21
|
-
stargazers_count: 5,
|
|
22
|
-
star: true,
|
|
23
|
-
stargazers: [
|
|
24
|
-
{
|
|
25
|
-
created_at: "2024-01-01T02:00:00+09:00",
|
|
26
|
-
body: "Great comment!",
|
|
27
|
-
user: {
|
|
28
|
-
name: "Stargazer User",
|
|
29
|
-
screen_name: "stargazer",
|
|
30
|
-
icon: "https://example.com/star-icon.png",
|
|
31
|
-
myself: false,
|
|
32
|
-
},
|
|
33
|
-
name: "Stargazer User",
|
|
34
|
-
screen_name: "stargazer",
|
|
35
|
-
icon: "https://example.com/star-icon.png",
|
|
36
|
-
myself: false,
|
|
37
|
-
email: "stargazer@example.com",
|
|
38
|
-
role: "member" as const,
|
|
39
|
-
posts_count: 10,
|
|
40
|
-
joined_at: "2024-01-01T00:00:00+09:00",
|
|
41
|
-
last_accessed_at: "2024-01-01T02:00:00+09:00",
|
|
42
|
-
},
|
|
43
|
-
],
|
|
44
|
-
...overrides,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function createLongContentComment(
|
|
49
|
-
length: number,
|
|
50
|
-
): components["schemas"]["Comment"] {
|
|
51
|
-
const longContent = "a".repeat(length);
|
|
52
|
-
return createMockComment({
|
|
53
|
-
body_md: longContent,
|
|
54
|
-
body_html: `<p>${longContent}</p>`,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function createNullBodyComment(
|
|
59
|
-
overrides?: Partial<components["schemas"]["Comment"]>,
|
|
60
|
-
): components["schemas"]["Comment"] {
|
|
61
|
-
return createMockComment({
|
|
62
|
-
body_md: "",
|
|
63
|
-
body_html: "",
|
|
64
|
-
...overrides,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function createExpectedTransformedComment(
|
|
69
|
-
comment: components["schemas"]["Comment"],
|
|
70
|
-
) {
|
|
71
|
-
return transformComment(comment);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function createMockCommentList(
|
|
75
|
-
overrides?: Partial<components["schemas"]["CommentList"]>,
|
|
76
|
-
): components["schemas"]["CommentList"] {
|
|
77
|
-
return {
|
|
78
|
-
comments: [
|
|
79
|
-
createMockComment({ id: 1, post_number: 123, body_md: "First comment" }),
|
|
80
|
-
createMockComment({ id: 2, post_number: 124, body_md: "Second comment" }),
|
|
81
|
-
],
|
|
82
|
-
prev_page: null,
|
|
83
|
-
next_page: 2,
|
|
84
|
-
total_count: 10,
|
|
85
|
-
page: 1,
|
|
86
|
-
per_page: 20,
|
|
87
|
-
max_per_page: 100,
|
|
88
|
-
...overrides,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type { components } from "../../generated/api-types.js";
|
|
2
|
-
import {
|
|
3
|
-
type PostTransformOptions,
|
|
4
|
-
transformPost,
|
|
5
|
-
} from "../../transformers/post-transformer.js";
|
|
6
|
-
|
|
7
|
-
export const createMockPost = (
|
|
8
|
-
overrides: Partial<components["schemas"]["Post"]> = {},
|
|
9
|
-
): components["schemas"]["Post"] => ({
|
|
10
|
-
number: 123,
|
|
11
|
-
name: "test-post.md",
|
|
12
|
-
tags: ["tag1", "tag2"],
|
|
13
|
-
category: "dev",
|
|
14
|
-
full_name: "dev/test-post.md #tag1 #tag2",
|
|
15
|
-
wip: false,
|
|
16
|
-
body_md: "# Test Post\n\nThis is a test post content.",
|
|
17
|
-
body_html: "<h1>Test Post</h1><p>This is a test post content.</p>",
|
|
18
|
-
created_at: "2024-01-01T00:00:00+09:00",
|
|
19
|
-
updated_at: "2024-01-02T00:00:00+09:00",
|
|
20
|
-
message: "Update test post",
|
|
21
|
-
url: "https://test-team.esa.example.com/posts/123",
|
|
22
|
-
revision_number: 3,
|
|
23
|
-
created_by: {
|
|
24
|
-
name: "user1",
|
|
25
|
-
screen_name: "user1",
|
|
26
|
-
icon: "https://example.com/icon1.png",
|
|
27
|
-
myself: false,
|
|
28
|
-
},
|
|
29
|
-
updated_by: {
|
|
30
|
-
name: "user2",
|
|
31
|
-
screen_name: "user2",
|
|
32
|
-
icon: "https://example.com/icon2.png",
|
|
33
|
-
myself: false,
|
|
34
|
-
},
|
|
35
|
-
kind: "stock",
|
|
36
|
-
comments_count: 5,
|
|
37
|
-
tasks_count: 3,
|
|
38
|
-
done_tasks_count: 2,
|
|
39
|
-
stargazers_count: 10,
|
|
40
|
-
watchers_count: 8,
|
|
41
|
-
star: true,
|
|
42
|
-
watch: false,
|
|
43
|
-
...overrides,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
export const createWipPost = (
|
|
47
|
-
overrides: Partial<components["schemas"]["Post"]> = {},
|
|
48
|
-
): components["schemas"]["Post"] =>
|
|
49
|
-
createMockPost({
|
|
50
|
-
wip: true,
|
|
51
|
-
number: 456,
|
|
52
|
-
name: "wip-post.md",
|
|
53
|
-
full_name: "docs/wip-post.md #wip",
|
|
54
|
-
url: "https://test-team.esa.example.com/posts/456",
|
|
55
|
-
kind: "flow",
|
|
56
|
-
...overrides,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
export const createLongContentPost = (
|
|
60
|
-
contentLength = 600,
|
|
61
|
-
): components["schemas"]["Post"] =>
|
|
62
|
-
createMockPost({
|
|
63
|
-
body_md: "a".repeat(contentLength),
|
|
64
|
-
body_html: `<p>${"a".repeat(contentLength)}</p>`,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
export const createNullBodyPost = (
|
|
68
|
-
overrides: Partial<components["schemas"]["Post"]> = {},
|
|
69
|
-
): components["schemas"]["Post"] =>
|
|
70
|
-
createMockPost({
|
|
71
|
-
body_md: undefined,
|
|
72
|
-
body_html: undefined,
|
|
73
|
-
...overrides,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
export const createExpectedTransformed = (
|
|
77
|
-
post: components["schemas"]["Post"],
|
|
78
|
-
options: PostTransformOptions = {},
|
|
79
|
-
) => transformPost(post, options);
|