@valbuild/mcp 0.129.0 → 0.131.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 CHANGED
@@ -1,5 +1,80 @@
1
1
  # @valbuild/mcp
2
2
 
3
+ ## 0.131.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`0d5857b`](https://github.com/valbuild/val/commit/0d5857b731e11f7e6a011f79297df6485908c31f)]:
8
+ - @valbuild/server@0.131.0
9
+
10
+ ## 0.130.0
11
+
12
+ ### Minor Changes
13
+
14
+ - [#684](https://github.com/valbuild/val/pull/684) [`cab4098`](https://github.com/valbuild/val/commit/cab4098969585977b8d7574e86d66fcb01cb1d75) Thanks [@freekh](https://github.com/freekh)! - A third ValOps mode, for a host that already holds its own source
15
+
16
+ EXPERIMENTAL. `fs` mode assumes a working tree it can watch and write; `http`
17
+ mode assumes Val's content service owns the patch chain and that a commit is a
18
+ git commit. A host that builds and publishes its own output is neither: it holds
19
+ the source already, it has nowhere to watch, and its "commit" is a new build.
20
+
21
+ Forcing such a host into `fs` mode cost three things, all now fixed: `/stat`
22
+ long-polled against watchers that could never fire, burning CPU for the whole
23
+ hold to learn nothing;
24
+ `/api/val/enable` 500'd; and every read of a `.val.ts` went through a shimmed
25
+ filesystem when the host could simply hand the source over.
26
+
27
+ `ValOpsMemory` takes the source as `sourceFiles`, refuses the local binary
28
+ members by name — this configuration uses Val's remote files — and answers the
29
+ history members with the same closed `not-supported-in-fs-mode` error `ValOpsFS`
30
+ gives, so the History UI degrades the way it already knows how rather than
31
+ inventing a commit list.
32
+
33
+ `getStat` still long-polls -- the hold is what paces the client, and an earlier
34
+ version that answered immediately turned a 20-second poll into a request every
35
+ 6ms -- but it parks on a SIGNAL rather than a timer. This mode owns its store,
36
+ so it is told when something changes: no timers while parked, and a patch
37
+ written by another tab is seen at once rather than up to 250ms later.
38
+
39
+ Two seams come with it. `commitPrepared` lets a host take what a save produced
40
+ instead of a git commit, and `publishOverride` lets a publish be something other
41
+ than a push. Both are opt-in; an app that sets neither behaves exactly as before.
42
+
43
+ The in-memory patch store is explicitly **not durable**. It is behind
44
+ `ValPatchStore`, so a durable implementation is a swap rather than a rewrite,
45
+ but as shipped a restart loses unpublished patches.
46
+
47
+ **Memory mode authenticates.** `ValOps` gained `requiresAuth` alongside
48
+ `patchesAreLocal`, because one flag was answering two questions: whether a store
49
+ auto-saves or publishes (behaviour, reported as `mode` and keyed off by the UI),
50
+ and whether an unauthenticated request may write (security). With two
51
+ implementations the answers coincided — `fs` is a developer's own machine where
52
+ no credential exists, `http` is remote — so `getAuth` was written against
53
+ `patchesAreLocal` and returned anonymous _success_ for a missing cookie, an
54
+ invalid JWT, an unparseable payload, or no configured secret.
55
+
56
+ Memory mode splits them: its store is local, and it runs deployed. It therefore
57
+ requires a verified session, like `http` mode. A host that authorises requests
58
+ before Val sees them can opt out with `unsafelyAllowUnauthenticated`, which is
59
+ spelled that way on purpose and warns at startup. `fs` mode is unchanged.
60
+
61
+ Val's own MCP endpoint refuses memory mode outright. It has the same absence fs
62
+ mode has — no credential, no backend, every permission check on the far side of
63
+ one — and unlike fs mode it is meant to run deployed, so the existing
64
+ "development only" and loopback guards refuse nothing. A host in this mode owns
65
+ its own trust boundary and can offer the tools through it.
66
+
67
+ Internally, the routes' `instanceof ValOpsFS` checks meant "is this a local
68
+ store" — correct with two implementations and silently wrong with three. They are
69
+ now `ValOps.patchesAreLocal` at all 17 policy sites.
70
+
71
+ ### Patch Changes
72
+
73
+ - Updated dependencies [[`cab4098`](https://github.com/valbuild/val/commit/cab4098969585977b8d7574e86d66fcb01cb1d75), [`8425378`](https://github.com/valbuild/val/commit/8425378c315ea46b5d822f1130b633e0449ff1b0), [`cab4098`](https://github.com/valbuild/val/commit/cab4098969585977b8d7574e86d66fcb01cb1d75), [`473a185`](https://github.com/valbuild/val/commit/473a185f70351b44388f3bc1852649e2c1dbe001), [`64f0de3`](https://github.com/valbuild/val/commit/64f0de339b8621cb5a6c422dfe55cae5b2bbe2a0), [`cab4098`](https://github.com/valbuild/val/commit/cab4098969585977b8d7574e86d66fcb01cb1d75)]:
74
+ - @valbuild/server@0.130.0
75
+ - @valbuild/core@0.130.0
76
+ - @valbuild/shared@0.130.0
77
+
3
78
  ## 0.129.0
4
79
 
5
80
  ### Patch Changes
@@ -3365,7 +3365,7 @@ var packageJson = {
3365
3365
  "./package.json": "./package.json"
3366
3366
  },
3367
3367
  types: "dist/valbuild-mcp.cjs.d.ts",
3368
- version: "0.129.0",
3368
+ version: "0.131.0",
3369
3369
  scripts: {
3370
3370
  typecheck: "tsc --noEmit",
3371
3371
  test: "jest",
@@ -4343,7 +4343,7 @@ function initValMcp(valModules, config, opts) {
4343
4343
  }
4344
4344
 
4345
4345
  /**
4346
- * The two ways this route is dangerous, both refused here.
4346
+ * The three ways this route is dangerous, all refused here.
4347
4347
  *
4348
4348
  * 1. **Local filesystem mode outside development.** In fs mode there is no
4349
4349
  * credential and no backend: the tools read and write the running process's
@@ -4361,10 +4361,24 @@ function initValMcp(valModules, config, opts) {
4361
4361
  * needs a credential in fs mode. So a cross-origin `Origin` is refused, and
4362
4362
  * in fs mode the request must actually be addressed to a loopback host.
4363
4363
  *
4364
+ * 3. **Memory mode, always.** A host that holds its own source has the same
4365
+ * absence fs mode has -- no credential, no backend, and every permission
4366
+ * check on the far side of one -- and unlike fs mode it is *meant* to run
4367
+ * deployed, so "only in development" refuses nothing. Neither does the
4368
+ * loopback check: there is no localhost to require. So this is refused
4369
+ * outright rather than narrowed. A host in this mode owns its own trust
4370
+ * boundary and can offer the tools through it; what it cannot do is let
4371
+ * Val's own endpoint write content for anyone who can reach the port.
4372
+ *
4364
4373
  * MCP clients are not browsers and send no `Origin`, so the check costs them
4365
4374
  * nothing.
4366
4375
  */
4367
4376
  function refuseUnsafeRequest(request, mode) {
4377
+ if (mode === "memory") {
4378
+ return jsonResponse(403, {
4379
+ error: "Val: the MCP endpoint is disabled. This project is running in memory mode, where the host holds the source and MCP calls would be unauthenticated writes to it. Serve the tools through the host's own authenticated surface instead."
4380
+ });
4381
+ }
4368
4382
  if (mode === "fs" && process.env.NODE_ENV !== "development") {
4369
4383
  return jsonResponse(403, {
4370
4384
  error: "Val: the MCP endpoint is disabled. This project is running in local filesystem mode, where MCP calls are unauthenticated and write directly to the working tree, so it is only served in development. Configure Val for proxy mode to use MCP on a deployed host."
@@ -3365,7 +3365,7 @@ var packageJson = {
3365
3365
  "./package.json": "./package.json"
3366
3366
  },
3367
3367
  types: "dist/valbuild-mcp.cjs.d.ts",
3368
- version: "0.129.0",
3368
+ version: "0.131.0",
3369
3369
  scripts: {
3370
3370
  typecheck: "tsc --noEmit",
3371
3371
  test: "jest",
@@ -4343,7 +4343,7 @@ function initValMcp(valModules, config, opts) {
4343
4343
  }
4344
4344
 
4345
4345
  /**
4346
- * The two ways this route is dangerous, both refused here.
4346
+ * The three ways this route is dangerous, all refused here.
4347
4347
  *
4348
4348
  * 1. **Local filesystem mode outside development.** In fs mode there is no
4349
4349
  * credential and no backend: the tools read and write the running process's
@@ -4361,10 +4361,24 @@ function initValMcp(valModules, config, opts) {
4361
4361
  * needs a credential in fs mode. So a cross-origin `Origin` is refused, and
4362
4362
  * in fs mode the request must actually be addressed to a loopback host.
4363
4363
  *
4364
+ * 3. **Memory mode, always.** A host that holds its own source has the same
4365
+ * absence fs mode has -- no credential, no backend, and every permission
4366
+ * check on the far side of one -- and unlike fs mode it is *meant* to run
4367
+ * deployed, so "only in development" refuses nothing. Neither does the
4368
+ * loopback check: there is no localhost to require. So this is refused
4369
+ * outright rather than narrowed. A host in this mode owns its own trust
4370
+ * boundary and can offer the tools through it; what it cannot do is let
4371
+ * Val's own endpoint write content for anyone who can reach the port.
4372
+ *
4364
4373
  * MCP clients are not browsers and send no `Origin`, so the check costs them
4365
4374
  * nothing.
4366
4375
  */
4367
4376
  function refuseUnsafeRequest(request, mode) {
4377
+ if (mode === "memory") {
4378
+ return jsonResponse(403, {
4379
+ error: "Val: the MCP endpoint is disabled. This project is running in memory mode, where the host holds the source and MCP calls would be unauthenticated writes to it. Serve the tools through the host's own authenticated surface instead."
4380
+ });
4381
+ }
4368
4382
  if (mode === "fs" && "production" !== "development") {
4369
4383
  return jsonResponse(403, {
4370
4384
  error: "Val: the MCP endpoint is disabled. This project is running in local filesystem mode, where MCP calls are unauthenticated and write directly to the working tree, so it is only served in development. Configure Val for proxy mode to use MCP on a deployed host."
@@ -3356,7 +3356,7 @@ var packageJson = {
3356
3356
  "./package.json": "./package.json"
3357
3357
  },
3358
3358
  types: "dist/valbuild-mcp.cjs.d.ts",
3359
- version: "0.129.0",
3359
+ version: "0.131.0",
3360
3360
  scripts: {
3361
3361
  typecheck: "tsc --noEmit",
3362
3362
  test: "jest",
@@ -4334,7 +4334,7 @@ function initValMcp(valModules, config, opts) {
4334
4334
  }
4335
4335
 
4336
4336
  /**
4337
- * The two ways this route is dangerous, both refused here.
4337
+ * The three ways this route is dangerous, all refused here.
4338
4338
  *
4339
4339
  * 1. **Local filesystem mode outside development.** In fs mode there is no
4340
4340
  * credential and no backend: the tools read and write the running process's
@@ -4352,10 +4352,24 @@ function initValMcp(valModules, config, opts) {
4352
4352
  * needs a credential in fs mode. So a cross-origin `Origin` is refused, and
4353
4353
  * in fs mode the request must actually be addressed to a loopback host.
4354
4354
  *
4355
+ * 3. **Memory mode, always.** A host that holds its own source has the same
4356
+ * absence fs mode has -- no credential, no backend, and every permission
4357
+ * check on the far side of one -- and unlike fs mode it is *meant* to run
4358
+ * deployed, so "only in development" refuses nothing. Neither does the
4359
+ * loopback check: there is no localhost to require. So this is refused
4360
+ * outright rather than narrowed. A host in this mode owns its own trust
4361
+ * boundary and can offer the tools through it; what it cannot do is let
4362
+ * Val's own endpoint write content for anyone who can reach the port.
4363
+ *
4355
4364
  * MCP clients are not browsers and send no `Origin`, so the check costs them
4356
4365
  * nothing.
4357
4366
  */
4358
4367
  function refuseUnsafeRequest(request, mode) {
4368
+ if (mode === "memory") {
4369
+ return jsonResponse(403, {
4370
+ error: "Val: the MCP endpoint is disabled. This project is running in memory mode, where the host holds the source and MCP calls would be unauthenticated writes to it. Serve the tools through the host's own authenticated surface instead."
4371
+ });
4372
+ }
4359
4373
  if (mode === "fs" && process.env.NODE_ENV !== "development") {
4360
4374
  return jsonResponse(403, {
4361
4375
  error: "Val: the MCP endpoint is disabled. This project is running in local filesystem mode, where MCP calls are unauthenticated and write directly to the working tree, so it is only served in development. Configure Val for proxy mode to use MCP on a deployed host."
package/package.json CHANGED
@@ -20,7 +20,7 @@
20
20
  "./package.json": "./package.json"
21
21
  },
22
22
  "types": "dist/valbuild-mcp.cjs.d.ts",
23
- "version": "0.129.0",
23
+ "version": "0.131.0",
24
24
  "preconstruct": {
25
25
  "entrypoints": [
26
26
  "./index.ts",
@@ -31,9 +31,9 @@
31
31
  "dependencies": {
32
32
  "minimatch": "^10.2.6",
33
33
  "zod": "^4.4.3",
34
- "@valbuild/server": "0.129.0",
35
- "@valbuild/core": "0.129.0",
36
- "@valbuild/shared": "0.129.0"
34
+ "@valbuild/core": "0.130.0",
35
+ "@valbuild/server": "0.131.0",
36
+ "@valbuild/shared": "0.130.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@prettier/sync": "^0.6.1",