From 6527e9d30dd620945c6fd36812a5e23fac07c7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Thu, 5 Mar 2026 15:28:35 -0800 Subject: [PATCH 1/4] feat(glyph): add basic-memory mcp --- hosts/glyph/services/basic-memory.nix | 43 +++++++++++++++++++++++++++ hosts/glyph/services/default.nix | 1 + 2 files changed, 44 insertions(+) create mode 100644 hosts/glyph/services/basic-memory.nix diff --git a/hosts/glyph/services/basic-memory.nix b/hosts/glyph/services/basic-memory.nix new file mode 100644 index 00000000..d39e196e --- /dev/null +++ b/hosts/glyph/services/basic-memory.nix @@ -0,0 +1,43 @@ +{pkgs, ...}: { + users.users.basic-memory = { + isSystemUser = true; + group = "basic-memory"; + home = "/var/lib/basic-memory"; + }; + users.groups.basic-memory = {}; + + systemd.services.basic-memory = { + description = "basic-memory MCP Server"; + after = ["network-online.target"]; + wants = ["network-online.target"]; + wantedBy = ["multi-user.target"]; + + environment = { + HOME = "/var/lib/basic-memory"; + BASIC_MEMORY_CONFIG_DIR = "/var/lib/basic-memory"; + UV_CACHE_DIR = "/var/lib/basic-memory/.cache/uv"; + }; + + serviceConfig = { + ExecStart = "${pkgs.uv}/bin/uvx basic-memory mcp --transport streamable-http --port 8091 --host 127.0.0.1"; + User = "basic-memory"; + Group = "basic-memory"; + WorkingDirectory = "/var/lib/basic-memory"; + StateDirectory = "basic-memory"; + Restart = "on-failure"; + RestartSec = 5; + + # Hardening + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectHome = true; + ProtectSystem = "strict"; + ReadWritePaths = ["/var/lib/basic-memory"]; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictSUIDSGID = true; + }; + }; +} diff --git a/hosts/glyph/services/default.nix b/hosts/glyph/services/default.nix index 2c5344c2..ced93e0a 100644 --- a/hosts/glyph/services/default.nix +++ b/hosts/glyph/services/default.nix @@ -5,6 +5,7 @@ }: { imports = [ ./avahi.nix + ./basic-memory.nix ./dns.nix ./filebrowser.nix ./jellyfin.nix From 4b6a0bfde045ac903bc65f353aac40c69dea5531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Thu, 5 Mar 2026 16:54:36 -0800 Subject: [PATCH 2/4] move basic-memory to nixos llm module --- hosts/glyph/services/basic-memory.nix | 43 ---------------- hosts/glyph/services/default.nix | 2 +- modules/nixos/llm/basic-memory.nix | 74 +++++++++++++++++++++++++++ modules/nixos/llm/default.nix | 1 + 4 files changed, 76 insertions(+), 44 deletions(-) delete mode 100644 hosts/glyph/services/basic-memory.nix create mode 100644 modules/nixos/llm/basic-memory.nix diff --git a/hosts/glyph/services/basic-memory.nix b/hosts/glyph/services/basic-memory.nix deleted file mode 100644 index d39e196e..00000000 --- a/hosts/glyph/services/basic-memory.nix +++ /dev/null @@ -1,43 +0,0 @@ -{pkgs, ...}: { - users.users.basic-memory = { - isSystemUser = true; - group = "basic-memory"; - home = "/var/lib/basic-memory"; - }; - users.groups.basic-memory = {}; - - systemd.services.basic-memory = { - description = "basic-memory MCP Server"; - after = ["network-online.target"]; - wants = ["network-online.target"]; - wantedBy = ["multi-user.target"]; - - environment = { - HOME = "/var/lib/basic-memory"; - BASIC_MEMORY_CONFIG_DIR = "/var/lib/basic-memory"; - UV_CACHE_DIR = "/var/lib/basic-memory/.cache/uv"; - }; - - serviceConfig = { - ExecStart = "${pkgs.uv}/bin/uvx basic-memory mcp --transport streamable-http --port 8091 --host 127.0.0.1"; - User = "basic-memory"; - Group = "basic-memory"; - WorkingDirectory = "/var/lib/basic-memory"; - StateDirectory = "basic-memory"; - Restart = "on-failure"; - RestartSec = 5; - - # Hardening - NoNewPrivileges = true; - PrivateDevices = true; - PrivateTmp = true; - ProtectHome = true; - ProtectSystem = "strict"; - ReadWritePaths = ["/var/lib/basic-memory"]; - ProtectKernelTunables = true; - ProtectKernelModules = true; - ProtectControlGroups = true; - RestrictSUIDSGID = true; - }; - }; -} diff --git a/hosts/glyph/services/default.nix b/hosts/glyph/services/default.nix index ced93e0a..d1f8970a 100644 --- a/hosts/glyph/services/default.nix +++ b/hosts/glyph/services/default.nix @@ -5,7 +5,6 @@ }: { imports = [ ./avahi.nix - ./basic-memory.nix ./dns.nix ./filebrowser.nix ./jellyfin.nix @@ -61,5 +60,6 @@ extraUpFlags = ["--ssh"]; }; + services.basic-memory.enable = true; services.mcpjungle.enable = true; } diff --git a/modules/nixos/llm/basic-memory.nix b/modules/nixos/llm/basic-memory.nix new file mode 100644 index 00000000..72f0a299 --- /dev/null +++ b/modules/nixos/llm/basic-memory.nix @@ -0,0 +1,74 @@ +{ + config, + pkgs, + lib, + ... +}: let + cfg = config.services.basic-memory; + dataDir = "/var/lib/basic-memory"; +in { + options.services.basic-memory = { + enable = lib.mkEnableOption "Basic Memory MCP server"; + + port = lib.mkOption { + type = lib.types.port; + default = 8091; + description = "Port for the streamable HTTP transport."; + }; + + host = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1"; + description = "Address to bind the HTTP server to."; + }; + + openFirewall = lib.mkEnableOption "opening firewall ports for Basic Memory"; + }; + + config = lib.mkIf cfg.enable { + users.users.basic-memory = { + isSystemUser = true; + group = "basic-memory"; + home = dataDir; + }; + users.groups.basic-memory = {}; + + systemd.services.basic-memory = { + description = "Basic Memory MCP Server"; + after = ["network-online.target"]; + wants = ["network-online.target"]; + wantedBy = ["multi-user.target"]; + + environment = { + HOME = dataDir; + BASIC_MEMORY_CONFIG_DIR = dataDir; + UV_CACHE_DIR = "${dataDir}/.cache/uv"; + }; + + serviceConfig = { + ExecStart = "${lib.getExe pkgs.uv} tool run basic-memory mcp --transport streamable-http --port ${toString cfg.port} --host ${cfg.host}"; + User = "basic-memory"; + Group = "basic-memory"; + WorkingDirectory = dataDir; + StateDirectory = "basic-memory"; + Restart = "on-failure"; + RestartSec = 5; + + # Hardening + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectHome = "tmpfs"; + BindPaths = ["${dataDir}"]; + ProtectSystem = "strict"; + ReadWritePaths = [dataDir]; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictSUIDSGID = true; + }; + }; + + networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [cfg.port]; + }; +} diff --git a/modules/nixos/llm/default.nix b/modules/nixos/llm/default.nix index f31ba072..e8b889b3 100644 --- a/modules/nixos/llm/default.nix +++ b/modules/nixos/llm/default.nix @@ -1,5 +1,6 @@ { imports = [ + ./basic-memory.nix ./mcpjungle.nix ]; } From e0965ee264f1bc5b77b73b2dad81e78dbe1fd727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Thu, 5 Mar 2026 17:05:03 -0800 Subject: [PATCH 3/4] Use nix-ld to allow uv to find dynamic linker --- modules/nixos/llm/basic-memory.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/nixos/llm/basic-memory.nix b/modules/nixos/llm/basic-memory.nix index 72f0a299..43fdd318 100644 --- a/modules/nixos/llm/basic-memory.nix +++ b/modules/nixos/llm/basic-memory.nix @@ -69,6 +69,8 @@ in { }; }; + programs.nix-ld.enable = true; + networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [cfg.port]; }; } From e164d25b361af2aa7d83a0d970b7b8078520ec4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Fri, 6 Mar 2026 07:19:37 -0800 Subject: [PATCH 4/4] register basic-memory with mcpjungle via one-shot service --- hosts/glyph/services/default.nix | 8 ++++++- modules/nixos/llm/mcpjungle.nix | 41 ++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/hosts/glyph/services/default.nix b/hosts/glyph/services/default.nix index d1f8970a..44ca37b4 100644 --- a/hosts/glyph/services/default.nix +++ b/hosts/glyph/services/default.nix @@ -61,5 +61,11 @@ }; services.basic-memory.enable = true; - services.mcpjungle.enable = true; + services.mcpjungle = { + enable = true; + servers.basic-memory = { + url = "http://127.0.0.1:8091/mcp"; + description = "Knowledge management with markdown files"; + }; + }; } diff --git a/modules/nixos/llm/mcpjungle.nix b/modules/nixos/llm/mcpjungle.nix index 9800a600..0c91297c 100644 --- a/modules/nixos/llm/mcpjungle.nix +++ b/modules/nixos/llm/mcpjungle.nix @@ -18,6 +18,24 @@ in { }; openFirewall = lib.mkEnableOption "opening firewall ports for MCPJungle"; + + servers = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule { + options = { + url = lib.mkOption { + type = lib.types.str; + description = "Streamable HTTP URL of the MCP server."; + }; + description = lib.mkOption { + type = lib.types.str; + default = ""; + description = "Description of the MCP server."; + }; + }; + }); + default = {}; + description = "MCP servers to register with the gateway."; + }; }; config = lib.mkIf cfg.enable { @@ -55,6 +73,29 @@ in { }; }; + systemd.services.mcpjungle-register = lib.mkIf (cfg.servers != {}) { + description = "Register MCP servers with MCPJungle"; + after = ["mcpjungle.service"]; + requires = ["mcpjungle.service"]; + wantedBy = ["multi-user.target"]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = let + registry = "http://127.0.0.1:${toString cfg.port}"; + bin = lib.getExe cfg.package; + registrations = lib.concatStringsSep "\n" (lib.mapAttrsToList (name: server: '' + if ! ${bin} list servers --registry ${registry} 2>/dev/null | grep -q '${name}'; then + ${bin} register --name '${name}' --description '${server.description}' --url '${server.url}' --registry ${registry} + fi + '') + cfg.servers); + in '' + ${registrations} + ''; + }; + networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [cfg.port]; }; }