Skip to content

Commit 39d9ec9

Browse files
jace-ryanclaude
andcommitted
fix: resolve config duplication, dead code, and ChromaDB singleton
- cw_tools.py: import CW_MCP_SERVER_DIR and CW_CLIENT_ID from config.py instead of reading os.getenv() directly (was duplicating config source) - cw_tools.py: remove unused _run() sync wrapper (dead code, never called) - vector_store.py: singleton ChromaDB PersistentClient (was creating new client per query — wasteful and unnecessary) - vector_store.py: consistent pathlib usage (was mixing os.path + glob with pathlib.Path from config) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent 283e870 commit 39d9ec9

2 files changed

Lines changed: 23 additions & 38 deletions

File tree

server/tools/cw_tools.py

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,15 @@
3030

3131
from config import (
3232
CW_BASE_URL,
33+
CW_CLIENT_ID,
3334
CW_COMPANY_ID,
35+
CW_MCP_SERVER_DIR,
3436
CW_PRIVATE_KEY,
3537
CW_PUBLIC_KEY,
3638
)
3739

3840
logger = logging.getLogger(__name__)
3941

40-
# Path to your built cw-mcp-server (must contain dist/index.js after `npm run build`)
41-
CW_MCP_SERVER_DIR: str = os.getenv("CW_MCP_SERVER_DIR", "")
42-
CW_CLIENT_ID: str = os.getenv("CW_CLIENT_ID", "")
43-
4442
# ---------------------------------------------------------------------------
4543
# MCP client session (lazy singleton)
4644
# ---------------------------------------------------------------------------
@@ -144,20 +142,6 @@ async def _close_session() -> None:
144142
_session = _client_ctx = _session_ctx = None
145143

146144

147-
# ---------------------------------------------------------------------------
148-
# Synchronous wrappers (FastAPI endpoints are async but tool callers may not be)
149-
# ---------------------------------------------------------------------------
150-
151-
def _run(coro):
152-
"""Run an async coroutine from sync context."""
153-
try:
154-
loop = asyncio.get_running_loop()
155-
# Already in an async context — just return the coroutine for await
156-
return coro
157-
except RuntimeError:
158-
return asyncio.run(coro)
159-
160-
161145
# ---------------------------------------------------------------------------
162146
# Public tool functions — IRP-relevant subset of the 73 tools
163147
# ---------------------------------------------------------------------------

server/vector_store.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77

88
from __future__ import annotations
99

10-
import glob
1110
import os
11+
from pathlib import Path
1212
from typing import Any
1313

1414
import chromadb
@@ -20,14 +20,19 @@
2020

2121
_embedding_fn = embedding_functions.DefaultEmbeddingFunction()
2222

23+
_client: chromadb.PersistentClient | None = None
24+
2325

2426
def _get_client() -> chromadb.PersistentClient:
25-
os.makedirs(CHROMA_DIR, exist_ok=True)
26-
return chromadb.PersistentClient(path=str(CHROMA_DIR))
27+
global _client
28+
if _client is None:
29+
os.makedirs(CHROMA_DIR, exist_ok=True)
30+
_client = chromadb.PersistentClient(path=str(CHROMA_DIR))
31+
return _client
2732

2833

29-
def _get_collection(client: chromadb.PersistentClient) -> Any:
30-
return client.get_or_create_collection(
34+
def _get_collection() -> Any:
35+
return _get_client().get_or_create_collection(
3136
name=COLLECTION_NAME,
3237
embedding_function=_embedding_fn,
3338
metadata={"hnsw:space": "cosine"},
@@ -51,9 +56,8 @@ def ingest_playbooks(playbooks_dir: str | None = None) -> dict[str, Any]:
5156
5257
Returns a summary dict with ``files_ingested``, ``total_chunks``, and ``details``.
5358
"""
54-
src = playbooks_dir or str(PLAYBOOKS_DIR)
55-
client = _get_client()
56-
collection = _get_collection(client)
59+
src = Path(playbooks_dir) if playbooks_dir else PLAYBOOKS_DIR
60+
collection = _get_collection()
5761

5862
# Wipe existing data for a clean re-index.
5963
try:
@@ -63,27 +67,26 @@ def ingest_playbooks(playbooks_dir: str | None = None) -> dict[str, Any]:
6367
except Exception:
6468
pass
6569

66-
md_files = sorted(glob.glob(os.path.join(src, "*.md")))
70+
skip = {"README.md", "full-irp-template.md"}
71+
md_files = sorted(src.glob("*.md"))
6772
total_chunks = 0
6873
details: list[dict[str, Any]] = []
6974

7075
for filepath in md_files:
71-
filename = os.path.basename(filepath)
72-
if filename in ("README.md", "full-irp-template.md"):
76+
if filepath.name in skip:
7377
continue
7478

75-
with open(filepath) as f:
76-
content = f.read()
79+
content = filepath.read_text()
7780
if not content.strip():
7881
continue
7982

80-
playbook_type = filename.removesuffix(".md")
83+
playbook_type = filepath.stem
8184
chunks = chunk_document(content)
8285

8386
ids = [f"{playbook_type}__chunk_{i}" for i in range(len(chunks))]
8487
metadatas = [
8588
{
86-
"source_file": filename,
89+
"source_file": filepath.name,
8790
"playbook_type": playbook_type,
8891
"chunk_index": i,
8992
"total_chunks": len(chunks),
@@ -93,7 +96,7 @@ def ingest_playbooks(playbooks_dir: str | None = None) -> dict[str, Any]:
9396

9497
collection.add(ids=ids, documents=chunks, metadatas=metadatas)
9598
total_chunks += len(chunks)
96-
details.append({"file": filename, "chunks": len(chunks)})
99+
details.append({"file": filepath.name, "chunks": len(chunks)})
97100

98101
return {
99102
"files_ingested": len(details),
@@ -104,8 +107,7 @@ def ingest_playbooks(playbooks_dir: str | None = None) -> dict[str, Any]:
104107

105108
def search_playbooks(query: str, n_results: int = 5) -> list[dict[str, Any]]:
106109
"""Semantic search — returns the *n_results* closest playbook chunks."""
107-
client = _get_client()
108-
collection = _get_collection(client)
110+
collection = _get_collection()
109111

110112
results = collection.query(query_texts=[query], n_results=n_results)
111113

@@ -124,8 +126,7 @@ def search_playbooks(query: str, n_results: int = 5) -> list[dict[str, Any]]:
124126

125127
def list_playbooks() -> list[dict[str, Any]]:
126128
"""Return de-duplicated metadata for every ingested playbook type."""
127-
client = _get_client()
128-
collection = _get_collection(client)
129+
collection = _get_collection()
129130

130131
seen: dict[str, dict[str, Any]] = {}
131132
for meta in collection.get()["metadatas"]:

0 commit comments

Comments
 (0)