Repository navigation
Expand file tree
/
Copy pathmcp_proxy.py
More file actions
1229 lines (1029 loc) · 41.9 KB
/
Copy pathmcp_proxy.py
File metadata and controls
1229 lines (1029 loc) · 41.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""Jeff MCP Proxy -- stable host-side entrypoint.
Claude Code points at this file in .mcp.json. It never changes.
On each tool call, reads .jeff-state.json to find the active chip
and routes to it dynamically. Swap chips without restarting.
"""
import json
import os
import sqlite3
import sys
import threading
JEFF_DIR = os.path.dirname(os.path.abspath(__file__))
STATE_FILE = os.path.join(JEFF_DIR, ".jeff-state.json")
SPECTRAL_DIR = os.path.join(JEFF_DIR, "spectral")
sys.path.insert(0, SPECTRAL_DIR)
from mcp.server.fastmcp import FastMCP
import spectral
mcp = FastMCP("jeff")
# ============================================================
# RESILIENCE LAYER
# ============================================================
class VolumeGoneError(Exception):
"""Raised when a volume/chip path is no longer reachable."""
pass
def _safe_read(path):
"""Read a file, raising VolumeGoneError if the volume vanished."""
try:
with open(path, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
raise VolumeGoneError("Path not found: %s" % path)
except PermissionError:
raise VolumeGoneError("Permission denied: %s" % path)
except OSError as exc:
raise VolumeGoneError("I/O error on %s: %s" % (path, exc))
def _safe_json(path):
"""Read and parse a JSON file, raising VolumeGoneError if unreachable."""
raw = _safe_read(path)
return json.loads(raw)
def _safe_listdir(path):
"""List a directory, raising VolumeGoneError if gone."""
try:
return sorted(os.listdir(path))
except FileNotFoundError:
raise VolumeGoneError("Directory not found: %s" % path)
except PermissionError:
raise VolumeGoneError("Permission denied: %s" % path)
except OSError as exc:
raise VolumeGoneError("I/O error on %s: %s" % (path, exc))
def _safe_db_query(db_path, query, params=()):
"""Run a read query against a sqlite db. Returns (rows, error).
On success: (list_of_dicts, None)
On failure: ([], error_string)
"""
if not db_path or not os.path.isfile(db_path):
return [], "Database not found: %s" % db_path
try:
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
rows = [dict(r) for r in conn.execute(query, params).fetchall()]
conn.close()
return rows, None
except sqlite3.OperationalError as exc:
return [], "Database error: %s" % exc
except OSError as exc:
return [], "Volume gone during query: %s" % exc
def _safe_db_execute(db_path, query, params=()):
"""Run a write query against a sqlite db. Returns result dict."""
if not db_path or not os.path.isfile(db_path):
return {"error": "Database not found: %s" % db_path}
try:
conn = sqlite3.connect(db_path)
cur = conn.execute(query, params)
conn.commit()
result = {"rows_affected": cur.rowcount, "lastrowid": cur.lastrowid}
conn.close()
return result
except sqlite3.OperationalError as exc:
return {"error": "Database error: %s" % exc}
except OSError as exc:
return {"error": "Volume gone during write: %s" % exc}
def _volume_alive(path):
"""Quick liveness check -- can we stat the path?"""
try:
os.stat(path)
return True
except (FileNotFoundError, PermissionError, OSError):
return False
def _scan_chip_surface(vol_path):
"""Scan a chip's full surface and return a structured manifest.
Reports everything on the chip -- not just vaults. Datasets, MCP service,
models, schemas, docs, sparseimage -- the whole picture.
"""
surface = {
"path": vol_path,
"alive": _volume_alive(vol_path),
"vaults": [],
"datasets": [],
"models": [],
"docs": [],
"schemas": [],
"has_mcp": False,
"has_heartbeat": False,
"has_sparseimage": False,
"sparseimage_mb": 0,
"model_name": None,
"label": None,
"device_id": None,
}
if not surface["alive"]:
return surface
try:
entries = _safe_listdir(vol_path)
except VolumeGoneError:
surface["alive"] = False
return surface
# Heartbeat
hb_path = os.path.join(vol_path, "heartbeat.json")
if os.path.isfile(hb_path):
surface["has_heartbeat"] = True
try:
hb = _safe_json(hb_path)
surface["label"] = hb.get("label")
surface["device_id"] = hb.get("device_id")
surface["model_name"] = hb.get("model")
except VolumeGoneError:
pass
# MCP service
mcp_dir = os.path.join(vol_path, "mcp")
if os.path.isdir(mcp_dir):
surface["has_mcp"] = True
# Datasets
ds_dir = os.path.join(vol_path, "datasets")
if os.path.isdir(ds_dir):
try:
for f in _safe_listdir(ds_dir):
if f.endswith(".json") or f.endswith(".md"):
surface["datasets"].append(f)
except VolumeGoneError:
pass
# Models
models_dir = os.path.join(vol_path, "models")
if os.path.isdir(models_dir):
try:
for f in _safe_listdir(models_dir):
full = os.path.join(models_dir, f)
if os.path.isfile(full):
size_mb = os.path.getsize(full) / (1024 * 1024)
surface["models"].append({"name": f, "size_mb": round(size_mb, 1)})
except VolumeGoneError:
pass
# Docs
jeff_docs = os.path.join(vol_path, ".jeff", "docs")
if os.path.isdir(jeff_docs):
try:
for f in _safe_listdir(jeff_docs):
if f.endswith(".md"):
surface["docs"].append(f)
except VolumeGoneError:
pass
# Schemas
for f in entries:
if f.endswith(".sql"):
surface["schemas"].append(f)
# Sparseimage
si_path = os.path.join(vol_path, "vault.sparseimage")
if os.path.isfile(si_path):
surface["has_sparseimage"] = True
try:
surface["sparseimage_mb"] = round(os.path.getsize(si_path) / (1024 * 1024), 1)
except OSError:
pass
# Vaults (vault-* dirs)
for f in entries:
vault_dir = os.path.join(vol_path, f)
if os.path.isdir(vault_dir) and f.startswith("vault-"):
vault_name = f.replace("vault-", "")
db = os.path.join(vault_dir, "vault.db")
has_db = os.path.isfile(db)
has_cuesheet = os.path.isfile(os.path.join(vault_dir, "cue-sheet.yaml"))
surface["vaults"].append({
"name": vault_name,
"has_db": has_db,
"has_cuesheet": has_cuesheet,
})
# Search index
idx = os.path.join(vol_path, ".jeff", "index", "search.json")
surface["has_search_index"] = os.path.isfile(idx)
return surface
def _auto_deactivate(vol_name):
"""Remove a volume from the active list when it vanishes."""
if not os.path.isfile(VOLUMES_ACTIVE_FILE):
return
try:
with open(VOLUMES_ACTIVE_FILE) as f:
active = json.load(f)
if vol_name in active:
active.remove(vol_name)
with open(VOLUMES_ACTIVE_FILE, "w") as f:
json.dump(sorted(set(active)), f, indent=2)
f.write("\n")
except Exception:
pass
# cue-vox stream notification (fire-and-forget)
CUE_VOX_PORT = os.environ.get("CUE_VOX_PORT", "3000")
CUE_VOX_CHIP_OP_URL = "http://localhost:%s/api/chip/op" % CUE_VOX_PORT
def _notify_stream(op, summary="", chip_data=None):
"""POST chip op to cue-vox stream. Non-blocking, best-effort."""
state = _active_state()
label = state.get("label", "?") if state else "?"
root = "#888888"
if state:
hb_path = os.path.join(state.get("volume_path", ""), "heartbeat.json")
if os.path.isfile(hb_path):
with open(hb_path) as f:
hb = json.load(f)
root = hb.get("tool_chain", {}).get("root", root)
payload = {
"op": op,
"label": label,
"root_color": root,
"summary": summary,
"chip_data": chip_data or {},
}
def _post():
try:
import urllib.request
req = urllib.request.Request(
CUE_VOX_CHIP_OP_URL,
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"},
method="POST",
)
urllib.request.urlopen(req, timeout=2)
except Exception:
pass # best-effort, cue-vox may not be running
threading.Thread(target=_post, daemon=True).start()
# ============================================================
# STATE HELPERS
# ============================================================
def _active_state():
"""Read current chip state. Returns dict or None."""
if not os.path.isfile(STATE_FILE):
return None
with open(STATE_FILE) as f:
state = json.load(f)
volume = state.get("volume_path", "")
if not volume or not os.path.isdir(volume):
return None
return state
def _require_chip():
"""Return active state or raise with a clear message."""
state = _active_state()
if state is None:
return None, "No chip active. Insert a chip and run: jeff activate /Volumes/NAME"
return state, None
# ============================================================
# CHIP TOOLS
# ============================================================
@mcp.tool()
def chip_status():
"""Chip identity, mode, and capabilities."""
state = _active_state()
# Active volumes (always available, even without a chip inserted)
vaults = _discover_active_vaults()
active_volumes = sorted(set(v["volume"] for v in vaults))
vault_count = len(vaults)
result = {
"active_volumes": active_volumes,
"vault_count": vault_count,
}
if state:
# A removable chip is inserted and active
volume = state["volume_path"]
hb_path = os.path.join(volume, "heartbeat.json")
hb = {}
try:
if os.path.isfile(hb_path):
hb = _safe_json(hb_path)
except VolumeGoneError:
result["chip"] = {"error": "Chip ejected (was %s)" % state.get("label", "?")}
return json.dumps(result, indent=2)
chain = hb.get("tool_chain", {})
root = chain.get("root", "#000000")
surface = _scan_chip_surface(volume)
result["chip"] = {
"label": state.get("label", "?"),
"device_id": state.get("device_id", "?"),
"mode": state.get("mode", "?"),
"root_color": root,
"band": spectral.resolve(root).get("band", "?") if root != "#000000" else "unknown",
"model": state.get("model", ""),
"volume": volume,
"vault_mounted": bool(state.get("vault_mount")),
"mount_count": hb.get("mount_count", 0),
}
result["surface"] = {
"datasets": surface["datasets"],
"models": surface["models"],
"docs": surface["docs"],
"schemas": surface["schemas"],
"has_mcp": surface["has_mcp"],
"has_sparseimage": surface["has_sparseimage"],
"sparseimage_mb": surface["sparseimage_mb"],
"has_search_index": surface["has_search_index"],
"vaults_on_surface": surface["vaults"],
}
else:
result["chip"] = None
_notify_stream("status", "status checked", result)
return json.dumps(result, indent=2)
@mcp.tool()
def chip_read_card(filename: str = ""):
"""Read a file from the card surface (no vault needed).
Args:
filename: File to read. Empty to list all files on the card.
"""
state, err = _require_chip()
if err:
return json.dumps({"error": err})
_notify_stream("read_card", "read: %s" % (filename or "(list)"))
volume = state["volume_path"]
try:
if not filename:
files = []
for f in _safe_listdir(volume):
if f.startswith(".") and f != ".jeff":
continue
full = os.path.join(volume, f)
if os.path.isfile(full):
files.append({"name": f, "size_bytes": os.path.getsize(full)})
elif os.path.isdir(full):
files.append({"name": f + "/", "type": "directory"})
return json.dumps({"volume": volume, "files": files}, indent=2)
requested = os.path.normpath(os.path.join(volume, filename))
if not requested.startswith(volume):
return json.dumps({"error": "Path traversal denied"})
if os.path.isdir(requested):
entries = []
for f in _safe_listdir(requested):
full = os.path.join(requested, f)
if os.path.isfile(full):
entries.append({"name": f, "size_bytes": os.path.getsize(full)})
elif os.path.isdir(full):
entries.append({"name": f + "/", "type": "directory"})
return json.dumps({"path": filename, "files": entries}, indent=2)
if not os.path.isfile(requested):
return json.dumps({"error": "File not found: %s" % filename})
content = _safe_read(requested)
return json.dumps({"file": filename, "content": content}, indent=2)
except VolumeGoneError as exc:
return json.dumps({"error": str(exc), "hint": "Chip may have been ejected"})
@mcp.tool()
def chip_tool_chain():
"""Show this chip's Merkle-VRGB tool chain and root color."""
state, err = _require_chip()
if err:
return json.dumps({"error": err})
_notify_stream("tool_chain", "tool chain verified")
hb_path = os.path.join(state["volume_path"], "heartbeat.json")
if os.path.isfile(hb_path):
with open(hb_path) as f:
hb = json.load(f)
chain = hb.get("tool_chain", {})
if chain:
chain["spectral"] = spectral.resolve(chain.get("root", "#000000"))
return json.dumps(chain, indent=2)
return json.dumps({"error": "No tool chain found"})
# ============================================================
# SPECTRAL BINDING (host-side, no chip needed)
# ============================================================
@mcp.tool()
def chip_resolve_hex(hex_color: str):
"""Resolve a hex color to its spectral band, hue, and position."""
return json.dumps(spectral.resolve(hex_color), indent=2)
@mcp.tool()
def chip_resolve_deep(hex_color: str, depth: int = 3):
"""Resolve a hex color through multiple zoom levels."""
return json.dumps(spectral.resolve_deep(hex_color, depth), indent=2)
@mcp.tool()
def chip_midpoint(hex_a: str, hex_b: str):
"""Find the midpoint between two hex colors in HSL space."""
return json.dumps(spectral.midpoint(hex_a, hex_b), indent=2)
@mcp.tool()
def chip_split_band(hex_a: str, hex_b: str, n: int = 2):
"""Split the range between two hex colors into n equal parts."""
points = spectral.split_band(hex_a, hex_b, n)
result = [{"hex": p, "hue": spectral.resolve(p)["hue"], "band": spectral.resolve(p)["band"]} for p in points]
return json.dumps(result, indent=2)
@mcp.tool()
def chip_distance(hex_a: str, hex_b: str):
"""Shortest arc hue distance between two hex colors."""
return json.dumps({"hex_a": hex_a, "hex_b": hex_b, "distance_degrees": spectral.hue_distance(hex_a, hex_b)}, indent=2)
@mcp.tool()
def chip_constellation(hex_colors: str):
"""Group hex colors by spectral band proximity.
Args:
hex_colors: Comma-separated hex colors.
"""
colors = [c.strip() for c in hex_colors.split(",") if c.strip()]
return json.dumps({
"groups": spectral.constellation(colors),
"summary": spectral.constellation_summary(colors),
}, indent=2)
@mcp.tool()
def chip_registry():
"""Show the Level 0 spectral band registry."""
return json.dumps(spectral.registry(), indent=2)
# ============================================================
# MODEL INFERENCE (delegates to chip)
# ============================================================
@mcp.tool()
def chip_query(prompt: str, system: str = "", max_tokens: int = 2048):
"""Ask the chip model a question."""
state, err = _require_chip()
if err:
return json.dumps({"error": err})
truncated = prompt[:60] + ("..." if len(prompt) > 60 else "")
_notify_stream("query", "query: \"%s\"" % truncated)
volume = state["volume_path"]
if not _volume_alive(volume):
return json.dumps({"error": "Chip ejected (was %s)" % state.get("label", "?")})
mcp_dir = os.path.join(volume, "mcp")
sys.path.insert(0, mcp_dir)
try:
import inference
return inference.generate(
prompt=prompt,
system=system or "You are Jeff, a helpful assistant on a Booster Chip.",
max_tokens=max_tokens,
)
except Exception as exc:
return json.dumps({"error": str(exc)})
finally:
sys.path.remove(mcp_dir) if mcp_dir in sys.path else None
@mcp.tool()
def chip_search(query: str, top_k: int = 5):
"""Search the chip's knowledge base.
Args:
query: What to search for in natural language.
top_k: Number of results to return (default 5).
"""
state, err = _require_chip()
if err:
return json.dumps({"error": err})
truncated_q = query[:60] + ("..." if len(query) > 60 else "")
_notify_stream("search", "search: \"%s\"" % truncated_q)
volume = state["volume_path"]
if not _volume_alive(volume):
return json.dumps({"error": "Chip ejected (was %s)" % state.get("label", "?")})
mcp_dir = os.path.join(volume, "mcp")
index_path = os.path.join(volume, ".jeff", "index", "search.json")
if not os.path.isfile(index_path):
return json.dumps({"error": "No search index. Run: python3 build_index.py %s" % volume})
sys.path.insert(0, mcp_dir)
try:
import inference
from build_index import search, cosine_similarity
query_embedding = inference.embed(query)
if not query_embedding:
return json.dumps({"error": "Embedding failed -- is Ollama running with nomic-embed-text?"})
results = search(index_path, query_embedding, top_k=top_k)
return json.dumps({"query": query, "results": results}, indent=2)
except Exception as exc:
return json.dumps({"error": str(exc)})
finally:
sys.path.remove(mcp_dir) if mcp_dir in sys.path else None
# ============================================================
# SEMANTIC SEARCH (host-side, C2D2 vector layer)
# ============================================================
MAESTRO_ROOT = os.path.dirname(os.path.dirname(JEFF_DIR))
C2D2_DIR = os.path.join(MAESTRO_ROOT, "tools", "c2d2")
C2D2_FALLBACK_VECTORS = os.path.join(C2D2_DIR, ".vectors")
def _resolve_sidecar(volume_path, name):
"""Find a .vectors/ sidecar for a volume. Returns (path, location) or (None, None)."""
on_volume = os.path.join(volume_path, ".vectors", "%s.npz" % name)
if os.path.isfile(on_volume):
return on_volume, "volume"
fallback = os.path.join(C2D2_FALLBACK_VECTORS, "%s.npz" % name)
if os.path.isfile(fallback):
return fallback, "fallback"
return None, None
@mcp.tool()
def chip_search_semantic(query: str, volume: str = "", top_k: int = 5):
"""Semantic search over a chip's unencrypted surface via the C2D2 vector layer.
Loads a per-volume .vectors/ sidecar built by:
python3 tools/c2d2/cli.py index-chip --volume <path>
Args:
query: Natural-language search query.
volume: Volume name ("gray", "yellow") OR absolute path.
If empty, uses the active chip.
top_k: Number of results to return (default 5).
"""
# Resolve volume path
if volume:
volume_path = _resolve_chip(volume) if not volume.startswith("/") else volume
if not volume_path:
return json.dumps({"error": "Volume not found: %s" % volume})
else:
state = _active_state()
if state is None:
return json.dumps({"error": "No active chip and no volume specified"})
volume_path = state["volume_path"]
if not _volume_alive(volume_path):
return json.dumps({"error": "Volume gone: %s" % volume_path})
# Sidecar lookup
name = os.path.basename(os.path.normpath(volume_path)).lower()
sidecar, location = _resolve_sidecar(volume_path, name)
if not sidecar:
return json.dumps({
"error": "No sidecar for %s. Build one: python3 tools/c2d2/cli.py index-chip --volume %s"
% (name, volume_path)
})
# Search via C2D2 vecstore
sys.path.insert(0, C2D2_DIR)
try:
import vecstore
vs = vecstore.VecStore(sidecar)
if not vs.load():
return json.dumps({"error": "Sidecar failed to load: %s" % sidecar})
results = vs.search(query, top_k=top_k)
except Exception as exc:
return json.dumps({"error": str(exc)})
finally:
if C2D2_DIR in sys.path:
sys.path.remove(C2D2_DIR)
return json.dumps({
"query": query,
"volume": volume_path,
"sidecar": sidecar,
"sidecar_location": location,
"results": results,
}, indent=2)
# ============================================================
# BACKUP (host-side, manages content file backups)
# ============================================================
sys.path.insert(0, JEFF_DIR)
import backup as _backup
def _resolve_chip(chip_name):
"""Resolve chip name to directory path.
Checks in order:
1. Staging dir in maestro (chip-blue/, chip-yellow/, etc.)
2. Active chip via jeff state
3. Physical chip at /Volumes/ by label match
"""
# Staging dir
chip_dir = os.path.join(MAESTRO_ROOT, "chip-%s" % chip_name.lower())
if os.path.isdir(chip_dir):
return chip_dir
# Active chip
state = _active_state()
if state and state.get("label", "").lower() == chip_name.lower():
return state["volume_path"]
# Physical chip at /Volumes/
candidate = os.path.join("/Volumes", chip_name.upper())
if os.path.isdir(candidate) and os.path.isfile(os.path.join(candidate, "heartbeat.json")):
return candidate
return None
@mcp.tool()
def chip_backup(chip_name: str):
"""Back up a chip's unencrypted surface to local volume and/or backup drive.
Creates a timestamped zip per the chip's heartbeat backup config.
Writes to ~/Documents/jeff-backups (iCloud-synced) and any mounted
backup-volume flash drive. FIFO rotates oldest backups.
Args:
chip_name: Chip color (yellow, red, blue) or label.
"""
chip_dir = _resolve_chip(chip_name)
if not chip_dir:
return json.dumps({"error": "Chip not found: %s" % chip_name})
if not _volume_alive(chip_dir):
return json.dumps({"error": "Chip not reachable: %s" % chip_dir})
_notify_stream("backup", "backing up %s" % chip_name)
result = _backup.backup(chip_name.lower(), chip_dir)
_notify_stream("backup_done", result.get("message", "backup complete"))
return json.dumps(result, indent=2)
@mcp.tool()
def chip_backup_status(chip_name: str):
"""Show backup history across all tiers (local + backup volumes).
Args:
chip_name: Chip color (yellow, red, blue) or label.
"""
return json.dumps(_backup.status(chip_name.lower()), indent=2)
@mcp.tool()
def chip_backup_rotate(chip_name: str, max_backups: int = 5):
"""Rotate old backups, keeping only the N most recent.
Blobs are shared and never deleted during rotation -- only manifests
are pruned. This is safe even with cross-chip deduplication.
Args:
chip_name: Chip color (yellow, red, blue) or label.
max_backups: Maximum number of backup manifests to retain (default 5).
"""
result = _backup.rotate(chip_name.lower(), max_backups)
return json.dumps(result, indent=2)
@mcp.tool()
def chip_backup_list(chip_name: str, manifest: str = ""):
"""List files in a backup manifest.
Shows each file, its hash, and whether the blob is available.
Defaults to the latest backup if no manifest specified.
Args:
chip_name: Chip color (yellow, red, blue) or label.
manifest: Specific manifest filename (default: latest).
"""
result = _backup.restore_list(chip_name.lower(), manifest or None)
return json.dumps(result, indent=2)
# ============================================================
# DYNAMIC VAULT ROUTING
# ============================================================
VOLUMES_ACTIVE_FILE = os.path.join(JEFF_DIR, ".jeff-volumes-active.json")
VOLUMES_FILE = os.path.join(JEFF_DIR, "volumes.json")
def _load_active_volumes():
"""Load active volume names."""
if not os.path.isfile(VOLUMES_ACTIVE_FILE):
return []
with open(VOLUMES_ACTIVE_FILE) as f:
return json.load(f)
def _load_registry():
"""Load volume registry, including physical chips at /Volumes/."""
if not os.path.isfile(VOLUMES_FILE):
registry = []
else:
with open(VOLUMES_FILE) as f:
registry = json.load(f)
# Scan /Volumes/ for physical chips
volumes_dir = "/Volumes"
if os.path.isdir(volumes_dir):
known_paths = {v["path"] for v in registry}
for name in sorted(os.listdir(volumes_dir)):
vol = os.path.join(volumes_dir, name)
hb = os.path.join(vol, "heartbeat.json")
if not os.path.isfile(hb) or name == "Macintosh HD":
continue
if vol in known_paths:
continue
try:
with open(hb) as f:
chip = json.load(f)
label = chip.get("label", name)
registry.append({
"name": "sd:%s" % label.lower(),
"type": "chip",
"path": vol,
"physical": True,
"label": label,
})
except Exception:
continue
return registry
def _chip_vault_mount_path(vol_path):
"""Return the mounted sparseimage path for a chip, or None."""
# Check .chip-mount.json
state_file = os.path.join(MAESTRO_ROOT, ".chip-mount.json")
if os.path.isfile(state_file):
try:
with open(state_file) as f:
state = json.load(f)
if state.get("volume_path") == vol_path and state.get("vault_mount"):
if os.path.isdir(state["vault_mount"]):
return state["vault_mount"]
except Exception:
pass
# Fallback: check heartbeat for vault_volume_name
hb_path = os.path.join(vol_path, "heartbeat.json")
if os.path.isfile(hb_path):
try:
with open(hb_path) as f:
hb = json.load(f)
vol_name = hb.get("vault_volume_name", "")
if vol_name:
mount = os.path.join("/Volumes", vol_name)
if os.path.isdir(mount):
return mount
except Exception:
pass
return None
def _discover_active_vaults():
"""Discover all vaults across active volumes. Returns list of vault dicts."""
active = set(_load_active_volumes())
registry = _load_registry()
vaults = []
for vol in registry:
if vol["name"] not in active:
continue
vol_path = vol["path"] if os.path.isabs(vol["path"]) else os.path.join(MAESTRO_ROOT, vol["path"])
# Liveness check -- if volume vanished, auto-deactivate and skip
if not _volume_alive(vol_path):
_auto_deactivate(vol["name"])
continue
if vol["type"] == "local":
# Local volume IS a vault
db = os.path.join(vol_path, "vault.db")
vaults.append({
"vault": vol["name"],
"volume": vol["name"],
"type": "local",
"path": vol_path,
"db": db if os.path.isfile(db) else None,
"env": vol.get("env", {}),
})
else:
# Chip -- discover vault-* subdirs
# Check card surface first, then mounted sparseimage
scan_path = vol_path
try:
surface_entries = _safe_listdir(vol_path)
except VolumeGoneError:
_auto_deactivate(vol["name"])
continue
surface_vaults = [d for d in surface_entries
if os.path.isdir(os.path.join(vol_path, d))
and d.startswith("vault-")]
if not surface_vaults:
# No vaults on surface -- check mounted sparseimage
mount_path = _chip_vault_mount_path(vol_path)
if mount_path:
scan_path = mount_path
try:
scan_entries = _safe_listdir(scan_path)
except VolumeGoneError:
_auto_deactivate(vol["name"])
continue
for d in scan_entries:
vault_dir = os.path.join(scan_path, d)
if not os.path.isdir(vault_dir) or not d.startswith("vault-"):
continue
db = os.path.join(vault_dir, "vault.db")
vault_name = d.replace("vault-", "")
vaults.append({
"vault": vault_name,
"volume": vol["name"],
"type": "chip",
"path": vault_dir,
"db": db if os.path.isfile(db) else None,
})
# If still no vaults found, use heartbeat manifest for metadata
if not any(v["volume"] == vol["name"] for v in vaults):
hb_path = os.path.join(vol_path, "heartbeat.json")
if os.path.isfile(hb_path):
try:
hb = _safe_json(hb_path)
for hb_vault in hb.get("vaults", []):
vault_name = hb_vault.get("name", "")
if vault_name:
vaults.append({
"vault": vault_name,
"volume": vol["name"],
"type": "chip",
"path": "",
"db": None,
"sealed": True,
"has_cuesheet": bool(hb_vault.get("cuesheet")),
})
except VolumeGoneError:
_auto_deactivate(vol["name"])
continue
return vaults
def _find_vault(vault_name):
"""Find a specific vault by name across active volumes."""
for v in _discover_active_vaults():
if v["vault"] == vault_name:
return v
return None
def _vault_db_query(vault, query, params=()):
"""Run a query against a vault's database. Returns (rows, error)."""
return _safe_db_query(vault.get("db"), query, params)
def _vault_db_execute(vault, query, params=()):
"""Execute a write query against a vault's database."""
return _safe_db_execute(vault.get("db"), query, params)
@mcp.tool()
def chip_discover():
"""Discover all active vaults across all activated volumes.
Call this first to see what vaults are available. Each vault
can be queried via vault_query(vault=name, operation=...).
"""
vaults = _discover_active_vaults()
result = []
for v in vaults:
info = {
"vault": v["vault"],
"volume": v["volume"],
"type": v["type"],
}
if v.get("db"):
rows, err = _safe_db_query(v["db"],
"SELECT COUNT(DISTINCT slug) as entries, COUNT(*) as images FROM vault_images")
if rows and not err:
info["entries"] = rows[0]["entries"]
info["images"] = rows[0]["images"]
else:
info["entries"] = 0
info["images"] = 0
if err:
info["db_error"] = err
else:
info["entries"] = 0
info["images"] = 0
# Check for cue-sheet
if v.get("sealed"):
info["has_cuesheet"] = v.get("has_cuesheet", False)
info["sealed"] = True
else:
info["has_cuesheet"] = os.path.isfile(os.path.join(v["path"], "cue-sheet.yaml"))
result.append(info)
# Scan physical chip surfaces for full manifest
surfaces = {}
registry = _load_registry()
active_names = set(_load_active_volumes())
for vol in registry:
if vol["name"] not in active_names:
continue
if not vol.get("physical"):
continue
vol_path = vol["path"] if os.path.isabs(vol["path"]) else os.path.join(MAESTRO_ROOT, vol["path"])
surface = _scan_chip_surface(vol_path)
surfaces[vol["name"]] = {
"datasets": surface["datasets"],
"models": surface["models"],
"docs": surface["docs"],
"schemas": surface["schemas"],
"has_mcp": surface["has_mcp"],
"has_sparseimage": surface["has_sparseimage"],
"has_search_index": surface["has_search_index"],
"model_name": surface["model_name"],