@selfagency/beans-mcp 0.1.3 → 0.4.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.md +63 -6
- package/{dist/beans-mcp-server.cjs → beans-mcp-server.cjs} +269 -34
- package/{dist/index.cjs → index.cjs} +269 -34
- package/{dist/index.d.ts → index.d.ts} +19 -1
- package/{dist/index.js → index.js} +269 -34
- package/package.json +28 -64
- package/.beans.yml +0 -6
- package/.claude/settings.local.json +0 -18
- package/.editorconfig +0 -13
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/release.yml +0 -235
- package/.github/workflows/test.yml +0 -84
- package/.husky/pre-commit +0 -1
- package/.nvmrc +0 -1
- package/.oxfmtrc.json +0 -11
- package/.oxlintrc.json +0 -37
- package/.vscode/settings.json +0 -3
- package/CHANGELOG.md +0 -160
- package/CONTRIBUTING.md +0 -139
- package/LICENSE.txt +0 -21
- package/codeql/codeql-custom-queries-actions/README.md +0 -14
- package/codeql/codeql-custom-queries-actions/codeql-pack.lock.yml +0 -32
- package/codeql/codeql-custom-queries-actions/codeql-pack.yml +0 -7
- package/codeql/codeql-custom-queries-actions/qlpack.yml +0 -6
- package/codeql/codeql-custom-queries-actions/queries/github-script-without-tojson.ql +0 -18
- package/codeql/codeql-custom-queries-actions/queries/strict-external-action-pinning.ql +0 -18
- package/codeql/codeql-custom-queries-javascript/README.md +0 -14
- package/codeql/codeql-custom-queries-javascript/codeql-pack.lock.yml +0 -30
- package/codeql/codeql-custom-queries-javascript/codeql-pack.yml +0 -7
- package/codeql/codeql-custom-queries-javascript/qlpack.yml +0 -6
- package/codeql/codeql-custom-queries-javascript/queries/child-process-shell-apis.ql +0 -26
- package/codeql/codeql-custom-queries-javascript/queries/innerhtml-assignment.ql +0 -24
- package/dist/README.md +0 -307
- package/dist/beans-mcp-server.cjs.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/package.json +0 -43
- package/pnpm-workspace.yaml +0 -2
- package/scripts/release.js +0 -433
- package/scripts/write-dist-package.js +0 -53
- package/src/cli.ts +0 -14
- package/src/index.ts +0 -21
- package/src/internal/graphql.ts +0 -33
- package/src/internal/queryHelpers.ts +0 -157
- package/src/server/BeansMcpServer.ts +0 -623
- package/src/server/backend.ts +0 -364
- package/src/test/BeansMcpServer.test.ts +0 -514
- package/src/test/handlers.unit.test.ts +0 -201
- package/src/test/parseCliArgs.test.ts +0 -69
- package/src/test/protocol.e2e.test.ts +0 -884
- package/src/test/queryHelpers.test.ts +0 -524
- package/src/test/startBeansMcpServer.test.ts +0 -146
- package/src/test/tools-integration.test.ts +0 -912
- package/src/test/utils.test.ts +0 -81
- package/src/types.ts +0 -46
- package/src/utils.ts +0 -20
- package/tsconfig.json +0 -24
- package/tsup.config.ts +0 -42
- package/vitest.config.ts +0 -18
|
@@ -22744,7 +22744,7 @@ var init_utils = __esm({
|
|
|
22744
22744
|
});
|
|
22745
22745
|
|
|
22746
22746
|
// src/internal/graphql.ts
|
|
22747
|
-
var LIST_BEANS_QUERY, CREATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION, DELETE_BEAN_MUTATION;
|
|
22747
|
+
var LIST_BEANS_QUERY, CREATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION_WITH_IF_MATCH, DELETE_BEAN_MUTATION;
|
|
22748
22748
|
var init_graphql = __esm({
|
|
22749
22749
|
"src/internal/graphql.ts"() {
|
|
22750
22750
|
"use strict";
|
|
@@ -22762,6 +22762,11 @@ var init_graphql = __esm({
|
|
|
22762
22762
|
mutation($id: ID!, $input: UpdateBeanInput!) {
|
|
22763
22763
|
updateBean(id: $id, input: $input) { id slug path title body status type priority tags parentId blockingIds blockedByIds createdAt updatedAt etag }
|
|
22764
22764
|
}
|
|
22765
|
+
`;
|
|
22766
|
+
UPDATE_BEAN_MUTATION_WITH_IF_MATCH = `
|
|
22767
|
+
mutation($id: ID!, $input: UpdateBeanInput!, $ifMatch: String!) {
|
|
22768
|
+
updateBean(id: $id, input: $input, ifMatch: $ifMatch) { id slug path title body status type priority tags parentId blockingIds blockedByIds createdAt updatedAt etag }
|
|
22769
|
+
}
|
|
22765
22770
|
`;
|
|
22766
22771
|
DELETE_BEAN_MUTATION = `
|
|
22767
22772
|
mutation($id: ID!) {
|
|
@@ -22919,10 +22924,50 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
22919
22924
|
if (updates.body !== void 0) {
|
|
22920
22925
|
updateInput.body = updates.body;
|
|
22921
22926
|
}
|
|
22922
|
-
const
|
|
22923
|
-
|
|
22924
|
-
|
|
22925
|
-
}
|
|
22927
|
+
const bodyMod = {};
|
|
22928
|
+
if (updates.bodyAppend !== void 0) {
|
|
22929
|
+
bodyMod.append = updates.bodyAppend;
|
|
22930
|
+
}
|
|
22931
|
+
if (Array.isArray(updates.bodyReplace) && updates.bodyReplace.length > 0) {
|
|
22932
|
+
bodyMod.replace = updates.bodyReplace;
|
|
22933
|
+
}
|
|
22934
|
+
if (Object.keys(bodyMod).length > 0) {
|
|
22935
|
+
updateInput.bodyMod = bodyMod;
|
|
22936
|
+
}
|
|
22937
|
+
let data;
|
|
22938
|
+
let errors;
|
|
22939
|
+
if (updates.ifMatch) {
|
|
22940
|
+
try {
|
|
22941
|
+
const res = await this.executeGraphQL(UPDATE_BEAN_MUTATION_WITH_IF_MATCH, {
|
|
22942
|
+
id: beanId,
|
|
22943
|
+
input: updateInput,
|
|
22944
|
+
ifMatch: updates.ifMatch
|
|
22945
|
+
});
|
|
22946
|
+
data = res.data;
|
|
22947
|
+
errors = res.errors;
|
|
22948
|
+
} catch (error48) {
|
|
22949
|
+
const message = error48.message || "";
|
|
22950
|
+
const unsupportedIfMatch = /unknown argument.*ifMatch|unknown field.*ifMatch|ifMatch.*not defined|field .*updateBean.* argument .*ifMatch/i.test(
|
|
22951
|
+
message
|
|
22952
|
+
);
|
|
22953
|
+
if (!unsupportedIfMatch) {
|
|
22954
|
+
throw error48;
|
|
22955
|
+
}
|
|
22956
|
+
const fallback = await this.executeGraphQL(UPDATE_BEAN_MUTATION, {
|
|
22957
|
+
id: beanId,
|
|
22958
|
+
input: updateInput
|
|
22959
|
+
});
|
|
22960
|
+
data = fallback.data;
|
|
22961
|
+
errors = fallback.errors;
|
|
22962
|
+
}
|
|
22963
|
+
} else {
|
|
22964
|
+
const res = await this.executeGraphQL(UPDATE_BEAN_MUTATION, {
|
|
22965
|
+
id: beanId,
|
|
22966
|
+
input: updateInput
|
|
22967
|
+
});
|
|
22968
|
+
data = res.data;
|
|
22969
|
+
errors = res.errors;
|
|
22970
|
+
}
|
|
22926
22971
|
if (errors && errors.length > 0) {
|
|
22927
22972
|
throw new Error(`GraphQL error: ${errors.map((e) => e.message).join(", ")}`);
|
|
22928
22973
|
}
|
|
@@ -22942,6 +22987,21 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
22942
22987
|
const content = await readFile(configPath, "utf8");
|
|
22943
22988
|
return { configPath, content };
|
|
22944
22989
|
}
|
|
22990
|
+
async primeInstructions() {
|
|
22991
|
+
const { stdout } = await execFileAsync(this.cliPath, ["prime"], {
|
|
22992
|
+
cwd: this.workspaceRoot,
|
|
22993
|
+
env: this.getSafeEnv(),
|
|
22994
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
22995
|
+
timeout: 3e4
|
|
22996
|
+
});
|
|
22997
|
+
return stdout.trim();
|
|
22998
|
+
}
|
|
22999
|
+
async writeInstructions(instructions) {
|
|
23000
|
+
const instructionsPath = join(this.workspaceRoot, ".github", "instructions", "beans-prime.instructions.md");
|
|
23001
|
+
await mkdir(dirname(instructionsPath), { recursive: true });
|
|
23002
|
+
await writeFile(instructionsPath, instructions, "utf8");
|
|
23003
|
+
return instructionsPath;
|
|
23004
|
+
}
|
|
22945
23005
|
async graphqlSchema() {
|
|
22946
23006
|
const { stdout } = await execFileAsync(this.cliPath, ["graphql", "--schema"], {
|
|
22947
23007
|
cwd: this.workspaceRoot,
|
|
@@ -31069,6 +31129,10 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
31069
31129
|
}
|
|
31070
31130
|
};
|
|
31071
31131
|
|
|
31132
|
+
// src/server/BeansMcpServer.ts
|
|
31133
|
+
import { execFile as execFile2 } from "child_process";
|
|
31134
|
+
import { promisify as promisify2 } from "util";
|
|
31135
|
+
|
|
31072
31136
|
// src/internal/queryHelpers.ts
|
|
31073
31137
|
function sortBeansInternal(beans, mode) {
|
|
31074
31138
|
const sorted = [...beans];
|
|
@@ -31124,15 +31188,23 @@ async function handleQueryOperation(backend, params) {
|
|
|
31124
31188
|
const { operation, mode, statuses, types, search, tags, writeToWorkspaceInstructions, includeClosed } = params;
|
|
31125
31189
|
if (operation === "llm_context") {
|
|
31126
31190
|
const graphqlSchema = typeof backend.graphqlSchema === "function" ? await backend.graphqlSchema() : "";
|
|
31127
|
-
|
|
31191
|
+
let generatedInstructions = "";
|
|
31192
|
+
if (typeof backend.primeInstructions === "function") {
|
|
31193
|
+
try {
|
|
31194
|
+
generatedInstructions = await backend.primeInstructions();
|
|
31195
|
+
} catch {
|
|
31196
|
+
generatedInstructions = "";
|
|
31197
|
+
}
|
|
31198
|
+
}
|
|
31199
|
+
const instructionsPath = writeToWorkspaceInstructions && typeof backend.writeInstructions === "function" ? await backend.writeInstructions(generatedInstructions) : null;
|
|
31128
31200
|
return {
|
|
31129
31201
|
content: [
|
|
31130
31202
|
{
|
|
31131
31203
|
type: "text",
|
|
31132
|
-
text: JSON.stringify({ graphqlSchema, generatedInstructions
|
|
31204
|
+
text: JSON.stringify({ graphqlSchema, generatedInstructions, instructionsPath }, null, 2)
|
|
31133
31205
|
}
|
|
31134
31206
|
],
|
|
31135
|
-
structuredContent: { graphqlSchema, generatedInstructions
|
|
31207
|
+
structuredContent: { graphqlSchema, generatedInstructions, instructionsPath }
|
|
31136
31208
|
};
|
|
31137
31209
|
}
|
|
31138
31210
|
if (operation === "open_config") {
|
|
@@ -31178,6 +31250,31 @@ async function handleQueryOperation(backend, params) {
|
|
|
31178
31250
|
structuredContent: { query: search, count: beans2.length, beans: beans2 }
|
|
31179
31251
|
};
|
|
31180
31252
|
}
|
|
31253
|
+
if (operation === "ready") {
|
|
31254
|
+
const allBeans = await backend.list();
|
|
31255
|
+
const byId = new Map(allBeans.map((bean) => [bean.id, bean]));
|
|
31256
|
+
const candidates = await backend.list({ status: normalizedStatuses, type: normalizedTypes, search });
|
|
31257
|
+
const readyBeans = candidates.filter((bean) => {
|
|
31258
|
+
if (bean.status !== "todo") {
|
|
31259
|
+
return false;
|
|
31260
|
+
}
|
|
31261
|
+
const blockedBy = bean.blockedByIds || [];
|
|
31262
|
+
if (blockedBy.length === 0) {
|
|
31263
|
+
return true;
|
|
31264
|
+
}
|
|
31265
|
+
return blockedBy.every((blockerId) => {
|
|
31266
|
+
const blocker = byId.get(blockerId);
|
|
31267
|
+
if (!blocker) {
|
|
31268
|
+
return false;
|
|
31269
|
+
}
|
|
31270
|
+
return blocker.status === "completed" || blocker.status === "scrapped";
|
|
31271
|
+
});
|
|
31272
|
+
});
|
|
31273
|
+
return {
|
|
31274
|
+
content: [{ type: "text", text: JSON.stringify({ count: readyBeans.length, beans: readyBeans }, null, 2) }],
|
|
31275
|
+
structuredContent: { count: readyBeans.length, beans: readyBeans }
|
|
31276
|
+
};
|
|
31277
|
+
}
|
|
31181
31278
|
const beans = await backend.list({ status: normalizedStatuses, type: normalizedTypes, search });
|
|
31182
31279
|
const sorted = sortBeansInternal(beans, mode ?? "status-priority-type-title");
|
|
31183
31280
|
return {
|
|
@@ -31200,7 +31297,7 @@ init_utils();
|
|
|
31200
31297
|
// package.json
|
|
31201
31298
|
var package_default = {
|
|
31202
31299
|
name: "@selfagency/beans-mcp",
|
|
31203
|
-
version: "0.
|
|
31300
|
+
version: "0.4.2",
|
|
31204
31301
|
private: false,
|
|
31205
31302
|
description: "MCP (Model Context Protocol) server for Beans issue tracker",
|
|
31206
31303
|
author: {
|
|
@@ -31216,6 +31313,7 @@ var package_default = {
|
|
|
31216
31313
|
type: "git",
|
|
31217
31314
|
url: "git+https://github.com/selfagency/beans-mcp.git"
|
|
31218
31315
|
},
|
|
31316
|
+
mcpName: "io.github.selfagency/beans-mcp",
|
|
31219
31317
|
keywords: [
|
|
31220
31318
|
"beans",
|
|
31221
31319
|
"mcp",
|
|
@@ -31254,18 +31352,18 @@ var package_default = {
|
|
|
31254
31352
|
devDependencies: {
|
|
31255
31353
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
31256
31354
|
"@octokit/rest": "^22.0.1",
|
|
31257
|
-
"@types/node": "
|
|
31258
|
-
"@vitest/coverage-v8": "^4.0
|
|
31259
|
-
"@vitest/ui": "4.0
|
|
31355
|
+
"@types/node": "25.5.0",
|
|
31356
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
31357
|
+
"@vitest/ui": "4.1.0",
|
|
31260
31358
|
husky: "^9.1.7",
|
|
31261
|
-
"lint-staged": "^16.
|
|
31359
|
+
"lint-staged": "^16.3.3",
|
|
31262
31360
|
ora: "^9.3.0",
|
|
31263
|
-
oxfmt: "^0.
|
|
31264
|
-
oxlint: "^1.
|
|
31265
|
-
"oxlint-tsgolint": "^0.
|
|
31361
|
+
oxfmt: "^0.40.0",
|
|
31362
|
+
oxlint: "^1.55.0",
|
|
31363
|
+
"oxlint-tsgolint": "^0.16.0",
|
|
31266
31364
|
tsup: "8.5.1",
|
|
31267
31365
|
typescript: "^5.9.3",
|
|
31268
|
-
vitest: "4.0
|
|
31366
|
+
vitest: "4.1.0",
|
|
31269
31367
|
zod: "4.3.6",
|
|
31270
31368
|
zx: "^8.8.5"
|
|
31271
31369
|
},
|
|
@@ -31281,6 +31379,45 @@ var package_default = {
|
|
|
31281
31379
|
};
|
|
31282
31380
|
|
|
31283
31381
|
// src/server/BeansMcpServer.ts
|
|
31382
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
31383
|
+
var PACKAGE_VERSION = package_default.version ?? "0.0.0-dev";
|
|
31384
|
+
function getSafeCliEnv(env) {
|
|
31385
|
+
const whitelist = ["PATH", "HOME", "USER", "LANG", "LC_ALL", "LC_CTYPE", "SHELL"];
|
|
31386
|
+
const safeEnv = {};
|
|
31387
|
+
for (const key of whitelist) {
|
|
31388
|
+
if (env[key]) {
|
|
31389
|
+
safeEnv[key] = env[key];
|
|
31390
|
+
}
|
|
31391
|
+
}
|
|
31392
|
+
for (const key in env) {
|
|
31393
|
+
if (key.startsWith("BEANS_")) {
|
|
31394
|
+
safeEnv[key] = env[key];
|
|
31395
|
+
}
|
|
31396
|
+
}
|
|
31397
|
+
return safeEnv;
|
|
31398
|
+
}
|
|
31399
|
+
function extractVersionFromOutput(output) {
|
|
31400
|
+
const trimmed = output.trim();
|
|
31401
|
+
if (!trimmed) {
|
|
31402
|
+
return null;
|
|
31403
|
+
}
|
|
31404
|
+
const match = trimmed.match(/(?:^|[^\d])v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/);
|
|
31405
|
+
return match?.[1] ?? null;
|
|
31406
|
+
}
|
|
31407
|
+
async function detectBeansCliVersion(cliPath, workspaceRoot) {
|
|
31408
|
+
try {
|
|
31409
|
+
const { stdout, stderr } = await execFileAsync2(cliPath, ["version"], {
|
|
31410
|
+
cwd: workspaceRoot,
|
|
31411
|
+
env: getSafeCliEnv(process.env),
|
|
31412
|
+
maxBuffer: 1024 * 1024,
|
|
31413
|
+
timeout: 5e3
|
|
31414
|
+
});
|
|
31415
|
+
return extractVersionFromOutput(`${stdout}
|
|
31416
|
+
${stderr}`);
|
|
31417
|
+
} catch {
|
|
31418
|
+
return null;
|
|
31419
|
+
}
|
|
31420
|
+
}
|
|
31284
31421
|
async function getBeanById(backend, beanId) {
|
|
31285
31422
|
try {
|
|
31286
31423
|
const beans = await backend.list();
|
|
@@ -31300,7 +31437,35 @@ function initHandler(backend) {
|
|
|
31300
31437
|
};
|
|
31301
31438
|
}
|
|
31302
31439
|
function viewHandler(backend) {
|
|
31303
|
-
return async ({ beanId }) =>
|
|
31440
|
+
return async ({ beanId, beanIds }) => {
|
|
31441
|
+
const ids = Array.isArray(beanIds) && beanIds.length > 0 ? beanIds : beanId ? [beanId] : [];
|
|
31442
|
+
if (ids.length === 0) {
|
|
31443
|
+
throw new Error("Either beanId or beanIds must be provided");
|
|
31444
|
+
}
|
|
31445
|
+
if (ids.length === 1) {
|
|
31446
|
+
const bean = await getBeanById(backend, ids[0]);
|
|
31447
|
+
return makeTextAndStructured({ bean });
|
|
31448
|
+
}
|
|
31449
|
+
const beans = await backend.list();
|
|
31450
|
+
const byId = new Map(beans.map((b) => [b.id, b]));
|
|
31451
|
+
const found = ids.map((id) => byId.get(id)).filter(Boolean);
|
|
31452
|
+
const missingBeanIds = ids.filter((id) => !byId.has(id));
|
|
31453
|
+
return makeTextAndStructured({ beans: found, missingBeanIds, count: found.length, requestedCount: ids.length });
|
|
31454
|
+
};
|
|
31455
|
+
}
|
|
31456
|
+
async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
|
|
31457
|
+
const detectedBeansVersion = await detector(cliPath, workspaceRoot);
|
|
31458
|
+
if (!detectedBeansVersion) {
|
|
31459
|
+
console.error(
|
|
31460
|
+
`[beans-mcp] warning: unable to determine Beans CLI version from \`${cliPath}\`; proceeding without version compatibility checks.`
|
|
31461
|
+
);
|
|
31462
|
+
return;
|
|
31463
|
+
}
|
|
31464
|
+
if (detectedBeansVersion !== PACKAGE_VERSION) {
|
|
31465
|
+
console.error(
|
|
31466
|
+
`[beans-mcp] warning: version mismatch detected (beans=${detectedBeansVersion}, beans-mcp=${PACKAGE_VERSION}); continuing startup.`
|
|
31467
|
+
);
|
|
31468
|
+
}
|
|
31304
31469
|
}
|
|
31305
31470
|
function createHandler(backend) {
|
|
31306
31471
|
return async (input) => makeTextAndStructured({ bean: await backend.create(input) });
|
|
@@ -31336,17 +31501,56 @@ function updateHandler(backend) {
|
|
|
31336
31501
|
clearParent: input.clearParent,
|
|
31337
31502
|
blocking: input.blocking,
|
|
31338
31503
|
blockedBy: input.blockedBy,
|
|
31339
|
-
body: input.body
|
|
31504
|
+
body: input.body,
|
|
31505
|
+
bodyAppend: input.bodyAppend,
|
|
31506
|
+
bodyReplace: input.bodyReplace,
|
|
31507
|
+
ifMatch: input.ifMatch
|
|
31340
31508
|
})
|
|
31341
31509
|
});
|
|
31342
31510
|
}
|
|
31343
31511
|
function deleteHandler(backend) {
|
|
31344
|
-
return async ({ beanId, force }) => {
|
|
31345
|
-
const
|
|
31346
|
-
if (
|
|
31347
|
-
throw new Error("
|
|
31512
|
+
return async ({ beanId, beanIds, force }) => {
|
|
31513
|
+
const ids = Array.isArray(beanIds) && beanIds.length > 0 ? beanIds : beanId ? [beanId] : [];
|
|
31514
|
+
if (ids.length === 0) {
|
|
31515
|
+
throw new Error("Either beanId or beanIds must be provided");
|
|
31516
|
+
}
|
|
31517
|
+
if (ids.length === 1) {
|
|
31518
|
+
const bean = await getBeanById(backend, ids[0]);
|
|
31519
|
+
if (!force && bean.status !== "draft" && bean.status !== "scrapped") {
|
|
31520
|
+
throw new Error("Only draft and scrapped beans are deletable unless force=true");
|
|
31521
|
+
}
|
|
31522
|
+
return makeTextAndStructured(await backend.delete(ids[0]));
|
|
31348
31523
|
}
|
|
31349
|
-
|
|
31524
|
+
const beans = await backend.list();
|
|
31525
|
+
const byId = new Map(beans.map((b) => [b.id, b]));
|
|
31526
|
+
const results = [];
|
|
31527
|
+
for (const id of ids) {
|
|
31528
|
+
const bean = byId.get(id);
|
|
31529
|
+
if (!bean) {
|
|
31530
|
+
results.push({ beanId: id, deleted: false, error: "Bean not found" });
|
|
31531
|
+
continue;
|
|
31532
|
+
}
|
|
31533
|
+
if (!force && bean.status !== "draft" && bean.status !== "scrapped") {
|
|
31534
|
+
results.push({
|
|
31535
|
+
beanId: id,
|
|
31536
|
+
deleted: false,
|
|
31537
|
+
error: "Only draft and scrapped beans are deletable unless force=true"
|
|
31538
|
+
});
|
|
31539
|
+
continue;
|
|
31540
|
+
}
|
|
31541
|
+
try {
|
|
31542
|
+
await backend.delete(id);
|
|
31543
|
+
results.push({ beanId: id, deleted: true });
|
|
31544
|
+
} catch (error48) {
|
|
31545
|
+
results.push({ beanId: id, deleted: false, error: error48.message });
|
|
31546
|
+
}
|
|
31547
|
+
}
|
|
31548
|
+
return makeTextAndStructured({
|
|
31549
|
+
results,
|
|
31550
|
+
requestedCount: ids.length,
|
|
31551
|
+
deletedCount: results.filter((r) => r.deleted).length,
|
|
31552
|
+
failedCount: results.filter((r) => !r.deleted).length
|
|
31553
|
+
});
|
|
31350
31554
|
};
|
|
31351
31555
|
}
|
|
31352
31556
|
function queryHandler(backend) {
|
|
@@ -31407,7 +31611,12 @@ function registerTools(server, backend) {
|
|
|
31407
31611
|
{
|
|
31408
31612
|
title: "View Bean",
|
|
31409
31613
|
description: "Fetch full bean details by ID.",
|
|
31410
|
-
inputSchema: external_exports3.object({
|
|
31614
|
+
inputSchema: external_exports3.object({
|
|
31615
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31616
|
+
beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional()
|
|
31617
|
+
}).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
|
|
31618
|
+
message: "Either beanId or beanIds must be provided"
|
|
31619
|
+
}),
|
|
31411
31620
|
annotations: {
|
|
31412
31621
|
readOnlyHint: true,
|
|
31413
31622
|
destructiveHint: false,
|
|
@@ -31496,8 +31705,21 @@ function registerTools(server, backend) {
|
|
|
31496
31705
|
clearParent: external_exports3.boolean().optional(),
|
|
31497
31706
|
blocking: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
|
|
31498
31707
|
blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
|
|
31499
|
-
body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional()
|
|
31500
|
-
|
|
31708
|
+
body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
31709
|
+
bodyAppend: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
31710
|
+
bodyReplace: external_exports3.array(
|
|
31711
|
+
external_exports3.object({
|
|
31712
|
+
old: external_exports3.string().max(MAX_DESCRIPTION_LENGTH),
|
|
31713
|
+
new: external_exports3.string().max(MAX_DESCRIPTION_LENGTH)
|
|
31714
|
+
})
|
|
31715
|
+
).optional(),
|
|
31716
|
+
ifMatch: external_exports3.string().max(MAX_METADATA_LENGTH).optional()
|
|
31717
|
+
}).refine(
|
|
31718
|
+
(input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
|
|
31719
|
+
{
|
|
31720
|
+
message: "body cannot be combined with bodyAppend/bodyReplace"
|
|
31721
|
+
}
|
|
31722
|
+
),
|
|
31501
31723
|
annotations: {
|
|
31502
31724
|
readOnlyHint: false,
|
|
31503
31725
|
destructiveHint: false,
|
|
@@ -31513,8 +31735,11 @@ function registerTools(server, backend) {
|
|
|
31513
31735
|
title: "Delete Bean",
|
|
31514
31736
|
description: "Delete a bean (intended for draft/scrapped beans).",
|
|
31515
31737
|
inputSchema: external_exports3.object({
|
|
31516
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
31738
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31739
|
+
beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional(),
|
|
31517
31740
|
force: external_exports3.boolean().default(false)
|
|
31741
|
+
}).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
|
|
31742
|
+
message: "Either beanId or beanIds must be provided"
|
|
31518
31743
|
}),
|
|
31519
31744
|
annotations: {
|
|
31520
31745
|
readOnlyHint: false,
|
|
@@ -31531,7 +31756,7 @@ function registerTools(server, backend) {
|
|
|
31531
31756
|
title: "Query Beans",
|
|
31532
31757
|
description: "Unified query tool for refresh, filter, search, and sort operations.",
|
|
31533
31758
|
inputSchema: external_exports3.object({
|
|
31534
|
-
operation: external_exports3.enum(["refresh", "filter", "search", "sort", "llm_context", "open_config"]).default("refresh"),
|
|
31759
|
+
operation: external_exports3.enum(["refresh", "filter", "search", "sort", "ready", "llm_context", "open_config"]).default("refresh"),
|
|
31535
31760
|
mode: external_exports3.enum(["status-priority-type-title", "updated", "created", "id"]).optional(),
|
|
31536
31761
|
statuses: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
31537
31762
|
types: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
@@ -31613,6 +31838,12 @@ var MutableBackend = class {
|
|
|
31613
31838
|
openConfig() {
|
|
31614
31839
|
return this.inner.openConfig();
|
|
31615
31840
|
}
|
|
31841
|
+
primeInstructions() {
|
|
31842
|
+
return this.inner.primeInstructions?.() ?? Promise.resolve("");
|
|
31843
|
+
}
|
|
31844
|
+
writeInstructions(instructions) {
|
|
31845
|
+
return this.inner.writeInstructions?.(instructions) ?? Promise.resolve(null);
|
|
31846
|
+
}
|
|
31616
31847
|
graphqlSchema() {
|
|
31617
31848
|
return this.inner.graphqlSchema();
|
|
31618
31849
|
}
|
|
@@ -31650,7 +31881,7 @@ async function createBeansMcpServer(opts) {
|
|
|
31650
31881
|
const backend = opts.backend || new BeansCliBackend2(opts.workspaceRoot, opts.cliPath || "beans", opts.logDir);
|
|
31651
31882
|
const server = new McpServer({
|
|
31652
31883
|
name: opts.name || "beans-mcp-server",
|
|
31653
|
-
version: opts.version ||
|
|
31884
|
+
version: opts.version || PACKAGE_VERSION
|
|
31654
31885
|
});
|
|
31655
31886
|
registerTools(server, backend);
|
|
31656
31887
|
return { server, backend };
|
|
@@ -31722,17 +31953,17 @@ function parseCliArgs(argv) {
|
|
|
31722
31953
|
}
|
|
31723
31954
|
return { workspaceRoot, workspaceExplicit, cliPath, port, logDir };
|
|
31724
31955
|
}
|
|
31725
|
-
async function startBeansMcpServer(argv, _resolveRoots) {
|
|
31956
|
+
async function startBeansMcpServer(argv, _resolveRoots, _detectBeansVersion) {
|
|
31726
31957
|
const { BeansCliBackend: BeansCliBackend2 } = await Promise.resolve().then(() => (init_backend(), backend_exports));
|
|
31727
31958
|
const { StdioServerTransport: StdioServerTransport2 } = await Promise.resolve().then(() => (init_stdio2(), stdio_exports));
|
|
31728
31959
|
const { workspaceRoot, workspaceExplicit, cliPath, port, logDir } = parseCliArgs(argv);
|
|
31960
|
+
let effectiveWorkspaceRoot = workspaceRoot;
|
|
31729
31961
|
process.env.BEANS_VSCODE_MCP_PORT = String(port);
|
|
31730
31962
|
process.env.BEANS_MCP_PORT = String(port);
|
|
31731
31963
|
try {
|
|
31732
|
-
const version2 = package_default.version ?? "0.0.0-dev";
|
|
31733
31964
|
const workspaceLabel = workspaceExplicit ? workspaceRoot : "(auto from roots)";
|
|
31734
31965
|
console.error(
|
|
31735
|
-
`[beans-mcp] v${
|
|
31966
|
+
`[beans-mcp] v${PACKAGE_VERSION} starting (port=${port}, workspace=${workspaceLabel}, cli=${cliPath}, logDir=${logDir})`
|
|
31736
31967
|
);
|
|
31737
31968
|
} catch {
|
|
31738
31969
|
}
|
|
@@ -31749,13 +31980,17 @@ async function startBeansMcpServer(argv, _resolveRoots) {
|
|
|
31749
31980
|
const resolver = _resolveRoots ?? resolveWorkspaceFromRoots;
|
|
31750
31981
|
const rootPath = await resolver(server);
|
|
31751
31982
|
if (rootPath) {
|
|
31752
|
-
mutable.setInner(new BeansCliBackend2(rootPath, cliPath));
|
|
31983
|
+
mutable.setInner(new BeansCliBackend2(rootPath, cliPath, logDir));
|
|
31984
|
+
effectiveWorkspaceRoot = rootPath;
|
|
31753
31985
|
try {
|
|
31754
31986
|
console.error(`[beans-mcp] workspace resolved from roots: ${rootPath}`);
|
|
31755
31987
|
} catch {
|
|
31756
31988
|
}
|
|
31757
31989
|
}
|
|
31758
31990
|
}
|
|
31991
|
+
const beansVersionDetector = _detectBeansVersion ?? detectBeansCliVersion;
|
|
31992
|
+
void checkVersionCompatibility(cliPath, effectiveWorkspaceRoot, beansVersionDetector).catch(() => {
|
|
31993
|
+
});
|
|
31759
31994
|
}
|
|
31760
31995
|
|
|
31761
31996
|
// src/index.ts
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selfagency/beans-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"private": false,
|
|
3
|
+
"version": "0.4.2",
|
|
5
4
|
"description": "MCP (Model Context Protocol) server for Beans issue tracker",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
5
|
+
"keywords": [
|
|
6
|
+
"beans",
|
|
7
|
+
"mcp",
|
|
8
|
+
"model-context-protocol",
|
|
9
|
+
"issue-tracker",
|
|
10
|
+
"ai"
|
|
11
|
+
],
|
|
11
12
|
"homepage": "https://github.com/hmans/beans",
|
|
12
13
|
"bugs": {
|
|
13
14
|
"url": "https://github.com/selfagency/beans-mcp/issues"
|
|
@@ -16,65 +17,28 @@
|
|
|
16
17
|
"type": "git",
|
|
17
18
|
"url": "git+https://github.com/selfagency/beans-mcp.git"
|
|
18
19
|
},
|
|
19
|
-
"keywords": [
|
|
20
|
-
"beans",
|
|
21
|
-
"mcp",
|
|
22
|
-
"model-context-protocol",
|
|
23
|
-
"issue-tracker",
|
|
24
|
-
"ai"
|
|
25
|
-
],
|
|
26
20
|
"license": "MIT",
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
"import": "./dist/index.js",
|
|
32
|
-
"require": "./dist/index.cjs"
|
|
33
|
-
}
|
|
21
|
+
"author": {
|
|
22
|
+
"name": "Daniel Sieradski",
|
|
23
|
+
"email": "daniel@self.agency",
|
|
24
|
+
"url": "https://self.agency"
|
|
34
25
|
},
|
|
35
|
-
"main": "./
|
|
36
|
-
"module": "./
|
|
37
|
-
"types": "./
|
|
26
|
+
"main": "./index.cjs",
|
|
27
|
+
"module": "./index.js",
|
|
28
|
+
"types": "./index.d.ts",
|
|
29
|
+
"files": [
|
|
30
|
+
"./index.cjs",
|
|
31
|
+
"./index.js",
|
|
32
|
+
"./index.d.ts"
|
|
33
|
+
],
|
|
38
34
|
"bin": {
|
|
39
|
-
"beans-mcp": "
|
|
40
|
-
},
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
43
|
-
"@octokit/rest": "^22.0.1",
|
|
44
|
-
"@types/node": "^20.19.0",
|
|
45
|
-
"@vitest/coverage-v8": "^4.0.18",
|
|
46
|
-
"@vitest/ui": "4.0.18",
|
|
47
|
-
"husky": "^9.1.7",
|
|
48
|
-
"lint-staged": "^16.2.7",
|
|
49
|
-
"ora": "^9.3.0",
|
|
50
|
-
"oxfmt": "^0.35.0",
|
|
51
|
-
"oxlint": "^1.50.0",
|
|
52
|
-
"oxlint-tsgolint": "^0.15.0",
|
|
53
|
-
"tsup": "8.5.1",
|
|
54
|
-
"typescript": "^5.9.3",
|
|
55
|
-
"vitest": "4.0.18",
|
|
56
|
-
"zod": "4.3.6",
|
|
57
|
-
"zx": "^8.8.5"
|
|
35
|
+
"beans-mcp": "./beans-mcp-server.cjs"
|
|
58
36
|
},
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"pnpm run lint:fix",
|
|
65
|
-
"pnpm run format"
|
|
66
|
-
]
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"import": "./index.js",
|
|
40
|
+
"require": "./index.cjs"
|
|
41
|
+
}
|
|
67
42
|
},
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
"format": "oxfmt",
|
|
71
|
-
"lint:fix": "oxlint --fix",
|
|
72
|
-
"lint": "oxlint",
|
|
73
|
-
"postbuild": "node ./scripts/write-dist-package.js",
|
|
74
|
-
"release": "zx ./scripts/release.js",
|
|
75
|
-
"test:coverage": "vitest run --coverage",
|
|
76
|
-
"test:watch": "vitest",
|
|
77
|
-
"test": "vitest run",
|
|
78
|
-
"type-check": "tsc --noEmit"
|
|
79
|
-
}
|
|
80
|
-
}
|
|
43
|
+
"mcpName": "io.github.selfagency/beans-mcp"
|
|
44
|
+
}
|
package/.beans.yml
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
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
DELETED
package/.github/dependabot.yml
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
-
# package ecosystems to update and where the package manifests are located.
|
|
3
|
-
# Please see the documentation for all configuration options:
|
|
4
|
-
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
5
|
-
|
|
6
|
-
version: 2
|
|
7
|
-
updates:
|
|
8
|
-
- package-ecosystem: "" # See documentation for possible values
|
|
9
|
-
directory: "/" # Location of package manifests
|
|
10
|
-
schedule:
|
|
11
|
-
interval: "weekly"
|