Skip to content

Commit 82c76de

Browse files
stackptrclaude
andauthored
feat(glyph): add Graphite MCP server to gateway (#337)
* feat(claude-code): add Graphite MCP server Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d363d90 commit 82c76de

7 files changed

Lines changed: 113 additions & 1 deletion

File tree

382 Bytes
Binary file not shown.

‎hosts/glyph/services/default.nix‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@
7373
mode = "440";
7474
};
7575

76+
age.secrets.graphite-auth-token = {
77+
file = ./../secrets/graphite-auth-token.age;
78+
mode = "440";
79+
owner = "graphite-mcp";
80+
group = "graphite-mcp";
81+
};
82+
7683
services.basic-memory.enable = true;
7784
rc.backup = {
7885
enable = true;
@@ -87,6 +94,10 @@
8794
enable = true;
8895
environmentFile = config.age.secrets.kagi-api-key.path;
8996
};
97+
services.graphite-mcp = {
98+
enable = true;
99+
authTokenFile = config.age.secrets.graphite-auth-token.path;
100+
};
90101
services.mcpjungle = {
91102
enable = true;
92103
servers.basic-memory = {
@@ -101,6 +112,10 @@
101112
url = "http://127.0.0.1:8093/mcp";
102113
description = "Kagi web search and page summarization";
103114
};
115+
servers.graphite = {
116+
url = "http://127.0.0.1:8094/mcp";
117+
description = "Graphite CLI for stacked PRs and code review";
118+
};
104119
servers.context7 = {
105120
url = "https://mcp.context7.com/mcp";
106121
description = "Up-to-date library documentation and code examples";

‎lib/secrets/glyph.nix‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ in {
77
"hosts/glyph/secrets/kagi-api-key.age".publicKeys = keys;
88
"hosts/glyph/secrets/context7-api-key.age".publicKeys = keys;
99
"hosts/glyph/secrets/open-webui-env.age".publicKeys = keys;
10+
"hosts/glyph/secrets/graphite-auth-token.age".publicKeys = keys;
1011
}

‎modules/home/development.nix‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ in {
154154
"mcp__glyph__basic-memory__view_note"
155155
"mcp__glyph__context7__resolve-library-id"
156156
"mcp__glyph__context7__get-library-docs"
157+
"mcp__glyph__graphite__learn_gt"
157158
];
158159
deny = [];
159160
};

‎modules/nixos/llm/default.nix‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
imports = [
33
./basic-memory.nix
4+
./graphite-mcp.nix
45
./kagi.nix
56
./mcp-nixos.nix
67
./mcpjungle.nix

‎modules/nixos/llm/graphite-mcp.nix‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
{
2+
config,
3+
pkgs,
4+
lib,
5+
...
6+
}: let
7+
cfg = config.services.graphite-mcp;
8+
9+
preStartScript = pkgs.writeShellScript "graphite-mcp-prestart" ''
10+
mkdir -p "$HOME/.config/graphite"
11+
token=$(cat "${cfg.authTokenFile}")
12+
printf '{"authToken":"%s"}' "$token" > "$HOME/.config/graphite/user_config"
13+
chmod 600 "$HOME/.config/graphite/user_config"
14+
'';
15+
16+
startScript = pkgs.writeShellScript "graphite-mcp-start" ''
17+
exec ${lib.getExe pkgs.mcp-proxy} \
18+
--host ${cfg.host} \
19+
--port ${toString cfg.port} \
20+
--transport streamablehttp \
21+
-- ${lib.getExe pkgs.graphite-cli} mcp
22+
'';
23+
in {
24+
options.services.graphite-mcp = {
25+
enable = lib.mkEnableOption "Graphite MCP server (stdio→HTTP bridge)";
26+
27+
port = lib.mkOption {
28+
type = lib.types.port;
29+
default = 8094;
30+
description = "Port for the streamable HTTP transport.";
31+
};
32+
33+
host = lib.mkOption {
34+
type = lib.types.str;
35+
default = "127.0.0.1";
36+
description = "Address to bind the HTTP server to.";
37+
};
38+
39+
authTokenFile = lib.mkOption {
40+
type = lib.types.path;
41+
description = "Path to file containing the Graphite auth token.";
42+
};
43+
44+
openFirewall = lib.mkEnableOption "opening firewall ports for Graphite MCP";
45+
};
46+
47+
config = lib.mkIf cfg.enable {
48+
users.users.graphite-mcp = {
49+
isSystemUser = true;
50+
group = "graphite-mcp";
51+
home = "/var/lib/graphite-mcp";
52+
};
53+
users.groups.graphite-mcp = {};
54+
55+
systemd.services.graphite-mcp = {
56+
description = "Graphite MCP Server";
57+
after = ["network-online.target"];
58+
wants = ["network-online.target"];
59+
wantedBy = ["multi-user.target"];
60+
61+
path = [pkgs.git];
62+
63+
environment = {
64+
HOME = "/var/lib/graphite-mcp";
65+
};
66+
67+
serviceConfig = {
68+
ExecStartPre = "${preStartScript}";
69+
ExecStart = "${startScript}";
70+
User = "graphite-mcp";
71+
Group = "graphite-mcp";
72+
WorkingDirectory = "/var/lib/graphite-mcp";
73+
StateDirectory = "graphite-mcp";
74+
Restart = "on-failure";
75+
RestartSec = 5;
76+
77+
# Hardening
78+
NoNewPrivileges = true;
79+
PrivateDevices = true;
80+
PrivateTmp = true;
81+
ProtectHome = "tmpfs";
82+
BindPaths = ["/var/lib/graphite-mcp"];
83+
ProtectSystem = "strict";
84+
ReadWritePaths = ["/var/lib/graphite-mcp"];
85+
ProtectKernelTunables = true;
86+
ProtectKernelModules = true;
87+
ProtectControlGroups = true;
88+
RestrictSUIDSGID = true;
89+
};
90+
};
91+
92+
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [cfg.port];
93+
};
94+
}

‎modules/nixos/llm/mcpjungle.nix‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ in {
132132
# Wait for server to be reachable before registering
133133
ready=false
134134
for i in $(seq 1 30); do
135-
http_code=$(curl -s -o /dev/null -w '%{http_code}' "${server.url}" 2>/dev/null)
135+
http_code=$(curl -s -o /dev/null -w '%{http_code}' "${server.url}" 2>/dev/null || true)
136136
if [ "$http_code" != "000" ]; then
137137
ready=true
138138
break

0 commit comments

Comments
 (0)