@selfagency/beans-mcp 0.1.0 → 0.1.1
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/.beans.yml +6 -0
- package/.claude/settings.local.json +18 -0
- package/.editorconfig +13 -0
- package/.github/workflows/release.yml +235 -0
- package/.github/workflows/test.yml +80 -0
- package/.husky/pre-commit +1 -0
- package/.nvmrc +1 -0
- package/.oxfmtrc.json +11 -0
- package/.oxlintrc.json +37 -0
- package/.vscode/settings.json +3 -0
- package/CHANGELOG.md +140 -0
- package/CONTRIBUTING.md +139 -0
- package/LICENSE.txt +21 -0
- package/README.md +4 -4
- package/dist/README.md +307 -0
- package/dist/beans-mcp-server.cjs.map +1 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +31676 -0
- package/dist/index.js.map +1 -0
- package/dist/package.json +43 -0
- package/package.json +64 -27
- package/pnpm-workspace.yaml +2 -0
- package/scripts/release.js +433 -0
- package/scripts/write-dist-package.js +53 -0
- package/src/cli.ts +14 -0
- package/src/index.ts +21 -0
- package/src/internal/graphql.ts +33 -0
- package/src/internal/queryHelpers.ts +157 -0
- package/src/server/BeansMcpServer.ts +600 -0
- package/src/server/backend.ts +358 -0
- package/src/test/BeansMcpServer.test.ts +514 -0
- package/src/test/handlers.unit.test.ts +184 -0
- package/src/test/parseCliArgs.test.ts +69 -0
- package/src/test/protocol.e2e.test.ts +884 -0
- package/src/test/queryHelpers.test.ts +524 -0
- package/src/test/startBeansMcpServer.test.ts +146 -0
- package/src/test/tools-integration.test.ts +912 -0
- package/src/test/utils.test.ts +80 -0
- package/src/types.ts +46 -0
- package/src/utils.ts +20 -0
- package/tsconfig.json +24 -0
- package/tsup.config.ts +42 -0
- package/vitest.config.ts +18 -0
- package/index.js +0 -15350
- /package/{beans-mcp-server.cjs → dist/beans-mcp-server.cjs} +0 -0
- /package/{index.cjs → dist/index.cjs} +0 -0
- /package/{index.d.ts → dist/index.d.ts} +0 -0
package/.beans.yml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(node /Users/daniel/Developer/beans-mcp-server/dist/index.cjs)",
|
|
5
|
+
"Bash(echo \"Exit: $?\")",
|
|
6
|
+
"Bash(pnpm run build)",
|
|
7
|
+
"Bash(node /Users/daniel/Developer/beans-mcp-server/dist/beans-mcp-server.cjs --help)",
|
|
8
|
+
"Bash(pnpm test)",
|
|
9
|
+
"Bash(pnpm test src/test/protocol.e2e.test.ts)",
|
|
10
|
+
"Bash(python3 -c \"import sys,json; p=json.load\\(sys.stdin\\); print\\(p.get\\(''version''\\)\\); print\\(list\\(p.get\\(''exports'',{}\\).keys\\(\\)\\)[:15]\\)\")",
|
|
11
|
+
"Bash(node -e \"const {z} = require\\(''./node_modules/zod''\\); const s = z.string\\(\\); console.log\\(''v4 internal marker:'', !!s._zod\\); console.log\\(''v3 internal marker:'', !!s._def\\);\")",
|
|
12
|
+
"Bash(pnpm run type-check)",
|
|
13
|
+
"Bash(pnpm build)",
|
|
14
|
+
"Bash(pnpm test --coverage)",
|
|
15
|
+
"Bash(git add README.md package.json scripts/write-dist-package.js src/cli.ts src/internal/queryHelpers.ts src/server/BeansMcpServer.ts src/server/backend.ts src/test/BeansMcpServer.test.ts src/test/handlers.unit.test.ts src/test/parseCliArgs.test.ts src/test/queryHelpers.test.ts tsup.config.ts src/test/protocol.e2e.test.ts src/test/startBeansMcpServer.test.ts CHANGELOG.md)"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
package/.editorconfig
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
version:
|
|
9
|
+
description: "Version to release (e.g. 1.2.3)"
|
|
10
|
+
required: true
|
|
11
|
+
type: string
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
actions: read
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: release-${{ github.ref }}
|
|
19
|
+
cancel-in-progress: true
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
detect-tag:
|
|
23
|
+
name: Detect release tag and readiness
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
timeout-minutes: 5
|
|
26
|
+
permissions:
|
|
27
|
+
contents: write
|
|
28
|
+
actions: read
|
|
29
|
+
|
|
30
|
+
outputs:
|
|
31
|
+
release_tag: ${{ steps.tag.outputs.release_tag }}
|
|
32
|
+
ready: ${{ steps.tag.outputs.ready }}
|
|
33
|
+
|
|
34
|
+
steps:
|
|
35
|
+
- name: Resolve tag and verify CI readiness
|
|
36
|
+
id: tag
|
|
37
|
+
uses: actions/github-script@v8
|
|
38
|
+
with:
|
|
39
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
40
|
+
script: |
|
|
41
|
+
const owner = context.repo.owner;
|
|
42
|
+
const repo = context.repo.repo;
|
|
43
|
+
|
|
44
|
+
let tagName;
|
|
45
|
+
|
|
46
|
+
if (context.eventName === 'workflow_dispatch') {
|
|
47
|
+
const versionInput = '${{ inputs.version }}'.trim();
|
|
48
|
+
if (!versionInput) {
|
|
49
|
+
core.setFailed('version input is required for workflow_dispatch.');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
tagName = versionInput.startsWith('v') ? versionInput : `v${versionInput}`;
|
|
53
|
+
|
|
54
|
+
// Resolve HEAD of main
|
|
55
|
+
const main = await github.rest.repos.getBranch({ owner, repo, branch: 'main' });
|
|
56
|
+
const mainSha = main.data.commit.sha;
|
|
57
|
+
|
|
58
|
+
// Create the tag via API
|
|
59
|
+
core.info(`Creating tag ${tagName} at ${mainSha}...`);
|
|
60
|
+
await github.rest.git.createRef({
|
|
61
|
+
owner,
|
|
62
|
+
repo,
|
|
63
|
+
ref: `refs/tags/${tagName}`,
|
|
64
|
+
sha: mainSha,
|
|
65
|
+
});
|
|
66
|
+
core.info(`Tag ${tagName} created.`);
|
|
67
|
+
} else {
|
|
68
|
+
tagName = context.ref.replace('refs/tags/', '');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!tagName || !tagName.startsWith('v')) {
|
|
72
|
+
core.info(`Ref '${context.ref}' is not a release tag; skipping.`);
|
|
73
|
+
core.setOutput('release_tag', '');
|
|
74
|
+
core.setOutput('ready', 'false');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const refData = await github.rest.git.getRef({
|
|
79
|
+
owner,
|
|
80
|
+
repo,
|
|
81
|
+
ref: `tags/${tagName}`,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const refObj = refData.data.object;
|
|
85
|
+
const taggedSha =
|
|
86
|
+
refObj.type === 'tag'
|
|
87
|
+
? (await github.rest.git.getTag({ owner, repo, tag_sha: refObj.sha })).data.object.sha
|
|
88
|
+
: refObj.sha;
|
|
89
|
+
const main = await github.rest.repos.getBranch({ owner, repo, branch: 'main' });
|
|
90
|
+
const mainSha = main.data.commit.sha;
|
|
91
|
+
|
|
92
|
+
if (taggedSha !== mainSha) {
|
|
93
|
+
core.setFailed(
|
|
94
|
+
`Release blocked: tag ${tagName} points to ${taggedSha}, but latest main is ${mainSha}.`
|
|
95
|
+
);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Verify the full test suite passed on this commit.
|
|
100
|
+
const requiredWorkflows = ['Test & Build'];
|
|
101
|
+
|
|
102
|
+
for (const workflowName of requiredWorkflows) {
|
|
103
|
+
const workflowsResp = await github.rest.actions.listRepoWorkflows({
|
|
104
|
+
owner,
|
|
105
|
+
repo,
|
|
106
|
+
per_page: 100,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const workflow = workflowsResp.data.workflows.find(w => w.name === workflowName);
|
|
110
|
+
if (!workflow) {
|
|
111
|
+
core.setFailed(`Release blocked: required workflow '${workflowName}' not found in repository.`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const runsResp = await github.rest.actions.listWorkflowRuns({
|
|
116
|
+
owner,
|
|
117
|
+
repo,
|
|
118
|
+
workflow_id: workflow.id,
|
|
119
|
+
branch: 'main',
|
|
120
|
+
head_sha: mainSha,
|
|
121
|
+
status: 'completed',
|
|
122
|
+
per_page: 10,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const run = runsResp.data.workflow_runs[0];
|
|
126
|
+
if (!run) {
|
|
127
|
+
core.setFailed(
|
|
128
|
+
`Release blocked: required workflow '${workflowName}' has no completed run for ${mainSha}.`
|
|
129
|
+
);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (run.conclusion !== 'success') {
|
|
133
|
+
core.setFailed(
|
|
134
|
+
`Release blocked: workflow '${workflowName}' concluded with '${run.conclusion}' on ${mainSha}.`
|
|
135
|
+
);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
core.info(`Release tag detected: ${tagName} on latest main commit ${mainSha}; all checks passed.`);
|
|
141
|
+
core.setOutput('release_tag', tagName);
|
|
142
|
+
core.setOutput('ready', 'true');
|
|
143
|
+
|
|
144
|
+
release:
|
|
145
|
+
name: Build and Release
|
|
146
|
+
needs: detect-tag
|
|
147
|
+
if: needs.detect-tag.outputs.release_tag != '' && needs.detect-tag.outputs.ready == 'true'
|
|
148
|
+
runs-on: ubuntu-latest
|
|
149
|
+
timeout-minutes: 10
|
|
150
|
+
permissions:
|
|
151
|
+
contents: write
|
|
152
|
+
|
|
153
|
+
steps:
|
|
154
|
+
- name: Checkout repository
|
|
155
|
+
uses: actions/checkout@v4
|
|
156
|
+
with:
|
|
157
|
+
ref: ${{ needs.detect-tag.outputs.release_tag }}
|
|
158
|
+
fetch-depth: 0
|
|
159
|
+
|
|
160
|
+
- name: Generate release notes
|
|
161
|
+
id: changelog_notes
|
|
162
|
+
uses: actions/github-script@v8
|
|
163
|
+
with:
|
|
164
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
165
|
+
script: |
|
|
166
|
+
const owner = context.repo.owner;
|
|
167
|
+
const repo = context.repo.repo;
|
|
168
|
+
const currentTag = '${{ needs.detect-tag.outputs.release_tag }}';
|
|
169
|
+
|
|
170
|
+
const tagsResp = await github.rest.repos.listTags({
|
|
171
|
+
owner,
|
|
172
|
+
repo,
|
|
173
|
+
per_page: 100,
|
|
174
|
+
});
|
|
175
|
+
const tags = tagsResp.data;
|
|
176
|
+
|
|
177
|
+
const index = tags.findIndex(tag => tag.name === currentTag);
|
|
178
|
+
const previousTag =
|
|
179
|
+
index >= 0
|
|
180
|
+
? (tags.slice(index + 1).find(tag => tag.name.startsWith('v'))?.name ?? '')
|
|
181
|
+
: '';
|
|
182
|
+
|
|
183
|
+
const notesResponse = await github.rest.repos.generateReleaseNotes({
|
|
184
|
+
owner,
|
|
185
|
+
repo,
|
|
186
|
+
tag_name: currentTag,
|
|
187
|
+
...(previousTag ? { previous_tag_name: previousTag } : {}),
|
|
188
|
+
target_commitish: 'main',
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
core.info(
|
|
192
|
+
previousTag
|
|
193
|
+
? `Generated changelog notes from ${previousTag} to ${currentTag}.`
|
|
194
|
+
: `Generated changelog notes for ${currentTag} (no previous tag found).`
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
core.setOutput('previous_tag', previousTag);
|
|
198
|
+
core.setOutput('notes', notesResponse.data.body ?? '');
|
|
199
|
+
|
|
200
|
+
- name: Write release notes to file
|
|
201
|
+
run: printf '%s' "$RELEASE_NOTES" > release-notes.md
|
|
202
|
+
env:
|
|
203
|
+
RELEASE_NOTES: ${{ steps.changelog_notes.outputs.notes }}
|
|
204
|
+
|
|
205
|
+
- name: Setup pnpm
|
|
206
|
+
uses: pnpm/action-setup@v4
|
|
207
|
+
with:
|
|
208
|
+
version: 10
|
|
209
|
+
|
|
210
|
+
- name: Setup Node.js
|
|
211
|
+
uses: actions/setup-node@v4
|
|
212
|
+
with:
|
|
213
|
+
node-version: "20"
|
|
214
|
+
cache: "pnpm"
|
|
215
|
+
|
|
216
|
+
- name: Install dependencies
|
|
217
|
+
run: pnpm install --frozen-lockfile
|
|
218
|
+
|
|
219
|
+
- name: Build
|
|
220
|
+
run: pnpm build
|
|
221
|
+
|
|
222
|
+
- name: Pack npm tarball
|
|
223
|
+
run: npm pack ./dist --pack-destination .
|
|
224
|
+
|
|
225
|
+
- name: Create GitHub Release
|
|
226
|
+
uses: softprops/action-gh-release@v2
|
|
227
|
+
with:
|
|
228
|
+
tag_name: ${{ needs.detect-tag.outputs.release_tag }}
|
|
229
|
+
body_path: release-notes.md
|
|
230
|
+
files: "*.tgz"
|
|
231
|
+
draft: false
|
|
232
|
+
prerelease: ${{ contains(needs.detect-tag.outputs.release_tag, '-') }}
|
|
233
|
+
env:
|
|
234
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
235
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
name: Test & Build
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
branches:
|
|
9
|
+
- main
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
node-version: [20.x]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Setup Node.js ${{ matrix.node-version }}
|
|
23
|
+
uses: actions/setup-node@v4
|
|
24
|
+
with:
|
|
25
|
+
node-version: ${{ matrix.node-version }}
|
|
26
|
+
|
|
27
|
+
- name: Setup pnpm
|
|
28
|
+
uses: pnpm/action-setup@v4
|
|
29
|
+
with:
|
|
30
|
+
version: 10
|
|
31
|
+
|
|
32
|
+
- name: Get pnpm store directory
|
|
33
|
+
id: pnpm-store
|
|
34
|
+
shell: bash
|
|
35
|
+
run: |
|
|
36
|
+
# pnpm may return a non-zero code in some environments; fall back to a sane default
|
|
37
|
+
STORE_PATH="$(pnpm store path 2>/dev/null || echo "${HOME}/.pnpm-store")"
|
|
38
|
+
echo "store-path=$STORE_PATH" >> $GITHUB_OUTPUT
|
|
39
|
+
|
|
40
|
+
- name: Cache pnpm store
|
|
41
|
+
uses: actions/cache@v4
|
|
42
|
+
with:
|
|
43
|
+
path: ${{ steps.pnpm-store.outputs.store-path }}
|
|
44
|
+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
45
|
+
restore-keys: ${{ runner.os }}-pnpm-store-
|
|
46
|
+
|
|
47
|
+
- name: Install dependencies
|
|
48
|
+
run: pnpm install --frozen-lockfile
|
|
49
|
+
|
|
50
|
+
- name: Type check
|
|
51
|
+
run: pnpm type-check
|
|
52
|
+
|
|
53
|
+
- name: Lint
|
|
54
|
+
run: pnpm lint
|
|
55
|
+
continue-on-error: true
|
|
56
|
+
|
|
57
|
+
- name: Build
|
|
58
|
+
run: pnpm build
|
|
59
|
+
|
|
60
|
+
- name: Verify build outputs
|
|
61
|
+
run: |
|
|
62
|
+
test -f dist/index.js || exit 1
|
|
63
|
+
test -f dist/index.cjs || exit 1
|
|
64
|
+
test -f dist/beans-mcp-server.cjs || exit 1
|
|
65
|
+
test -f dist/index.d.ts || exit 1
|
|
66
|
+
echo "✓ All build outputs verified"
|
|
67
|
+
|
|
68
|
+
- name: Test
|
|
69
|
+
run: pnpm test:coverage --reporter=junit --outputFile=test-report.junit.xml
|
|
70
|
+
|
|
71
|
+
- name: Upload test results to Codecov
|
|
72
|
+
if: ${{ !cancelled() }}
|
|
73
|
+
uses: codecov/test-results-action@v1
|
|
74
|
+
with:
|
|
75
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
76
|
+
|
|
77
|
+
- name: Upload coverage reports to Codecov
|
|
78
|
+
uses: codecov/codecov-action@v5
|
|
79
|
+
with:
|
|
80
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx lint-staged
|
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
20
|
package/.oxfmtrc.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
|
3
|
+
"ignorePatterns": ["node_modules", "dist"],
|
|
4
|
+
"arrowParens": "avoid",
|
|
5
|
+
"bracketSpacing": true,
|
|
6
|
+
"printWidth": 120,
|
|
7
|
+
"proseWrap": "preserve",
|
|
8
|
+
"semi": true,
|
|
9
|
+
"singleQuote": true,
|
|
10
|
+
"tabWidth": 2
|
|
11
|
+
}
|
package/.oxlintrc.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
"plugins": [
|
|
4
|
+
"unicorn",
|
|
5
|
+
"typescript",
|
|
6
|
+
"oxc",
|
|
7
|
+
"import",
|
|
8
|
+
"promise",
|
|
9
|
+
"node",
|
|
10
|
+
"vitest"
|
|
11
|
+
],
|
|
12
|
+
"categories": {},
|
|
13
|
+
"rules": {},
|
|
14
|
+
"settings": {
|
|
15
|
+
"jsdoc": {
|
|
16
|
+
"ignorePrivate": false,
|
|
17
|
+
"ignoreInternal": false,
|
|
18
|
+
"ignoreReplacesDocs": true,
|
|
19
|
+
"overrideReplacesDocs": true,
|
|
20
|
+
"augmentsExtendsReplacesDocs": false,
|
|
21
|
+
"implementsReplacesDocs": false,
|
|
22
|
+
"exemptDestructuredRootsFromChecks": false,
|
|
23
|
+
"tagNamePreference": {}
|
|
24
|
+
},
|
|
25
|
+
"vitest": {
|
|
26
|
+
"typecheck": false
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"env": {
|
|
30
|
+
"builtin": true
|
|
31
|
+
},
|
|
32
|
+
"globals": {},
|
|
33
|
+
"ignorePatterns": [
|
|
34
|
+
"node_modules",
|
|
35
|
+
"dist"
|
|
36
|
+
]
|
|
37
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.1] - 2026-02-25
|
|
11
|
+
|
|
12
|
+
**Full Changelog**: https://github.com/selfagency/beans-mcp/compare/v0.1.0...v0.1.1
|
|
13
|
+
|
|
14
|
+
_Source: changes from v0.1.0 to v0.1.1._
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [0.1.0] - 2026-02-25
|
|
18
|
+
|
|
19
|
+
**Full Changelog**: https://github.com/selfagency/beans-mcp/commits/v0.1.0
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Initial public release. Extracted and substantially reworked from the
|
|
23
|
+
[selfagency.beans-vscode](https://marketplace.visualstudio.com/items?itemName=selfagency.beans-vscode)
|
|
24
|
+
VS Code extension's embedded MCP server into a standalone, independently
|
|
25
|
+
installable package.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
#### MCP Tools
|
|
30
|
+
|
|
31
|
+
All 14 Beans MCP tools are implemented and registered:
|
|
32
|
+
|
|
33
|
+
- `beans_init` — Initialize the workspace (optional prefix).
|
|
34
|
+
- `beans_list` — List beans with filtering by status, type, tags, and search.
|
|
35
|
+
- `beans_view` — View a single bean by ID.
|
|
36
|
+
- `beans_create` — Create a new bean.
|
|
37
|
+
- `beans_update` / `beans_edit` — Update an existing bean (aliases).
|
|
38
|
+
- `beans_reopen` — Reopen a completed or scrapped bean.
|
|
39
|
+
- `beans_delete` — Delete a draft or scrapped bean.
|
|
40
|
+
- `beans_set_status` — Set a bean's status directly.
|
|
41
|
+
- `beans_query` — Run llm_context, refresh, and workspace-instructions operations.
|
|
42
|
+
- `beans_bean_file` — Read, edit, create, or delete raw bean markdown files.
|
|
43
|
+
- `beans_output` — Read the Beans CLI output log.
|
|
44
|
+
- `beans_open_config` — Return the workspace config file path and content.
|
|
45
|
+
- `beans_graphql_schema` — Return the Beans GraphQL schema.
|
|
46
|
+
|
|
47
|
+
#### Public API
|
|
48
|
+
|
|
49
|
+
- `createBeansMcpServer(opts)` — Programmatic factory for embedding a Beans
|
|
50
|
+
MCP server in other applications; accepts an optional `backend` parameter
|
|
51
|
+
for dependency injection.
|
|
52
|
+
- `startBeansMcpServer(argv)` — CLI entrypoint; launches the server with a
|
|
53
|
+
`StdioServerTransport`.
|
|
54
|
+
- `parseCliArgs(argv)` — Parse and validate CLI arguments; returns a
|
|
55
|
+
`workspaceExplicit` flag so callers can distinguish user-supplied roots from
|
|
56
|
+
the cwd default.
|
|
57
|
+
- `BeansCliBackend` — Concrete backend that shells out to the `beans` CLI.
|
|
58
|
+
- `BackendInterface` — Interface for custom backend implementations.
|
|
59
|
+
- `MutableBackend` — Thin delegation wrapper whose inner backend can be
|
|
60
|
+
hot-swapped after MCP roots discovery without re-registering tools.
|
|
61
|
+
- `resolveWorkspaceFromRoots(server)` — Queries the connected client's
|
|
62
|
+
declared MCP roots and returns the first `file://` path as a local workspace
|
|
63
|
+
path, or `null` if none are declared.
|
|
64
|
+
- `sortBeans`, `isPathWithinRoot`, `makeTextAndStructured` — Utility helpers.
|
|
65
|
+
|
|
66
|
+
#### Workspace Resolution
|
|
67
|
+
|
|
68
|
+
The server resolves its workspace in priority order:
|
|
69
|
+
|
|
70
|
+
1. `--workspace-root` / positional CLI argument (explicit)
|
|
71
|
+
2. MCP roots declared by the connected client (`roots/list`)
|
|
72
|
+
3. `process.cwd()` (fallback)
|
|
73
|
+
|
|
74
|
+
This enables using the server without CLI arguments: AI clients that declare
|
|
75
|
+
MCP roots (e.g. Cursor, Claude Desktop) automatically provide the workspace
|
|
76
|
+
path after connecting.
|
|
77
|
+
|
|
78
|
+
#### CLI
|
|
79
|
+
|
|
80
|
+
- `beans-mcp` binary accepts:
|
|
81
|
+
- Positional or `--workspace-root` for the workspace path.
|
|
82
|
+
- `--cli-path` — path to the `beans` executable (default: `beans`).
|
|
83
|
+
- `--port` — MCP server port (default: 39173).
|
|
84
|
+
- `--log-dir` — log directory (defaults to workspace root).
|
|
85
|
+
- `-h` / `--help` — print usage and exit.
|
|
86
|
+
|
|
87
|
+
#### Build
|
|
88
|
+
|
|
89
|
+
- Multi-config `tsup.config.ts` produces three outputs:
|
|
90
|
+
- ESM library (`dist/index.js` + `dist/index.d.ts`)
|
|
91
|
+
- CJS library (`dist/index.cjs`)
|
|
92
|
+
- CJS CLI binary (`dist/beans-mcp-server.cjs`) with `#!/usr/bin/env node` shebang
|
|
93
|
+
- All CJS configs use `target: 'node18'`, `splitting: false`, `cjsInterop: true`.
|
|
94
|
+
- `postbuild` script writes a trimmed `dist/package.json` with correct `bin`,
|
|
95
|
+
`exports`, `main`, `module`, and `types` fields.
|
|
96
|
+
|
|
97
|
+
#### Tests
|
|
98
|
+
|
|
99
|
+
- **Protocol E2E tests** (`src/test/protocol.e2e.test.ts`) — 52 tests using
|
|
100
|
+
`InMemoryTransport` + MCP `Client` to exercise the full JSON-RPC wire format,
|
|
101
|
+
Zod input validation, backend error surfacing as `{ isError: true }` tool
|
|
102
|
+
results, and the MCP roots protocol.
|
|
103
|
+
- **`startBeansMcpServer` integration tests** (`src/test/startBeansMcpServer.test.ts`)
|
|
104
|
+
— mocked dynamic imports for `BeansCliBackend` and `StdioServerTransport`.
|
|
105
|
+
- Handler unit tests — exported handler factories tested in isolation.
|
|
106
|
+
- `MutableBackend` unit tests — delegation and `setInner` swap behaviour.
|
|
107
|
+
- `resolveWorkspaceFromRoots` unit tests — all branches (found, skipped,
|
|
108
|
+
empty list, throws).
|
|
109
|
+
- `parseCliArgs` tests — `workspaceExplicit` flag, `--help`/`-h` output and
|
|
110
|
+
exit code.
|
|
111
|
+
- Statement and function coverage: **100%** for `BeansMcpServer.ts`.
|
|
112
|
+
|
|
113
|
+
#### CI
|
|
114
|
+
|
|
115
|
+
- GitHub Actions workflow runs lint, type-check, build, and test on Node 18
|
|
116
|
+
and 22 across Ubuntu and macOS.
|
|
117
|
+
- pnpm store cache keyed on lockfile hash with `~/.pnpm-store` fallback.
|
|
118
|
+
|
|
119
|
+
### Changed
|
|
120
|
+
|
|
121
|
+
- Tool IDs renamed to remove the `_vscode` suffix carried over from the
|
|
122
|
+
extension (e.g. `beans_init_vscode` → `beans_init`).
|
|
123
|
+
- `--log-dir` now defaults to the workspace root when omitted.
|
|
124
|
+
- `cli.ts` simplified: removed the `isMainModule` guard; always invokes
|
|
125
|
+
`startBeansMcpServer`.
|
|
126
|
+
- Bin command renamed from `beans-mcp-server` to `beans-mcp`.
|
|
127
|
+
|
|
128
|
+
### Fixed
|
|
129
|
+
|
|
130
|
+
- Build script was overriding `tsup.config.ts` with inline CLI flags, causing
|
|
131
|
+
the CLI binary to never be produced. Fixed by setting `"build": "tsup"`.
|
|
132
|
+
- `package.json` exports paths corrected to include the `dist/` prefix.
|
|
133
|
+
- Eliminated all `any` types: `queryHandler` opts, `backend.ts` filter
|
|
134
|
+
parameter, and `queryHelpers.ts` return type narrowed to
|
|
135
|
+
`Record<string, unknown>`.
|
|
136
|
+
- README: corrected package import name (`@selfagency/beans-mcp`), server
|
|
137
|
+
default name (`beans-mcp-server`), removed the non-existent `allowedRoots`
|
|
138
|
+
option from the `createBeansMcpServer` docs.
|
|
139
|
+
|
|
140
|
+
[Unreleased]: https://github.com/selfagency/beans-mcp/compare/v0.1.0...HEAD
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Contributing to beans-mcp-server
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to the Beans MCP server! This document provides guidelines and instructions for contributing.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
### Prerequisites
|
|
8
|
+
|
|
9
|
+
- Node.js 18+
|
|
10
|
+
- pnpm 9+
|
|
11
|
+
- Beans CLI installed and in PATH (or specify via `--cli-path`)
|
|
12
|
+
|
|
13
|
+
### Getting Started
|
|
14
|
+
|
|
15
|
+
1. Clone the repository
|
|
16
|
+
2. Install dependencies:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
3. Build the project:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm build
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
4. Run tests:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm test
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Development Workflow
|
|
35
|
+
|
|
36
|
+
### Running Tests
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Run all tests
|
|
40
|
+
pnpm test
|
|
41
|
+
|
|
42
|
+
# Run specific test file
|
|
43
|
+
pnpm test src/test/utils.test.ts
|
|
44
|
+
|
|
45
|
+
# Run with coverage
|
|
46
|
+
pnpm test --coverage
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Building
|
|
50
|
+
|
|
51
|
+
The project uses `tsup` for bundling:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Build all formats (ESM, CJS, CLI)
|
|
55
|
+
pnpm build
|
|
56
|
+
|
|
57
|
+
# Watch mode (development)
|
|
58
|
+
pnpm build --watch
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Build outputs:
|
|
62
|
+
|
|
63
|
+
- `dist/index.js` - ESM entry point
|
|
64
|
+
- `dist/index.cjs` - CommonJS entry point
|
|
65
|
+
- `dist/beans-mcp-server.cjs` - CLI executable (with shebang)
|
|
66
|
+
- `dist/index.d.ts` - TypeScript type definitions
|
|
67
|
+
|
|
68
|
+
### Code Style
|
|
69
|
+
|
|
70
|
+
- Use TypeScript for all source code
|
|
71
|
+
- Format with Prettier (configured in project)
|
|
72
|
+
- Lint with ESLint
|
|
73
|
+
- Follow existing patterns in the codebase
|
|
74
|
+
|
|
75
|
+
### Testing Requirements
|
|
76
|
+
|
|
77
|
+
- All changes should include tests
|
|
78
|
+
- Use Vitest for unit tests
|
|
79
|
+
- Tests should use Arrange-Act-Assert pattern
|
|
80
|
+
- Aim for high test coverage
|
|
81
|
+
|
|
82
|
+
## Architecture
|
|
83
|
+
|
|
84
|
+
### Project Structure
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
src/
|
|
88
|
+
├── index.ts # Public API exports
|
|
89
|
+
├── cli.ts # CLI entry point
|
|
90
|
+
├── types.ts # TypeScript types and constants
|
|
91
|
+
├── utils.ts # Utility functions
|
|
92
|
+
├── server/
|
|
93
|
+
│ ├── BeansMcpServer.ts # Main MCP server implementation
|
|
94
|
+
│ └── backend.ts # Backend interface and Beans CLI backend
|
|
95
|
+
├── internal/
|
|
96
|
+
│ ├── graphql.ts # GraphQL queries/mutations
|
|
97
|
+
│ └── queryHelpers.ts # Query and sorting utilities
|
|
98
|
+
└── test/
|
|
99
|
+
├── utils.test.ts
|
|
100
|
+
└── parseCliArgs.test.ts
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Key Modules
|
|
104
|
+
|
|
105
|
+
- **BeansMcpServer.ts**: Main server class that manages MCP tools and backend interaction
|
|
106
|
+
- **backend.ts**: Interface for backend implementations; includes BeansCliBackend that wraps Beans CLI
|
|
107
|
+
- **graphql.ts**: GraphQL queries and mutations for bean operations
|
|
108
|
+
- **queryHelpers.ts**: Sorting, filtering, and query handling utilities
|
|
109
|
+
|
|
110
|
+
## Submitting Changes
|
|
111
|
+
|
|
112
|
+
1. Create a feature branch from `main`
|
|
113
|
+
2. Make your changes with clear, atomic commits
|
|
114
|
+
3. Add or update tests as needed
|
|
115
|
+
4. Run `pnpm test` to ensure all tests pass
|
|
116
|
+
5. Run `pnpm build` to verify the build succeeds
|
|
117
|
+
6. Submit a pull request with a clear description
|
|
118
|
+
|
|
119
|
+
## Pull Request Guidelines
|
|
120
|
+
|
|
121
|
+
- Include a clear description of what changed and why
|
|
122
|
+
- Reference any related issues
|
|
123
|
+
- Ensure all tests pass
|
|
124
|
+
- Update documentation if needed
|
|
125
|
+
- Keep commits focused and atomic
|
|
126
|
+
|
|
127
|
+
## Reporting Issues
|
|
128
|
+
|
|
129
|
+
When reporting bugs, please include:
|
|
130
|
+
|
|
131
|
+
- Steps to reproduce
|
|
132
|
+
- Expected behavior
|
|
133
|
+
- Actual behavior
|
|
134
|
+
- Node.js and pnpm versions
|
|
135
|
+
- Relevant error messages or logs
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
By contributing, you agree that your contributions will be licensed under the MIT License.
|