Skip to content

Commit 7856dd4

Browse files
phernandezclaude
andcommitted
refactor(mcp): convert sync_status.py to context manager pattern
Convert sync_status MCP tool from module-level client import to the new get_client() context manager pattern, enabling proper dependency injection. Changes: - Import get_client() instead of client from async_client module - Wrap function body in 'async with get_client() as client:' block - Indent all code inside context manager (lines 99-257) - Update SPEC-16 checklist (14 of 16 main tools converted) The sync_status tool provides detailed information about ongoing or completed sync operations, helping users understand when their files are ready. It shows: - Overall sync status and system readiness - Active sync operations with progress percentages - Failed project details with troubleshooting guidance - Comprehensive project status for all configured projects - Optional project context with path information This conversion maintains the tool's extensive status reporting while ensuring: - Proper client lifecycle management - Cloud app can inject custom transport via factory - Tests can mock client behavior - Auth happens at client creation, not per-request Testing: - Typecheck passed (0 errors, 0 warnings) - Tool function properly scoped (client inside async with block) Part of Phase 0.3: Converting 16 MCP tools to context manager pattern. Related to SPEC-16 MCP Cloud Service Consolidation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent af78cf8 commit 7856dd4

2 files changed

Lines changed: 130 additions & 129 deletions

File tree

specs/SPEC-16 MCP Cloud Service Consolidation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ Convert from `from async_client import client` to `async with get_client() as cl
461461
- [x] `tools/edit_note.py` (17/17 tests passing)
462462
- [x] `tools/canvas.py` (5/5 tests passing)
463463
- [x] `tools/build_context.py` (6/6 tests passing)
464-
- [ ] `tools/sync_status.py`
464+
- [x] `tools/sync_status.py` (typecheck passed)
465465
- [ ] `prompts/search.py`
466466
- [ ] `prompts/continue_conversation.py`
467467
- [ ] `resources/project_info.py`

src/basic_memory/mcp/tools/sync_status.py

Lines changed: 129 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from fastmcp import Context
77

88
from basic_memory.config import ConfigManager
9-
from basic_memory.mcp.async_client import client
9+
from basic_memory.mcp.async_client import get_client
1010
from basic_memory.mcp.server import mcp
1111
from basic_memory.mcp.project_context import get_active_project
1212
from basic_memory.services.sync_status_service import sync_status_tracker
@@ -95,162 +95,163 @@ async def sync_status(project: Optional[str] = None, context: Context | None = N
9595
"""
9696
logger.info("MCP tool call tool=sync_status")
9797

98-
status_lines = []
98+
async with get_client() as client:
99+
status_lines = []
99100

100-
try:
101-
from basic_memory.services.sync_status_service import sync_status_tracker
102-
103-
# Get overall summary
104-
summary = sync_status_tracker.get_summary()
105-
is_ready = sync_status_tracker.is_ready
106-
107-
# Header
108-
status_lines.extend(
109-
[
110-
"# Basic Memory Sync Status",
111-
"",
112-
f"**Current Status**: {summary}",
113-
f"**System Ready**: {'✅ Yes' if is_ready else '🔄 Processing'}",
114-
"",
115-
]
116-
)
117-
118-
if is_ready:
101+
try:
102+
from basic_memory.services.sync_status_service import sync_status_tracker
103+
104+
# Get overall summary
105+
summary = sync_status_tracker.get_summary()
106+
is_ready = sync_status_tracker.is_ready
107+
108+
# Header
119109
status_lines.extend(
120110
[
121-
"✅ **All sync operations completed**",
111+
"# Basic Memory Sync Status",
122112
"",
123-
"- File indexing is complete",
124-
"- Knowledge graphs are up to date",
125-
"- All Basic Memory tools are fully operational",
113+
f"**Current Status**: {summary}",
114+
f"**System Ready**: {'✅ Yes' if is_ready else '🔄 Processing'}",
126115
"",
127-
"Your knowledge base is ready for use!",
128116
]
129117
)
130118

131-
# Show all projects status even when ready
132-
status_lines.extend(_get_all_projects_status())
133-
else:
134-
# System is still processing - show both active and all projects
135-
all_sync_projects = sync_status_tracker.get_all_projects()
136-
137-
active_projects = [
138-
p for p in all_sync_projects.values() if p.status.value in ["scanning", "syncing"]
139-
]
140-
failed_projects = [p for p in all_sync_projects.values() if p.status.value == "failed"]
141-
142-
if active_projects:
119+
if is_ready:
143120
status_lines.extend(
144121
[
145-
"🔄 **File synchronization in progress**",
122+
" **All sync operations completed**",
146123
"",
147-
"Basic Memory is automatically processing all configured projects and building knowledge graphs.",
148-
"This typically takes 1-3 minutes depending on the amount of content.",
124+
"- File indexing is complete",
125+
"- Knowledge graphs are up to date",
126+
"- All Basic Memory tools are fully operational",
149127
"",
150-
"**Currently Processing:**",
128+
"Your knowledge base is ready for use!",
151129
]
152130
)
153131

154-
for project_status in active_projects:
155-
progress = ""
156-
if project_status.files_total > 0:
157-
progress_pct = (
158-
project_status.files_processed / project_status.files_total
159-
) * 100
160-
progress = f" ({project_status.files_processed}/{project_status.files_total}, {progress_pct:.0f}%)"
132+
# Show all projects status even when ready
133+
status_lines.extend(_get_all_projects_status())
134+
else:
135+
# System is still processing - show both active and all projects
136+
all_sync_projects = sync_status_tracker.get_all_projects()
161137

162-
status_lines.append(
163-
f"- **{project_status.project_name}**: {project_status.message}{progress}"
138+
active_projects = [
139+
p for p in all_sync_projects.values() if p.status.value in ["scanning", "syncing"]
140+
]
141+
failed_projects = [p for p in all_sync_projects.values() if p.status.value == "failed"]
142+
143+
if active_projects:
144+
status_lines.extend(
145+
[
146+
"🔄 **File synchronization in progress**",
147+
"",
148+
"Basic Memory is automatically processing all configured projects and building knowledge graphs.",
149+
"This typically takes 1-3 minutes depending on the amount of content.",
150+
"",
151+
"**Currently Processing:**",
152+
]
164153
)
165154

166-
status_lines.extend(
167-
[
168-
"",
169-
"**What's happening:**",
170-
"- Scanning and indexing markdown files",
171-
"- Building entity and relationship graphs",
172-
"- Setting up full-text search indexes",
173-
"- Processing file changes and updates",
174-
"",
175-
"**What you can do:**",
176-
"- Wait for automatic processing to complete - no action needed",
177-
"- Use this tool again to check progress",
178-
"- Simple operations may work already",
179-
"- All projects will be available once sync finishes",
180-
]
181-
)
182-
183-
# Handle failed projects (independent of active projects)
184-
if failed_projects:
185-
status_lines.extend(["", "❌ **Some projects failed to sync:**", ""])
186-
187-
for project_status in failed_projects:
188-
status_lines.append(
189-
f"- **{project_status.project_name}**: {project_status.error or 'Unknown error'}"
155+
for project_status in active_projects:
156+
progress = ""
157+
if project_status.files_total > 0:
158+
progress_pct = (
159+
project_status.files_processed / project_status.files_total
160+
) * 100
161+
progress = f" ({project_status.files_processed}/{project_status.files_total}, {progress_pct:.0f}%)"
162+
163+
status_lines.append(
164+
f"- **{project_status.project_name}**: {project_status.message}{progress}"
165+
)
166+
167+
status_lines.extend(
168+
[
169+
"",
170+
"**What's happening:**",
171+
"- Scanning and indexing markdown files",
172+
"- Building entity and relationship graphs",
173+
"- Settings up full-text search indexes",
174+
"- Processing file changes and updates",
175+
"",
176+
"**What you can do:**",
177+
"- Wait for automatic processing to complete - no action needed",
178+
"- Use this tool again to check progress",
179+
"- Simple operations may work already",
180+
"- All projects will be available once sync finishes",
181+
]
190182
)
191183

192-
status_lines.extend(
193-
[
194-
"",
195-
"**Next steps:**",
196-
"1. Check the logs for detailed error information",
197-
"2. Ensure file permissions allow read/write access",
198-
"3. Try restarting the MCP server",
199-
"4. If issues persist, consider filing a support issue",
200-
]
201-
)
202-
elif not active_projects:
203-
# No active or failed projects - must be pending
204-
status_lines.extend(
205-
[
206-
"⏳ **Sync operations pending**",
207-
"",
208-
"File synchronization has been queued but hasn't started yet.",
209-
"This usually resolves automatically within a few seconds.",
210-
]
211-
)
212-
213-
# Add comprehensive project status for all configured projects
214-
all_projects_status = _get_all_projects_status()
215-
if all_projects_status:
216-
status_lines.extend(all_projects_status)
184+
# Handle failed projects (independent of active projects)
185+
if failed_projects:
186+
status_lines.extend(["", "❌ **Some projects failed to sync:**", ""])
187+
188+
for project_status in failed_projects:
189+
status_lines.append(
190+
f"- **{project_status.project_name}**: {project_status.error or 'Unknown error'}"
191+
)
192+
193+
status_lines.extend(
194+
[
195+
"",
196+
"**Next steps:**",
197+
"1. Check the logs for detailed error information",
198+
"2. Ensure file permissions allow read/write access",
199+
"3. Try restarting the MCP server",
200+
"4. If issues persist, consider filing a support issue",
201+
]
202+
)
203+
elif not active_projects:
204+
# No active or failed projects - must be pending
205+
status_lines.extend(
206+
[
207+
"⏳ **Sync operations pending**",
208+
"",
209+
"File synchronization has been queued but hasn't started yet.",
210+
"This usually resolves automatically within a few seconds.",
211+
]
212+
)
217213

218-
# Add explanation about automatic syncing if there are unsynced projects
219-
unsynced_count = sum(1 for line in all_projects_status if "⏳" in line)
220-
if unsynced_count > 0 and not is_ready:
221-
status_lines.extend(
222-
[
223-
"",
224-
"**Note**: All configured projects will be automatically synced during startup.",
225-
]
226-
)
214+
# Add comprehensive project status for all configured projects
215+
all_projects_status = _get_all_projects_status()
216+
if all_projects_status:
217+
status_lines.extend(all_projects_status)
218+
219+
# Add explanation about automatic syncing if there are unsynced projects
220+
unsynced_count = sum(1 for line in all_projects_status if "⏳" in line)
221+
if unsynced_count > 0 and not is_ready:
222+
status_lines.extend(
223+
[
224+
"",
225+
"**Note**: All configured projects will be automatically synced during startup.",
226+
]
227+
)
227228

228-
# Add project context if provided
229-
if project:
230-
try:
231-
active_project = await get_active_project(client, project, context)
232-
status_lines.extend(
233-
[
234-
"",
235-
"---",
236-
"",
237-
f"**Active Project**: {active_project.name}",
238-
f"**Project Path**: {active_project.home}",
239-
]
240-
)
241-
except Exception as e:
242-
logger.debug(f"Could not get project info: {e}")
229+
# Add project context if provided
230+
if project:
231+
try:
232+
active_project = await get_active_project(client, project, context)
233+
status_lines.extend(
234+
[
235+
"",
236+
"---",
237+
"",
238+
f"**Active Project**: {active_project.name}",
239+
f"**Project Path**: {active_project.home}",
240+
]
241+
)
242+
except Exception as e:
243+
logger.debug(f"Could not get project info: {e}")
243244

244-
return "\n".join(status_lines)
245+
return "\n".join(status_lines)
245246

246-
except Exception as e:
247-
return f"""# Sync Status - Error
247+
except Exception as e:
248+
return f"""# Sync Status - Error
248249
249250
❌ **Unable to check sync status**: {str(e)}
250251
251252
**Troubleshooting:**
252253
- The system may still be starting up
253-
- Try waiting a few seconds and checking again
254+
- Try waiting a few seconds and checking again
254255
- Check logs for detailed error information
255256
- Consider restarting if the issue persists
256257
"""

0 commit comments

Comments
 (0)