kyp-mem 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -65,7 +65,7 @@ Opens a rich interface at `localhost:3333` with:
65
65
  | Command | What it does |
66
66
  |---------|-------------|
67
67
  | `kyp-mem init` | First-time setup — choose vault location |
68
- | `kyp-mem setup-claude` | Auto-configure Claude Code MCP settings |
68
+ | `kyp-mem setup-claude` | Register the MCP server with Claude Code for this project |
69
69
  | `kyp-mem setup-claude --global` | Configure globally (all projects) |
70
70
  | `kyp-mem serve` | Start MCP server (used by Claude, not you) |
71
71
  | `kyp-mem ui` | Open web UI at localhost:3333 |
@@ -110,21 +110,11 @@ Settings are defined in `HedgeConfig`. See [[Risk Management]] for safety checks
110
110
 
111
111
  If you prefer to configure manually instead of using `setup-claude`:
112
112
 
113
- ```json
114
- {
115
- "mcpServers": {
116
- "kyp-mem": {
117
- "command": "npx",
118
- "args": ["-y", "kyp-mem", "serve"],
119
- "env": {
120
- "KYP_VAULT": "~/.kyp-mem/vault"
121
- }
122
- }
123
- }
124
- }
113
+ ```bash
114
+ claude mcp add -s local -e KYP_VAULT="$HOME/.kyp-mem/vault" kyp-mem -- npx -y kyp-mem serve
125
115
  ```
126
116
 
127
- Add to `~/.claude/settings.json` (global) or `.claude/settings.json` (per-project).
117
+ Use `-s user` instead of `-s local` to make it available in all projects.
128
118
 
129
119
  ## Architecture
130
120
 
package/kyp_mem/cli.py CHANGED
@@ -4,6 +4,7 @@ import os
4
4
  import json
5
5
  import shutil
6
6
  import argparse
7
+ import subprocess
7
8
  from pathlib import Path
8
9
 
9
10
  C = "\033[36m" # cyan
@@ -107,28 +108,112 @@ def _run_setup_claude(global_config: bool = False):
107
108
  from .config import get_vault_path
108
109
 
109
110
  vault_path = get_vault_path()
111
+ mcp_command, mcp_args = _get_mcp_command()
112
+ claude_scope = "user" if global_config else "local"
113
+ scope_label = "global user" if global_config else "local project"
114
+
115
+ registered, detail = _register_with_claude_mcp(
116
+ claude_scope,
117
+ mcp_command,
118
+ mcp_args,
119
+ vault_path,
120
+ )
121
+
122
+ print()
123
+ print(f" {C}KYP-MEM{R} — Claude Code Setup")
124
+ print()
125
+ if registered:
126
+ print(f" {G}✓{R} MCP server registered with Claude Code ({scope_label})")
127
+ else:
128
+ settings_path = _write_legacy_claude_settings(global_config, mcp_command, mcp_args, vault_path)
129
+ print(f" {Y}✗{R} Could not register with Claude Code's MCP manager")
130
+ print(f" {D} Reason: {detail}{R}")
131
+ print(f" {Y}!{R} Wrote legacy settings as a fallback")
132
+ print(f" {D} File: {settings_path}{R}")
133
+ print(f" {D} Command: {mcp_command} {' '.join(mcp_args)}{R}")
134
+ print(f" {D} Vault: {vault_path}{R}")
135
+ print()
136
+ print(f" {C}Done!{R} Restart Claude Code and kyp-mem will run automatically.")
137
+ print(f" Claude gets these tools: kyp_list, kyp_read, kyp_write, kyp_delete,")
138
+ print(f" kyp_search, kyp_tags, kyp_related, kyp_recent, kyp_stats")
139
+ print()
140
+ print(f" {D}To open the web UI anytime:{R} {Y}kyp-mem ui{R}")
141
+ print()
142
+
143
+
144
+ def _get_mcp_command() -> tuple[str, list[str]]:
110
145
  kyp_mem_bin = shutil.which("kyp-mem")
111
146
  npx_bin = shutil.which("npx")
112
147
 
113
148
  if kyp_mem_bin and "_npx" not in Path(kyp_mem_bin).parts:
114
- mcp_command = kyp_mem_bin
115
- mcp_args = ["serve"]
116
- elif npx_bin:
117
- mcp_command = npx_bin
118
- mcp_args = ["-y", "kyp-mem", "serve"]
119
- else:
120
- print(f" {Y}Warning:{R} 'kyp-mem' not found in PATH.")
121
- print(f" {D}Make sure you installed with: npm install -g kyp-mem{R}")
122
- print()
123
- mcp_command = "kyp-mem"
124
- mcp_args = ["serve"]
149
+ return kyp_mem_bin, ["serve"]
150
+ if npx_bin:
151
+ return npx_bin, ["-y", "kyp-mem", "serve"]
152
+
153
+ print(f" {Y}Warning:{R} 'kyp-mem' not found in PATH.")
154
+ print(f" {D}Make sure you installed with: npm install -g kyp-mem{R}")
155
+ print()
156
+ return "kyp-mem", ["serve"]
157
+
158
+
159
+ def _register_with_claude_mcp(
160
+ scope: str,
161
+ mcp_command: str,
162
+ mcp_args: list[str],
163
+ vault_path: str,
164
+ ) -> tuple[bool, str]:
165
+ claude_bin = shutil.which("claude")
166
+ if not claude_bin:
167
+ return False, "'claude' CLI not found in PATH"
168
+
169
+ server_config = {
170
+ "type": "stdio",
171
+ "command": mcp_command,
172
+ "args": mcp_args,
173
+ "env": {
174
+ "KYP_VAULT": vault_path,
175
+ },
176
+ }
177
+
178
+ # Make setup idempotent when the user reruns it with a new vault or binary.
179
+ subprocess.run(
180
+ [claude_bin, "mcp", "remove", "-s", scope, "kyp-mem"],
181
+ stdout=subprocess.DEVNULL,
182
+ stderr=subprocess.DEVNULL,
183
+ text=True,
184
+ )
185
+
186
+ result = subprocess.run(
187
+ [
188
+ claude_bin,
189
+ "mcp",
190
+ "add-json",
191
+ "-s",
192
+ scope,
193
+ "kyp-mem",
194
+ json.dumps(server_config),
195
+ ],
196
+ capture_output=True,
197
+ text=True,
198
+ )
199
+
200
+ if result.returncode == 0:
201
+ return True, result.stdout.strip()
202
+
203
+ detail = (result.stderr or result.stdout or "unknown error").strip()
204
+ return False, detail
125
205
 
206
+
207
+ def _write_legacy_claude_settings(
208
+ global_config: bool,
209
+ mcp_command: str,
210
+ mcp_args: list[str],
211
+ vault_path: str,
212
+ ) -> Path:
126
213
  if global_config:
127
214
  settings_path = Path.home() / ".claude" / "settings.json"
128
- scope = "global"
129
215
  else:
130
216
  settings_path = Path.cwd() / ".claude" / "settings.json"
131
- scope = "project"
132
217
 
133
218
  settings_path.parent.mkdir(parents=True, exist_ok=True)
134
219
 
@@ -140,7 +225,6 @@ def _run_setup_claude(global_config: bool = False):
140
225
  settings = {}
141
226
 
142
227
  mcp_servers = settings.setdefault("mcpServers", {})
143
-
144
228
  mcp_servers["kyp-mem"] = {
145
229
  "command": mcp_command,
146
230
  "args": mcp_args,
@@ -150,21 +234,7 @@ def _run_setup_claude(global_config: bool = False):
150
234
  }
151
235
 
152
236
  settings_path.write_text(json.dumps(settings, indent=2) + "\n")
153
-
154
- print()
155
- print(f" {C}KYP-MEM{R} — Claude Code Setup")
156
- print()
157
- print(f" {G}✓{R} MCP server added to {scope} settings")
158
- print(f" {D} File: {settings_path}{R}")
159
- print(f" {D} Command: {mcp_command} {' '.join(mcp_args)}{R}")
160
- print(f" {D} Vault: {vault_path}{R}")
161
- print()
162
- print(f" {C}Done!{R} Restart Claude Code and kyp-mem will run automatically.")
163
- print(f" Claude gets these tools: kyp_list, kyp_read, kyp_write, kyp_delete,")
164
- print(f" kyp_search, kyp_tags, kyp_related, kyp_recent, kyp_stats")
165
- print()
166
- print(f" {D}To open the web UI anytime:{R} {Y}kyp-mem ui{R}")
167
- print()
237
+ return settings_path
168
238
 
169
239
 
170
240
  def _run_stats():
@@ -212,22 +282,37 @@ def _run_doctor():
212
282
  else:
213
283
  print(f" {Y}✗{R} Vault not found: {vault_path}")
214
284
 
215
- # Claude Code config
216
- for label, path in [
285
+ # Claude Code MCP registration
286
+ claude_bin = shutil.which("claude")
287
+ if claude_bin:
288
+ result = subprocess.run(
289
+ [claude_bin, "mcp", "get", "kyp-mem"],
290
+ capture_output=True,
291
+ text=True,
292
+ )
293
+ if result.returncode == 0 and "Status: ✓ Connected" in result.stdout:
294
+ print(f" {G}✓{R} Claude Code MCP: kyp-mem connected")
295
+ elif result.returncode == 0:
296
+ print(f" {Y}✗{R} Claude Code MCP: kyp-mem registered but not connected")
297
+ else:
298
+ print(f" {Y}✗{R} Claude Code MCP: kyp-mem not active")
299
+ else:
300
+ print(f" {Y}✗{R} Claude Code CLI not found in PATH")
301
+
302
+ legacy_paths = [
217
303
  ("project", Path.cwd() / ".claude" / "settings.json"),
218
304
  ("global", Path.home() / ".claude" / "settings.json"),
219
- ]:
220
- if path.exists():
221
- try:
222
- s = json.loads(path.read_text())
223
- if "kyp-mem" in s.get("mcpServers", {}):
224
- print(f" {G}✓{R} Claude Code ({label}): kyp-mem configured")
225
- else:
226
- print(f" {D}·{R} Claude Code ({label}): exists but kyp-mem not configured")
227
- except json.JSONDecodeError:
228
- print(f" {Y}✗{R} Claude Code ({label}): invalid JSON")
229
- else:
230
- print(f" {D}·{R} Claude Code ({label}): no settings file")
305
+ ]
306
+ for label, path in legacy_paths:
307
+ if not path.exists():
308
+ continue
309
+ try:
310
+ s = json.loads(path.read_text())
311
+ except json.JSONDecodeError:
312
+ print(f" {Y}{R} Legacy Claude settings ({label}): invalid JSON")
313
+ continue
314
+ if "kyp-mem" in s.get("mcpServers", {}):
315
+ print(f" {D}·{R} Legacy Claude settings ({label}): kyp-mem entry present")
231
316
 
232
317
  # Binary
233
318
  kyp_bin = shutil.which("kyp-mem")
package/kyp_mem/server.py CHANGED
@@ -7,7 +7,7 @@ from .vault import Vault
7
7
 
8
8
  vault = Vault(get_vault_path())
9
9
 
10
- mcp = FastMCP("kyp-mem", description="Know Your Project — headless knowledge base for AI agents")
10
+ mcp = FastMCP("kyp-mem")
11
11
 
12
12
 
13
13
  @mcp.tool()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kyp-mem",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Know Your Project — Headless knowledge base for AI agents. MCP-powered with wikilinks, backlinks, and neon web UI.",
5
5
  "bin": {
6
6
  "kyp-mem": "bin/cli.mjs"
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "kyp-mem"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "Know Your Project — Headless knowledge base for AI agents. MCP-powered with wikilinks, backlinks, and neon web UI."
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}