gobby 0.2.5__py3-none-any.whl

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.
Files changed (383) hide show
  1. gobby/__init__.py +3 -0
  2. gobby/adapters/__init__.py +30 -0
  3. gobby/adapters/base.py +93 -0
  4. gobby/adapters/claude_code.py +276 -0
  5. gobby/adapters/codex.py +1292 -0
  6. gobby/adapters/gemini.py +343 -0
  7. gobby/agents/__init__.py +37 -0
  8. gobby/agents/codex_session.py +120 -0
  9. gobby/agents/constants.py +112 -0
  10. gobby/agents/context.py +362 -0
  11. gobby/agents/definitions.py +133 -0
  12. gobby/agents/gemini_session.py +111 -0
  13. gobby/agents/registry.py +618 -0
  14. gobby/agents/runner.py +968 -0
  15. gobby/agents/session.py +259 -0
  16. gobby/agents/spawn.py +916 -0
  17. gobby/agents/spawners/__init__.py +77 -0
  18. gobby/agents/spawners/base.py +142 -0
  19. gobby/agents/spawners/cross_platform.py +266 -0
  20. gobby/agents/spawners/embedded.py +225 -0
  21. gobby/agents/spawners/headless.py +226 -0
  22. gobby/agents/spawners/linux.py +125 -0
  23. gobby/agents/spawners/macos.py +277 -0
  24. gobby/agents/spawners/windows.py +308 -0
  25. gobby/agents/tty_config.py +319 -0
  26. gobby/autonomous/__init__.py +32 -0
  27. gobby/autonomous/progress_tracker.py +447 -0
  28. gobby/autonomous/stop_registry.py +269 -0
  29. gobby/autonomous/stuck_detector.py +383 -0
  30. gobby/cli/__init__.py +67 -0
  31. gobby/cli/__main__.py +8 -0
  32. gobby/cli/agents.py +529 -0
  33. gobby/cli/artifacts.py +266 -0
  34. gobby/cli/daemon.py +329 -0
  35. gobby/cli/extensions.py +526 -0
  36. gobby/cli/github.py +263 -0
  37. gobby/cli/init.py +53 -0
  38. gobby/cli/install.py +614 -0
  39. gobby/cli/installers/__init__.py +37 -0
  40. gobby/cli/installers/antigravity.py +65 -0
  41. gobby/cli/installers/claude.py +363 -0
  42. gobby/cli/installers/codex.py +192 -0
  43. gobby/cli/installers/gemini.py +294 -0
  44. gobby/cli/installers/git_hooks.py +377 -0
  45. gobby/cli/installers/shared.py +737 -0
  46. gobby/cli/linear.py +250 -0
  47. gobby/cli/mcp.py +30 -0
  48. gobby/cli/mcp_proxy.py +698 -0
  49. gobby/cli/memory.py +304 -0
  50. gobby/cli/merge.py +384 -0
  51. gobby/cli/projects.py +79 -0
  52. gobby/cli/sessions.py +622 -0
  53. gobby/cli/tasks/__init__.py +30 -0
  54. gobby/cli/tasks/_utils.py +658 -0
  55. gobby/cli/tasks/ai.py +1025 -0
  56. gobby/cli/tasks/commits.py +169 -0
  57. gobby/cli/tasks/crud.py +685 -0
  58. gobby/cli/tasks/deps.py +135 -0
  59. gobby/cli/tasks/labels.py +63 -0
  60. gobby/cli/tasks/main.py +273 -0
  61. gobby/cli/tasks/search.py +178 -0
  62. gobby/cli/tui.py +34 -0
  63. gobby/cli/utils.py +513 -0
  64. gobby/cli/workflows.py +927 -0
  65. gobby/cli/worktrees.py +481 -0
  66. gobby/config/__init__.py +129 -0
  67. gobby/config/app.py +551 -0
  68. gobby/config/extensions.py +167 -0
  69. gobby/config/features.py +472 -0
  70. gobby/config/llm_providers.py +98 -0
  71. gobby/config/logging.py +66 -0
  72. gobby/config/mcp.py +346 -0
  73. gobby/config/persistence.py +247 -0
  74. gobby/config/servers.py +141 -0
  75. gobby/config/sessions.py +250 -0
  76. gobby/config/tasks.py +784 -0
  77. gobby/hooks/__init__.py +104 -0
  78. gobby/hooks/artifact_capture.py +213 -0
  79. gobby/hooks/broadcaster.py +243 -0
  80. gobby/hooks/event_handlers.py +723 -0
  81. gobby/hooks/events.py +218 -0
  82. gobby/hooks/git.py +169 -0
  83. gobby/hooks/health_monitor.py +171 -0
  84. gobby/hooks/hook_manager.py +856 -0
  85. gobby/hooks/hook_types.py +575 -0
  86. gobby/hooks/plugins.py +813 -0
  87. gobby/hooks/session_coordinator.py +396 -0
  88. gobby/hooks/verification_runner.py +268 -0
  89. gobby/hooks/webhooks.py +339 -0
  90. gobby/install/claude/commands/gobby/bug.md +51 -0
  91. gobby/install/claude/commands/gobby/chore.md +51 -0
  92. gobby/install/claude/commands/gobby/epic.md +52 -0
  93. gobby/install/claude/commands/gobby/eval.md +235 -0
  94. gobby/install/claude/commands/gobby/feat.md +49 -0
  95. gobby/install/claude/commands/gobby/nit.md +52 -0
  96. gobby/install/claude/commands/gobby/ref.md +52 -0
  97. gobby/install/claude/hooks/HOOK_SCHEMAS.md +632 -0
  98. gobby/install/claude/hooks/hook_dispatcher.py +364 -0
  99. gobby/install/claude/hooks/validate_settings.py +102 -0
  100. gobby/install/claude/hooks-template.json +118 -0
  101. gobby/install/codex/hooks/hook_dispatcher.py +153 -0
  102. gobby/install/codex/prompts/forget.md +7 -0
  103. gobby/install/codex/prompts/memories.md +7 -0
  104. gobby/install/codex/prompts/recall.md +7 -0
  105. gobby/install/codex/prompts/remember.md +13 -0
  106. gobby/install/gemini/hooks/hook_dispatcher.py +268 -0
  107. gobby/install/gemini/hooks-template.json +138 -0
  108. gobby/install/shared/plugins/code_guardian.py +456 -0
  109. gobby/install/shared/plugins/example_notify.py +331 -0
  110. gobby/integrations/__init__.py +10 -0
  111. gobby/integrations/github.py +145 -0
  112. gobby/integrations/linear.py +145 -0
  113. gobby/llm/__init__.py +40 -0
  114. gobby/llm/base.py +120 -0
  115. gobby/llm/claude.py +578 -0
  116. gobby/llm/claude_executor.py +503 -0
  117. gobby/llm/codex.py +322 -0
  118. gobby/llm/codex_executor.py +513 -0
  119. gobby/llm/executor.py +316 -0
  120. gobby/llm/factory.py +34 -0
  121. gobby/llm/gemini.py +258 -0
  122. gobby/llm/gemini_executor.py +339 -0
  123. gobby/llm/litellm.py +287 -0
  124. gobby/llm/litellm_executor.py +303 -0
  125. gobby/llm/resolver.py +499 -0
  126. gobby/llm/service.py +236 -0
  127. gobby/mcp_proxy/__init__.py +29 -0
  128. gobby/mcp_proxy/actions.py +175 -0
  129. gobby/mcp_proxy/daemon_control.py +198 -0
  130. gobby/mcp_proxy/importer.py +436 -0
  131. gobby/mcp_proxy/lazy.py +325 -0
  132. gobby/mcp_proxy/manager.py +798 -0
  133. gobby/mcp_proxy/metrics.py +609 -0
  134. gobby/mcp_proxy/models.py +139 -0
  135. gobby/mcp_proxy/registries.py +215 -0
  136. gobby/mcp_proxy/schema_hash.py +381 -0
  137. gobby/mcp_proxy/semantic_search.py +706 -0
  138. gobby/mcp_proxy/server.py +549 -0
  139. gobby/mcp_proxy/services/__init__.py +0 -0
  140. gobby/mcp_proxy/services/fallback.py +306 -0
  141. gobby/mcp_proxy/services/recommendation.py +224 -0
  142. gobby/mcp_proxy/services/server_mgmt.py +214 -0
  143. gobby/mcp_proxy/services/system.py +72 -0
  144. gobby/mcp_proxy/services/tool_filter.py +231 -0
  145. gobby/mcp_proxy/services/tool_proxy.py +309 -0
  146. gobby/mcp_proxy/stdio.py +565 -0
  147. gobby/mcp_proxy/tools/__init__.py +27 -0
  148. gobby/mcp_proxy/tools/agents.py +1103 -0
  149. gobby/mcp_proxy/tools/artifacts.py +207 -0
  150. gobby/mcp_proxy/tools/hub.py +335 -0
  151. gobby/mcp_proxy/tools/internal.py +337 -0
  152. gobby/mcp_proxy/tools/memory.py +543 -0
  153. gobby/mcp_proxy/tools/merge.py +422 -0
  154. gobby/mcp_proxy/tools/metrics.py +283 -0
  155. gobby/mcp_proxy/tools/orchestration/__init__.py +23 -0
  156. gobby/mcp_proxy/tools/orchestration/cleanup.py +619 -0
  157. gobby/mcp_proxy/tools/orchestration/monitor.py +380 -0
  158. gobby/mcp_proxy/tools/orchestration/orchestrate.py +746 -0
  159. gobby/mcp_proxy/tools/orchestration/review.py +736 -0
  160. gobby/mcp_proxy/tools/orchestration/utils.py +16 -0
  161. gobby/mcp_proxy/tools/session_messages.py +1056 -0
  162. gobby/mcp_proxy/tools/task_dependencies.py +219 -0
  163. gobby/mcp_proxy/tools/task_expansion.py +591 -0
  164. gobby/mcp_proxy/tools/task_github.py +393 -0
  165. gobby/mcp_proxy/tools/task_linear.py +379 -0
  166. gobby/mcp_proxy/tools/task_orchestration.py +77 -0
  167. gobby/mcp_proxy/tools/task_readiness.py +522 -0
  168. gobby/mcp_proxy/tools/task_sync.py +351 -0
  169. gobby/mcp_proxy/tools/task_validation.py +843 -0
  170. gobby/mcp_proxy/tools/tasks/__init__.py +25 -0
  171. gobby/mcp_proxy/tools/tasks/_context.py +112 -0
  172. gobby/mcp_proxy/tools/tasks/_crud.py +516 -0
  173. gobby/mcp_proxy/tools/tasks/_factory.py +176 -0
  174. gobby/mcp_proxy/tools/tasks/_helpers.py +129 -0
  175. gobby/mcp_proxy/tools/tasks/_lifecycle.py +517 -0
  176. gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +301 -0
  177. gobby/mcp_proxy/tools/tasks/_resolution.py +55 -0
  178. gobby/mcp_proxy/tools/tasks/_search.py +215 -0
  179. gobby/mcp_proxy/tools/tasks/_session.py +125 -0
  180. gobby/mcp_proxy/tools/workflows.py +973 -0
  181. gobby/mcp_proxy/tools/worktrees.py +1264 -0
  182. gobby/mcp_proxy/transports/__init__.py +0 -0
  183. gobby/mcp_proxy/transports/base.py +95 -0
  184. gobby/mcp_proxy/transports/factory.py +44 -0
  185. gobby/mcp_proxy/transports/http.py +139 -0
  186. gobby/mcp_proxy/transports/stdio.py +213 -0
  187. gobby/mcp_proxy/transports/websocket.py +136 -0
  188. gobby/memory/backends/__init__.py +116 -0
  189. gobby/memory/backends/mem0.py +408 -0
  190. gobby/memory/backends/memu.py +485 -0
  191. gobby/memory/backends/null.py +111 -0
  192. gobby/memory/backends/openmemory.py +537 -0
  193. gobby/memory/backends/sqlite.py +304 -0
  194. gobby/memory/context.py +87 -0
  195. gobby/memory/manager.py +1001 -0
  196. gobby/memory/protocol.py +451 -0
  197. gobby/memory/search/__init__.py +66 -0
  198. gobby/memory/search/text.py +127 -0
  199. gobby/memory/viz.py +258 -0
  200. gobby/prompts/__init__.py +13 -0
  201. gobby/prompts/defaults/expansion/system.md +119 -0
  202. gobby/prompts/defaults/expansion/user.md +48 -0
  203. gobby/prompts/defaults/external_validation/agent.md +72 -0
  204. gobby/prompts/defaults/external_validation/external.md +63 -0
  205. gobby/prompts/defaults/external_validation/spawn.md +83 -0
  206. gobby/prompts/defaults/external_validation/system.md +6 -0
  207. gobby/prompts/defaults/features/import_mcp.md +22 -0
  208. gobby/prompts/defaults/features/import_mcp_github.md +17 -0
  209. gobby/prompts/defaults/features/import_mcp_search.md +16 -0
  210. gobby/prompts/defaults/features/recommend_tools.md +32 -0
  211. gobby/prompts/defaults/features/recommend_tools_hybrid.md +35 -0
  212. gobby/prompts/defaults/features/recommend_tools_llm.md +30 -0
  213. gobby/prompts/defaults/features/server_description.md +20 -0
  214. gobby/prompts/defaults/features/server_description_system.md +6 -0
  215. gobby/prompts/defaults/features/task_description.md +31 -0
  216. gobby/prompts/defaults/features/task_description_system.md +6 -0
  217. gobby/prompts/defaults/features/tool_summary.md +17 -0
  218. gobby/prompts/defaults/features/tool_summary_system.md +6 -0
  219. gobby/prompts/defaults/research/step.md +58 -0
  220. gobby/prompts/defaults/validation/criteria.md +47 -0
  221. gobby/prompts/defaults/validation/validate.md +38 -0
  222. gobby/prompts/loader.py +346 -0
  223. gobby/prompts/models.py +113 -0
  224. gobby/py.typed +0 -0
  225. gobby/runner.py +488 -0
  226. gobby/search/__init__.py +23 -0
  227. gobby/search/protocol.py +104 -0
  228. gobby/search/tfidf.py +232 -0
  229. gobby/servers/__init__.py +7 -0
  230. gobby/servers/http.py +636 -0
  231. gobby/servers/models.py +31 -0
  232. gobby/servers/routes/__init__.py +23 -0
  233. gobby/servers/routes/admin.py +416 -0
  234. gobby/servers/routes/dependencies.py +118 -0
  235. gobby/servers/routes/mcp/__init__.py +24 -0
  236. gobby/servers/routes/mcp/hooks.py +135 -0
  237. gobby/servers/routes/mcp/plugins.py +121 -0
  238. gobby/servers/routes/mcp/tools.py +1337 -0
  239. gobby/servers/routes/mcp/webhooks.py +159 -0
  240. gobby/servers/routes/sessions.py +582 -0
  241. gobby/servers/websocket.py +766 -0
  242. gobby/sessions/__init__.py +13 -0
  243. gobby/sessions/analyzer.py +322 -0
  244. gobby/sessions/lifecycle.py +240 -0
  245. gobby/sessions/manager.py +563 -0
  246. gobby/sessions/processor.py +225 -0
  247. gobby/sessions/summary.py +532 -0
  248. gobby/sessions/transcripts/__init__.py +41 -0
  249. gobby/sessions/transcripts/base.py +125 -0
  250. gobby/sessions/transcripts/claude.py +386 -0
  251. gobby/sessions/transcripts/codex.py +143 -0
  252. gobby/sessions/transcripts/gemini.py +195 -0
  253. gobby/storage/__init__.py +21 -0
  254. gobby/storage/agents.py +409 -0
  255. gobby/storage/artifact_classifier.py +341 -0
  256. gobby/storage/artifacts.py +285 -0
  257. gobby/storage/compaction.py +67 -0
  258. gobby/storage/database.py +357 -0
  259. gobby/storage/inter_session_messages.py +194 -0
  260. gobby/storage/mcp.py +680 -0
  261. gobby/storage/memories.py +562 -0
  262. gobby/storage/merge_resolutions.py +550 -0
  263. gobby/storage/migrations.py +860 -0
  264. gobby/storage/migrations_legacy.py +1359 -0
  265. gobby/storage/projects.py +166 -0
  266. gobby/storage/session_messages.py +251 -0
  267. gobby/storage/session_tasks.py +97 -0
  268. gobby/storage/sessions.py +817 -0
  269. gobby/storage/task_dependencies.py +223 -0
  270. gobby/storage/tasks/__init__.py +42 -0
  271. gobby/storage/tasks/_aggregates.py +180 -0
  272. gobby/storage/tasks/_crud.py +449 -0
  273. gobby/storage/tasks/_id.py +104 -0
  274. gobby/storage/tasks/_lifecycle.py +311 -0
  275. gobby/storage/tasks/_manager.py +889 -0
  276. gobby/storage/tasks/_models.py +300 -0
  277. gobby/storage/tasks/_ordering.py +119 -0
  278. gobby/storage/tasks/_path_cache.py +110 -0
  279. gobby/storage/tasks/_queries.py +343 -0
  280. gobby/storage/tasks/_search.py +143 -0
  281. gobby/storage/workflow_audit.py +393 -0
  282. gobby/storage/worktrees.py +547 -0
  283. gobby/sync/__init__.py +29 -0
  284. gobby/sync/github.py +333 -0
  285. gobby/sync/linear.py +304 -0
  286. gobby/sync/memories.py +284 -0
  287. gobby/sync/tasks.py +641 -0
  288. gobby/tasks/__init__.py +8 -0
  289. gobby/tasks/build_verification.py +193 -0
  290. gobby/tasks/commits.py +633 -0
  291. gobby/tasks/context.py +747 -0
  292. gobby/tasks/criteria.py +342 -0
  293. gobby/tasks/enhanced_validator.py +226 -0
  294. gobby/tasks/escalation.py +263 -0
  295. gobby/tasks/expansion.py +626 -0
  296. gobby/tasks/external_validator.py +764 -0
  297. gobby/tasks/issue_extraction.py +171 -0
  298. gobby/tasks/prompts/expand.py +327 -0
  299. gobby/tasks/research.py +421 -0
  300. gobby/tasks/tdd.py +352 -0
  301. gobby/tasks/tree_builder.py +263 -0
  302. gobby/tasks/validation.py +712 -0
  303. gobby/tasks/validation_history.py +357 -0
  304. gobby/tasks/validation_models.py +89 -0
  305. gobby/tools/__init__.py +0 -0
  306. gobby/tools/summarizer.py +170 -0
  307. gobby/tui/__init__.py +5 -0
  308. gobby/tui/api_client.py +281 -0
  309. gobby/tui/app.py +327 -0
  310. gobby/tui/screens/__init__.py +25 -0
  311. gobby/tui/screens/agents.py +333 -0
  312. gobby/tui/screens/chat.py +450 -0
  313. gobby/tui/screens/dashboard.py +377 -0
  314. gobby/tui/screens/memory.py +305 -0
  315. gobby/tui/screens/metrics.py +231 -0
  316. gobby/tui/screens/orchestrator.py +904 -0
  317. gobby/tui/screens/sessions.py +412 -0
  318. gobby/tui/screens/tasks.py +442 -0
  319. gobby/tui/screens/workflows.py +289 -0
  320. gobby/tui/screens/worktrees.py +174 -0
  321. gobby/tui/widgets/__init__.py +21 -0
  322. gobby/tui/widgets/chat.py +210 -0
  323. gobby/tui/widgets/conductor.py +104 -0
  324. gobby/tui/widgets/menu.py +132 -0
  325. gobby/tui/widgets/message_panel.py +160 -0
  326. gobby/tui/widgets/review_gate.py +224 -0
  327. gobby/tui/widgets/task_tree.py +99 -0
  328. gobby/tui/widgets/token_budget.py +166 -0
  329. gobby/tui/ws_client.py +258 -0
  330. gobby/utils/__init__.py +3 -0
  331. gobby/utils/daemon_client.py +235 -0
  332. gobby/utils/git.py +222 -0
  333. gobby/utils/id.py +38 -0
  334. gobby/utils/json_helpers.py +161 -0
  335. gobby/utils/logging.py +376 -0
  336. gobby/utils/machine_id.py +135 -0
  337. gobby/utils/metrics.py +589 -0
  338. gobby/utils/project_context.py +182 -0
  339. gobby/utils/project_init.py +263 -0
  340. gobby/utils/status.py +256 -0
  341. gobby/utils/validation.py +80 -0
  342. gobby/utils/version.py +23 -0
  343. gobby/workflows/__init__.py +4 -0
  344. gobby/workflows/actions.py +1310 -0
  345. gobby/workflows/approval_flow.py +138 -0
  346. gobby/workflows/artifact_actions.py +103 -0
  347. gobby/workflows/audit_helpers.py +110 -0
  348. gobby/workflows/autonomous_actions.py +286 -0
  349. gobby/workflows/context_actions.py +394 -0
  350. gobby/workflows/definitions.py +130 -0
  351. gobby/workflows/detection_helpers.py +208 -0
  352. gobby/workflows/engine.py +485 -0
  353. gobby/workflows/evaluator.py +669 -0
  354. gobby/workflows/git_utils.py +96 -0
  355. gobby/workflows/hooks.py +169 -0
  356. gobby/workflows/lifecycle_evaluator.py +613 -0
  357. gobby/workflows/llm_actions.py +70 -0
  358. gobby/workflows/loader.py +333 -0
  359. gobby/workflows/mcp_actions.py +60 -0
  360. gobby/workflows/memory_actions.py +272 -0
  361. gobby/workflows/premature_stop.py +164 -0
  362. gobby/workflows/session_actions.py +139 -0
  363. gobby/workflows/state_actions.py +123 -0
  364. gobby/workflows/state_manager.py +104 -0
  365. gobby/workflows/stop_signal_actions.py +163 -0
  366. gobby/workflows/summary_actions.py +344 -0
  367. gobby/workflows/task_actions.py +249 -0
  368. gobby/workflows/task_enforcement_actions.py +901 -0
  369. gobby/workflows/templates.py +52 -0
  370. gobby/workflows/todo_actions.py +84 -0
  371. gobby/workflows/webhook.py +223 -0
  372. gobby/workflows/webhook_executor.py +399 -0
  373. gobby/worktrees/__init__.py +5 -0
  374. gobby/worktrees/git.py +690 -0
  375. gobby/worktrees/merge/__init__.py +20 -0
  376. gobby/worktrees/merge/conflict_parser.py +177 -0
  377. gobby/worktrees/merge/resolver.py +485 -0
  378. gobby-0.2.5.dist-info/METADATA +351 -0
  379. gobby-0.2.5.dist-info/RECORD +383 -0
  380. gobby-0.2.5.dist-info/WHEEL +5 -0
  381. gobby-0.2.5.dist-info/entry_points.txt +2 -0
  382. gobby-0.2.5.dist-info/licenses/LICENSE.md +193 -0
  383. gobby-0.2.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,565 @@
1
+ """
2
+ Stdio MCP server implementation.
3
+
4
+ This server runs as a stdio process for Claude Code and proxies
5
+ tool calls to the HTTP daemon.
6
+ """
7
+
8
+ import asyncio
9
+ import logging
10
+ import sys
11
+ from typing import Any
12
+
13
+ import httpx
14
+ from mcp.server.fastmcp import FastMCP
15
+
16
+ from gobby.config.app import load_config
17
+ from gobby.mcp_proxy.daemon_control import (
18
+ check_daemon_http_health,
19
+ get_daemon_pid,
20
+ is_daemon_running,
21
+ restart_daemon_process,
22
+ start_daemon_process,
23
+ stop_daemon_process,
24
+ )
25
+ from gobby.mcp_proxy.registries import setup_internal_registries
26
+
27
+ __all__ = [
28
+ "create_stdio_mcp_server",
29
+ "check_daemon_http_health",
30
+ "get_daemon_pid",
31
+ "is_daemon_running",
32
+ "restart_daemon_process",
33
+ "start_daemon_process",
34
+ "stop_daemon_process",
35
+ ]
36
+
37
+ logger = logging.getLogger("gobby.mcp.stdio")
38
+
39
+
40
+ class DaemonProxy:
41
+ """Proxy for HTTP daemon API calls."""
42
+
43
+ def __init__(self, port: int):
44
+ self.port = port
45
+ self.base_url = f"http://localhost:{port}"
46
+
47
+ async def _request(
48
+ self,
49
+ method: str,
50
+ path: str,
51
+ json: dict[str, Any] | None = None,
52
+ timeout: float = 30.0,
53
+ ) -> dict[str, Any]:
54
+ """Make HTTP request to daemon."""
55
+ try:
56
+ async with httpx.AsyncClient() as client:
57
+ resp = await client.request(
58
+ method,
59
+ f"{self.base_url}{path}",
60
+ json=json,
61
+ timeout=timeout,
62
+ )
63
+ if resp.status_code == 200:
64
+ data: dict[str, Any] = resp.json()
65
+ return data
66
+ else:
67
+ return {"success": False, "error": f"HTTP {resp.status_code}: {resp.text}"}
68
+ except httpx.ConnectError:
69
+ return {"success": False, "error": "Daemon not running or not reachable"}
70
+ except Exception as e:
71
+ error_msg = str(e) or f"{type(e).__name__}: (no message)"
72
+ return {"success": False, "error": error_msg}
73
+
74
+ async def get_status(self) -> dict[str, Any]:
75
+ """Get daemon status."""
76
+ return await self._request("GET", "/admin/status")
77
+
78
+ async def list_tools(self, server: str | None = None) -> dict[str, Any]:
79
+ """List tools from MCP servers."""
80
+ if server:
81
+ return await self._request("GET", f"/mcp/{server}/tools")
82
+ # List all - need to get server list first
83
+ status = await self.get_status()
84
+ if status.get("success") is False:
85
+ return status
86
+ servers = status.get("mcp_servers", {})
87
+ all_tools: dict[str, list[dict[str, Any]]] = {}
88
+ for srv_name in servers:
89
+ result = await self._request("GET", f"/mcp/{srv_name}/tools")
90
+ if result.get("success"):
91
+ all_tools[srv_name] = result.get("tools", [])
92
+ return {
93
+ "success": True,
94
+ "servers": [{"name": n, "tools": t} for n, t in all_tools.items()],
95
+ }
96
+
97
+ async def call_tool(
98
+ self, server_name: str, tool_name: str, arguments: dict[str, Any] | None = None
99
+ ) -> dict[str, Any]:
100
+ # Tool-specific timeouts
101
+ config = load_config()
102
+ # Default to standard timeout
103
+ timeout = 30.0
104
+ # Check for tool-specific override in config
105
+ if (
106
+ config.mcp_client_proxy.tool_timeouts
107
+ and tool_name in config.mcp_client_proxy.tool_timeouts
108
+ ):
109
+ timeout = config.mcp_client_proxy.tool_timeouts[tool_name]
110
+ # Fallback for LLM-based task tools if not explicit in config
111
+ elif tool_name in (
112
+ "expand_task",
113
+ "apply_tdd",
114
+ "suggest_next_task",
115
+ "validate_task",
116
+ ):
117
+ timeout = 300.0
118
+
119
+ return await self._request(
120
+ "POST",
121
+ f"/mcp/{server_name}/tools/{tool_name}",
122
+ json=arguments or {},
123
+ timeout=timeout,
124
+ )
125
+
126
+ async def get_tool_schema(self, server_name: str, tool_name: str) -> dict[str, Any]:
127
+ """Get schema for a specific tool."""
128
+ result = await self._request(
129
+ "POST",
130
+ "/mcp/tools/schema",
131
+ json={"server_name": server_name, "tool_name": tool_name},
132
+ )
133
+ if "error" in result:
134
+ return {"success": False, "error": result["error"]}
135
+ return {
136
+ "success": True,
137
+ "tool": {
138
+ "name": result.get("name"),
139
+ "description": result.get("description"),
140
+ "inputSchema": result.get("inputSchema"),
141
+ },
142
+ }
143
+
144
+ async def list_mcp_servers(self) -> dict[str, Any]:
145
+ """List configured MCP servers (includes internal gobby-* servers)."""
146
+ return await self._request("GET", "/mcp/servers")
147
+
148
+ async def recommend_tools(
149
+ self,
150
+ task_description: str,
151
+ agent_id: str | None = None,
152
+ search_mode: str = "llm",
153
+ top_k: int = 10,
154
+ min_similarity: float = 0.3,
155
+ cwd: str | None = None,
156
+ ) -> dict[str, Any]:
157
+ """Get tool recommendations for a task."""
158
+ return await self._request(
159
+ "POST",
160
+ "/mcp/tools/recommend",
161
+ json={
162
+ "task_description": task_description,
163
+ "agent_id": agent_id,
164
+ "search_mode": search_mode,
165
+ "top_k": top_k,
166
+ "min_similarity": min_similarity,
167
+ "cwd": cwd,
168
+ },
169
+ timeout=60.0,
170
+ )
171
+
172
+ async def search_tools(
173
+ self,
174
+ query: str,
175
+ top_k: int = 10,
176
+ min_similarity: float = 0.0,
177
+ server: str | None = None,
178
+ cwd: str | None = None,
179
+ ) -> dict[str, Any]:
180
+ """Search for tools using semantic similarity."""
181
+ return await self._request(
182
+ "POST",
183
+ "/mcp/tools/search",
184
+ json={
185
+ "query": query,
186
+ "top_k": top_k,
187
+ "min_similarity": min_similarity,
188
+ "server": server,
189
+ "cwd": cwd,
190
+ },
191
+ timeout=60.0,
192
+ )
193
+
194
+ async def add_mcp_server(
195
+ self,
196
+ name: str,
197
+ transport: str,
198
+ url: str | None = None,
199
+ headers: dict[str, str] | None = None,
200
+ command: str | None = None,
201
+ args: list[str] | None = None,
202
+ env: dict[str, str] | None = None,
203
+ enabled: bool = True,
204
+ ) -> dict[str, Any]:
205
+ """Add a new MCP server to the daemon's configuration."""
206
+ return await self._request(
207
+ "POST",
208
+ "/mcp/servers",
209
+ json={
210
+ "name": name,
211
+ "transport": transport,
212
+ "url": url,
213
+ "headers": headers,
214
+ "command": command,
215
+ "args": args,
216
+ "env": env,
217
+ "enabled": enabled,
218
+ },
219
+ )
220
+
221
+ async def remove_mcp_server(self, name: str) -> dict[str, Any]:
222
+ """Remove an MCP server from the daemon's configuration."""
223
+ return await self._request("DELETE", f"/mcp/servers/{name}")
224
+
225
+ async def import_mcp_server(
226
+ self,
227
+ from_project: str | None = None,
228
+ servers: list[str] | None = None,
229
+ github_url: str | None = None,
230
+ query: str | None = None,
231
+ ) -> dict[str, Any]:
232
+ """Import MCP servers from various sources."""
233
+ return await self._request(
234
+ "POST",
235
+ "/mcp/servers/import",
236
+ json={
237
+ "from_project": from_project,
238
+ "servers": servers,
239
+ "github_url": github_url,
240
+ "query": query,
241
+ },
242
+ )
243
+
244
+ async def init_project(
245
+ self, name: str | None = None, github_url: str | None = None
246
+ ) -> dict[str, Any]:
247
+ """Initialize a project - use 'gobby init' CLI command instead."""
248
+ return {
249
+ "success": False,
250
+ "error": "init_project requires CLI access. Run 'gobby init' from your terminal.",
251
+ }
252
+
253
+
254
+ def create_stdio_mcp_server() -> FastMCP:
255
+ """Create stdio MCP server."""
256
+ # Load configuration
257
+ config = load_config()
258
+
259
+ # Initialize basic managers (mocked/simplified for this refactor example)
260
+ session_manager = None
261
+ memory_manager = None
262
+
263
+ # Setup internal registries using extracted function
264
+ _ = setup_internal_registries(config, session_manager, memory_manager)
265
+
266
+ # Initialize MCP server and daemon proxy
267
+ mcp = FastMCP("gobby")
268
+ proxy = DaemonProxy(config.daemon_port)
269
+
270
+ register_proxy_tools(mcp, proxy)
271
+
272
+ return mcp
273
+
274
+
275
+ def register_proxy_tools(mcp: FastMCP, proxy: DaemonProxy) -> None:
276
+ """Register proxy tools on the MCP server."""
277
+
278
+ @mcp.tool()
279
+ async def list_mcp_servers() -> dict[str, Any]:
280
+ """
281
+ List all MCP servers configured in the daemon.
282
+
283
+ Returns details about each MCP server including connection status,
284
+ available tools, and resources.
285
+
286
+ Returns:
287
+ Dict with servers list, total count, and connected count
288
+ """
289
+ return await proxy.list_mcp_servers()
290
+
291
+ @mcp.tool()
292
+ async def list_tools(server: str) -> dict[str, Any]:
293
+ """
294
+ List tools from MCP servers.
295
+
296
+ Use this to discover tools available on servers.
297
+
298
+ Args:
299
+ server: Server name (e.g., "context7", "supabase").
300
+ Use list_mcp_servers() first to discover available servers.
301
+
302
+ Returns:
303
+ Dict with tool listings
304
+ """
305
+ return await proxy.list_tools(server)
306
+
307
+ @mcp.tool()
308
+ async def get_tool_schema(server_name: str, tool_name: str) -> dict[str, Any]:
309
+ """
310
+ Get full schema (inputSchema) for a specific MCP tool.
311
+
312
+ Use list_tools() first to discover available tools, then use this to get
313
+ full details before calling the tool.
314
+
315
+ Args:
316
+ server_name: Name of the MCP server (e.g., "context7", "supabase")
317
+ tool_name: Name of the tool (e.g., "get-library-docs", "list_tables")
318
+
319
+ Returns:
320
+ Dict with tool name, description, and full inputSchema
321
+ """
322
+ return await proxy.get_tool_schema(server_name, tool_name)
323
+
324
+ @mcp.tool()
325
+ async def call_tool(
326
+ server_name: str,
327
+ tool_name: str,
328
+ arguments: dict[str, Any] | None = None,
329
+ ) -> dict[str, Any]:
330
+ """
331
+ Execute a tool on a connected MCP server.
332
+
333
+ This is the primary way to interact with MCP servers (Supabase, memory, etc.)
334
+ through the Gobby daemon.
335
+
336
+ Args:
337
+ server_name: Name of the MCP server
338
+ tool_name: Name of the specific tool to execute
339
+ arguments: Dictionary of arguments required by the tool (optional)
340
+
341
+ Returns:
342
+ Dictionary with success status and tool execution result
343
+ """
344
+ return await proxy.call_tool(server_name, tool_name, arguments)
345
+
346
+ @mcp.tool()
347
+ async def recommend_tools(
348
+ task_description: str,
349
+ agent_id: str | None = None,
350
+ search_mode: str = "llm",
351
+ top_k: int = 10,
352
+ min_similarity: float = 0.3,
353
+ ) -> dict[str, Any]:
354
+ """
355
+ Get intelligent tool recommendations for a given task.
356
+
357
+ Args:
358
+ task_description: Description of what you're trying to accomplish
359
+ agent_id: Optional agent profile ID to filter tools by assigned permissions
360
+ search_mode: How to search - "llm" (default), "semantic", or "hybrid"
361
+ top_k: Maximum recommendations to return (semantic/hybrid modes)
362
+ min_similarity: Minimum similarity threshold (semantic/hybrid modes)
363
+
364
+ Returns:
365
+ Dict with tool recommendations and usage suggestions
366
+ """
367
+ import os
368
+
369
+ cwd = os.getcwd()
370
+ return await proxy.recommend_tools(
371
+ task_description,
372
+ agent_id,
373
+ search_mode=search_mode,
374
+ top_k=top_k,
375
+ min_similarity=min_similarity,
376
+ cwd=cwd,
377
+ )
378
+
379
+ @mcp.tool()
380
+ async def search_tools(
381
+ query: str,
382
+ top_k: int = 10,
383
+ min_similarity: float = 0.0,
384
+ server: str | None = None,
385
+ ) -> dict[str, Any]:
386
+ """
387
+ Search for tools using semantic similarity.
388
+
389
+ Uses embedding-based search to find tools matching a natural language query.
390
+ Requires embeddings to be generated first (happens automatically on first search).
391
+
392
+ Args:
393
+ query: Natural language description of the tool you need
394
+ top_k: Maximum number of results to return (default: 10)
395
+ min_similarity: Minimum similarity threshold 0-1 (default: 0.0)
396
+ server: Optional server name to filter results
397
+
398
+ Returns:
399
+ Dict with matching tools sorted by similarity
400
+ """
401
+ import os
402
+
403
+ cwd = os.getcwd()
404
+ return await proxy.search_tools(
405
+ query,
406
+ top_k=top_k,
407
+ min_similarity=min_similarity,
408
+ server=server,
409
+ cwd=cwd,
410
+ )
411
+
412
+ @mcp.tool()
413
+ async def init_project(
414
+ name: str | None = None, github_url: str | None = None
415
+ ) -> dict[str, Any]:
416
+ """
417
+ Initialize a new Gobby project in the current directory.
418
+
419
+ Args:
420
+ name: Optional project name (auto-detected from directory name if not provided)
421
+ github_url: Optional GitHub URL (auto-detected from git remote if not provided)
422
+
423
+ Returns:
424
+ Dict with success status and project details
425
+ """
426
+ return await proxy.init_project(name, github_url)
427
+
428
+ @mcp.tool()
429
+ async def add_mcp_server(
430
+ name: str,
431
+ transport: str,
432
+ url: str | None = None,
433
+ headers: dict[str, str] | None = None,
434
+ command: str | None = None,
435
+ args: list[str] | None = None,
436
+ env: dict[str, str] | None = None,
437
+ enabled: bool = True,
438
+ ) -> dict[str, Any]:
439
+ """
440
+ Add a new MCP server to the daemon's configuration.
441
+
442
+ Args:
443
+ name: Unique server name
444
+ transport: Transport type - "http", "stdio", or "websocket"
445
+ url: Server URL (required for http/websocket)
446
+ headers: Custom HTTP headers (optional)
447
+ command: Command to run (required for stdio)
448
+ args: Command arguments (optional for stdio)
449
+ env: Environment variables (optional for stdio)
450
+ enabled: Whether server is enabled (default: True)
451
+
452
+ Returns:
453
+ Result dict with success status
454
+ """
455
+ return await proxy.add_mcp_server(
456
+ name=name,
457
+ transport=transport,
458
+ url=url,
459
+ headers=headers,
460
+ command=command,
461
+ args=args,
462
+ env=env,
463
+ enabled=enabled,
464
+ )
465
+
466
+ @mcp.tool()
467
+ async def remove_mcp_server(name: str) -> dict[str, Any]:
468
+ """
469
+ Remove an MCP server from the daemon's configuration.
470
+
471
+ Args:
472
+ name: Server name to remove
473
+
474
+ Returns:
475
+ Result dict with success status
476
+ """
477
+ return await proxy.remove_mcp_server(name)
478
+
479
+ @mcp.tool()
480
+ async def import_mcp_server(
481
+ from_project: str | None = None,
482
+ servers: list[str] | None = None,
483
+ github_url: str | None = None,
484
+ query: str | None = None,
485
+ ) -> dict[str, Any]:
486
+ """
487
+ Import MCP servers from various sources.
488
+
489
+ Args:
490
+ from_project: Source project name to import servers from
491
+ servers: Optional list of specific server names to import
492
+ github_url: GitHub repository URL to parse for MCP server config
493
+ query: Natural language search query
494
+
495
+ Returns:
496
+ Result dict with imported servers or config to fill in
497
+ """
498
+ return await proxy.import_mcp_server(
499
+ from_project=from_project,
500
+ servers=servers,
501
+ github_url=github_url,
502
+ query=query,
503
+ )
504
+
505
+
506
+ async def ensure_daemon_running() -> None:
507
+ """Ensure the Gobby daemon is running and healthy."""
508
+ config = load_config()
509
+ port = config.daemon_port
510
+ ws_port = config.websocket.port
511
+
512
+ # Check if running
513
+ if is_daemon_running():
514
+ # Check health
515
+ if await check_daemon_http_health(port):
516
+ return
517
+
518
+ # Unhealthy, restart
519
+ logger.warning("Daemon running but unhealthy, restarting...")
520
+ pid = get_daemon_pid()
521
+ await restart_daemon_process(pid, port, ws_port)
522
+ else:
523
+ # Start
524
+ result = await start_daemon_process(port, ws_port)
525
+ if not result.get("success"):
526
+ logger.error(
527
+ "Failed to start daemon: %s (port=%d, ws_port=%d)",
528
+ result.get("error", "unknown error"),
529
+ port,
530
+ ws_port,
531
+ )
532
+ sys.exit(1)
533
+
534
+ # Wait for health
535
+ last_health_response = None
536
+ for _i in range(10):
537
+ last_health_response = await check_daemon_http_health(port)
538
+ if last_health_response:
539
+ return
540
+ await asyncio.sleep(1)
541
+
542
+ # Health check timed out
543
+ pid = get_daemon_pid()
544
+ logger.error(
545
+ "Daemon failed to become healthy after 10 attempts (pid=%s, port=%d, ws_port=%d, last_health=%s)",
546
+ pid,
547
+ port,
548
+ ws_port,
549
+ last_health_response,
550
+ )
551
+ sys.exit(1)
552
+
553
+
554
+ async def main() -> None:
555
+ """Main entry point for stdio MCP server."""
556
+ # Ensure daemon is running first
557
+ await ensure_daemon_running()
558
+
559
+ # Create and run the MCP server
560
+ mcp = create_stdio_mcp_server()
561
+ await mcp.run_stdio_async()
562
+
563
+
564
+ if __name__ == "__main__":
565
+ asyncio.run(main())
@@ -0,0 +1,27 @@
1
+ """
2
+ MCP proxy tools module.
3
+
4
+ Provides factory functions for creating tool registries.
5
+ """
6
+
7
+ # Main task registry (facade that merges all task-related registries)
8
+ # Extracted task module registries (for direct use or testing)
9
+ from gobby.mcp_proxy.tools.task_dependencies import create_dependency_registry
10
+ from gobby.mcp_proxy.tools.task_expansion import create_expansion_registry
11
+ from gobby.mcp_proxy.tools.task_github import create_github_sync_registry
12
+ from gobby.mcp_proxy.tools.task_readiness import create_readiness_registry
13
+ from gobby.mcp_proxy.tools.task_sync import create_sync_registry
14
+ from gobby.mcp_proxy.tools.task_validation import create_validation_registry
15
+ from gobby.mcp_proxy.tools.tasks import create_task_registry
16
+
17
+ __all__ = [
18
+ # Main facade
19
+ "create_task_registry",
20
+ # Extracted registries
21
+ "create_dependency_registry",
22
+ "create_expansion_registry",
23
+ "create_github_sync_registry",
24
+ "create_readiness_registry",
25
+ "create_sync_registry",
26
+ "create_validation_registry",
27
+ ]