@procedures/mcp-dev-tools 0.3.0 → 0.5.0
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/CHANGELOG.md +55 -0
- package/README.md +24 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/host.d.ts +5 -0
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +13 -1
- package/dist/host.js.map +1 -1
- package/package.json +6 -5
- package/src/config.ts +9 -0
- package/src/host.ts +14 -1
package/dist/config.d.ts
CHANGED
|
@@ -33,6 +33,12 @@ export interface McpServerConfig {
|
|
|
33
33
|
* true: a program with an error does not start, so no step of it writes.
|
|
34
34
|
*/
|
|
35
35
|
programCheck?: boolean | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* The folder that confines each path of the `fs.*` procedures of the host (roadmap R33). The
|
|
38
|
+
* nested `fs.*` calls of the tools (`lib.scan`, `cue.add`) are confined too. A path outside
|
|
39
|
+
* gives `FORBIDDEN` with the reason `outside-root`. No value: no root.
|
|
40
|
+
*/
|
|
41
|
+
fsRoot?: string | undefined;
|
|
36
42
|
}
|
|
37
43
|
/**
|
|
38
44
|
* Load configuration from environment variables.
|
|
@@ -46,6 +52,7 @@ export interface McpServerConfig {
|
|
|
46
52
|
* - MCP_READ_ONLY: "true" or "1" gives only the tools that change nothing (default: false)
|
|
47
53
|
* - MCP_HTTP_PORT, MCP_HTTP_HOST, MCP_HTTP_TOKEN: serve over Streamable HTTP, not stdio
|
|
48
54
|
* - MCP_PROGRAM_CHECK: "false" or "0" starts a program without the check before the run
|
|
55
|
+
* - MCP_FS_ROOT: the folder that confines each path of the fs.* procedures (default: no root)
|
|
49
56
|
*/
|
|
50
57
|
/** The default time limit of a tool call: a build or a test run can take minutes. */
|
|
51
58
|
export declare const DEFAULT_TOOL_TIMEOUT_MS = 600000;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,sEAAsE;IACtE,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GAAG,SAAS,CAAC;IAC3F;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,sEAAsE;IACtE,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GAAG,SAAS,CAAC;IAC3F;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED;;;;;;;;;;;;;GAaG;AACH,qFAAqF;AACrF,eAAO,MAAM,uBAAuB,SAAU,CAAC;AAE/C,oGAAoG;AACpG,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAI9D;AAED,wBAAgB,UAAU,IAAI,eAAe,CAwB5C"}
|
package/dist/config.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* - MCP_READ_ONLY: "true" or "1" gives only the tools that change nothing (default: false)
|
|
16
16
|
* - MCP_HTTP_PORT, MCP_HTTP_HOST, MCP_HTTP_TOKEN: serve over Streamable HTTP, not stdio
|
|
17
17
|
* - MCP_PROGRAM_CHECK: "false" or "0" starts a program without the check before the run
|
|
18
|
+
* - MCP_FS_ROOT: the folder that confines each path of the fs.* procedures (default: no root)
|
|
18
19
|
*/
|
|
19
20
|
/** The default time limit of a tool call: a build or a test run can take minutes. */
|
|
20
21
|
export const DEFAULT_TOOL_TIMEOUT_MS = 600_000;
|
|
@@ -28,6 +29,7 @@ export function parseTimeout(value) {
|
|
|
28
29
|
export function loadConfig() {
|
|
29
30
|
const bundlesEnv = process.env["MCP_BUNDLES"] ?? "@procedures/bundle-mcp";
|
|
30
31
|
const bundles = bundlesEnv.split(",").map((b) => b.trim()).filter(Boolean);
|
|
32
|
+
const fsRoot = process.env["MCP_FS_ROOT"]?.trim();
|
|
31
33
|
return {
|
|
32
34
|
bundles,
|
|
33
35
|
serverName: process.env["MCP_SERVER_NAME"] ?? "impl-mcp-dev",
|
|
@@ -36,6 +38,7 @@ export function loadConfig() {
|
|
|
36
38
|
toolTimeoutMs: parseTimeout(process.env["MCP_TOOL_TIMEOUT_MS"]),
|
|
37
39
|
readOnly: process.env["MCP_READ_ONLY"] === "true" || process.env["MCP_READ_ONLY"] === "1",
|
|
38
40
|
programCheck: !(process.env["MCP_PROGRAM_CHECK"] === "false" || process.env["MCP_PROGRAM_CHECK"] === "0"),
|
|
41
|
+
...(fsRoot ? { fsRoot } : {}),
|
|
39
42
|
...(process.env["MCP_HTTP_PORT"]
|
|
40
43
|
? {
|
|
41
44
|
http: {
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAoCH;;;;;;;;;;;;;GAaG;AACH,qFAAqF;AACrF,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,CAAC;AAE/C,oGAAoG;AACpG,MAAM,UAAU,YAAY,CAAC,KAAyB;IACpD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,uBAAuB,CAAC;IAC/E,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACzB,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,uBAAuB,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,wBAAwB,CAAC;IAC1E,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3E,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC;IAElD,OAAO;QACL,OAAO;QACP,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,cAAc;QAC5D,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,OAAO;QAC3D,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,MAAM;QAC1C,aAAa,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QAC/D,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,GAAG;QACzF,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,KAAK,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,KAAK,GAAG,CAAC;QACzG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;YAC9B,CAAC,CAAC;gBACE,IAAI,EAAE;oBACJ,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;oBAC1C,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC/E,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACnF;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC"}
|
package/dist/host.d.ts
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
*
|
|
10
10
|
* `server.ts` (the file in the MCP configuration of Claude Code) and `mark mcp` both start the
|
|
11
11
|
* server with `startDevToolsServer()`.
|
|
12
|
+
*
|
|
13
|
+
* `MCP_FS_ROOT` confines the `fs.*` procedures to one folder. The host serves no remote events:
|
|
14
|
+
* its transport carries only MCP, and `events.subscribe` is not a tool.
|
|
12
15
|
*/
|
|
13
16
|
import { ProcedureServer, type HostPolicy, type ProcedureModule, type ProcedureRegistry, type ToolSurface } from "@procedures/client";
|
|
14
17
|
import { type BundleSurfaceModule } from "@procedures/mcp-server";
|
|
@@ -31,6 +34,8 @@ export interface DevToolsHost {
|
|
|
31
34
|
surface: ToolSurface | undefined;
|
|
32
35
|
policy: HostPolicy;
|
|
33
36
|
server: ProcedureServer;
|
|
37
|
+
/** The absolute path of the root of the `fs.*` procedures (`MCP_FS_ROOT`), or undefined. */
|
|
38
|
+
fsRoot?: string | undefined;
|
|
34
39
|
}
|
|
35
40
|
/** This function builds the host. It starts no transport. */
|
|
36
41
|
export declare function createDevToolsHost(config?: McpServerConfig): Promise<DevToolsHost>;
|
package/dist/host.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAEL,eAAe,EAQf,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAqC,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAErG,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAQ/D,sDAAsD;AACtD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,kFAAkF;AAClF,wBAAsB,WAAW,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAgBpF;AAaD,kFAAkF;AAClF,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,eAAe,CAAC;IACxB,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,6DAA6D;AAC7D,wBAAsB,kBAAkB,CAAC,MAAM,GAAE,eAA8B,GAAG,OAAO,CAAC,YAAY,CAAC,CAqCtG;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,GAAE,eAA8B,GAAG,OAAO,CAAC,YAAY,CAAC,CAyCvG"}
|
package/dist/host.js
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
*
|
|
10
10
|
* `server.ts` (the file in the MCP configuration of Claude Code) and `mark mcp` both start the
|
|
11
11
|
* server with `startDevToolsServer()`.
|
|
12
|
+
*
|
|
13
|
+
* `MCP_FS_ROOT` confines the `fs.*` procedures to one folder. The host serves no remote events:
|
|
14
|
+
* its transport carries only MCP, and `events.subscribe` is not a tool.
|
|
12
15
|
*/
|
|
13
16
|
import { PROCEDURE_REGISTRY, ProcedureServer, createRegistry, registerClientProcedures, capabilityRefusal, narrowCapability, surfaceRule, hostPolicy, procedureServerOptions, } from "@procedures/client";
|
|
14
17
|
import { McpServerTransport, mergeSurfaces } from "@procedures/mcp-server";
|
|
@@ -49,6 +52,13 @@ async function loadBundlesGlobally(bundles) {
|
|
|
49
52
|
export async function createDevToolsHost(config = loadConfig()) {
|
|
50
53
|
const loaded = await loadBundles(config.bundles);
|
|
51
54
|
const registry = loaded.global ? PROCEDURE_REGISTRY : createRegistry(...loaded.modules);
|
|
55
|
+
// MCP_FS_ROOT (roadmap R33): each fs.* procedure of the registry confines its paths, so the
|
|
56
|
+
// nested fs calls of the tools are confined too. It adds no procedure: the tools stay the same.
|
|
57
|
+
let fsRoot;
|
|
58
|
+
if (config.fsRoot !== undefined) {
|
|
59
|
+
const { confineFsProcedures } = await import("@procedures/fs/module");
|
|
60
|
+
fsRoot = confineFsProcedures(registry, config.fsRoot).root;
|
|
61
|
+
}
|
|
52
62
|
// Bundles without mcpNamespaces say nothing about their tools: then every procedure is a tool
|
|
53
63
|
const surface = mergeSurfaces(loaded.surfaces);
|
|
54
64
|
// Read only: the tools are the procedures that the capability permits, so the list shows only
|
|
@@ -76,7 +86,7 @@ export async function createDevToolsHost(config = loadConfig()) {
|
|
|
76
86
|
...(programCheck.length > 0 ? { interceptors: programCheck } : {}),
|
|
77
87
|
});
|
|
78
88
|
const server = new ProcedureServer(procedureServerOptions(policy, registry));
|
|
79
|
-
return { registry, surface, policy, server };
|
|
89
|
+
return { registry, surface, policy, server, ...(fsRoot !== undefined ? { fsRoot } : {}) };
|
|
80
90
|
}
|
|
81
91
|
/**
|
|
82
92
|
* This function starts the dev-tools MCP server over stdio, or over Streamable HTTP with
|
|
@@ -118,6 +128,8 @@ export async function startDevToolsServer(config = loadConfig()) {
|
|
|
118
128
|
if (config.debug) {
|
|
119
129
|
console.error(`[mcp-server] Started with ${transport.getState().toolCount} tools`);
|
|
120
130
|
}
|
|
131
|
+
if (host.fsRoot !== undefined)
|
|
132
|
+
console.error(`[mcp-server] fs.* refuses each path outside ${host.fsRoot}`);
|
|
121
133
|
const url = transport.getState().url;
|
|
122
134
|
if (url)
|
|
123
135
|
console.error(`[mcp-server] Streamable HTTP at ${url}${config.http?.token ? " (send Authorization: Bearer <token>)" : ""}`);
|
package/dist/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,wBAAwB,EACxB,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,sBAAsB,GAKvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAA4B,MAAM,wBAAwB,CAAC;AACrG,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAwB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AAkBxC,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAA0B;IAC1D,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAC3C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,OAA4B,CAAC;QACjC,IAAI,CAAC;YACH,sEAAsE;YACtE,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC,CAAwB,CAAC;QACtE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,OAAO;YAAE,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAC9C,CAAC;AAED,mFAAmF;AACnF,KAAK,UAAU,mBAAmB,CAAC,OAA0B;IAC3D,qGAAqG;IACrG,wBAAwB,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAC3C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,GAAG,MAAM,cAAc,CAAC,CAAwB,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACjD,CAAC;AAYD,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAM,GAAoB,UAAU,EAAE;IAC7E,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACxF,4FAA4F;IAC5F,gGAAgG;IAChG,IAAI,MAA0B,CAAC;IAC/B,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC;QACtE,MAAM,GAAG,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;IAC7D,CAAC;IACD,8FAA8F;IAC9F,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/C,8FAA8F;IAC9F,+CAA+C;IAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC;IAC1C,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnG,MAAM,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,MAAM,MAAM,GAAG,UAAU;QACvB,CAAC,CAAC,CAAC,IAAc,EAAW,EAAE;YAC1B,IAAI,aAAa,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;gBAAE,OAAO,KAAK,CAAC;YACxD,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACrC,OAAO,SAAS,KAAK,SAAS,IAAI,iBAAiB,CAAC,SAAS,EAAE,UAAU,CAAC,KAAK,SAAS,CAAC;QAC3F,CAAC;QACH,CAAC,CAAC,OAAO,CAAC;IACZ,+FAA+F;IAC/F,6CAA6C;IAC7C,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACpG,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,uBAAuB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;IACtH,MAAM,MAAM,GAAG,UAAU,CAAC;QACxB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,WAAW,EAAE,IAAI;QACjB,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC;QACjC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,GAAG,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnE,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,sBAAsB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7E,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC5F,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAM,GAAoB,UAAU,EAAE;IAC9E,+FAA+F;IAC/F,6EAA6E;IAC7E,+CAA+C;IAC/C,OAAO,CAAC,GAAG,GAAG,CAAC,GAAG,IAAe,EAAQ,EAAE;QACzC,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IACzB,CAAC,CAAC;IACF,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACvF,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE;QACpD,GAAG,CAAC,MAAM,CAAC,IAAI;YACb,CAAC,CAAC;gBACE,SAAS,EAAE,MAAe;gBAC1B,WAAW,EAAE;oBACX,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI;oBACtB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACvD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC3D;aACF;YACH,CAAC,CAAC,EAAE,SAAS,EAAE,OAAgB,EAAE,CAAC;QACpC,UAAU,EAAE;YACV,IAAI,EAAE,MAAM,CAAC,UAAU;YACvB,OAAO,EAAE,MAAM,CAAC,aAAa;SAC9B;QACD,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvD,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;IACpC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IAE1B,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,CAAC,KAAK,CAAC,6BAA6B,SAAS,CAAC,QAAQ,EAAE,CAAC,SAAS,QAAQ,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,CAAC,KAAK,CAAC,+CAA+C,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3G,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC;IACrC,IAAI,GAAG;QAAE,OAAO,CAAC,KAAK,CAAC,mCAAmC,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,uCAAuC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACrI,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@procedures/mcp-dev-tools",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"description": "A ready-to-use MCP server for Claude Code and Claude Desktop: 91 developer tools (docker, mongo, sqlite, cue, vitest, workspace) with a strict call policy.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mcp",
|
|
@@ -53,10 +53,11 @@
|
|
|
53
53
|
"provenance": true
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@procedures/bundle-mcp": "0.
|
|
57
|
-
"@procedures/client": "0.
|
|
58
|
-
"@procedures/compiler": "0.
|
|
59
|
-
"@procedures/
|
|
56
|
+
"@procedures/bundle-mcp": "0.5.0",
|
|
57
|
+
"@procedures/client": "0.5.0",
|
|
58
|
+
"@procedures/compiler": "0.5.0",
|
|
59
|
+
"@procedures/fs": "0.5.0",
|
|
60
|
+
"@procedures/mcp-server": "0.5.0"
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
63
|
"@mark1russell7/cue": "^1.0.0",
|
package/src/config.ts
CHANGED
|
@@ -30,6 +30,12 @@ export interface McpServerConfig {
|
|
|
30
30
|
* true: a program with an error does not start, so no step of it writes.
|
|
31
31
|
*/
|
|
32
32
|
programCheck?: boolean | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* The folder that confines each path of the `fs.*` procedures of the host (roadmap R33). The
|
|
35
|
+
* nested `fs.*` calls of the tools (`lib.scan`, `cue.add`) are confined too. A path outside
|
|
36
|
+
* gives `FORBIDDEN` with the reason `outside-root`. No value: no root.
|
|
37
|
+
*/
|
|
38
|
+
fsRoot?: string | undefined;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
/**
|
|
@@ -44,6 +50,7 @@ export interface McpServerConfig {
|
|
|
44
50
|
* - MCP_READ_ONLY: "true" or "1" gives only the tools that change nothing (default: false)
|
|
45
51
|
* - MCP_HTTP_PORT, MCP_HTTP_HOST, MCP_HTTP_TOKEN: serve over Streamable HTTP, not stdio
|
|
46
52
|
* - MCP_PROGRAM_CHECK: "false" or "0" starts a program without the check before the run
|
|
53
|
+
* - MCP_FS_ROOT: the folder that confines each path of the fs.* procedures (default: no root)
|
|
47
54
|
*/
|
|
48
55
|
/** The default time limit of a tool call: a build or a test run can take minutes. */
|
|
49
56
|
export const DEFAULT_TOOL_TIMEOUT_MS = 600_000;
|
|
@@ -58,6 +65,7 @@ export function parseTimeout(value: string | undefined): number {
|
|
|
58
65
|
export function loadConfig(): McpServerConfig {
|
|
59
66
|
const bundlesEnv = process.env["MCP_BUNDLES"] ?? "@procedures/bundle-mcp";
|
|
60
67
|
const bundles = bundlesEnv.split(",").map((b) => b.trim()).filter(Boolean);
|
|
68
|
+
const fsRoot = process.env["MCP_FS_ROOT"]?.trim();
|
|
61
69
|
|
|
62
70
|
return {
|
|
63
71
|
bundles,
|
|
@@ -67,6 +75,7 @@ export function loadConfig(): McpServerConfig {
|
|
|
67
75
|
toolTimeoutMs: parseTimeout(process.env["MCP_TOOL_TIMEOUT_MS"]),
|
|
68
76
|
readOnly: process.env["MCP_READ_ONLY"] === "true" || process.env["MCP_READ_ONLY"] === "1",
|
|
69
77
|
programCheck: !(process.env["MCP_PROGRAM_CHECK"] === "false" || process.env["MCP_PROGRAM_CHECK"] === "0"),
|
|
78
|
+
...(fsRoot ? { fsRoot } : {}),
|
|
70
79
|
...(process.env["MCP_HTTP_PORT"]
|
|
71
80
|
? {
|
|
72
81
|
http: {
|
package/src/host.ts
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
*
|
|
10
10
|
* `server.ts` (the file in the MCP configuration of Claude Code) and `mark mcp` both start the
|
|
11
11
|
* server with `startDevToolsServer()`.
|
|
12
|
+
*
|
|
13
|
+
* `MCP_FS_ROOT` confines the `fs.*` procedures to one folder. The host serves no remote events:
|
|
14
|
+
* its transport carries only MCP, and `events.subscribe` is not a tool.
|
|
12
15
|
*/
|
|
13
16
|
|
|
14
17
|
import {
|
|
@@ -83,12 +86,21 @@ export interface DevToolsHost {
|
|
|
83
86
|
surface: ToolSurface | undefined;
|
|
84
87
|
policy: HostPolicy;
|
|
85
88
|
server: ProcedureServer;
|
|
89
|
+
/** The absolute path of the root of the `fs.*` procedures (`MCP_FS_ROOT`), or undefined. */
|
|
90
|
+
fsRoot?: string | undefined;
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
/** This function builds the host. It starts no transport. */
|
|
89
94
|
export async function createDevToolsHost(config: McpServerConfig = loadConfig()): Promise<DevToolsHost> {
|
|
90
95
|
const loaded = await loadBundles(config.bundles);
|
|
91
96
|
const registry = loaded.global ? PROCEDURE_REGISTRY : createRegistry(...loaded.modules);
|
|
97
|
+
// MCP_FS_ROOT (roadmap R33): each fs.* procedure of the registry confines its paths, so the
|
|
98
|
+
// nested fs calls of the tools are confined too. It adds no procedure: the tools stay the same.
|
|
99
|
+
let fsRoot: string | undefined;
|
|
100
|
+
if (config.fsRoot !== undefined) {
|
|
101
|
+
const { confineFsProcedures } = await import("@procedures/fs/module");
|
|
102
|
+
fsRoot = confineFsProcedures(registry, config.fsRoot).root;
|
|
103
|
+
}
|
|
92
104
|
// Bundles without mcpNamespaces say nothing about their tools: then every procedure is a tool
|
|
93
105
|
const surface = mergeSurfaces(loaded.surfaces);
|
|
94
106
|
// Read only: the tools are the procedures that the capability permits, so the list shows only
|
|
@@ -115,7 +127,7 @@ export async function createDevToolsHost(config: McpServerConfig = loadConfig())
|
|
|
115
127
|
...(programCheck.length > 0 ? { interceptors: programCheck } : {}),
|
|
116
128
|
});
|
|
117
129
|
const server = new ProcedureServer(procedureServerOptions(policy, registry));
|
|
118
|
-
return { registry, surface, policy, server };
|
|
130
|
+
return { registry, surface, policy, server, ...(fsRoot !== undefined ? { fsRoot } : {}) };
|
|
119
131
|
}
|
|
120
132
|
|
|
121
133
|
/**
|
|
@@ -160,6 +172,7 @@ export async function startDevToolsServer(config: McpServerConfig = loadConfig()
|
|
|
160
172
|
if (config.debug) {
|
|
161
173
|
console.error(`[mcp-server] Started with ${transport.getState().toolCount} tools`);
|
|
162
174
|
}
|
|
175
|
+
if (host.fsRoot !== undefined) console.error(`[mcp-server] fs.* refuses each path outside ${host.fsRoot}`);
|
|
163
176
|
const url = transport.getState().url;
|
|
164
177
|
if (url) console.error(`[mcp-server] Streamable HTTP at ${url}${config.http?.token ? " (send Authorization: Bearer <token>)" : ""}`);
|
|
165
178
|
return host;
|