diff --git a/hosts/glyph/services/default.nix b/hosts/glyph/services/default.nix index 2c5344c2..44ca37b4 100644 --- a/hosts/glyph/services/default.nix +++ b/hosts/glyph/services/default.nix @@ -60,5 +60,12 @@ extraUpFlags = ["--ssh"]; }; - services.mcpjungle.enable = true; + services.basic-memory.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/basic-memory.nix b/modules/nixos/llm/basic-memory.nix new file mode 100644 index 00000000..43fdd318 --- /dev/null +++ b/modules/nixos/llm/basic-memory.nix @@ -0,0 +1,76 @@ +{ + 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; + }; + }; + + programs.nix-ld.enable = 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 ]; } 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]; }; }