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.
- gobby/__init__.py +3 -0
- gobby/adapters/__init__.py +30 -0
- gobby/adapters/base.py +93 -0
- gobby/adapters/claude_code.py +276 -0
- gobby/adapters/codex.py +1292 -0
- gobby/adapters/gemini.py +343 -0
- gobby/agents/__init__.py +37 -0
- gobby/agents/codex_session.py +120 -0
- gobby/agents/constants.py +112 -0
- gobby/agents/context.py +362 -0
- gobby/agents/definitions.py +133 -0
- gobby/agents/gemini_session.py +111 -0
- gobby/agents/registry.py +618 -0
- gobby/agents/runner.py +968 -0
- gobby/agents/session.py +259 -0
- gobby/agents/spawn.py +916 -0
- gobby/agents/spawners/__init__.py +77 -0
- gobby/agents/spawners/base.py +142 -0
- gobby/agents/spawners/cross_platform.py +266 -0
- gobby/agents/spawners/embedded.py +225 -0
- gobby/agents/spawners/headless.py +226 -0
- gobby/agents/spawners/linux.py +125 -0
- gobby/agents/spawners/macos.py +277 -0
- gobby/agents/spawners/windows.py +308 -0
- gobby/agents/tty_config.py +319 -0
- gobby/autonomous/__init__.py +32 -0
- gobby/autonomous/progress_tracker.py +447 -0
- gobby/autonomous/stop_registry.py +269 -0
- gobby/autonomous/stuck_detector.py +383 -0
- gobby/cli/__init__.py +67 -0
- gobby/cli/__main__.py +8 -0
- gobby/cli/agents.py +529 -0
- gobby/cli/artifacts.py +266 -0
- gobby/cli/daemon.py +329 -0
- gobby/cli/extensions.py +526 -0
- gobby/cli/github.py +263 -0
- gobby/cli/init.py +53 -0
- gobby/cli/install.py +614 -0
- gobby/cli/installers/__init__.py +37 -0
- gobby/cli/installers/antigravity.py +65 -0
- gobby/cli/installers/claude.py +363 -0
- gobby/cli/installers/codex.py +192 -0
- gobby/cli/installers/gemini.py +294 -0
- gobby/cli/installers/git_hooks.py +377 -0
- gobby/cli/installers/shared.py +737 -0
- gobby/cli/linear.py +250 -0
- gobby/cli/mcp.py +30 -0
- gobby/cli/mcp_proxy.py +698 -0
- gobby/cli/memory.py +304 -0
- gobby/cli/merge.py +384 -0
- gobby/cli/projects.py +79 -0
- gobby/cli/sessions.py +622 -0
- gobby/cli/tasks/__init__.py +30 -0
- gobby/cli/tasks/_utils.py +658 -0
- gobby/cli/tasks/ai.py +1025 -0
- gobby/cli/tasks/commits.py +169 -0
- gobby/cli/tasks/crud.py +685 -0
- gobby/cli/tasks/deps.py +135 -0
- gobby/cli/tasks/labels.py +63 -0
- gobby/cli/tasks/main.py +273 -0
- gobby/cli/tasks/search.py +178 -0
- gobby/cli/tui.py +34 -0
- gobby/cli/utils.py +513 -0
- gobby/cli/workflows.py +927 -0
- gobby/cli/worktrees.py +481 -0
- gobby/config/__init__.py +129 -0
- gobby/config/app.py +551 -0
- gobby/config/extensions.py +167 -0
- gobby/config/features.py +472 -0
- gobby/config/llm_providers.py +98 -0
- gobby/config/logging.py +66 -0
- gobby/config/mcp.py +346 -0
- gobby/config/persistence.py +247 -0
- gobby/config/servers.py +141 -0
- gobby/config/sessions.py +250 -0
- gobby/config/tasks.py +784 -0
- gobby/hooks/__init__.py +104 -0
- gobby/hooks/artifact_capture.py +213 -0
- gobby/hooks/broadcaster.py +243 -0
- gobby/hooks/event_handlers.py +723 -0
- gobby/hooks/events.py +218 -0
- gobby/hooks/git.py +169 -0
- gobby/hooks/health_monitor.py +171 -0
- gobby/hooks/hook_manager.py +856 -0
- gobby/hooks/hook_types.py +575 -0
- gobby/hooks/plugins.py +813 -0
- gobby/hooks/session_coordinator.py +396 -0
- gobby/hooks/verification_runner.py +268 -0
- gobby/hooks/webhooks.py +339 -0
- gobby/install/claude/commands/gobby/bug.md +51 -0
- gobby/install/claude/commands/gobby/chore.md +51 -0
- gobby/install/claude/commands/gobby/epic.md +52 -0
- gobby/install/claude/commands/gobby/eval.md +235 -0
- gobby/install/claude/commands/gobby/feat.md +49 -0
- gobby/install/claude/commands/gobby/nit.md +52 -0
- gobby/install/claude/commands/gobby/ref.md +52 -0
- gobby/install/claude/hooks/HOOK_SCHEMAS.md +632 -0
- gobby/install/claude/hooks/hook_dispatcher.py +364 -0
- gobby/install/claude/hooks/validate_settings.py +102 -0
- gobby/install/claude/hooks-template.json +118 -0
- gobby/install/codex/hooks/hook_dispatcher.py +153 -0
- gobby/install/codex/prompts/forget.md +7 -0
- gobby/install/codex/prompts/memories.md +7 -0
- gobby/install/codex/prompts/recall.md +7 -0
- gobby/install/codex/prompts/remember.md +13 -0
- gobby/install/gemini/hooks/hook_dispatcher.py +268 -0
- gobby/install/gemini/hooks-template.json +138 -0
- gobby/install/shared/plugins/code_guardian.py +456 -0
- gobby/install/shared/plugins/example_notify.py +331 -0
- gobby/integrations/__init__.py +10 -0
- gobby/integrations/github.py +145 -0
- gobby/integrations/linear.py +145 -0
- gobby/llm/__init__.py +40 -0
- gobby/llm/base.py +120 -0
- gobby/llm/claude.py +578 -0
- gobby/llm/claude_executor.py +503 -0
- gobby/llm/codex.py +322 -0
- gobby/llm/codex_executor.py +513 -0
- gobby/llm/executor.py +316 -0
- gobby/llm/factory.py +34 -0
- gobby/llm/gemini.py +258 -0
- gobby/llm/gemini_executor.py +339 -0
- gobby/llm/litellm.py +287 -0
- gobby/llm/litellm_executor.py +303 -0
- gobby/llm/resolver.py +499 -0
- gobby/llm/service.py +236 -0
- gobby/mcp_proxy/__init__.py +29 -0
- gobby/mcp_proxy/actions.py +175 -0
- gobby/mcp_proxy/daemon_control.py +198 -0
- gobby/mcp_proxy/importer.py +436 -0
- gobby/mcp_proxy/lazy.py +325 -0
- gobby/mcp_proxy/manager.py +798 -0
- gobby/mcp_proxy/metrics.py +609 -0
- gobby/mcp_proxy/models.py +139 -0
- gobby/mcp_proxy/registries.py +215 -0
- gobby/mcp_proxy/schema_hash.py +381 -0
- gobby/mcp_proxy/semantic_search.py +706 -0
- gobby/mcp_proxy/server.py +549 -0
- gobby/mcp_proxy/services/__init__.py +0 -0
- gobby/mcp_proxy/services/fallback.py +306 -0
- gobby/mcp_proxy/services/recommendation.py +224 -0
- gobby/mcp_proxy/services/server_mgmt.py +214 -0
- gobby/mcp_proxy/services/system.py +72 -0
- gobby/mcp_proxy/services/tool_filter.py +231 -0
- gobby/mcp_proxy/services/tool_proxy.py +309 -0
- gobby/mcp_proxy/stdio.py +565 -0
- gobby/mcp_proxy/tools/__init__.py +27 -0
- gobby/mcp_proxy/tools/agents.py +1103 -0
- gobby/mcp_proxy/tools/artifacts.py +207 -0
- gobby/mcp_proxy/tools/hub.py +335 -0
- gobby/mcp_proxy/tools/internal.py +337 -0
- gobby/mcp_proxy/tools/memory.py +543 -0
- gobby/mcp_proxy/tools/merge.py +422 -0
- gobby/mcp_proxy/tools/metrics.py +283 -0
- gobby/mcp_proxy/tools/orchestration/__init__.py +23 -0
- gobby/mcp_proxy/tools/orchestration/cleanup.py +619 -0
- gobby/mcp_proxy/tools/orchestration/monitor.py +380 -0
- gobby/mcp_proxy/tools/orchestration/orchestrate.py +746 -0
- gobby/mcp_proxy/tools/orchestration/review.py +736 -0
- gobby/mcp_proxy/tools/orchestration/utils.py +16 -0
- gobby/mcp_proxy/tools/session_messages.py +1056 -0
- gobby/mcp_proxy/tools/task_dependencies.py +219 -0
- gobby/mcp_proxy/tools/task_expansion.py +591 -0
- gobby/mcp_proxy/tools/task_github.py +393 -0
- gobby/mcp_proxy/tools/task_linear.py +379 -0
- gobby/mcp_proxy/tools/task_orchestration.py +77 -0
- gobby/mcp_proxy/tools/task_readiness.py +522 -0
- gobby/mcp_proxy/tools/task_sync.py +351 -0
- gobby/mcp_proxy/tools/task_validation.py +843 -0
- gobby/mcp_proxy/tools/tasks/__init__.py +25 -0
- gobby/mcp_proxy/tools/tasks/_context.py +112 -0
- gobby/mcp_proxy/tools/tasks/_crud.py +516 -0
- gobby/mcp_proxy/tools/tasks/_factory.py +176 -0
- gobby/mcp_proxy/tools/tasks/_helpers.py +129 -0
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +517 -0
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +301 -0
- gobby/mcp_proxy/tools/tasks/_resolution.py +55 -0
- gobby/mcp_proxy/tools/tasks/_search.py +215 -0
- gobby/mcp_proxy/tools/tasks/_session.py +125 -0
- gobby/mcp_proxy/tools/workflows.py +973 -0
- gobby/mcp_proxy/tools/worktrees.py +1264 -0
- gobby/mcp_proxy/transports/__init__.py +0 -0
- gobby/mcp_proxy/transports/base.py +95 -0
- gobby/mcp_proxy/transports/factory.py +44 -0
- gobby/mcp_proxy/transports/http.py +139 -0
- gobby/mcp_proxy/transports/stdio.py +213 -0
- gobby/mcp_proxy/transports/websocket.py +136 -0
- gobby/memory/backends/__init__.py +116 -0
- gobby/memory/backends/mem0.py +408 -0
- gobby/memory/backends/memu.py +485 -0
- gobby/memory/backends/null.py +111 -0
- gobby/memory/backends/openmemory.py +537 -0
- gobby/memory/backends/sqlite.py +304 -0
- gobby/memory/context.py +87 -0
- gobby/memory/manager.py +1001 -0
- gobby/memory/protocol.py +451 -0
- gobby/memory/search/__init__.py +66 -0
- gobby/memory/search/text.py +127 -0
- gobby/memory/viz.py +258 -0
- gobby/prompts/__init__.py +13 -0
- gobby/prompts/defaults/expansion/system.md +119 -0
- gobby/prompts/defaults/expansion/user.md +48 -0
- gobby/prompts/defaults/external_validation/agent.md +72 -0
- gobby/prompts/defaults/external_validation/external.md +63 -0
- gobby/prompts/defaults/external_validation/spawn.md +83 -0
- gobby/prompts/defaults/external_validation/system.md +6 -0
- gobby/prompts/defaults/features/import_mcp.md +22 -0
- gobby/prompts/defaults/features/import_mcp_github.md +17 -0
- gobby/prompts/defaults/features/import_mcp_search.md +16 -0
- gobby/prompts/defaults/features/recommend_tools.md +32 -0
- gobby/prompts/defaults/features/recommend_tools_hybrid.md +35 -0
- gobby/prompts/defaults/features/recommend_tools_llm.md +30 -0
- gobby/prompts/defaults/features/server_description.md +20 -0
- gobby/prompts/defaults/features/server_description_system.md +6 -0
- gobby/prompts/defaults/features/task_description.md +31 -0
- gobby/prompts/defaults/features/task_description_system.md +6 -0
- gobby/prompts/defaults/features/tool_summary.md +17 -0
- gobby/prompts/defaults/features/tool_summary_system.md +6 -0
- gobby/prompts/defaults/research/step.md +58 -0
- gobby/prompts/defaults/validation/criteria.md +47 -0
- gobby/prompts/defaults/validation/validate.md +38 -0
- gobby/prompts/loader.py +346 -0
- gobby/prompts/models.py +113 -0
- gobby/py.typed +0 -0
- gobby/runner.py +488 -0
- gobby/search/__init__.py +23 -0
- gobby/search/protocol.py +104 -0
- gobby/search/tfidf.py +232 -0
- gobby/servers/__init__.py +7 -0
- gobby/servers/http.py +636 -0
- gobby/servers/models.py +31 -0
- gobby/servers/routes/__init__.py +23 -0
- gobby/servers/routes/admin.py +416 -0
- gobby/servers/routes/dependencies.py +118 -0
- gobby/servers/routes/mcp/__init__.py +24 -0
- gobby/servers/routes/mcp/hooks.py +135 -0
- gobby/servers/routes/mcp/plugins.py +121 -0
- gobby/servers/routes/mcp/tools.py +1337 -0
- gobby/servers/routes/mcp/webhooks.py +159 -0
- gobby/servers/routes/sessions.py +582 -0
- gobby/servers/websocket.py +766 -0
- gobby/sessions/__init__.py +13 -0
- gobby/sessions/analyzer.py +322 -0
- gobby/sessions/lifecycle.py +240 -0
- gobby/sessions/manager.py +563 -0
- gobby/sessions/processor.py +225 -0
- gobby/sessions/summary.py +532 -0
- gobby/sessions/transcripts/__init__.py +41 -0
- gobby/sessions/transcripts/base.py +125 -0
- gobby/sessions/transcripts/claude.py +386 -0
- gobby/sessions/transcripts/codex.py +143 -0
- gobby/sessions/transcripts/gemini.py +195 -0
- gobby/storage/__init__.py +21 -0
- gobby/storage/agents.py +409 -0
- gobby/storage/artifact_classifier.py +341 -0
- gobby/storage/artifacts.py +285 -0
- gobby/storage/compaction.py +67 -0
- gobby/storage/database.py +357 -0
- gobby/storage/inter_session_messages.py +194 -0
- gobby/storage/mcp.py +680 -0
- gobby/storage/memories.py +562 -0
- gobby/storage/merge_resolutions.py +550 -0
- gobby/storage/migrations.py +860 -0
- gobby/storage/migrations_legacy.py +1359 -0
- gobby/storage/projects.py +166 -0
- gobby/storage/session_messages.py +251 -0
- gobby/storage/session_tasks.py +97 -0
- gobby/storage/sessions.py +817 -0
- gobby/storage/task_dependencies.py +223 -0
- gobby/storage/tasks/__init__.py +42 -0
- gobby/storage/tasks/_aggregates.py +180 -0
- gobby/storage/tasks/_crud.py +449 -0
- gobby/storage/tasks/_id.py +104 -0
- gobby/storage/tasks/_lifecycle.py +311 -0
- gobby/storage/tasks/_manager.py +889 -0
- gobby/storage/tasks/_models.py +300 -0
- gobby/storage/tasks/_ordering.py +119 -0
- gobby/storage/tasks/_path_cache.py +110 -0
- gobby/storage/tasks/_queries.py +343 -0
- gobby/storage/tasks/_search.py +143 -0
- gobby/storage/workflow_audit.py +393 -0
- gobby/storage/worktrees.py +547 -0
- gobby/sync/__init__.py +29 -0
- gobby/sync/github.py +333 -0
- gobby/sync/linear.py +304 -0
- gobby/sync/memories.py +284 -0
- gobby/sync/tasks.py +641 -0
- gobby/tasks/__init__.py +8 -0
- gobby/tasks/build_verification.py +193 -0
- gobby/tasks/commits.py +633 -0
- gobby/tasks/context.py +747 -0
- gobby/tasks/criteria.py +342 -0
- gobby/tasks/enhanced_validator.py +226 -0
- gobby/tasks/escalation.py +263 -0
- gobby/tasks/expansion.py +626 -0
- gobby/tasks/external_validator.py +764 -0
- gobby/tasks/issue_extraction.py +171 -0
- gobby/tasks/prompts/expand.py +327 -0
- gobby/tasks/research.py +421 -0
- gobby/tasks/tdd.py +352 -0
- gobby/tasks/tree_builder.py +263 -0
- gobby/tasks/validation.py +712 -0
- gobby/tasks/validation_history.py +357 -0
- gobby/tasks/validation_models.py +89 -0
- gobby/tools/__init__.py +0 -0
- gobby/tools/summarizer.py +170 -0
- gobby/tui/__init__.py +5 -0
- gobby/tui/api_client.py +281 -0
- gobby/tui/app.py +327 -0
- gobby/tui/screens/__init__.py +25 -0
- gobby/tui/screens/agents.py +333 -0
- gobby/tui/screens/chat.py +450 -0
- gobby/tui/screens/dashboard.py +377 -0
- gobby/tui/screens/memory.py +305 -0
- gobby/tui/screens/metrics.py +231 -0
- gobby/tui/screens/orchestrator.py +904 -0
- gobby/tui/screens/sessions.py +412 -0
- gobby/tui/screens/tasks.py +442 -0
- gobby/tui/screens/workflows.py +289 -0
- gobby/tui/screens/worktrees.py +174 -0
- gobby/tui/widgets/__init__.py +21 -0
- gobby/tui/widgets/chat.py +210 -0
- gobby/tui/widgets/conductor.py +104 -0
- gobby/tui/widgets/menu.py +132 -0
- gobby/tui/widgets/message_panel.py +160 -0
- gobby/tui/widgets/review_gate.py +224 -0
- gobby/tui/widgets/task_tree.py +99 -0
- gobby/tui/widgets/token_budget.py +166 -0
- gobby/tui/ws_client.py +258 -0
- gobby/utils/__init__.py +3 -0
- gobby/utils/daemon_client.py +235 -0
- gobby/utils/git.py +222 -0
- gobby/utils/id.py +38 -0
- gobby/utils/json_helpers.py +161 -0
- gobby/utils/logging.py +376 -0
- gobby/utils/machine_id.py +135 -0
- gobby/utils/metrics.py +589 -0
- gobby/utils/project_context.py +182 -0
- gobby/utils/project_init.py +263 -0
- gobby/utils/status.py +256 -0
- gobby/utils/validation.py +80 -0
- gobby/utils/version.py +23 -0
- gobby/workflows/__init__.py +4 -0
- gobby/workflows/actions.py +1310 -0
- gobby/workflows/approval_flow.py +138 -0
- gobby/workflows/artifact_actions.py +103 -0
- gobby/workflows/audit_helpers.py +110 -0
- gobby/workflows/autonomous_actions.py +286 -0
- gobby/workflows/context_actions.py +394 -0
- gobby/workflows/definitions.py +130 -0
- gobby/workflows/detection_helpers.py +208 -0
- gobby/workflows/engine.py +485 -0
- gobby/workflows/evaluator.py +669 -0
- gobby/workflows/git_utils.py +96 -0
- gobby/workflows/hooks.py +169 -0
- gobby/workflows/lifecycle_evaluator.py +613 -0
- gobby/workflows/llm_actions.py +70 -0
- gobby/workflows/loader.py +333 -0
- gobby/workflows/mcp_actions.py +60 -0
- gobby/workflows/memory_actions.py +272 -0
- gobby/workflows/premature_stop.py +164 -0
- gobby/workflows/session_actions.py +139 -0
- gobby/workflows/state_actions.py +123 -0
- gobby/workflows/state_manager.py +104 -0
- gobby/workflows/stop_signal_actions.py +163 -0
- gobby/workflows/summary_actions.py +344 -0
- gobby/workflows/task_actions.py +249 -0
- gobby/workflows/task_enforcement_actions.py +901 -0
- gobby/workflows/templates.py +52 -0
- gobby/workflows/todo_actions.py +84 -0
- gobby/workflows/webhook.py +223 -0
- gobby/workflows/webhook_executor.py +399 -0
- gobby/worktrees/__init__.py +5 -0
- gobby/worktrees/git.py +690 -0
- gobby/worktrees/merge/__init__.py +20 -0
- gobby/worktrees/merge/conflict_parser.py +177 -0
- gobby/worktrees/merge/resolver.py +485 -0
- gobby-0.2.5.dist-info/METADATA +351 -0
- gobby-0.2.5.dist-info/RECORD +383 -0
- gobby-0.2.5.dist-info/WHEEL +5 -0
- gobby-0.2.5.dist-info/entry_points.txt +2 -0
- gobby-0.2.5.dist-info/licenses/LICENSE.md +193 -0
- gobby-0.2.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gobby Daemon Tools MCP Server.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from datetime import UTC
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from mcp.server.fastmcp import FastMCP
|
|
10
|
+
|
|
11
|
+
from gobby.config.app import DaemonConfig
|
|
12
|
+
from gobby.mcp_proxy.manager import MCPClientManager
|
|
13
|
+
from gobby.mcp_proxy.services.recommendation import RecommendationService, SearchMode
|
|
14
|
+
from gobby.mcp_proxy.services.server_mgmt import ServerManagementService
|
|
15
|
+
from gobby.mcp_proxy.services.system import SystemService
|
|
16
|
+
from gobby.mcp_proxy.services.tool_proxy import ToolProxyService
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger("gobby.mcp.server")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class GobbyDaemonTools:
|
|
22
|
+
"""Handler for Gobby Daemon MCP tools (Refactored to use services)."""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
mcp_manager: MCPClientManager,
|
|
27
|
+
daemon_port: int,
|
|
28
|
+
websocket_port: int,
|
|
29
|
+
start_time: float,
|
|
30
|
+
internal_manager: Any,
|
|
31
|
+
config: DaemonConfig | None = None,
|
|
32
|
+
llm_service: Any | None = None,
|
|
33
|
+
session_manager: Any | None = None,
|
|
34
|
+
memory_manager: Any | None = None,
|
|
35
|
+
config_manager: Any | None = None,
|
|
36
|
+
semantic_search: Any | None = None,
|
|
37
|
+
tool_filter: Any | None = None,
|
|
38
|
+
fallback_resolver: Any | None = None,
|
|
39
|
+
):
|
|
40
|
+
self.config = config
|
|
41
|
+
self.internal_manager = internal_manager
|
|
42
|
+
self._mcp_manager = mcp_manager # Store for project_id access
|
|
43
|
+
self._semantic_search = semantic_search # Store for direct search access
|
|
44
|
+
|
|
45
|
+
# Initialize services
|
|
46
|
+
self.system_service = SystemService(mcp_manager, daemon_port, websocket_port, start_time)
|
|
47
|
+
self.tool_proxy = ToolProxyService(
|
|
48
|
+
mcp_manager,
|
|
49
|
+
internal_manager=internal_manager,
|
|
50
|
+
tool_filter=tool_filter,
|
|
51
|
+
fallback_resolver=fallback_resolver,
|
|
52
|
+
)
|
|
53
|
+
self.server_mgmt = ServerManagementService(mcp_manager, config_manager, config)
|
|
54
|
+
self.recommendation = RecommendationService(
|
|
55
|
+
llm_service,
|
|
56
|
+
mcp_manager,
|
|
57
|
+
semantic_search=semantic_search,
|
|
58
|
+
project_id=mcp_manager.project_id,
|
|
59
|
+
config=config.recommend_tools if config else None,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# --- System Tools ---
|
|
63
|
+
|
|
64
|
+
async def status(self) -> dict[str, Any]:
|
|
65
|
+
"""Get daemon status."""
|
|
66
|
+
return self.system_service.get_status()
|
|
67
|
+
|
|
68
|
+
async def list_mcp_servers(self) -> dict[str, Any]:
|
|
69
|
+
"""List configured MCP servers."""
|
|
70
|
+
status = self.system_service.get_status()
|
|
71
|
+
servers_info = status.get("mcp_servers", {})
|
|
72
|
+
|
|
73
|
+
server_list = []
|
|
74
|
+
for name, info in servers_info.items():
|
|
75
|
+
server_list.append(
|
|
76
|
+
{
|
|
77
|
+
"name": name,
|
|
78
|
+
"state": info["state"],
|
|
79
|
+
"connected": info["state"] == "connected",
|
|
80
|
+
# Additional fields can be fetched from config if we had access
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
"success": True,
|
|
86
|
+
"servers": server_list,
|
|
87
|
+
"total_count": len(server_list),
|
|
88
|
+
"connected_count": len([s for s in server_list if s["connected"]]),
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# --- Tool Proxying ---
|
|
92
|
+
|
|
93
|
+
async def call_tool(
|
|
94
|
+
self,
|
|
95
|
+
server_name: str,
|
|
96
|
+
tool_name: str,
|
|
97
|
+
arguments: dict[str, Any] | None = None,
|
|
98
|
+
) -> Any:
|
|
99
|
+
"""Call a tool."""
|
|
100
|
+
return await self.tool_proxy.call_tool(server_name, tool_name, arguments)
|
|
101
|
+
|
|
102
|
+
async def list_tools(self, server: str, session_id: str | None = None) -> dict[str, Any]:
|
|
103
|
+
"""List tools for a specific server, optionally filtered by workflow phase restrictions."""
|
|
104
|
+
return await self.tool_proxy.list_tools(server, session_id=session_id)
|
|
105
|
+
|
|
106
|
+
async def get_tool_schema(self, server_name: str, tool_name: str) -> dict[str, Any]:
|
|
107
|
+
"""Get tool schema."""
|
|
108
|
+
return await self.tool_proxy.get_tool_schema(server_name, tool_name)
|
|
109
|
+
|
|
110
|
+
async def read_mcp_resource(self, server_name: str, resource_uri: str) -> Any:
|
|
111
|
+
"""Read resource."""
|
|
112
|
+
return await self.tool_proxy.read_resource(server_name, resource_uri)
|
|
113
|
+
|
|
114
|
+
# --- Server Management ---
|
|
115
|
+
|
|
116
|
+
async def add_mcp_server(
|
|
117
|
+
self,
|
|
118
|
+
name: str,
|
|
119
|
+
transport: str,
|
|
120
|
+
url: str | None = None,
|
|
121
|
+
headers: dict[str, str] | None = None,
|
|
122
|
+
command: str | None = None,
|
|
123
|
+
args: list[str] | None = None,
|
|
124
|
+
env: dict[str, str] | None = None,
|
|
125
|
+
enabled: bool = True,
|
|
126
|
+
) -> dict[str, Any]:
|
|
127
|
+
"""Add server."""
|
|
128
|
+
return await self.server_mgmt.add_server(
|
|
129
|
+
name, transport, url, command, args, env, headers, enabled
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
async def remove_mcp_server(self, name: str) -> dict[str, Any]:
|
|
133
|
+
"""Remove server."""
|
|
134
|
+
return await self.server_mgmt.remove_server(name)
|
|
135
|
+
|
|
136
|
+
async def import_mcp_server(
|
|
137
|
+
self,
|
|
138
|
+
from_project: str | None = None,
|
|
139
|
+
servers: list[str] | None = None,
|
|
140
|
+
github_url: str | None = None,
|
|
141
|
+
query: str | None = None,
|
|
142
|
+
) -> dict[str, Any]:
|
|
143
|
+
"""Import server."""
|
|
144
|
+
return await self.server_mgmt.import_server(from_project, github_url, query, servers)
|
|
145
|
+
|
|
146
|
+
# --- Recommendation ---
|
|
147
|
+
|
|
148
|
+
async def recommend_tools(
|
|
149
|
+
self,
|
|
150
|
+
task_description: str,
|
|
151
|
+
agent_id: str | None = None,
|
|
152
|
+
search_mode: SearchMode = "llm",
|
|
153
|
+
top_k: int = 10,
|
|
154
|
+
min_similarity: float = 0.3,
|
|
155
|
+
project_id: str | None = None,
|
|
156
|
+
) -> dict[str, Any]:
|
|
157
|
+
"""Recommend tools for a task.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
task_description: What the user wants to accomplish
|
|
161
|
+
agent_id: Optional agent profile for filtering (reserved)
|
|
162
|
+
search_mode: How to search - "llm" (default), "semantic", or "hybrid"
|
|
163
|
+
top_k: Maximum recommendations to return (semantic/hybrid modes)
|
|
164
|
+
min_similarity: Minimum similarity threshold (semantic/hybrid modes)
|
|
165
|
+
project_id: Project ID for semantic/hybrid search
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Dict with tool recommendations
|
|
169
|
+
"""
|
|
170
|
+
return await self.recommendation.recommend_tools(
|
|
171
|
+
task_description,
|
|
172
|
+
agent_id=agent_id,
|
|
173
|
+
search_mode=search_mode,
|
|
174
|
+
top_k=top_k,
|
|
175
|
+
min_similarity=min_similarity,
|
|
176
|
+
project_id=project_id,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# --- Fallback Resolver ---
|
|
180
|
+
|
|
181
|
+
async def get_tool_alternatives(
|
|
182
|
+
self,
|
|
183
|
+
server_name: str,
|
|
184
|
+
tool_name: str,
|
|
185
|
+
error_message: str | None = None,
|
|
186
|
+
top_k: int = 5,
|
|
187
|
+
) -> dict[str, Any]:
|
|
188
|
+
"""Get alternative tool suggestions when a tool fails.
|
|
189
|
+
|
|
190
|
+
Uses semantic similarity and historical success rates to suggest
|
|
191
|
+
similar tools that might work as alternatives.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
server_name: Server where the tool failed
|
|
195
|
+
tool_name: Name of the failed tool
|
|
196
|
+
error_message: Optional error message for context-aware matching
|
|
197
|
+
top_k: Maximum number of alternatives to return (default: 5)
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Dict with alternative tool suggestions and scores
|
|
201
|
+
"""
|
|
202
|
+
project_id = self._mcp_manager.project_id
|
|
203
|
+
if not project_id:
|
|
204
|
+
return {
|
|
205
|
+
"success": False,
|
|
206
|
+
"error": "No project_id available. Run 'gobby init' first.",
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
fallback_resolver = self.tool_proxy._fallback_resolver
|
|
210
|
+
if not fallback_resolver:
|
|
211
|
+
return {
|
|
212
|
+
"success": False,
|
|
213
|
+
"error": "Fallback resolver not configured",
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
suggestions = await fallback_resolver.find_alternatives_for_error(
|
|
218
|
+
server_name=server_name,
|
|
219
|
+
tool_name=tool_name,
|
|
220
|
+
error_message=error_message or "Tool call failed",
|
|
221
|
+
project_id=project_id,
|
|
222
|
+
top_k=top_k,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
"success": True,
|
|
227
|
+
"failed_tool": f"{server_name}/{tool_name}",
|
|
228
|
+
"alternatives": suggestions,
|
|
229
|
+
"count": len(suggestions),
|
|
230
|
+
}
|
|
231
|
+
except Exception as e:
|
|
232
|
+
logger.error(f"Failed to get tool alternatives: {e}")
|
|
233
|
+
return {"success": False, "error": str(e)}
|
|
234
|
+
|
|
235
|
+
# --- Semantic Search ---
|
|
236
|
+
|
|
237
|
+
async def search_tools(
|
|
238
|
+
self,
|
|
239
|
+
query: str,
|
|
240
|
+
top_k: int = 10,
|
|
241
|
+
min_similarity: float = 0.0,
|
|
242
|
+
server: str | None = None,
|
|
243
|
+
) -> dict[str, Any]:
|
|
244
|
+
"""Search for tools using semantic similarity.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
query: Natural language query describing the tool you need
|
|
248
|
+
top_k: Maximum number of results to return (default: 10)
|
|
249
|
+
min_similarity: Minimum similarity threshold (default: 0.0)
|
|
250
|
+
server: Optional server name to filter results
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Dict with search results and metadata
|
|
254
|
+
"""
|
|
255
|
+
if not self._semantic_search:
|
|
256
|
+
return {
|
|
257
|
+
"success": False,
|
|
258
|
+
"error": "Semantic search not configured",
|
|
259
|
+
"query": query,
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
project_id = self._mcp_manager.project_id
|
|
263
|
+
if not project_id:
|
|
264
|
+
return {
|
|
265
|
+
"success": False,
|
|
266
|
+
"error": "No project_id available. Run 'gobby init' first.",
|
|
267
|
+
"query": query,
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
try:
|
|
271
|
+
results = await self._semantic_search.search_tools(
|
|
272
|
+
query=query,
|
|
273
|
+
project_id=project_id,
|
|
274
|
+
top_k=top_k,
|
|
275
|
+
min_similarity=min_similarity,
|
|
276
|
+
server_filter=server,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
"success": True,
|
|
281
|
+
"query": query,
|
|
282
|
+
"results": [r.to_dict() for r in results],
|
|
283
|
+
"total_results": len(results),
|
|
284
|
+
}
|
|
285
|
+
except Exception as e:
|
|
286
|
+
logger.error(f"Semantic search failed: {e}")
|
|
287
|
+
return {"success": False, "error": str(e), "query": query}
|
|
288
|
+
|
|
289
|
+
# --- Hook Extensions ---
|
|
290
|
+
|
|
291
|
+
async def list_hook_handlers(self) -> dict[str, Any]:
|
|
292
|
+
"""List registered hook handlers from plugins.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
Dict with handler information organized by event type
|
|
296
|
+
"""
|
|
297
|
+
# Access plugin registry via internal_manager if available
|
|
298
|
+
if not self.internal_manager:
|
|
299
|
+
return {
|
|
300
|
+
"success": False,
|
|
301
|
+
"error": "Internal manager not available",
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
# Get hook_manager from app state if available (set during HTTP server startup)
|
|
305
|
+
hook_manager = getattr(self.internal_manager, "_hook_manager", None)
|
|
306
|
+
if not hook_manager:
|
|
307
|
+
return {
|
|
308
|
+
"success": True,
|
|
309
|
+
"handlers": {},
|
|
310
|
+
"message": "No hook manager available - plugins not loaded",
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
plugin_loader = getattr(hook_manager, "plugin_loader", None)
|
|
314
|
+
if not plugin_loader:
|
|
315
|
+
return {
|
|
316
|
+
"success": True,
|
|
317
|
+
"handlers": {},
|
|
318
|
+
"message": "Plugin system not initialized",
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
# Get handlers organized by event type
|
|
322
|
+
from gobby.hooks.events import HookEventType
|
|
323
|
+
|
|
324
|
+
handlers_by_event: dict[str, list[dict[str, Any]]] = {}
|
|
325
|
+
|
|
326
|
+
for event_type in HookEventType:
|
|
327
|
+
handlers = plugin_loader.registry.get_handlers(event_type)
|
|
328
|
+
if handlers:
|
|
329
|
+
handlers_by_event[event_type.value] = [
|
|
330
|
+
{
|
|
331
|
+
"plugin": h.plugin.name,
|
|
332
|
+
"method": h.method.__name__,
|
|
333
|
+
"priority": h.priority,
|
|
334
|
+
"is_pre_handler": h.priority < 50,
|
|
335
|
+
}
|
|
336
|
+
for h in handlers
|
|
337
|
+
]
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
"success": True,
|
|
341
|
+
"handlers": handlers_by_event,
|
|
342
|
+
"total_handlers": sum(len(h) for h in handlers_by_event.values()),
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async def test_hook_event(
|
|
346
|
+
self,
|
|
347
|
+
event_type: str,
|
|
348
|
+
source: str = "claude",
|
|
349
|
+
data: dict[str, Any] | None = None,
|
|
350
|
+
) -> dict[str, Any]:
|
|
351
|
+
"""Test a hook event by sending it through the hook system.
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
event_type: Hook event type (e.g., "session_start", "before_tool")
|
|
355
|
+
source: Source CLI to simulate (claude, gemini, codex)
|
|
356
|
+
data: Optional additional data for the event
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Hook execution result
|
|
360
|
+
"""
|
|
361
|
+
if not self.internal_manager:
|
|
362
|
+
return {
|
|
363
|
+
"success": False,
|
|
364
|
+
"error": "Internal manager not available",
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
hook_manager = getattr(self.internal_manager, "_hook_manager", None)
|
|
368
|
+
if not hook_manager:
|
|
369
|
+
return {
|
|
370
|
+
"success": False,
|
|
371
|
+
"error": "No hook manager available",
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
# Build test event
|
|
375
|
+
from datetime import datetime
|
|
376
|
+
|
|
377
|
+
from gobby.hooks.events import HookEvent, HookEventType, SessionSource
|
|
378
|
+
|
|
379
|
+
try:
|
|
380
|
+
hook_event_type = HookEventType(event_type)
|
|
381
|
+
except ValueError:
|
|
382
|
+
return {
|
|
383
|
+
"success": False,
|
|
384
|
+
"error": f"Invalid event type: {event_type}",
|
|
385
|
+
"valid_types": [e.value for e in HookEventType],
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
# Map source string to SessionSource enum
|
|
389
|
+
try:
|
|
390
|
+
session_source = SessionSource(source)
|
|
391
|
+
except ValueError:
|
|
392
|
+
session_source = SessionSource.CLAUDE # Default to Claude
|
|
393
|
+
|
|
394
|
+
test_data = {
|
|
395
|
+
"session_id": "test-mcp-event",
|
|
396
|
+
"source": source,
|
|
397
|
+
**(data or {}),
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
event = HookEvent(
|
|
401
|
+
event_type=hook_event_type,
|
|
402
|
+
session_id="test-mcp-event",
|
|
403
|
+
source=session_source,
|
|
404
|
+
timestamp=datetime.now(UTC),
|
|
405
|
+
data=test_data,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
# Process through hook manager
|
|
409
|
+
try:
|
|
410
|
+
result = hook_manager.process_event(event)
|
|
411
|
+
return {
|
|
412
|
+
"success": True,
|
|
413
|
+
"event_type": event_type,
|
|
414
|
+
"continue": result.get("continue", True),
|
|
415
|
+
"reason": result.get("reason"),
|
|
416
|
+
"inject_context": result.get("inject_context"),
|
|
417
|
+
}
|
|
418
|
+
except Exception as e:
|
|
419
|
+
return {
|
|
420
|
+
"success": False,
|
|
421
|
+
"error": str(e),
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async def list_plugins(self) -> dict[str, Any]:
|
|
425
|
+
"""List loaded Python plugins.
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
Dict with plugin information
|
|
429
|
+
"""
|
|
430
|
+
if not self.internal_manager:
|
|
431
|
+
return {
|
|
432
|
+
"success": False,
|
|
433
|
+
"error": "Internal manager not available",
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
hook_manager = getattr(self.internal_manager, "_hook_manager", None)
|
|
437
|
+
if not hook_manager:
|
|
438
|
+
return {
|
|
439
|
+
"success": True,
|
|
440
|
+
"enabled": False,
|
|
441
|
+
"plugins": [],
|
|
442
|
+
"message": "Plugin system not initialized",
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
plugin_loader = getattr(hook_manager, "plugin_loader", None)
|
|
446
|
+
if not plugin_loader:
|
|
447
|
+
return {
|
|
448
|
+
"success": True,
|
|
449
|
+
"enabled": False,
|
|
450
|
+
"plugins": [],
|
|
451
|
+
"message": "No plugin loader available",
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
plugins = plugin_loader.registry.list_plugins()
|
|
455
|
+
|
|
456
|
+
return {
|
|
457
|
+
"success": True,
|
|
458
|
+
"enabled": plugin_loader.config.enabled,
|
|
459
|
+
"plugins": plugins,
|
|
460
|
+
"plugin_dirs": plugin_loader.config.plugin_dirs,
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async def reload_plugin(self, name: str) -> dict[str, Any]:
|
|
464
|
+
"""Reload a plugin by name.
|
|
465
|
+
|
|
466
|
+
Args:
|
|
467
|
+
name: Plugin name to reload
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
Reload result
|
|
471
|
+
"""
|
|
472
|
+
if not self.internal_manager:
|
|
473
|
+
return {
|
|
474
|
+
"success": False,
|
|
475
|
+
"error": "Internal manager not available",
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
hook_manager = getattr(self.internal_manager, "_hook_manager", None)
|
|
479
|
+
if not hook_manager:
|
|
480
|
+
return {
|
|
481
|
+
"success": False,
|
|
482
|
+
"error": "Plugin system not initialized",
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
plugin_loader = getattr(hook_manager, "plugin_loader", None)
|
|
486
|
+
if not plugin_loader:
|
|
487
|
+
return {
|
|
488
|
+
"success": False,
|
|
489
|
+
"error": "No plugin loader available",
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
try:
|
|
493
|
+
plugin = plugin_loader.reload_plugin(name)
|
|
494
|
+
if plugin is None:
|
|
495
|
+
return {
|
|
496
|
+
"success": False,
|
|
497
|
+
"error": f"Plugin not found or reload failed: {name}",
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
"success": True,
|
|
502
|
+
"name": plugin.name,
|
|
503
|
+
"version": plugin.version,
|
|
504
|
+
"description": plugin.description,
|
|
505
|
+
}
|
|
506
|
+
except Exception as e:
|
|
507
|
+
logger.error(f"Plugin reload failed: {e}")
|
|
508
|
+
return {
|
|
509
|
+
"success": False,
|
|
510
|
+
"error": str(e),
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def create_mcp_server(tools_handler: GobbyDaemonTools) -> FastMCP:
|
|
515
|
+
"""Create the FastMCP server instance for the HTTP daemon."""
|
|
516
|
+
mcp = FastMCP("gobby")
|
|
517
|
+
|
|
518
|
+
# System tools
|
|
519
|
+
mcp.add_tool(tools_handler.status)
|
|
520
|
+
mcp.add_tool(tools_handler.list_mcp_servers)
|
|
521
|
+
|
|
522
|
+
# Tool Proxy
|
|
523
|
+
mcp.add_tool(tools_handler.call_tool)
|
|
524
|
+
mcp.add_tool(tools_handler.list_tools)
|
|
525
|
+
mcp.add_tool(tools_handler.get_tool_schema)
|
|
526
|
+
# read_mcp_resource is a tool that proxies resource reading
|
|
527
|
+
mcp.add_tool(tools_handler.read_mcp_resource)
|
|
528
|
+
|
|
529
|
+
# Server Management
|
|
530
|
+
mcp.add_tool(tools_handler.add_mcp_server)
|
|
531
|
+
mcp.add_tool(tools_handler.remove_mcp_server)
|
|
532
|
+
mcp.add_tool(tools_handler.import_mcp_server)
|
|
533
|
+
|
|
534
|
+
# Recommendation
|
|
535
|
+
mcp.add_tool(tools_handler.recommend_tools)
|
|
536
|
+
|
|
537
|
+
# Fallback Resolver
|
|
538
|
+
mcp.add_tool(tools_handler.get_tool_alternatives)
|
|
539
|
+
|
|
540
|
+
# Semantic Search
|
|
541
|
+
mcp.add_tool(tools_handler.search_tools)
|
|
542
|
+
|
|
543
|
+
# Hook Extensions
|
|
544
|
+
mcp.add_tool(tools_handler.list_hook_handlers)
|
|
545
|
+
mcp.add_tool(tools_handler.test_hook_event)
|
|
546
|
+
mcp.add_tool(tools_handler.list_plugins)
|
|
547
|
+
mcp.add_tool(tools_handler.reload_plugin)
|
|
548
|
+
|
|
549
|
+
return mcp
|
|
File without changes
|