From 9a8cd0fb3432b297f1fa0380e64e75e0f00886e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Mon, 9 Mar 2026 13:22:43 -0700 Subject: [PATCH 1/4] feat(claude-code): add Graphite MCP server Co-Authored-By: Claude Opus 4.6 --- modules/home/development.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/home/development.nix b/modules/home/development.nix index 086f013..48332ba 100644 --- a/modules/home/development.nix +++ b/modules/home/development.nix @@ -31,6 +31,10 @@ in { enable = true; memory.source = "${llm-profile}/README.md"; mcpServers = { + graphite = { + command = "gt"; + args = ["mcp"]; + }; glyph = { type = "http"; url = "http://glyph:8090/mcp"; From afeac506a45916d1d2395b52ff739eecbb1216f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Mon, 9 Mar 2026 15:34:48 -0700 Subject: [PATCH 2/4] feat(glyph): add Graphite MCP server to gateway Bridge the Graphite CLI's stdio MCP server to streamable HTTP using mcp-proxy, and register it with the mcpjungle gateway. This moves the Graphite MCP from running locally on the client to being served via the glyph MCP gateway alongside basic-memory, kagi, and mcp-nixos. - Add modules/nixos/llm/graphite-mcp.nix (port 8094, mcp-proxy bridge) - Manage auth token via agenix secret - Register with mcpjungle gateway - Remove local Graphite MCP entry from Claude Code config Co-Authored-By: Claude Opus 4.6 --- hosts/glyph/secrets/graphite-auth-token.age | Bin 0 -> 382 bytes hosts/glyph/services/default.nix | 15 ++++ lib/secrets/glyph.nix | 1 + modules/home/development.nix | 4 - modules/nixos/llm/default.nix | 1 + modules/nixos/llm/graphite-mcp.nix | 94 ++++++++++++++++++++ 6 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 hosts/glyph/secrets/graphite-auth-token.age create mode 100644 modules/nixos/llm/graphite-mcp.nix diff --git a/hosts/glyph/secrets/graphite-auth-token.age b/hosts/glyph/secrets/graphite-auth-token.age new file mode 100644 index 0000000000000000000000000000000000000000..4548c7caf1d548e6099f491466ee9d310a1655fd GIT binary patch literal 382 zcmYdHPt{G$OD?J`D9Oyv)5|YP*Do{V(zR14F3!+RO))YxHMCSH3NF$va#Sd^D0S5j z2?;E63n}mjsq(FKO)D(-5B77<&M5XzG70f@@{BAoarFrcv*1e3Ecf>c49*R6jc~T` z@k>oM&vXnc$?+{sE_2E=kH|3hi75BY_4O&Y$Va!$*fl&OFHoT%Fe%B%BFITUKQKw( zDKaX#I3zbcBfQcxINQ{vEXy@4s>D05AkW>^DVr;@(lI>9JHo>vJ1f^Is4Obgw>VEb zBs(KGU%N0fE6OXg#LYZ6GAuVVxSUH@S687br^>C+xWc)pJSVHXGOyG#F*w;L$s{|s zDka#lyu?jE%sDqAKtIyFsF;hD!Fb2Jl8vb;CKLVEtd}k2xj0>;=FLUE(kH "$HOME/.config/graphite/user_config" + chmod 600 "$HOME/.config/graphite/user_config" + ''; + + startScript = pkgs.writeShellScript "graphite-mcp-start" '' + exec ${lib.getExe pkgs.mcp-proxy} \ + --host ${cfg.host} \ + --port ${toString cfg.port} \ + --transport streamablehttp \ + -- ${lib.getExe pkgs.graphite-cli} mcp + ''; +in { + options.services.graphite-mcp = { + enable = lib.mkEnableOption "Graphite MCP server (stdio→HTTP bridge)"; + + port = lib.mkOption { + type = lib.types.port; + default = 8094; + 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."; + }; + + authTokenFile = lib.mkOption { + type = lib.types.path; + description = "Path to file containing the Graphite auth token."; + }; + + openFirewall = lib.mkEnableOption "opening firewall ports for Graphite MCP"; + }; + + config = lib.mkIf cfg.enable { + users.users.graphite-mcp = { + isSystemUser = true; + group = "graphite-mcp"; + home = "/var/lib/graphite-mcp"; + }; + users.groups.graphite-mcp = {}; + + systemd.services.graphite-mcp = { + description = "Graphite MCP Server"; + after = ["network-online.target"]; + wants = ["network-online.target"]; + wantedBy = ["multi-user.target"]; + + path = [pkgs.git]; + + environment = { + HOME = "/var/lib/graphite-mcp"; + }; + + serviceConfig = { + ExecStartPre = "${preStartScript}"; + ExecStart = "${startScript}"; + User = "graphite-mcp"; + Group = "graphite-mcp"; + WorkingDirectory = "/var/lib/graphite-mcp"; + StateDirectory = "graphite-mcp"; + Restart = "on-failure"; + RestartSec = 5; + + # Hardening + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectHome = "tmpfs"; + BindPaths = ["/var/lib/graphite-mcp"]; + ProtectSystem = "strict"; + ReadWritePaths = ["/var/lib/graphite-mcp"]; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictSUIDSGID = true; + }; + }; + + networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [cfg.port]; + }; +} From eba37cde852e98b66181480b3cd315dbb6adf355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Mon, 9 Mar 2026 15:34:57 -0700 Subject: [PATCH 3/4] fix(glyph): prevent mcpjungle registration failure on unreachable servers The registration script's curl health check fails with a non-zero exit code when a server isn't reachable yet. Since NixOS wraps systemd scripts with set -e, this aborts the entire registration process instead of letting the retry loop handle it. Add || true so the exit code is captured as http_code "000" and the loop continues as intended. Co-Authored-By: Claude Opus 4.6 --- modules/nixos/llm/mcpjungle.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nixos/llm/mcpjungle.nix b/modules/nixos/llm/mcpjungle.nix index f1c855e..a14815a 100644 --- a/modules/nixos/llm/mcpjungle.nix +++ b/modules/nixos/llm/mcpjungle.nix @@ -132,7 +132,7 @@ in { # Wait for server to be reachable before registering ready=false for i in $(seq 1 30); do - http_code=$(curl -s -o /dev/null -w '%{http_code}' "${server.url}" 2>/dev/null) + http_code=$(curl -s -o /dev/null -w '%{http_code}' "${server.url}" 2>/dev/null || true) if [ "$http_code" != "000" ]; then ready=true break From 97dbf2719f1eac4d65966b18cf66c3fea9757e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey?= Date: Mon, 9 Mar 2026 15:37:43 -0700 Subject: [PATCH 4/4] feat(claude-code): allow Graphite learn_gt tool Add the read-only Graphite MCP tool to the auto-allow list so Claude Code can access gt documentation without prompting. Co-Authored-By: Claude Opus 4.6 --- modules/home/development.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/home/development.nix b/modules/home/development.nix index 086f013..9211129 100644 --- a/modules/home/development.nix +++ b/modules/home/development.nix @@ -154,6 +154,7 @@ in { "mcp__glyph__basic-memory__view_note" "mcp__glyph__context7__resolve-library-id" "mcp__glyph__context7__get-library-docs" + "mcp__glyph__graphite__learn_gt" ]; deny = []; };