From 21656f0c9156bdf886526426602daae8f9747ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Fri, 6 Mar 2026 16:56:36 -0800 Subject: [PATCH] feat(glyph): setup kagi mcp --- hosts/glyph/secrets/kagi-api-key.age | Bin 0 -> 423 bytes hosts/glyph/services/default.nix | 15 +++++ lib/secrets/glyph.nix | 1 + modules/nixos/llm/default.nix | 1 + modules/nixos/llm/kagi.nix | 89 +++++++++++++++++++++++++++ modules/nixos/llm/mcpjungle.nix | 2 +- 6 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 hosts/glyph/secrets/kagi-api-key.age create mode 100644 modules/nixos/llm/kagi.nix diff --git a/hosts/glyph/secrets/kagi-api-key.age b/hosts/glyph/secrets/kagi-api-key.age new file mode 100644 index 0000000000000000000000000000000000000000..64a47ed14797239f6188a3e4f293c74f1ebfe395 GIT binary patch literal 423 zcmYdHPt{G$OD?J`D9Oyv)5|YP*Do{V(zR14F3!+RO))YxHMCSH3NF$va#RQ|&kqjC z)Ap%H|Yw!Q!TT}K?VjBNb zjsLd3+8o<7KN;QHWgB4oSmkX^h|u}F*_+SzewY&JCTXp^>&3i@wUtX3T~kibc*S@0 HR&fge2QQu~ literal 0 HcmV?d00001 diff --git a/hosts/glyph/services/default.nix b/hosts/glyph/services/default.nix index 5fe409e4..0a96bcbf 100644 --- a/hosts/glyph/services/default.nix +++ b/hosts/glyph/services/default.nix @@ -60,8 +60,19 @@ extraUpFlags = ["--ssh"]; }; + age.secrets.kagi-api-key = { + file = ./../secrets/kagi-api-key.age; + mode = "440"; + owner = "kagi-mcp"; + group = "kagi-mcp"; + }; + services.basic-memory.enable = true; services.mcp-nixos.enable = true; + services.kagi-mcp = { + enable = true; + environmentFile = config.age.secrets.kagi-api-key.path; + }; services.mcpjungle = { enable = true; servers.basic-memory = { @@ -72,5 +83,9 @@ url = "http://127.0.0.1:8092/mcp"; description = "NixOS options, packages, and Home Manager search"; }; + servers.kagi = { + url = "http://127.0.0.1:8093/mcp"; + description = "Kagi web search and page summarization"; + }; }; } diff --git a/lib/secrets/glyph.nix b/lib/secrets/glyph.nix index dc25202e..8ab50744 100644 --- a/lib/secrets/glyph.nix +++ b/lib/secrets/glyph.nix @@ -4,4 +4,5 @@ in { "hosts/glyph/secrets/filebrowser-env.age".publicKeys = keys; "hosts/glyph/secrets/pushover-app-token.age".publicKeys = keys; "hosts/glyph/secrets/pushover-user-token.age".publicKeys = keys; + "hosts/glyph/secrets/kagi-api-key.age".publicKeys = keys; } diff --git a/modules/nixos/llm/default.nix b/modules/nixos/llm/default.nix index 14b47fe5..db9b8edb 100644 --- a/modules/nixos/llm/default.nix +++ b/modules/nixos/llm/default.nix @@ -1,6 +1,7 @@ { imports = [ ./basic-memory.nix + ./kagi.nix ./mcp-nixos.nix ./mcpjungle.nix ]; diff --git a/modules/nixos/llm/kagi.nix b/modules/nixos/llm/kagi.nix new file mode 100644 index 00000000..e7c3d8ad --- /dev/null +++ b/modules/nixos/llm/kagi.nix @@ -0,0 +1,89 @@ +{ + config, + pkgs, + lib, + ... +}: let + cfg = config.services.kagi-mcp; + + startScript = pkgs.writeShellScript "kagi-mcp-start" '' + exec ${lib.getExe pkgs.uv} run --with kagimcp python -c " + from kagimcp.server import mcp + mcp.settings.host = '${cfg.host}' + mcp.settings.port = ${toString cfg.port} + mcp.run(transport='streamable-http') + " + ''; +in { + options.services.kagi-mcp = { + enable = lib.mkEnableOption "Kagi MCP server"; + + port = lib.mkOption { + type = lib.types.port; + default = 8093; + 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."; + }; + + environmentFile = lib.mkOption { + type = lib.types.path; + description = "Path to environment file containing KAGI_API_KEY."; + }; + + openFirewall = lib.mkEnableOption "opening firewall ports for Kagi MCP"; + }; + + config = lib.mkIf cfg.enable { + users.users.kagi-mcp = { + isSystemUser = true; + group = "kagi-mcp"; + home = "/var/lib/kagi-mcp"; + }; + users.groups.kagi-mcp = {}; + + systemd.services.kagi-mcp = { + description = "Kagi MCP Server"; + after = ["network-online.target"]; + wants = ["network-online.target"]; + wantedBy = ["multi-user.target"]; + + environment = { + HOME = "/var/lib/kagi-mcp"; + UV_CACHE_DIR = "/var/lib/kagi-mcp/.cache/uv"; + }; + + serviceConfig = { + ExecStart = "${startScript}"; + EnvironmentFile = cfg.environmentFile; + User = "kagi-mcp"; + Group = "kagi-mcp"; + WorkingDirectory = "/var/lib/kagi-mcp"; + StateDirectory = "kagi-mcp"; + Restart = "on-failure"; + RestartSec = 5; + + # Hardening + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectHome = "tmpfs"; + BindPaths = ["/var/lib/kagi-mcp"]; + ProtectSystem = "strict"; + ReadWritePaths = ["/var/lib/kagi-mcp"]; + 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/mcpjungle.nix b/modules/nixos/llm/mcpjungle.nix index e5639416..6c5fa466 100644 --- a/modules/nixos/llm/mcpjungle.nix +++ b/modules/nixos/llm/mcpjungle.nix @@ -104,7 +104,7 @@ in { done if [ "$ready" = true ]; then - ${bin} register --name '${name}' --description '${server.description}' --url '${server.url}' --registry ${registry} + ${bin} register --name '${name}' --description '${server.description}' --url '${server.url}' --registry ${registry} || echo "ERROR: failed to register ${name}" else echo "WARNING: ${name} at ${server.url} not reachable after 60s, skipping registration." fi