@memoryprocess/mcp 0.0.3-beta.9 → 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 JoShMiQueL
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -25,6 +25,11 @@ memoryprocess-mcp [--no-eval] [--help] [--version]
25
25
  MEMP_NO_EVAL=1 # same as --no-eval
26
26
  ```
27
27
 
28
+ `--no-eval` runs **restricted mode**: `eval_js` AND the write tools
29
+ (`mem_write`, `mem_freeze`, `mem_undo`) are hidden from `tools/list` and
30
+ rejected by name -- the model gets a read/describe/scan/pointer/watch
31
+ surface only, nothing that mutates the target.
32
+
28
33
  ## Session model
29
34
 
30
35
  There is no implicit "current process". `proc_open` returns a session handle
@@ -34,25 +39,27 @@ watches/freezes bound to it.
34
39
 
35
40
  ## Tools
36
41
 
37
- | Tool | Description |
38
- | --------------------- | -------------------------------------------------------------------------------------------------------- |
39
- | `proc_list` | List running processes `{pid,name}[]` |
40
- | `proc_open` | Open by `{pid}` or `{name}` -> `{session, describe}`. `access:"ro"` = read-only |
41
- | `proc_close` | Close a session |
42
- | `mem_describe` | `Process.describe()`: arch, modules, writable/exec regions, threads |
43
- | `mem_read` | Typed read: `i8...i64/u8...u64`, `f32/f64`, `ptr`, `str`, `wstr`, `bytes[:N]` (+`len`) |
44
- | `mem_write` | Typed write. `dryRun:true` -> `{addr,current,would,bytes}` preview. Journaled |
45
- | `mem_scan` | CE value scan -> `{hits:[{addr,value}],count,truncated}` (`module`/`start`/`end`/`limit`) |
46
- | `mem_scan_next` | Refine hits: `exact`/`changed`/`unchanged`/`increased`/`decreased`/`increasedBy`/`decreasedBy`/`between` |
47
- | `mem_pointer_resolve` | `module`+`rva`+`offsets[]` -> resolved address (CE pointer syntax) |
48
- | `mem_watch` | Poll an i32 every `ms` -> `{watchId}`; changes accumulate |
49
- | `mem_watch_poll` | Drain recorded changes `[{value,ts}]` for a `watchId` |
50
- | `mem_unwatch` | Stop a watch |
51
- | `mem_freeze` | Re-write a value every `ms` -> `{freezeId}` |
52
- | `mem_unfreeze` | Stop a freeze |
53
- | `mem_undo` | Revert last `n` journaled writes (self-correction primitive) |
54
- | `mem_journal` | Write-log snapshot `[{index,addr,bytes}]` |
55
- | `eval_js` | Run JS with `proc` (`Process`) in scope - the escape hatch |
42
+ | Tool | Description |
43
+ | --------------------- | ----------------------------------------------------------------------------------------------------------- |
44
+ | `proc_list` | List running processes `{pid,name}[]` |
45
+ | `proc_open` | Open by `{pid}` or `{name}` -> `{session, describe}`. `access:"ro"` = read-only |
46
+ | `proc_close` | Close a session |
47
+ | `mem_describe` | `Process.describe()`: arch, modules, writable/exec regions, threads |
48
+ | `mem_read` | Typed read: `i8...i64/u8...u64`, `f32/f64`, `ptr`, `str`, `wstr`, `bytes[:N]` (+`len`) |
49
+ | `mem_write` | Typed write. `dryRun:true` -> `{addr,current,would,bytes}` preview. Journaled |
50
+ | `mem_scan` | CE value scan -> `{hits:[{addr,value}],count,truncated}` (`module`/`start`/`end`/`limit`) |
51
+ | `mem_scan_next` | Refine hits: `exact`/`changed`/`unchanged`/`increased`/`decreased`/`increasedBy`/`decreasedBy`/`between` |
52
+ | `mem_scan_pattern` | AOB signature scan (`"48 8B ?? 10"`) -> `{hits,count,truncated}` (`module`/`section`/`start`/`end`/`limit`) |
53
+ | `mem_regions` | VirtualQueryEx region map `{base,size,protect,state,type}` (`writableOnly`/`executableOnly` filters) |
54
+ | `mem_pointer_resolve` | `module`+`rva`+optional `offsets[]` -> resolved address (CE pointer syntax; no offsets = module base+rva) |
55
+ | `mem_watch` | Poll an i32 every `ms` -> `{watchId}`; changes accumulate |
56
+ | `mem_watch_poll` | Drain recorded changes `[{value,ts}]` for a `watchId` |
57
+ | `mem_unwatch` | Stop a watch |
58
+ | `mem_freeze` | Re-write a value every `ms` -> `{freezeId}` |
59
+ | `mem_unfreeze` | Stop a freeze |
60
+ | `mem_undo` | Revert last `n` journaled writes (self-correction primitive) |
61
+ | `mem_journal` | Write-log snapshot `[{index,addr,bytes}]` |
62
+ | `eval_js` | Run JS with `proc` (`Process`) in scope - the escape hatch |
56
63
 
57
64
  ## Wire format (docs/json-conventions.md)
58
65
 
@@ -70,5 +77,12 @@ watches/freezes bound to it.
70
77
  All writes are journaled -> `mem_journal` + `mem_undo` let an agent preview
71
78
  (`dryRun`), commit, inspect, and revert. `eval_js` runs arbitrary JS on the
72
79
  host - it's on by default (agents already have code execution) and can be
73
- disabled with `--no-eval` / `MEMP_NO_EVAL=1`, which omits it from
74
- `tools/list` entirely.
80
+ disabled with `--no-eval` / `MEMP_NO_EVAL=1`, which is the **restricted
81
+ mode**: `eval_js` and the write tools (`mem_write`, `mem_freeze`,
82
+ `mem_undo`) are omitted from `tools/list` entirely, leaving a
83
+ read/describe/scan/pointer/watch surface that cannot mutate the target.
84
+
85
+ The coarse tool surface is intentionally narrow: `alloc`, `protect`,
86
+ `injectDll`, thread control, `hook`, `callRemote`, `readMany`/`entity` live
87
+ in `eval_js` by design (escape hatch), and are therefore unreachable under
88
+ `--no-eval` -- that is the point of restricted mode.
package/dist/server.mjs CHANGED
@@ -414,6 +414,7 @@ const toolDefs = {
414
414
  dryRun: z.boolean().optional().describe("preview only, do not write")
415
415
  },
416
416
  destructive: true,
417
+ writeOnly: true,
417
418
  handler: (s, a) => {
418
419
  const proc = s.get(a.session);
419
420
  const r = writeTyped(proc, parseAddress(proc, a.addr), a);
@@ -583,7 +584,7 @@ const toolDefs = {
583
584
  ...sessionArg,
584
585
  module: z.string().describe("module basename e.g. \"victim.exe\""),
585
586
  rva: z.union([z.string(), z.number()]).describe("offset from module base (hex ok)"),
586
- offsets: z.array(z.union([z.string(), z.number()])).describe("pointer-chain offsets; all but the last dereference")
587
+ offsets: z.array(z.union([z.string(), z.number()])).optional().describe("pointer-chain offsets; omit (or []) for plain module+rva -- no dereference")
587
588
  },
588
589
  handler: (s, a) => {
589
590
  const proc = s.get(a.session);
@@ -595,7 +596,7 @@ const toolDefs = {
595
596
  } catch {
596
597
  throw fail("INVALID_ARGS", `bad rva "${a.rva}"`);
597
598
  }
598
- const offsets = a.offsets.map((o, i) => {
599
+ const offsets = (a.offsets ?? []).map((o, i) => {
599
600
  const n = typeof o === "string" ? Number(BigInt(o)) : Number(o);
600
601
  if (!Number.isFinite(n)) throw fail("INVALID_ARGS", `offsets[${i}] invalid`);
601
602
  return n;
@@ -660,6 +661,7 @@ const toolDefs = {
660
661
  ms: z.number().int().positive().optional()
661
662
  },
662
663
  destructive: true,
664
+ writeOnly: true,
663
665
  handler: (s, a) => {
664
666
  const proc = s.get(a.session);
665
667
  const addr = parseAddress(proc, a.addr);
@@ -696,6 +698,7 @@ const toolDefs = {
696
698
  n: z.number().int().positive().optional()
697
699
  },
698
700
  destructive: true,
701
+ writeOnly: true,
699
702
  handler: (s, a) => {
700
703
  return { reverted: s.get(a.session).undo(a.n ?? 1) };
701
704
  }
@@ -725,16 +728,19 @@ const toolDefs = {
725
728
  }
726
729
  };
727
730
  const TOOL_DEFS = toolDefs;
731
+ /** Tools visible under the given mode. restricted = --no-eval/MEMP_NO_EVAL. */
728
732
  function toolNames(opts = {}) {
729
733
  const evalEnabled = opts.evalEnabled ?? true;
730
- return Object.keys(toolDefs).filter((n) => evalEnabled || n !== "eval_js");
734
+ const restricted = opts.restricted ?? false;
735
+ return Object.keys(toolDefs).filter((n) => (evalEnabled || n !== "eval_js") && (!restricted || !toolDefs[n].writeOnly));
731
736
  }
732
737
  /** Direct dispatcher used by both the stdio server and the tests. */
733
738
  async function callTool(sessions, name, args, opts = {}) {
734
739
  try {
735
740
  const evalEnabled = opts.evalEnabled ?? true;
741
+ const restricted = opts.restricted ?? false;
736
742
  const def = toolDefs[name];
737
- if (!def || name === "eval_js" && !evalEnabled) throw fail("UNKNOWN_TOOL", `unknown tool "${name}"`);
743
+ if (!def || name === "eval_js" && !evalEnabled || restricted && def.writeOnly) throw fail("UNKNOWN_TOOL", `unknown tool "${name}"`);
738
744
  const parsed = z.object(def.schema).safeParse(args ?? {});
739
745
  if (!parsed.success) {
740
746
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
@@ -755,7 +761,8 @@ async function callTool(sessions, name, args, opts = {}) {
755
761
  *
756
762
  * Usage:
757
763
  * memoryprocess-mcp # serve MCP over stdio
758
- * memoryprocess-mcp --no-eval # omit the eval_js tool (restricted mode)
764
+ * memoryprocess-mcp --no-eval # restricted mode: no eval_js and no write
765
+ * # tools (read/describe/scan/watch/undo only)
759
766
  * memoryprocess-mcp --help # list tools + exit
760
767
  * memoryprocess-mcp --version
761
768
  *
@@ -765,6 +772,7 @@ const dir = dirname(fileURLToPath(import.meta.url));
765
772
  const pkg = JSON.parse(readFileSync(join(dir, "..", "package.json"), "utf8"));
766
773
  const argv = process.argv.slice(2);
767
774
  const evalEnabled = !argv.includes("--no-eval") && process.env.MEMP_NO_EVAL !== "1";
775
+ const restricted = !evalEnabled;
768
776
  if (argv.includes("--version") || argv.includes("-v")) {
769
777
  console.log(`${pkg.name} ${pkg.version}`);
770
778
  process.exit(0);
@@ -775,27 +783,37 @@ if (argv.includes("--help") || argv.includes("-h")) {
775
783
  console.log("");
776
784
  console.log("Usage: memoryprocess-mcp [--no-eval] [--help] [--version]");
777
785
  console.log(" MEMP_NO_EVAL=1 is equivalent to --no-eval");
786
+ console.log(" --no-eval = restricted mode: eval_js AND the write tools");
787
+ console.log(" (mem_write, mem_freeze, mem_undo) are hidden. Read,");
788
+ console.log(" describe, regions, scans, pointers and watches remain.");
778
789
  console.log("");
779
790
  console.log("Session model: proc_open returns a handle; every mem_* tool");
780
791
  console.log("takes it as `session`. Writes are journaled (mem_journal) and");
781
792
  console.log("reversible (mem_undo); mem_write supports dryRun previews.");
782
793
  console.log("");
783
794
  console.log("Tools:");
784
- const names = toolNames({ evalEnabled });
795
+ const names = toolNames({
796
+ evalEnabled,
797
+ restricted
798
+ });
785
799
  const width = Math.max(...names.map((n) => n.length));
786
800
  for (const n of names) {
787
801
  const def = TOOL_DEFS[n];
788
802
  console.log(` ${n.padEnd(width)} ${def.description}`);
789
803
  }
790
- if (!evalEnabled) console.log("\n(eval_js is disabled by --no-eval / MEMP_NO_EVAL=1)");
804
+ if (restricted) console.log("\n(restricted mode: eval_js and write tools hidden -- read/describe/scan only)");
805
+ else if (!evalEnabled) console.log("\n(eval_js is disabled by --no-eval / MEMP_NO_EVAL=1)");
791
806
  process.exit(0);
792
807
  }
793
808
  const sessions = createSessions();
794
809
  const server = new McpServer({
795
810
  name: pkg.name,
796
811
  version: pkg.version
797
- }, { instructions: "Process-memory toolkit. proc_open a target to get a session handle; all mem_* tools take it. Writes are journaled: mem_write with dryRun:true previews, mem_undo reverts. Prefer the coarse tools; eval_js is the escape hatch for anything else." });
798
- for (const name of toolNames({ evalEnabled })) {
812
+ }, { instructions: "Process-memory toolkit. proc_open a target to get a session handle; all mem_* tools take it. Writes are journaled: mem_write with dryRun:true previews, mem_undo reverts. Prefer the coarse tools; eval_js is the escape hatch for anything else (alloc/inject/threads/hooks/readMany live there by design -- the coarse tool surface stays narrow on purpose). --no-eval runs restricted: read/describe/scan/watch only, no write tools." });
813
+ for (const name of toolNames({
814
+ evalEnabled,
815
+ restricted
816
+ })) {
799
817
  const def = TOOL_DEFS[name];
800
818
  server.registerTool(name, {
801
819
  description: def.description,
@@ -805,7 +823,10 @@ for (const name of toolNames({ evalEnabled })) {
805
823
  destructiveHint: def.destructive === true
806
824
  }
807
825
  }, async (args) => {
808
- const result = await callTool(sessions, name, args, { evalEnabled });
826
+ const result = await callTool(sessions, name, args, {
827
+ evalEnabled,
828
+ restricted
829
+ });
809
830
  const data = result.ok === true ? result.data !== null && typeof result.data === "object" && !Array.isArray(result.data) ? result.data : { result: result.data } : void 0;
810
831
  return {
811
832
  content: [{
@@ -828,5 +849,8 @@ process.on("SIGINT", shutdown);
828
849
  process.on("SIGTERM", shutdown);
829
850
  const transport = new StdioServerTransport();
830
851
  await server.connect(transport);
831
- process.stderr.write(`${pkg.name} ${pkg.version} ready on stdio (${toolNames({ evalEnabled }).length} tools${evalEnabled ? "" : ", eval_js disabled"})\n`);
852
+ process.stderr.write(`${pkg.name} ${pkg.version} ready on stdio (${toolNames({
853
+ evalEnabled,
854
+ restricted
855
+ }).length} tools${restricted ? ", restricted: no eval/write" : ""})\n`);
832
856
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memoryprocess/mcp",
3
- "version": "0.0.3-beta.9",
3
+ "version": "1.0.0",
4
4
  "description": "MCP server exposing the memoryprocess toolkit to LLM agents - the primary agentic surface.",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.4.4",
@@ -11,7 +11,9 @@
11
11
  ".": "./dist/server.mjs"
12
12
  },
13
13
  "files": [
14
- "dist"
14
+ "dist",
15
+ "LICENSE",
16
+ "README.md"
15
17
  ],
16
18
  "os": [
17
19
  "win32"
@@ -45,7 +47,7 @@
45
47
  "agent"
46
48
  ],
47
49
  "dependencies": {
48
- "@memoryprocess/core": "^0.0.3-beta.9",
50
+ "@memoryprocess/core":"^0.1.0-beta.0",
49
51
  "@modelcontextprotocol/sdk": "^1.32.2",
50
52
  "zod": "^4.6.7"
51
53
  },