@selfagency/beans-mcp 0.1.1 → 0.1.3
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/.github/dependabot.yml +11 -0
- package/.github/workflows/test.yml +4 -0
- package/CHANGELOG.md +20 -0
- package/codeql/codeql-custom-queries-actions/README.md +14 -0
- package/codeql/codeql-custom-queries-actions/codeql-pack.lock.yml +32 -0
- package/codeql/codeql-custom-queries-actions/codeql-pack.yml +7 -0
- package/codeql/codeql-custom-queries-actions/qlpack.yml +6 -0
- package/codeql/codeql-custom-queries-actions/queries/github-script-without-tojson.ql +18 -0
- package/codeql/codeql-custom-queries-actions/queries/strict-external-action-pinning.ql +18 -0
- package/codeql/codeql-custom-queries-javascript/README.md +14 -0
- package/codeql/codeql-custom-queries-javascript/codeql-pack.lock.yml +30 -0
- package/codeql/codeql-custom-queries-javascript/codeql-pack.yml +7 -0
- package/codeql/codeql-custom-queries-javascript/qlpack.yml +6 -0
- package/codeql/codeql-custom-queries-javascript/queries/child-process-shell-apis.ql +26 -0
- package/codeql/codeql-custom-queries-javascript/queries/innerhtml-assignment.ql +24 -0
- package/dist/beans-mcp-server.cjs +105 -4
- package/dist/beans-mcp-server.cjs.map +1 -1
- package/dist/index.cjs +105 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +105 -4
- package/dist/index.js.map +1 -1
- package/dist/package.json +2 -2
- package/package.json +3 -3
- package/src/server/BeansMcpServer.ts +23 -0
- package/src/server/backend.ts +6 -0
- package/src/test/handlers.unit.test.ts +27 -10
- package/src/test/utils.test.ts +44 -43
- package/src/utils.ts +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
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"
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.3] - 2026-02-27
|
|
11
|
+
|
|
12
|
+
**Full Changelog**: https://github.com/selfagency/beans-mcp/compare/v0.1.2...v0.1.3
|
|
13
|
+
|
|
14
|
+
_Source: changes from v0.1.2 to v0.1.3._
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [0.1.2] - 2026-02-27
|
|
18
|
+
|
|
19
|
+
## What's Changed
|
|
20
|
+
* feat: support body updates and de-duplicate MCP results by @selfagency in https://github.com/selfagency/beans-mcp/pull/1
|
|
21
|
+
|
|
22
|
+
## New Contributors
|
|
23
|
+
* @selfagency made their first contribution in https://github.com/selfagency/beans-mcp/pull/1
|
|
24
|
+
|
|
25
|
+
**Full Changelog**: https://github.com/selfagency/beans-mcp/compare/v0.1.1...v0.1.2
|
|
26
|
+
|
|
27
|
+
_Source: changes from v0.1.1 to v0.1.2._
|
|
28
|
+
|
|
29
|
+
|
|
10
30
|
## [0.1.1] - 2026-02-25
|
|
11
31
|
|
|
12
32
|
**Full Changelog**: https://github.com/selfagency/beans-mcp/compare/v0.1.0...v0.1.1
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Custom CodeQL Queries (GitHub Actions)
|
|
2
|
+
|
|
3
|
+
This pack adds repository-specific hardening checks for workflow security.
|
|
4
|
+
|
|
5
|
+
## Queries
|
|
6
|
+
|
|
7
|
+
- `queries/strict-external-action-pinning.ql`
|
|
8
|
+
- Flags external `uses:` steps that are not pinned to a full 40-character commit SHA.
|
|
9
|
+
- `queries/github-script-without-tojson.ql`
|
|
10
|
+
- Flags `actions/github-script` steps whose `script` argument does not appear to use `toJson(...)`.
|
|
11
|
+
|
|
12
|
+
## Why these checks
|
|
13
|
+
|
|
14
|
+
These checks focus on practical workflow hardening against supply-chain and interpolation risks while keeping alerts actionable.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
lockVersion: 1.0.0
|
|
3
|
+
dependencies:
|
|
4
|
+
codeql/actions-all:
|
|
5
|
+
version: 0.4.27
|
|
6
|
+
codeql/concepts:
|
|
7
|
+
version: 0.0.15
|
|
8
|
+
codeql/controlflow:
|
|
9
|
+
version: 2.0.25
|
|
10
|
+
codeql/dataflow:
|
|
11
|
+
version: 2.0.25
|
|
12
|
+
codeql/javascript-all:
|
|
13
|
+
version: 2.6.21
|
|
14
|
+
codeql/mad:
|
|
15
|
+
version: 1.0.41
|
|
16
|
+
codeql/regex:
|
|
17
|
+
version: 1.0.41
|
|
18
|
+
codeql/ssa:
|
|
19
|
+
version: 2.0.17
|
|
20
|
+
codeql/threat-models:
|
|
21
|
+
version: 1.0.41
|
|
22
|
+
codeql/tutorial:
|
|
23
|
+
version: 1.0.41
|
|
24
|
+
codeql/typetracking:
|
|
25
|
+
version: 2.0.25
|
|
26
|
+
codeql/util:
|
|
27
|
+
version: 2.0.28
|
|
28
|
+
codeql/xml:
|
|
29
|
+
version: 1.0.41
|
|
30
|
+
codeql/yaml:
|
|
31
|
+
version: 1.0.41
|
|
32
|
+
compiled: false
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @name github-script step without toJson hardening
|
|
3
|
+
* @description Inline scripts passed to actions/github-script should use toJson when handling expression values.
|
|
4
|
+
* @kind problem
|
|
5
|
+
* @problem.severity recommendation
|
|
6
|
+
* @precision medium
|
|
7
|
+
* @id actions/custom/github-script-without-tojson
|
|
8
|
+
* @tags actions
|
|
9
|
+
* security
|
|
10
|
+
* external/cwe/cwe-116
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import actions
|
|
14
|
+
|
|
15
|
+
from UsesStep step
|
|
16
|
+
where step.getCallee() = "actions/github-script"
|
|
17
|
+
select step,
|
|
18
|
+
"Review this github-script step for safe interpolation, validation, and least-privilege token use."
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @name Uses step not pinned to a full commit SHA
|
|
3
|
+
* @description Detects workflow/action `uses:` steps that are not pinned to a 40-character commit SHA.
|
|
4
|
+
* @kind problem
|
|
5
|
+
* @problem.severity warning
|
|
6
|
+
* @precision high
|
|
7
|
+
* @id actions/custom/strict-external-action-pinning
|
|
8
|
+
* @tags actions
|
|
9
|
+
* security
|
|
10
|
+
* external/cwe/cwe-829
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import actions
|
|
14
|
+
|
|
15
|
+
from UsesStep uses
|
|
16
|
+
where not uses.getVersion().regexpMatch("^[A-Fa-f0-9]{40}$")
|
|
17
|
+
select uses,
|
|
18
|
+
"Action version is not pinned to a full commit SHA; review and pin to an immutable revision."
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Custom CodeQL Queries (JavaScript / TypeScript)
|
|
2
|
+
|
|
3
|
+
This pack adds repository-specific hardening checks for Node.js/TypeScript code.
|
|
4
|
+
|
|
5
|
+
## Queries
|
|
6
|
+
|
|
7
|
+
- `queries/child-process-shell-apis.ql`
|
|
8
|
+
- Flags `exec`/`execSync` usage outside tests.
|
|
9
|
+
- `queries/innerhtml-assignment.ql`
|
|
10
|
+
- Flags assignment to `innerHTML` outside tests.
|
|
11
|
+
|
|
12
|
+
## Why these checks
|
|
13
|
+
|
|
14
|
+
These checks focus on high-signal security hotspots that often benefit from stricter review in extensions and tooling codebases.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
lockVersion: 1.0.0
|
|
3
|
+
dependencies:
|
|
4
|
+
codeql/concepts:
|
|
5
|
+
version: 0.0.15
|
|
6
|
+
codeql/controlflow:
|
|
7
|
+
version: 2.0.25
|
|
8
|
+
codeql/dataflow:
|
|
9
|
+
version: 2.0.25
|
|
10
|
+
codeql/javascript-all:
|
|
11
|
+
version: 2.6.21
|
|
12
|
+
codeql/mad:
|
|
13
|
+
version: 1.0.41
|
|
14
|
+
codeql/regex:
|
|
15
|
+
version: 1.0.41
|
|
16
|
+
codeql/ssa:
|
|
17
|
+
version: 2.0.17
|
|
18
|
+
codeql/threat-models:
|
|
19
|
+
version: 1.0.41
|
|
20
|
+
codeql/tutorial:
|
|
21
|
+
version: 1.0.41
|
|
22
|
+
codeql/typetracking:
|
|
23
|
+
version: 2.0.25
|
|
24
|
+
codeql/util:
|
|
25
|
+
version: 2.0.28
|
|
26
|
+
codeql/xml:
|
|
27
|
+
version: 1.0.41
|
|
28
|
+
codeql/yaml:
|
|
29
|
+
version: 1.0.41
|
|
30
|
+
compiled: false
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @name Use of shell-based child_process APIs
|
|
3
|
+
* @description Calls to exec/execSync run through a shell and are riskier than argument-array alternatives.
|
|
4
|
+
* @kind problem
|
|
5
|
+
* @problem.severity warning
|
|
6
|
+
* @precision high
|
|
7
|
+
* @id js/custom/child-process-shell-apis
|
|
8
|
+
* @tags security
|
|
9
|
+
* external/cwe/cwe-078
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import javascript
|
|
13
|
+
|
|
14
|
+
private predicate inUserSource(InvokeExpr call) {
|
|
15
|
+
not call.getTopLevel().getFile().getRelativePath().regexpMatch("(^|.*/)(test|tests|__tests__|mocks?)/.*")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
from CallExpr call
|
|
19
|
+
where
|
|
20
|
+
inUserSource(call) and
|
|
21
|
+
not call.getCallee() instanceof PropAccess and
|
|
22
|
+
call.getCalleeName() = ["exec", "execSync"]
|
|
23
|
+
select call,
|
|
24
|
+
"Shell-based process execution ($@) is harder to secure. Prefer execFile/spawn with argument arrays and strict input validation.",
|
|
25
|
+
call,
|
|
26
|
+
call.getCalleeName()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @name Assignment to innerHTML
|
|
3
|
+
* @description Assigning to innerHTML can introduce XSS risk if any untrusted content reaches the sink.
|
|
4
|
+
* @kind problem
|
|
5
|
+
* @problem.severity warning
|
|
6
|
+
* @precision medium
|
|
7
|
+
* @id js/custom/innerhtml-assignment
|
|
8
|
+
* @tags security
|
|
9
|
+
* external/cwe/cwe-079
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import javascript
|
|
13
|
+
|
|
14
|
+
private predicate inUserSource(AssignExpr assign) {
|
|
15
|
+
not assign.getTopLevel().getFile().getRelativePath().regexpMatch("(^|.*/)(test|tests|__tests__|mocks?)/.*")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
from AssignExpr assign, PropAccess lhs
|
|
19
|
+
where
|
|
20
|
+
inUserSource(assign) and
|
|
21
|
+
lhs = assign.getLhs() and
|
|
22
|
+
lhs.getPropertyName() = "innerHTML"
|
|
23
|
+
select assign,
|
|
24
|
+
"Assignment to innerHTML can be unsafe. Prefer textContent or sanitization before rendering HTML."
|
|
@@ -22735,8 +22735,7 @@ function isPathWithinRoot(root, target) {
|
|
|
22735
22735
|
}
|
|
22736
22736
|
function makeTextAndStructured(value) {
|
|
22737
22737
|
return {
|
|
22738
|
-
content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
|
|
22739
|
-
structuredContent: value
|
|
22738
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
|
|
22740
22739
|
};
|
|
22741
22740
|
}
|
|
22742
22741
|
var import_node_path;
|
|
@@ -22920,6 +22919,9 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
22920
22919
|
if (updates.blockedBy) {
|
|
22921
22920
|
updateInput.addBlockedBy = updates.blockedBy;
|
|
22922
22921
|
}
|
|
22922
|
+
if (updates.body !== void 0) {
|
|
22923
|
+
updateInput.body = updates.body;
|
|
22924
|
+
}
|
|
22923
22925
|
const { data, errors } = await this.executeGraphQL(UPDATE_BEAN_MUTATION, {
|
|
22924
22926
|
id: beanId,
|
|
22925
22927
|
input: updateInput
|
|
@@ -31197,6 +31199,91 @@ var MAX_PATH_LENGTH = 1024;
|
|
|
31197
31199
|
|
|
31198
31200
|
// src/server/BeansMcpServer.ts
|
|
31199
31201
|
init_utils();
|
|
31202
|
+
|
|
31203
|
+
// package.json
|
|
31204
|
+
var package_default = {
|
|
31205
|
+
name: "@selfagency/beans-mcp",
|
|
31206
|
+
version: "0.1.3",
|
|
31207
|
+
private: false,
|
|
31208
|
+
description: "MCP (Model Context Protocol) server for Beans issue tracker",
|
|
31209
|
+
author: {
|
|
31210
|
+
name: "Daniel Sieradski",
|
|
31211
|
+
email: "daniel@self.agency",
|
|
31212
|
+
url: "https://self.agency"
|
|
31213
|
+
},
|
|
31214
|
+
homepage: "https://github.com/hmans/beans",
|
|
31215
|
+
bugs: {
|
|
31216
|
+
url: "https://github.com/selfagency/beans-mcp/issues"
|
|
31217
|
+
},
|
|
31218
|
+
repository: {
|
|
31219
|
+
type: "git",
|
|
31220
|
+
url: "git+https://github.com/selfagency/beans-mcp.git"
|
|
31221
|
+
},
|
|
31222
|
+
keywords: [
|
|
31223
|
+
"beans",
|
|
31224
|
+
"mcp",
|
|
31225
|
+
"model-context-protocol",
|
|
31226
|
+
"issue-tracker",
|
|
31227
|
+
"ai"
|
|
31228
|
+
],
|
|
31229
|
+
license: "MIT",
|
|
31230
|
+
type: "module",
|
|
31231
|
+
exports: {
|
|
31232
|
+
".": {
|
|
31233
|
+
types: "./dist/index.d.ts",
|
|
31234
|
+
import: "./dist/index.js",
|
|
31235
|
+
require: "./dist/index.cjs"
|
|
31236
|
+
}
|
|
31237
|
+
},
|
|
31238
|
+
main: "./dist/index.cjs",
|
|
31239
|
+
module: "./dist/index.js",
|
|
31240
|
+
types: "./dist/index.d.ts",
|
|
31241
|
+
bin: {
|
|
31242
|
+
"beans-mcp": "dist/beans-mcp-server.cjs"
|
|
31243
|
+
},
|
|
31244
|
+
scripts: {
|
|
31245
|
+
build: "tsup",
|
|
31246
|
+
format: "oxfmt",
|
|
31247
|
+
"lint:fix": "oxlint --fix",
|
|
31248
|
+
lint: "oxlint",
|
|
31249
|
+
postbuild: "node ./scripts/write-dist-package.js",
|
|
31250
|
+
prepare: "husky",
|
|
31251
|
+
release: "zx ./scripts/release.js",
|
|
31252
|
+
"test:coverage": "vitest run --coverage",
|
|
31253
|
+
"test:watch": "vitest",
|
|
31254
|
+
test: "vitest run",
|
|
31255
|
+
"type-check": "tsc --noEmit"
|
|
31256
|
+
},
|
|
31257
|
+
devDependencies: {
|
|
31258
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
31259
|
+
"@octokit/rest": "^22.0.1",
|
|
31260
|
+
"@types/node": "^20.19.0",
|
|
31261
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
31262
|
+
"@vitest/ui": "4.0.18",
|
|
31263
|
+
husky: "^9.1.7",
|
|
31264
|
+
"lint-staged": "^16.2.7",
|
|
31265
|
+
ora: "^9.3.0",
|
|
31266
|
+
oxfmt: "^0.35.0",
|
|
31267
|
+
oxlint: "^1.50.0",
|
|
31268
|
+
"oxlint-tsgolint": "^0.15.0",
|
|
31269
|
+
tsup: "8.5.1",
|
|
31270
|
+
typescript: "^5.9.3",
|
|
31271
|
+
vitest: "4.0.18",
|
|
31272
|
+
zod: "4.3.6",
|
|
31273
|
+
zx: "^8.8.5"
|
|
31274
|
+
},
|
|
31275
|
+
engines: {
|
|
31276
|
+
node: ">=18"
|
|
31277
|
+
},
|
|
31278
|
+
"lint-staged": {
|
|
31279
|
+
"src/**/*.ts": [
|
|
31280
|
+
"pnpm run lint:fix",
|
|
31281
|
+
"pnpm run format"
|
|
31282
|
+
]
|
|
31283
|
+
}
|
|
31284
|
+
};
|
|
31285
|
+
|
|
31286
|
+
// src/server/BeansMcpServer.ts
|
|
31200
31287
|
async function getBeanById(backend, beanId) {
|
|
31201
31288
|
try {
|
|
31202
31289
|
const beans = await backend.list();
|
|
@@ -31251,7 +31338,8 @@ function updateHandler(backend) {
|
|
|
31251
31338
|
parent: input.parent,
|
|
31252
31339
|
clearParent: input.clearParent,
|
|
31253
31340
|
blocking: input.blocking,
|
|
31254
|
-
blockedBy: input.blockedBy
|
|
31341
|
+
blockedBy: input.blockedBy,
|
|
31342
|
+
body: input.body
|
|
31255
31343
|
})
|
|
31256
31344
|
});
|
|
31257
31345
|
}
|
|
@@ -31410,7 +31498,8 @@ function registerTools(server, backend) {
|
|
|
31410
31498
|
parent: external_exports3.string().max(MAX_ID_LENGTH).optional(),
|
|
31411
31499
|
clearParent: external_exports3.boolean().optional(),
|
|
31412
31500
|
blocking: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
|
|
31413
|
-
blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional()
|
|
31501
|
+
blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
|
|
31502
|
+
body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional()
|
|
31414
31503
|
}),
|
|
31415
31504
|
annotations: {
|
|
31416
31505
|
readOnlyHint: false,
|
|
@@ -31642,6 +31731,14 @@ async function startBeansMcpServer(argv, _resolveRoots) {
|
|
|
31642
31731
|
const { workspaceRoot, workspaceExplicit, cliPath, port, logDir } = parseCliArgs(argv);
|
|
31643
31732
|
process.env.BEANS_VSCODE_MCP_PORT = String(port);
|
|
31644
31733
|
process.env.BEANS_MCP_PORT = String(port);
|
|
31734
|
+
try {
|
|
31735
|
+
const version2 = package_default.version ?? "0.0.0-dev";
|
|
31736
|
+
const workspaceLabel = workspaceExplicit ? workspaceRoot : "(auto from roots)";
|
|
31737
|
+
console.error(
|
|
31738
|
+
`[beans-mcp] v${version2} starting (port=${port}, workspace=${workspaceLabel}, cli=${cliPath}, logDir=${logDir})`
|
|
31739
|
+
);
|
|
31740
|
+
} catch {
|
|
31741
|
+
}
|
|
31645
31742
|
const mutable = new MutableBackend(new BeansCliBackend2(workspaceRoot, cliPath, logDir));
|
|
31646
31743
|
const { server } = await createBeansMcpServer({
|
|
31647
31744
|
workspaceRoot,
|
|
@@ -31656,6 +31753,10 @@ async function startBeansMcpServer(argv, _resolveRoots) {
|
|
|
31656
31753
|
const rootPath = await resolver(server);
|
|
31657
31754
|
if (rootPath) {
|
|
31658
31755
|
mutable.setInner(new BeansCliBackend2(rootPath, cliPath));
|
|
31756
|
+
try {
|
|
31757
|
+
console.error(`[beans-mcp] workspace resolved from roots: ${rootPath}`);
|
|
31758
|
+
} catch {
|
|
31759
|
+
}
|
|
31659
31760
|
}
|
|
31660
31761
|
}
|
|
31661
31762
|
}
|