@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
|
@@ -22747,7 +22747,7 @@ var init_utils = __esm({
|
|
|
22747
22747
|
});
|
|
22748
22748
|
|
|
22749
22749
|
// src/internal/graphql.ts
|
|
22750
|
-
var LIST_BEANS_QUERY, CREATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION, DELETE_BEAN_MUTATION;
|
|
22750
|
+
var LIST_BEANS_QUERY, CREATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION_WITH_IF_MATCH, DELETE_BEAN_MUTATION;
|
|
22751
22751
|
var init_graphql = __esm({
|
|
22752
22752
|
"src/internal/graphql.ts"() {
|
|
22753
22753
|
"use strict";
|
|
@@ -22765,6 +22765,11 @@ var init_graphql = __esm({
|
|
|
22765
22765
|
mutation($id: ID!, $input: UpdateBeanInput!) {
|
|
22766
22766
|
updateBean(id: $id, input: $input) { id slug path title body status type priority tags parentId blockingIds blockedByIds createdAt updatedAt etag }
|
|
22767
22767
|
}
|
|
22768
|
+
`;
|
|
22769
|
+
UPDATE_BEAN_MUTATION_WITH_IF_MATCH = `
|
|
22770
|
+
mutation($id: ID!, $input: UpdateBeanInput!, $ifMatch: String!) {
|
|
22771
|
+
updateBean(id: $id, input: $input, ifMatch: $ifMatch) { id slug path title body status type priority tags parentId blockingIds blockedByIds createdAt updatedAt etag }
|
|
22772
|
+
}
|
|
22768
22773
|
`;
|
|
22769
22774
|
DELETE_BEAN_MUTATION = `
|
|
22770
22775
|
mutation($id: ID!) {
|
|
@@ -22922,10 +22927,50 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
22922
22927
|
if (updates.body !== void 0) {
|
|
22923
22928
|
updateInput.body = updates.body;
|
|
22924
22929
|
}
|
|
22925
|
-
const
|
|
22926
|
-
|
|
22927
|
-
|
|
22928
|
-
}
|
|
22930
|
+
const bodyMod = {};
|
|
22931
|
+
if (updates.bodyAppend !== void 0) {
|
|
22932
|
+
bodyMod.append = updates.bodyAppend;
|
|
22933
|
+
}
|
|
22934
|
+
if (Array.isArray(updates.bodyReplace) && updates.bodyReplace.length > 0) {
|
|
22935
|
+
bodyMod.replace = updates.bodyReplace;
|
|
22936
|
+
}
|
|
22937
|
+
if (Object.keys(bodyMod).length > 0) {
|
|
22938
|
+
updateInput.bodyMod = bodyMod;
|
|
22939
|
+
}
|
|
22940
|
+
let data;
|
|
22941
|
+
let errors;
|
|
22942
|
+
if (updates.ifMatch) {
|
|
22943
|
+
try {
|
|
22944
|
+
const res = await this.executeGraphQL(UPDATE_BEAN_MUTATION_WITH_IF_MATCH, {
|
|
22945
|
+
id: beanId,
|
|
22946
|
+
input: updateInput,
|
|
22947
|
+
ifMatch: updates.ifMatch
|
|
22948
|
+
});
|
|
22949
|
+
data = res.data;
|
|
22950
|
+
errors = res.errors;
|
|
22951
|
+
} catch (error48) {
|
|
22952
|
+
const message = error48.message || "";
|
|
22953
|
+
const unsupportedIfMatch = /unknown argument.*ifMatch|unknown field.*ifMatch|ifMatch.*not defined|field .*updateBean.* argument .*ifMatch/i.test(
|
|
22954
|
+
message
|
|
22955
|
+
);
|
|
22956
|
+
if (!unsupportedIfMatch) {
|
|
22957
|
+
throw error48;
|
|
22958
|
+
}
|
|
22959
|
+
const fallback = await this.executeGraphQL(UPDATE_BEAN_MUTATION, {
|
|
22960
|
+
id: beanId,
|
|
22961
|
+
input: updateInput
|
|
22962
|
+
});
|
|
22963
|
+
data = fallback.data;
|
|
22964
|
+
errors = fallback.errors;
|
|
22965
|
+
}
|
|
22966
|
+
} else {
|
|
22967
|
+
const res = await this.executeGraphQL(UPDATE_BEAN_MUTATION, {
|
|
22968
|
+
id: beanId,
|
|
22969
|
+
input: updateInput
|
|
22970
|
+
});
|
|
22971
|
+
data = res.data;
|
|
22972
|
+
errors = res.errors;
|
|
22973
|
+
}
|
|
22929
22974
|
if (errors && errors.length > 0) {
|
|
22930
22975
|
throw new Error(`GraphQL error: ${errors.map((e) => e.message).join(", ")}`);
|
|
22931
22976
|
}
|
|
@@ -22945,6 +22990,21 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
22945
22990
|
const content = await (0, import_promises.readFile)(configPath, "utf8");
|
|
22946
22991
|
return { configPath, content };
|
|
22947
22992
|
}
|
|
22993
|
+
async primeInstructions() {
|
|
22994
|
+
const { stdout } = await execFileAsync(this.cliPath, ["prime"], {
|
|
22995
|
+
cwd: this.workspaceRoot,
|
|
22996
|
+
env: this.getSafeEnv(),
|
|
22997
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
22998
|
+
timeout: 3e4
|
|
22999
|
+
});
|
|
23000
|
+
return stdout.trim();
|
|
23001
|
+
}
|
|
23002
|
+
async writeInstructions(instructions) {
|
|
23003
|
+
const instructionsPath = (0, import_node_path2.join)(this.workspaceRoot, ".github", "instructions", "beans-prime.instructions.md");
|
|
23004
|
+
await (0, import_promises.mkdir)((0, import_node_path2.dirname)(instructionsPath), { recursive: true });
|
|
23005
|
+
await (0, import_promises.writeFile)(instructionsPath, instructions, "utf8");
|
|
23006
|
+
return instructionsPath;
|
|
23007
|
+
}
|
|
22948
23008
|
async graphqlSchema() {
|
|
22949
23009
|
const { stdout } = await execFileAsync(this.cliPath, ["graphql", "--schema"], {
|
|
22950
23010
|
cwd: this.workspaceRoot,
|
|
@@ -31089,6 +31149,10 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
31089
31149
|
}
|
|
31090
31150
|
};
|
|
31091
31151
|
|
|
31152
|
+
// src/server/BeansMcpServer.ts
|
|
31153
|
+
var import_node_child_process2 = require("child_process");
|
|
31154
|
+
var import_node_util2 = require("util");
|
|
31155
|
+
|
|
31092
31156
|
// src/internal/queryHelpers.ts
|
|
31093
31157
|
function sortBeansInternal(beans, mode) {
|
|
31094
31158
|
const sorted = [...beans];
|
|
@@ -31144,15 +31208,23 @@ async function handleQueryOperation(backend, params) {
|
|
|
31144
31208
|
const { operation, mode, statuses, types, search, tags, writeToWorkspaceInstructions, includeClosed } = params;
|
|
31145
31209
|
if (operation === "llm_context") {
|
|
31146
31210
|
const graphqlSchema = typeof backend.graphqlSchema === "function" ? await backend.graphqlSchema() : "";
|
|
31147
|
-
|
|
31211
|
+
let generatedInstructions = "";
|
|
31212
|
+
if (typeof backend.primeInstructions === "function") {
|
|
31213
|
+
try {
|
|
31214
|
+
generatedInstructions = await backend.primeInstructions();
|
|
31215
|
+
} catch {
|
|
31216
|
+
generatedInstructions = "";
|
|
31217
|
+
}
|
|
31218
|
+
}
|
|
31219
|
+
const instructionsPath = writeToWorkspaceInstructions && typeof backend.writeInstructions === "function" ? await backend.writeInstructions(generatedInstructions) : null;
|
|
31148
31220
|
return {
|
|
31149
31221
|
content: [
|
|
31150
31222
|
{
|
|
31151
31223
|
type: "text",
|
|
31152
|
-
text: JSON.stringify({ graphqlSchema, generatedInstructions
|
|
31224
|
+
text: JSON.stringify({ graphqlSchema, generatedInstructions, instructionsPath }, null, 2)
|
|
31153
31225
|
}
|
|
31154
31226
|
],
|
|
31155
|
-
structuredContent: { graphqlSchema, generatedInstructions
|
|
31227
|
+
structuredContent: { graphqlSchema, generatedInstructions, instructionsPath }
|
|
31156
31228
|
};
|
|
31157
31229
|
}
|
|
31158
31230
|
if (operation === "open_config") {
|
|
@@ -31198,6 +31270,31 @@ async function handleQueryOperation(backend, params) {
|
|
|
31198
31270
|
structuredContent: { query: search, count: beans2.length, beans: beans2 }
|
|
31199
31271
|
};
|
|
31200
31272
|
}
|
|
31273
|
+
if (operation === "ready") {
|
|
31274
|
+
const allBeans = await backend.list();
|
|
31275
|
+
const byId = new Map(allBeans.map((bean) => [bean.id, bean]));
|
|
31276
|
+
const candidates = await backend.list({ status: normalizedStatuses, type: normalizedTypes, search });
|
|
31277
|
+
const readyBeans = candidates.filter((bean) => {
|
|
31278
|
+
if (bean.status !== "todo") {
|
|
31279
|
+
return false;
|
|
31280
|
+
}
|
|
31281
|
+
const blockedBy = bean.blockedByIds || [];
|
|
31282
|
+
if (blockedBy.length === 0) {
|
|
31283
|
+
return true;
|
|
31284
|
+
}
|
|
31285
|
+
return blockedBy.every((blockerId) => {
|
|
31286
|
+
const blocker = byId.get(blockerId);
|
|
31287
|
+
if (!blocker) {
|
|
31288
|
+
return false;
|
|
31289
|
+
}
|
|
31290
|
+
return blocker.status === "completed" || blocker.status === "scrapped";
|
|
31291
|
+
});
|
|
31292
|
+
});
|
|
31293
|
+
return {
|
|
31294
|
+
content: [{ type: "text", text: JSON.stringify({ count: readyBeans.length, beans: readyBeans }, null, 2) }],
|
|
31295
|
+
structuredContent: { count: readyBeans.length, beans: readyBeans }
|
|
31296
|
+
};
|
|
31297
|
+
}
|
|
31201
31298
|
const beans = await backend.list({ status: normalizedStatuses, type: normalizedTypes, search });
|
|
31202
31299
|
const sorted = sortBeansInternal(beans, mode ?? "status-priority-type-title");
|
|
31203
31300
|
return {
|
|
@@ -31220,7 +31317,7 @@ init_utils();
|
|
|
31220
31317
|
// package.json
|
|
31221
31318
|
var package_default = {
|
|
31222
31319
|
name: "@selfagency/beans-mcp",
|
|
31223
|
-
version: "0.
|
|
31320
|
+
version: "0.4.2",
|
|
31224
31321
|
private: false,
|
|
31225
31322
|
description: "MCP (Model Context Protocol) server for Beans issue tracker",
|
|
31226
31323
|
author: {
|
|
@@ -31236,6 +31333,7 @@ var package_default = {
|
|
|
31236
31333
|
type: "git",
|
|
31237
31334
|
url: "git+https://github.com/selfagency/beans-mcp.git"
|
|
31238
31335
|
},
|
|
31336
|
+
mcpName: "io.github.selfagency/beans-mcp",
|
|
31239
31337
|
keywords: [
|
|
31240
31338
|
"beans",
|
|
31241
31339
|
"mcp",
|
|
@@ -31274,18 +31372,18 @@ var package_default = {
|
|
|
31274
31372
|
devDependencies: {
|
|
31275
31373
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
31276
31374
|
"@octokit/rest": "^22.0.1",
|
|
31277
|
-
"@types/node": "
|
|
31278
|
-
"@vitest/coverage-v8": "^4.0
|
|
31279
|
-
"@vitest/ui": "4.0
|
|
31375
|
+
"@types/node": "25.5.0",
|
|
31376
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
31377
|
+
"@vitest/ui": "4.1.0",
|
|
31280
31378
|
husky: "^9.1.7",
|
|
31281
|
-
"lint-staged": "^16.
|
|
31379
|
+
"lint-staged": "^16.3.3",
|
|
31282
31380
|
ora: "^9.3.0",
|
|
31283
|
-
oxfmt: "^0.
|
|
31284
|
-
oxlint: "^1.
|
|
31285
|
-
"oxlint-tsgolint": "^0.
|
|
31381
|
+
oxfmt: "^0.40.0",
|
|
31382
|
+
oxlint: "^1.55.0",
|
|
31383
|
+
"oxlint-tsgolint": "^0.16.0",
|
|
31286
31384
|
tsup: "8.5.1",
|
|
31287
31385
|
typescript: "^5.9.3",
|
|
31288
|
-
vitest: "4.0
|
|
31386
|
+
vitest: "4.1.0",
|
|
31289
31387
|
zod: "4.3.6",
|
|
31290
31388
|
zx: "^8.8.5"
|
|
31291
31389
|
},
|
|
@@ -31301,6 +31399,45 @@ var package_default = {
|
|
|
31301
31399
|
};
|
|
31302
31400
|
|
|
31303
31401
|
// src/server/BeansMcpServer.ts
|
|
31402
|
+
var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
31403
|
+
var PACKAGE_VERSION = package_default.version ?? "0.0.0-dev";
|
|
31404
|
+
function getSafeCliEnv(env) {
|
|
31405
|
+
const whitelist = ["PATH", "HOME", "USER", "LANG", "LC_ALL", "LC_CTYPE", "SHELL"];
|
|
31406
|
+
const safeEnv = {};
|
|
31407
|
+
for (const key of whitelist) {
|
|
31408
|
+
if (env[key]) {
|
|
31409
|
+
safeEnv[key] = env[key];
|
|
31410
|
+
}
|
|
31411
|
+
}
|
|
31412
|
+
for (const key in env) {
|
|
31413
|
+
if (key.startsWith("BEANS_")) {
|
|
31414
|
+
safeEnv[key] = env[key];
|
|
31415
|
+
}
|
|
31416
|
+
}
|
|
31417
|
+
return safeEnv;
|
|
31418
|
+
}
|
|
31419
|
+
function extractVersionFromOutput(output) {
|
|
31420
|
+
const trimmed = output.trim();
|
|
31421
|
+
if (!trimmed) {
|
|
31422
|
+
return null;
|
|
31423
|
+
}
|
|
31424
|
+
const match = trimmed.match(/(?:^|[^\d])v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/);
|
|
31425
|
+
return match?.[1] ?? null;
|
|
31426
|
+
}
|
|
31427
|
+
async function detectBeansCliVersion(cliPath, workspaceRoot) {
|
|
31428
|
+
try {
|
|
31429
|
+
const { stdout, stderr } = await execFileAsync2(cliPath, ["version"], {
|
|
31430
|
+
cwd: workspaceRoot,
|
|
31431
|
+
env: getSafeCliEnv(process.env),
|
|
31432
|
+
maxBuffer: 1024 * 1024,
|
|
31433
|
+
timeout: 5e3
|
|
31434
|
+
});
|
|
31435
|
+
return extractVersionFromOutput(`${stdout}
|
|
31436
|
+
${stderr}`);
|
|
31437
|
+
} catch {
|
|
31438
|
+
return null;
|
|
31439
|
+
}
|
|
31440
|
+
}
|
|
31304
31441
|
async function getBeanById(backend, beanId) {
|
|
31305
31442
|
try {
|
|
31306
31443
|
const beans = await backend.list();
|
|
@@ -31320,7 +31457,35 @@ function initHandler(backend) {
|
|
|
31320
31457
|
};
|
|
31321
31458
|
}
|
|
31322
31459
|
function viewHandler(backend) {
|
|
31323
|
-
return async ({ beanId }) =>
|
|
31460
|
+
return async ({ beanId, beanIds }) => {
|
|
31461
|
+
const ids = Array.isArray(beanIds) && beanIds.length > 0 ? beanIds : beanId ? [beanId] : [];
|
|
31462
|
+
if (ids.length === 0) {
|
|
31463
|
+
throw new Error("Either beanId or beanIds must be provided");
|
|
31464
|
+
}
|
|
31465
|
+
if (ids.length === 1) {
|
|
31466
|
+
const bean = await getBeanById(backend, ids[0]);
|
|
31467
|
+
return makeTextAndStructured({ bean });
|
|
31468
|
+
}
|
|
31469
|
+
const beans = await backend.list();
|
|
31470
|
+
const byId = new Map(beans.map((b) => [b.id, b]));
|
|
31471
|
+
const found = ids.map((id) => byId.get(id)).filter(Boolean);
|
|
31472
|
+
const missingBeanIds = ids.filter((id) => !byId.has(id));
|
|
31473
|
+
return makeTextAndStructured({ beans: found, missingBeanIds, count: found.length, requestedCount: ids.length });
|
|
31474
|
+
};
|
|
31475
|
+
}
|
|
31476
|
+
async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
|
|
31477
|
+
const detectedBeansVersion = await detector(cliPath, workspaceRoot);
|
|
31478
|
+
if (!detectedBeansVersion) {
|
|
31479
|
+
console.error(
|
|
31480
|
+
`[beans-mcp] warning: unable to determine Beans CLI version from \`${cliPath}\`; proceeding without version compatibility checks.`
|
|
31481
|
+
);
|
|
31482
|
+
return;
|
|
31483
|
+
}
|
|
31484
|
+
if (detectedBeansVersion !== PACKAGE_VERSION) {
|
|
31485
|
+
console.error(
|
|
31486
|
+
`[beans-mcp] warning: version mismatch detected (beans=${detectedBeansVersion}, beans-mcp=${PACKAGE_VERSION}); continuing startup.`
|
|
31487
|
+
);
|
|
31488
|
+
}
|
|
31324
31489
|
}
|
|
31325
31490
|
function createHandler(backend) {
|
|
31326
31491
|
return async (input) => makeTextAndStructured({ bean: await backend.create(input) });
|
|
@@ -31356,17 +31521,56 @@ function updateHandler(backend) {
|
|
|
31356
31521
|
clearParent: input.clearParent,
|
|
31357
31522
|
blocking: input.blocking,
|
|
31358
31523
|
blockedBy: input.blockedBy,
|
|
31359
|
-
body: input.body
|
|
31524
|
+
body: input.body,
|
|
31525
|
+
bodyAppend: input.bodyAppend,
|
|
31526
|
+
bodyReplace: input.bodyReplace,
|
|
31527
|
+
ifMatch: input.ifMatch
|
|
31360
31528
|
})
|
|
31361
31529
|
});
|
|
31362
31530
|
}
|
|
31363
31531
|
function deleteHandler(backend) {
|
|
31364
|
-
return async ({ beanId, force }) => {
|
|
31365
|
-
const
|
|
31366
|
-
if (
|
|
31367
|
-
throw new Error("
|
|
31532
|
+
return async ({ beanId, beanIds, force }) => {
|
|
31533
|
+
const ids = Array.isArray(beanIds) && beanIds.length > 0 ? beanIds : beanId ? [beanId] : [];
|
|
31534
|
+
if (ids.length === 0) {
|
|
31535
|
+
throw new Error("Either beanId or beanIds must be provided");
|
|
31536
|
+
}
|
|
31537
|
+
if (ids.length === 1) {
|
|
31538
|
+
const bean = await getBeanById(backend, ids[0]);
|
|
31539
|
+
if (!force && bean.status !== "draft" && bean.status !== "scrapped") {
|
|
31540
|
+
throw new Error("Only draft and scrapped beans are deletable unless force=true");
|
|
31541
|
+
}
|
|
31542
|
+
return makeTextAndStructured(await backend.delete(ids[0]));
|
|
31368
31543
|
}
|
|
31369
|
-
|
|
31544
|
+
const beans = await backend.list();
|
|
31545
|
+
const byId = new Map(beans.map((b) => [b.id, b]));
|
|
31546
|
+
const results = [];
|
|
31547
|
+
for (const id of ids) {
|
|
31548
|
+
const bean = byId.get(id);
|
|
31549
|
+
if (!bean) {
|
|
31550
|
+
results.push({ beanId: id, deleted: false, error: "Bean not found" });
|
|
31551
|
+
continue;
|
|
31552
|
+
}
|
|
31553
|
+
if (!force && bean.status !== "draft" && bean.status !== "scrapped") {
|
|
31554
|
+
results.push({
|
|
31555
|
+
beanId: id,
|
|
31556
|
+
deleted: false,
|
|
31557
|
+
error: "Only draft and scrapped beans are deletable unless force=true"
|
|
31558
|
+
});
|
|
31559
|
+
continue;
|
|
31560
|
+
}
|
|
31561
|
+
try {
|
|
31562
|
+
await backend.delete(id);
|
|
31563
|
+
results.push({ beanId: id, deleted: true });
|
|
31564
|
+
} catch (error48) {
|
|
31565
|
+
results.push({ beanId: id, deleted: false, error: error48.message });
|
|
31566
|
+
}
|
|
31567
|
+
}
|
|
31568
|
+
return makeTextAndStructured({
|
|
31569
|
+
results,
|
|
31570
|
+
requestedCount: ids.length,
|
|
31571
|
+
deletedCount: results.filter((r) => r.deleted).length,
|
|
31572
|
+
failedCount: results.filter((r) => !r.deleted).length
|
|
31573
|
+
});
|
|
31370
31574
|
};
|
|
31371
31575
|
}
|
|
31372
31576
|
function queryHandler(backend) {
|
|
@@ -31427,7 +31631,12 @@ function registerTools(server, backend) {
|
|
|
31427
31631
|
{
|
|
31428
31632
|
title: "View Bean",
|
|
31429
31633
|
description: "Fetch full bean details by ID.",
|
|
31430
|
-
inputSchema: external_exports3.object({
|
|
31634
|
+
inputSchema: external_exports3.object({
|
|
31635
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31636
|
+
beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional()
|
|
31637
|
+
}).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
|
|
31638
|
+
message: "Either beanId or beanIds must be provided"
|
|
31639
|
+
}),
|
|
31431
31640
|
annotations: {
|
|
31432
31641
|
readOnlyHint: true,
|
|
31433
31642
|
destructiveHint: false,
|
|
@@ -31516,8 +31725,21 @@ function registerTools(server, backend) {
|
|
|
31516
31725
|
clearParent: external_exports3.boolean().optional(),
|
|
31517
31726
|
blocking: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
|
|
31518
31727
|
blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
|
|
31519
|
-
body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional()
|
|
31520
|
-
|
|
31728
|
+
body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
31729
|
+
bodyAppend: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
31730
|
+
bodyReplace: external_exports3.array(
|
|
31731
|
+
external_exports3.object({
|
|
31732
|
+
old: external_exports3.string().max(MAX_DESCRIPTION_LENGTH),
|
|
31733
|
+
new: external_exports3.string().max(MAX_DESCRIPTION_LENGTH)
|
|
31734
|
+
})
|
|
31735
|
+
).optional(),
|
|
31736
|
+
ifMatch: external_exports3.string().max(MAX_METADATA_LENGTH).optional()
|
|
31737
|
+
}).refine(
|
|
31738
|
+
(input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
|
|
31739
|
+
{
|
|
31740
|
+
message: "body cannot be combined with bodyAppend/bodyReplace"
|
|
31741
|
+
}
|
|
31742
|
+
),
|
|
31521
31743
|
annotations: {
|
|
31522
31744
|
readOnlyHint: false,
|
|
31523
31745
|
destructiveHint: false,
|
|
@@ -31533,8 +31755,11 @@ function registerTools(server, backend) {
|
|
|
31533
31755
|
title: "Delete Bean",
|
|
31534
31756
|
description: "Delete a bean (intended for draft/scrapped beans).",
|
|
31535
31757
|
inputSchema: external_exports3.object({
|
|
31536
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
31758
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31759
|
+
beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional(),
|
|
31537
31760
|
force: external_exports3.boolean().default(false)
|
|
31761
|
+
}).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
|
|
31762
|
+
message: "Either beanId or beanIds must be provided"
|
|
31538
31763
|
}),
|
|
31539
31764
|
annotations: {
|
|
31540
31765
|
readOnlyHint: false,
|
|
@@ -31551,7 +31776,7 @@ function registerTools(server, backend) {
|
|
|
31551
31776
|
title: "Query Beans",
|
|
31552
31777
|
description: "Unified query tool for refresh, filter, search, and sort operations.",
|
|
31553
31778
|
inputSchema: external_exports3.object({
|
|
31554
|
-
operation: external_exports3.enum(["refresh", "filter", "search", "sort", "llm_context", "open_config"]).default("refresh"),
|
|
31779
|
+
operation: external_exports3.enum(["refresh", "filter", "search", "sort", "ready", "llm_context", "open_config"]).default("refresh"),
|
|
31555
31780
|
mode: external_exports3.enum(["status-priority-type-title", "updated", "created", "id"]).optional(),
|
|
31556
31781
|
statuses: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
31557
31782
|
types: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
@@ -31633,6 +31858,12 @@ var MutableBackend = class {
|
|
|
31633
31858
|
openConfig() {
|
|
31634
31859
|
return this.inner.openConfig();
|
|
31635
31860
|
}
|
|
31861
|
+
primeInstructions() {
|
|
31862
|
+
return this.inner.primeInstructions?.() ?? Promise.resolve("");
|
|
31863
|
+
}
|
|
31864
|
+
writeInstructions(instructions) {
|
|
31865
|
+
return this.inner.writeInstructions?.(instructions) ?? Promise.resolve(null);
|
|
31866
|
+
}
|
|
31636
31867
|
graphqlSchema() {
|
|
31637
31868
|
return this.inner.graphqlSchema();
|
|
31638
31869
|
}
|
|
@@ -31670,7 +31901,7 @@ async function createBeansMcpServer(opts) {
|
|
|
31670
31901
|
const backend = opts.backend || new BeansCliBackend2(opts.workspaceRoot, opts.cliPath || "beans", opts.logDir);
|
|
31671
31902
|
const server = new McpServer({
|
|
31672
31903
|
name: opts.name || "beans-mcp-server",
|
|
31673
|
-
version: opts.version ||
|
|
31904
|
+
version: opts.version || PACKAGE_VERSION
|
|
31674
31905
|
});
|
|
31675
31906
|
registerTools(server, backend);
|
|
31676
31907
|
return { server, backend };
|
|
@@ -31742,17 +31973,17 @@ function parseCliArgs(argv) {
|
|
|
31742
31973
|
}
|
|
31743
31974
|
return { workspaceRoot, workspaceExplicit, cliPath, port, logDir };
|
|
31744
31975
|
}
|
|
31745
|
-
async function startBeansMcpServer(argv, _resolveRoots) {
|
|
31976
|
+
async function startBeansMcpServer(argv, _resolveRoots, _detectBeansVersion) {
|
|
31746
31977
|
const { BeansCliBackend: BeansCliBackend2 } = await Promise.resolve().then(() => (init_backend(), backend_exports));
|
|
31747
31978
|
const { StdioServerTransport: StdioServerTransport2 } = await Promise.resolve().then(() => (init_stdio2(), stdio_exports));
|
|
31748
31979
|
const { workspaceRoot, workspaceExplicit, cliPath, port, logDir } = parseCliArgs(argv);
|
|
31980
|
+
let effectiveWorkspaceRoot = workspaceRoot;
|
|
31749
31981
|
process.env.BEANS_VSCODE_MCP_PORT = String(port);
|
|
31750
31982
|
process.env.BEANS_MCP_PORT = String(port);
|
|
31751
31983
|
try {
|
|
31752
|
-
const version2 = package_default.version ?? "0.0.0-dev";
|
|
31753
31984
|
const workspaceLabel = workspaceExplicit ? workspaceRoot : "(auto from roots)";
|
|
31754
31985
|
console.error(
|
|
31755
|
-
`[beans-mcp] v${
|
|
31986
|
+
`[beans-mcp] v${PACKAGE_VERSION} starting (port=${port}, workspace=${workspaceLabel}, cli=${cliPath}, logDir=${logDir})`
|
|
31756
31987
|
);
|
|
31757
31988
|
} catch {
|
|
31758
31989
|
}
|
|
@@ -31769,13 +32000,17 @@ async function startBeansMcpServer(argv, _resolveRoots) {
|
|
|
31769
32000
|
const resolver = _resolveRoots ?? resolveWorkspaceFromRoots;
|
|
31770
32001
|
const rootPath = await resolver(server);
|
|
31771
32002
|
if (rootPath) {
|
|
31772
|
-
mutable.setInner(new BeansCliBackend2(rootPath, cliPath));
|
|
32003
|
+
mutable.setInner(new BeansCliBackend2(rootPath, cliPath, logDir));
|
|
32004
|
+
effectiveWorkspaceRoot = rootPath;
|
|
31773
32005
|
try {
|
|
31774
32006
|
console.error(`[beans-mcp] workspace resolved from roots: ${rootPath}`);
|
|
31775
32007
|
} catch {
|
|
31776
32008
|
}
|
|
31777
32009
|
}
|
|
31778
32010
|
}
|
|
32011
|
+
const beansVersionDetector = _detectBeansVersion ?? detectBeansCliVersion;
|
|
32012
|
+
void checkVersionCompatibility(cliPath, effectiveWorkspaceRoot, beansVersionDetector).catch(() => {
|
|
32013
|
+
});
|
|
31779
32014
|
}
|
|
31780
32015
|
|
|
31781
32016
|
// src/index.ts
|
|
@@ -69,12 +69,20 @@ interface BackendInterface {
|
|
|
69
69
|
blocking?: string[];
|
|
70
70
|
blockedBy?: string[];
|
|
71
71
|
body?: string;
|
|
72
|
+
bodyAppend?: string;
|
|
73
|
+
bodyReplace?: Array<{
|
|
74
|
+
old: string;
|
|
75
|
+
new: string;
|
|
76
|
+
}>;
|
|
77
|
+
ifMatch?: string;
|
|
72
78
|
}): Promise<BeanRecord>;
|
|
73
79
|
delete(beanId: string): Promise<Record<string, unknown>>;
|
|
74
80
|
openConfig(): Promise<{
|
|
75
81
|
configPath: string;
|
|
76
82
|
content: string;
|
|
77
83
|
}>;
|
|
84
|
+
primeInstructions?(): Promise<string>;
|
|
85
|
+
writeInstructions?(instructions: string): Promise<string | null>;
|
|
78
86
|
graphqlSchema(): Promise<string>;
|
|
79
87
|
readOutputLog(options?: {
|
|
80
88
|
lines?: number;
|
|
@@ -146,12 +154,20 @@ declare class BeansCliBackend implements BackendInterface {
|
|
|
146
154
|
blocking?: string[];
|
|
147
155
|
blockedBy?: string[];
|
|
148
156
|
body?: string;
|
|
157
|
+
bodyAppend?: string;
|
|
158
|
+
bodyReplace?: Array<{
|
|
159
|
+
old: string;
|
|
160
|
+
new: string;
|
|
161
|
+
}>;
|
|
162
|
+
ifMatch?: string;
|
|
149
163
|
}): Promise<BeanRecord>;
|
|
150
164
|
delete(beanId: string): Promise<Record<string, unknown>>;
|
|
151
165
|
openConfig(): Promise<{
|
|
152
166
|
configPath: string;
|
|
153
167
|
content: string;
|
|
154
168
|
}>;
|
|
169
|
+
primeInstructions(): Promise<string>;
|
|
170
|
+
writeInstructions(instructions: string): Promise<string>;
|
|
155
171
|
graphqlSchema(): Promise<string>;
|
|
156
172
|
readOutputLog(options?: {
|
|
157
173
|
lines?: number;
|
|
@@ -202,7 +218,9 @@ declare function parseCliArgs(argv: string[]): {
|
|
|
202
218
|
};
|
|
203
219
|
declare function startBeansMcpServer(argv: string[],
|
|
204
220
|
/** For testing only: override the roots resolver so tests can cover the setInner branch. */
|
|
205
|
-
_resolveRoots?: (server: McpServer) => Promise<string | null
|
|
221
|
+
_resolveRoots?: (server: McpServer) => Promise<string | null>,
|
|
222
|
+
/** For testing only: override Beans CLI version detection. */
|
|
223
|
+
_detectBeansVersion?: (cliPath: string, workspaceRoot: string) => Promise<string | null>): Promise<void>;
|
|
206
224
|
|
|
207
225
|
/**
|
|
208
226
|
* Check whether `target` is contained within `root` after resolving both paths.
|