stravinsky 0.1.2__py3-none-any.whl → 0.2.7__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.
Potentially problematic release.
This version of stravinsky might be problematic. Click here for more details.
- mcp_bridge/__init__.py +1 -5
- mcp_bridge/auth/cli.py +7 -0
- mcp_bridge/hooks/__init__.py +28 -0
- mcp_bridge/hooks/budget_optimizer.py +38 -0
- mcp_bridge/hooks/compaction.py +32 -0
- mcp_bridge/hooks/directory_context.py +40 -0
- mcp_bridge/hooks/edit_recovery.py +41 -0
- mcp_bridge/hooks/manager.py +77 -0
- mcp_bridge/hooks/truncator.py +19 -0
- mcp_bridge/native_hooks/context.py +38 -0
- mcp_bridge/native_hooks/edit_recovery.py +46 -0
- mcp_bridge/native_hooks/truncator.py +23 -0
- mcp_bridge/prompts/stravinsky.py +29 -19
- mcp_bridge/server.py +225 -668
- mcp_bridge/server_tools.py +529 -0
- mcp_bridge/tools/__init__.py +11 -2
- mcp_bridge/tools/agent_manager.py +99 -7
- mcp_bridge/tools/continuous_loop.py +67 -0
- mcp_bridge/tools/init.py +50 -0
- mcp_bridge/tools/lsp/tools.py +15 -15
- mcp_bridge/tools/model_invoke.py +64 -0
- mcp_bridge/tools/task_runner.py +97 -0
- mcp_bridge/tools/templates.py +86 -0
- {stravinsky-0.1.2.dist-info → stravinsky-0.2.7.dist-info}/METADATA +61 -10
- stravinsky-0.2.7.dist-info/RECORD +47 -0
- stravinsky-0.1.2.dist-info/RECORD +0 -32
- {stravinsky-0.1.2.dist-info → stravinsky-0.2.7.dist-info}/WHEEL +0 -0
- {stravinsky-0.1.2.dist-info → stravinsky-0.2.7.dist-info}/entry_points.txt +0 -0
mcp_bridge/server.py
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Stravinsky MCP Bridge Server - Zero-Import-Weight Architecture
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
- Session management
|
|
9
|
-
|
|
10
|
-
Run with: python -m mcp_bridge.server
|
|
4
|
+
Optimized for extremely fast startup and protocol compliance:
|
|
5
|
+
- Lazy-loads all tool implementations and dependencies.
|
|
6
|
+
- Minimal top-level imports.
|
|
7
|
+
- Robust crash logging to stderr and /tmp.
|
|
11
8
|
"""
|
|
12
9
|
|
|
10
|
+
import sys
|
|
11
|
+
import os
|
|
13
12
|
import asyncio
|
|
14
13
|
import logging
|
|
14
|
+
import time
|
|
15
15
|
from typing import Any
|
|
16
16
|
|
|
17
17
|
from mcp.server import Server
|
|
@@ -25,799 +25,337 @@ from mcp.types import (
|
|
|
25
25
|
GetPromptResult,
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
-
from .
|
|
29
|
-
from .tools.model_invoke import invoke_gemini, invoke_openai
|
|
30
|
-
from .tools.code_search import lsp_diagnostics, ast_grep_search, ast_grep_replace, grep_search, glob_files
|
|
31
|
-
from .tools.session_manager import list_sessions, read_session, search_sessions, get_session_info
|
|
32
|
-
from .tools.skill_loader import list_skills, get_skill, create_skill
|
|
33
|
-
from .tools.background_tasks import task_spawn, task_status, task_list
|
|
34
|
-
from .tools.agent_manager import agent_spawn, agent_output, agent_cancel, agent_list, agent_progress
|
|
35
|
-
from .tools.project_context import get_project_context, get_system_health
|
|
36
|
-
from .tools.lsp import (
|
|
37
|
-
lsp_hover,
|
|
38
|
-
lsp_goto_definition,
|
|
39
|
-
lsp_find_references,
|
|
40
|
-
lsp_document_symbols,
|
|
41
|
-
lsp_workspace_symbols,
|
|
42
|
-
lsp_prepare_rename,
|
|
43
|
-
lsp_rename,
|
|
44
|
-
lsp_code_actions,
|
|
45
|
-
lsp_servers,
|
|
46
|
-
)
|
|
47
|
-
from .prompts import stravinsky, delphi, dewey, explore, frontend, document_writer, multimodal
|
|
28
|
+
from . import __version__
|
|
48
29
|
|
|
49
|
-
#
|
|
50
|
-
logging.basicConfig(level=logging.INFO)
|
|
51
|
-
logger = logging.getLogger(__name__)
|
|
30
|
+
# --- CRITICAL: PROTOCOL HYGIENE ---
|
|
52
31
|
|
|
53
|
-
#
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
32
|
+
# Configure logging to stderr explicitly to avoid protocol corruption
|
|
33
|
+
logging.basicConfig(
|
|
34
|
+
level=logging.INFO,
|
|
35
|
+
format='%(levelname)s:%(name)s:%(message)s',
|
|
36
|
+
stream=sys.stderr
|
|
37
|
+
)
|
|
38
|
+
logger = logging.getLogger(__name__)
|
|
58
39
|
|
|
40
|
+
# Pre-async crash logger
|
|
41
|
+
def install_emergency_logger():
|
|
42
|
+
def handle_exception(exc_type, exc_value, exc_traceback):
|
|
43
|
+
if issubclass(exc_type, KeyboardInterrupt):
|
|
44
|
+
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
|
45
|
+
return
|
|
46
|
+
logger.critical("FATAL PRE-STARTUP ERROR", exc_info=(exc_type, exc_value, exc_traceback))
|
|
47
|
+
try:
|
|
48
|
+
with open("/tmp/stravinsky_crash.log", "a") as f:
|
|
49
|
+
import traceback
|
|
50
|
+
f.write(f"\n--- CRASH AT {time.ctime()} ---\n")
|
|
51
|
+
traceback.print_exception(exc_type, exc_value, exc_traceback, file=f)
|
|
52
|
+
except:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
sys.excepthook = handle_exception
|
|
56
|
+
|
|
57
|
+
install_emergency_logger()
|
|
58
|
+
|
|
59
|
+
# --- SERVER INITIALIZATION ---
|
|
60
|
+
|
|
61
|
+
server = Server("stravinsky", version=__version__)
|
|
62
|
+
|
|
63
|
+
# Lazy-loaded systems
|
|
64
|
+
_token_store = None
|
|
65
|
+
_hook_manager = None
|
|
66
|
+
|
|
67
|
+
def get_token_store():
|
|
68
|
+
global _token_store
|
|
69
|
+
if _token_store is None:
|
|
70
|
+
from .auth.token_store import TokenStore
|
|
71
|
+
_token_store = TokenStore()
|
|
72
|
+
return _token_store
|
|
73
|
+
|
|
74
|
+
def get_hook_manager_lazy():
|
|
75
|
+
global _hook_manager
|
|
76
|
+
if _hook_manager is None:
|
|
77
|
+
from .hooks.manager import get_hook_manager
|
|
78
|
+
_hook_manager = get_hook_manager()
|
|
79
|
+
return _hook_manager
|
|
80
|
+
|
|
81
|
+
# --- MCP INTERFACE ---
|
|
59
82
|
|
|
60
83
|
@server.list_tools()
|
|
61
84
|
async def list_tools() -> list[Tool]:
|
|
62
|
-
"""List
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
name="invoke_gemini",
|
|
66
|
-
description=(
|
|
67
|
-
"Invoke a Gemini model with the given prompt. "
|
|
68
|
-
"Requires OAuth authentication with Google. "
|
|
69
|
-
"Use this for tasks requiring Gemini's capabilities like "
|
|
70
|
-
"frontend UI generation, documentation writing, or multimodal analysis."
|
|
71
|
-
),
|
|
72
|
-
inputSchema={
|
|
73
|
-
"type": "object",
|
|
74
|
-
"properties": {
|
|
75
|
-
"prompt": {
|
|
76
|
-
"type": "string",
|
|
77
|
-
"description": "The prompt to send to Gemini",
|
|
78
|
-
},
|
|
79
|
-
"model": {
|
|
80
|
-
"type": "string",
|
|
81
|
-
"description": "Gemini model to use (default: gemini-3-flash)",
|
|
82
|
-
"default": "gemini-3-flash",
|
|
83
|
-
},
|
|
84
|
-
"temperature": {
|
|
85
|
-
"type": "number",
|
|
86
|
-
"description": "Sampling temperature (0.0-2.0)",
|
|
87
|
-
"default": 0.7,
|
|
88
|
-
},
|
|
89
|
-
"max_tokens": {
|
|
90
|
-
"type": "integer",
|
|
91
|
-
"description": "Maximum tokens in response",
|
|
92
|
-
"default": 4096,
|
|
93
|
-
},
|
|
94
|
-
"thinking_budget": {
|
|
95
|
-
"type": "integer",
|
|
96
|
-
"description": "Tokens reserved for internal reasoning (if model supports it)",
|
|
97
|
-
"default": 0,
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
"required": ["prompt"],
|
|
101
|
-
},
|
|
102
|
-
),
|
|
103
|
-
Tool(
|
|
104
|
-
name="invoke_openai",
|
|
105
|
-
description=(
|
|
106
|
-
"Invoke an OpenAI model with the given prompt. "
|
|
107
|
-
"Requires OAuth authentication with OpenAI. "
|
|
108
|
-
"Use this for tasks requiring GPT capabilities like "
|
|
109
|
-
"strategic advice, code review, or complex reasoning."
|
|
110
|
-
),
|
|
111
|
-
inputSchema={
|
|
112
|
-
"type": "object",
|
|
113
|
-
"properties": {
|
|
114
|
-
"prompt": {
|
|
115
|
-
"type": "string",
|
|
116
|
-
"description": "The prompt to send to OpenAI",
|
|
117
|
-
},
|
|
118
|
-
"model": {
|
|
119
|
-
"type": "string",
|
|
120
|
-
"description": "OpenAI model to use (default: gpt-5.2)",
|
|
121
|
-
"default": "gpt-5.2",
|
|
122
|
-
},
|
|
123
|
-
"temperature": {
|
|
124
|
-
"type": "number",
|
|
125
|
-
"description": "Sampling temperature (0.0-2.0)",
|
|
126
|
-
"default": 0.7,
|
|
127
|
-
},
|
|
128
|
-
"max_tokens": {
|
|
129
|
-
"type": "integer",
|
|
130
|
-
"description": "Maximum tokens in response",
|
|
131
|
-
"default": 4096,
|
|
132
|
-
},
|
|
133
|
-
"thinking_budget": {
|
|
134
|
-
"type": "integer",
|
|
135
|
-
"description": "Tokens reserved for internal reasoning (e.g. gpt-5.2 / o1 / o3)",
|
|
136
|
-
"default": 0,
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
"required": ["prompt"],
|
|
140
|
-
},
|
|
141
|
-
),
|
|
142
|
-
Tool(
|
|
143
|
-
name="get_project_context",
|
|
144
|
-
description="Summarize project environment including Git status, local rules (.claude/rules/), and pending todos.",
|
|
145
|
-
inputSchema={
|
|
146
|
-
"type": "object",
|
|
147
|
-
"properties": {
|
|
148
|
-
"project_path": {"type": "string", "description": "Path to the project root"},
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
),
|
|
152
|
-
Tool(
|
|
153
|
-
name="get_system_health",
|
|
154
|
-
description="Comprehensive check of system dependencies (rg, fd, sg, etc.) and authentication status.",
|
|
155
|
-
inputSchema={
|
|
156
|
-
"type": "object",
|
|
157
|
-
"properties": {},
|
|
158
|
-
},
|
|
159
|
-
),
|
|
160
|
-
Tool(
|
|
161
|
-
name="lsp_diagnostics",
|
|
162
|
-
description="Get diagnostics (errors, warnings) for a file using language tools (tsc, ruff).",
|
|
163
|
-
inputSchema={
|
|
164
|
-
"type": "object",
|
|
165
|
-
"properties": {
|
|
166
|
-
"file_path": {"type": "string", "description": "Path to file to analyze"},
|
|
167
|
-
"severity": {"type": "string", "description": "Filter: error, warning, all", "default": "all"},
|
|
168
|
-
},
|
|
169
|
-
"required": ["file_path"],
|
|
170
|
-
},
|
|
171
|
-
),
|
|
172
|
-
Tool(
|
|
173
|
-
name="ast_grep_search",
|
|
174
|
-
description="Search codebase using ast-grep for structural AST patterns.",
|
|
175
|
-
inputSchema={
|
|
176
|
-
"type": "object",
|
|
177
|
-
"properties": {
|
|
178
|
-
"pattern": {"type": "string", "description": "ast-grep pattern"},
|
|
179
|
-
"directory": {"type": "string", "description": "Directory to search", "default": "."},
|
|
180
|
-
"language": {"type": "string", "description": "Filter by language"},
|
|
181
|
-
},
|
|
182
|
-
"required": ["pattern"],
|
|
183
|
-
},
|
|
184
|
-
),
|
|
185
|
-
Tool(
|
|
186
|
-
name="grep_search",
|
|
187
|
-
description="Fast text search using ripgrep.",
|
|
188
|
-
inputSchema={
|
|
189
|
-
"type": "object",
|
|
190
|
-
"properties": {
|
|
191
|
-
"pattern": {"type": "string", "description": "Search pattern (regex)"},
|
|
192
|
-
"directory": {"type": "string", "description": "Directory to search", "default": "."},
|
|
193
|
-
"file_pattern": {"type": "string", "description": "Glob filter (e.g. *.py)"},
|
|
194
|
-
},
|
|
195
|
-
"required": ["pattern"],
|
|
196
|
-
},
|
|
197
|
-
),
|
|
198
|
-
Tool(
|
|
199
|
-
name="glob_files",
|
|
200
|
-
description="Find files matching a glob pattern.",
|
|
201
|
-
inputSchema={
|
|
202
|
-
"type": "object",
|
|
203
|
-
"properties": {
|
|
204
|
-
"pattern": {"type": "string", "description": "Glob pattern (e.g. **/*.py)"},
|
|
205
|
-
"directory": {"type": "string", "description": "Base directory", "default": "."},
|
|
206
|
-
},
|
|
207
|
-
"required": ["pattern"],
|
|
208
|
-
},
|
|
209
|
-
),
|
|
210
|
-
Tool(
|
|
211
|
-
name="session_list",
|
|
212
|
-
description="List Claude Code sessions with optional filtering.",
|
|
213
|
-
inputSchema={
|
|
214
|
-
"type": "object",
|
|
215
|
-
"properties": {
|
|
216
|
-
"project_path": {"type": "string", "description": "Filter by project path"},
|
|
217
|
-
"limit": {"type": "integer", "description": "Max sessions", "default": 20},
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
),
|
|
221
|
-
Tool(
|
|
222
|
-
name="session_read",
|
|
223
|
-
description="Read messages from a Claude Code session.",
|
|
224
|
-
inputSchema={
|
|
225
|
-
"type": "object",
|
|
226
|
-
"properties": {
|
|
227
|
-
"session_id": {"type": "string", "description": "Session ID"},
|
|
228
|
-
"limit": {"type": "integer", "description": "Max messages"},
|
|
229
|
-
},
|
|
230
|
-
"required": ["session_id"],
|
|
231
|
-
},
|
|
232
|
-
),
|
|
233
|
-
Tool(
|
|
234
|
-
name="session_search",
|
|
235
|
-
description="Search across Claude Code session messages.",
|
|
236
|
-
inputSchema={
|
|
237
|
-
"type": "object",
|
|
238
|
-
"properties": {
|
|
239
|
-
"query": {"type": "string", "description": "Search query"},
|
|
240
|
-
"session_id": {"type": "string", "description": "Search in specific session"},
|
|
241
|
-
"limit": {"type": "integer", "description": "Max results", "default": 20},
|
|
242
|
-
},
|
|
243
|
-
"required": ["query"],
|
|
244
|
-
},
|
|
245
|
-
),
|
|
246
|
-
Tool(
|
|
247
|
-
name="skill_list",
|
|
248
|
-
description="List available Claude Code skills/commands from .claude/commands/.",
|
|
249
|
-
inputSchema={
|
|
250
|
-
"type": "object",
|
|
251
|
-
"properties": {
|
|
252
|
-
"project_path": {"type": "string", "description": "Project directory"},
|
|
253
|
-
},
|
|
254
|
-
},
|
|
255
|
-
),
|
|
256
|
-
Tool(
|
|
257
|
-
name="skill_get",
|
|
258
|
-
description="Get the content of a specific skill/command.",
|
|
259
|
-
inputSchema={
|
|
260
|
-
"type": "object",
|
|
261
|
-
"properties": {
|
|
262
|
-
"name": {"type": "string", "description": "Skill name"},
|
|
263
|
-
"project_path": {"type": "string", "description": "Project directory"},
|
|
264
|
-
},
|
|
265
|
-
"required": ["name"],
|
|
266
|
-
},
|
|
267
|
-
),
|
|
268
|
-
Tool(
|
|
269
|
-
name="task_spawn",
|
|
270
|
-
description=(
|
|
271
|
-
"Spawn a background task to execute a prompt asynchronously. "
|
|
272
|
-
"Returns a Task ID. Best for deep research or parallel processing."
|
|
273
|
-
),
|
|
274
|
-
inputSchema={
|
|
275
|
-
"type": "object",
|
|
276
|
-
"properties": {
|
|
277
|
-
"prompt": {"type": "string", "description": "The prompt for the background agent"},
|
|
278
|
-
"model": {
|
|
279
|
-
"type": "string",
|
|
280
|
-
"description": "Model to use (gemini-3-flash or gpt-5.2)",
|
|
281
|
-
"default": "gemini-3-flash"
|
|
282
|
-
},
|
|
283
|
-
},
|
|
284
|
-
"required": ["prompt"],
|
|
285
|
-
},
|
|
286
|
-
),
|
|
287
|
-
Tool(
|
|
288
|
-
name="task_status",
|
|
289
|
-
description="Check the status and retrieve results of a background task.",
|
|
290
|
-
inputSchema={
|
|
291
|
-
"type": "object",
|
|
292
|
-
"properties": {
|
|
293
|
-
"task_id": {"type": "string", "description": "The ID of the task to check"},
|
|
294
|
-
},
|
|
295
|
-
"required": ["task_id"],
|
|
296
|
-
},
|
|
297
|
-
),
|
|
298
|
-
Tool(
|
|
299
|
-
name="task_list",
|
|
300
|
-
description="List all active and recent background tasks.",
|
|
301
|
-
inputSchema={
|
|
302
|
-
"type": "object",
|
|
303
|
-
"properties": {},
|
|
304
|
-
},
|
|
305
|
-
),
|
|
306
|
-
# New Agent Tools with Full Tool Access
|
|
307
|
-
Tool(
|
|
308
|
-
name="agent_spawn",
|
|
309
|
-
description=(
|
|
310
|
-
"Spawn a background agent. Uses Gemini by default for fast execution. "
|
|
311
|
-
"Set model='claude' to use Claude Code CLI with full tool access."
|
|
312
|
-
),
|
|
313
|
-
inputSchema={
|
|
314
|
-
"type": "object",
|
|
315
|
-
"properties": {
|
|
316
|
-
"prompt": {"type": "string", "description": "The task for the agent to perform"},
|
|
317
|
-
"agent_type": {
|
|
318
|
-
"type": "string",
|
|
319
|
-
"description": "Agent type: explore, dewey, frontend, delphi",
|
|
320
|
-
"default": "explore",
|
|
321
|
-
},
|
|
322
|
-
"description": {"type": "string", "description": "Short description for status display"},
|
|
323
|
-
"model": {
|
|
324
|
-
"type": "string",
|
|
325
|
-
"description": "Model: gemini-3-flash (default) or claude",
|
|
326
|
-
"default": "gemini-3-flash",
|
|
327
|
-
},
|
|
328
|
-
"thinking_budget": {
|
|
329
|
-
"type": "integer",
|
|
330
|
-
"description": "Tokens reserved for internal reasoning (if model supports it)",
|
|
331
|
-
"default": 0,
|
|
332
|
-
},
|
|
333
|
-
},
|
|
334
|
-
"required": ["prompt"],
|
|
335
|
-
},
|
|
336
|
-
),
|
|
337
|
-
Tool(
|
|
338
|
-
name="agent_output",
|
|
339
|
-
description="Get output from a background agent. Use block=true to wait for completion.",
|
|
340
|
-
inputSchema={
|
|
341
|
-
"type": "object",
|
|
342
|
-
"properties": {
|
|
343
|
-
"task_id": {"type": "string", "description": "The agent task ID"},
|
|
344
|
-
"block": {"type": "boolean", "description": "Wait for completion", "default": False},
|
|
345
|
-
},
|
|
346
|
-
"required": ["task_id"],
|
|
347
|
-
},
|
|
348
|
-
),
|
|
349
|
-
Tool(
|
|
350
|
-
name="agent_cancel",
|
|
351
|
-
description="Cancel a running background agent.",
|
|
352
|
-
inputSchema={
|
|
353
|
-
"type": "object",
|
|
354
|
-
"properties": {
|
|
355
|
-
"task_id": {"type": "string", "description": "The agent task ID to cancel"},
|
|
356
|
-
},
|
|
357
|
-
"required": ["task_id"],
|
|
358
|
-
},
|
|
359
|
-
),
|
|
360
|
-
Tool(
|
|
361
|
-
name="agent_list",
|
|
362
|
-
description="List all background agent tasks with their status.",
|
|
363
|
-
inputSchema={
|
|
364
|
-
"type": "object",
|
|
365
|
-
"properties": {},
|
|
366
|
-
},
|
|
367
|
-
),
|
|
368
|
-
Tool(
|
|
369
|
-
name="agent_progress",
|
|
370
|
-
description="Get real-time progress from a running background agent. Shows recent output lines to monitor what the agent is doing.",
|
|
371
|
-
inputSchema={
|
|
372
|
-
"type": "object",
|
|
373
|
-
"properties": {
|
|
374
|
-
"task_id": {"type": "string", "description": "The agent task ID"},
|
|
375
|
-
"lines": {"type": "integer", "description": "Number of recent lines to show", "default": 20},
|
|
376
|
-
},
|
|
377
|
-
"required": ["task_id"],
|
|
378
|
-
},
|
|
379
|
-
),
|
|
380
|
-
# LSP Tools
|
|
381
|
-
Tool(
|
|
382
|
-
name="lsp_hover",
|
|
383
|
-
description="Get type info, documentation, and signature at a position in a file.",
|
|
384
|
-
inputSchema={
|
|
385
|
-
"type": "object",
|
|
386
|
-
"properties": {
|
|
387
|
-
"file_path": {"type": "string", "description": "Absolute path to the file"},
|
|
388
|
-
"line": {"type": "integer", "description": "Line number (1-indexed)"},
|
|
389
|
-
"character": {"type": "integer", "description": "Character position (0-indexed)"},
|
|
390
|
-
},
|
|
391
|
-
"required": ["file_path", "line", "character"],
|
|
392
|
-
},
|
|
393
|
-
),
|
|
394
|
-
Tool(
|
|
395
|
-
name="lsp_goto_definition",
|
|
396
|
-
description="Find where a symbol is defined. Jump to symbol definition.",
|
|
397
|
-
inputSchema={
|
|
398
|
-
"type": "object",
|
|
399
|
-
"properties": {
|
|
400
|
-
"file_path": {"type": "string", "description": "Absolute path to the file"},
|
|
401
|
-
"line": {"type": "integer", "description": "Line number (1-indexed)"},
|
|
402
|
-
"character": {"type": "integer", "description": "Character position (0-indexed)"},
|
|
403
|
-
},
|
|
404
|
-
"required": ["file_path", "line", "character"],
|
|
405
|
-
},
|
|
406
|
-
),
|
|
407
|
-
Tool(
|
|
408
|
-
name="lsp_find_references",
|
|
409
|
-
description="Find all references to a symbol across the workspace.",
|
|
410
|
-
inputSchema={
|
|
411
|
-
"type": "object",
|
|
412
|
-
"properties": {
|
|
413
|
-
"file_path": {"type": "string", "description": "Absolute path to the file"},
|
|
414
|
-
"line": {"type": "integer", "description": "Line number (1-indexed)"},
|
|
415
|
-
"character": {"type": "integer", "description": "Character position (0-indexed)"},
|
|
416
|
-
"include_declaration": {"type": "boolean", "description": "Include the declaration itself", "default": True},
|
|
417
|
-
},
|
|
418
|
-
"required": ["file_path", "line", "character"],
|
|
419
|
-
},
|
|
420
|
-
),
|
|
421
|
-
Tool(
|
|
422
|
-
name="lsp_document_symbols",
|
|
423
|
-
description="Get hierarchical outline of all symbols (functions, classes, methods) in a file.",
|
|
424
|
-
inputSchema={
|
|
425
|
-
"type": "object",
|
|
426
|
-
"properties": {
|
|
427
|
-
"file_path": {"type": "string", "description": "Absolute path to the file"},
|
|
428
|
-
},
|
|
429
|
-
"required": ["file_path"],
|
|
430
|
-
},
|
|
431
|
-
),
|
|
432
|
-
Tool(
|
|
433
|
-
name="lsp_workspace_symbols",
|
|
434
|
-
description="Search for symbols by name across the entire workspace.",
|
|
435
|
-
inputSchema={
|
|
436
|
-
"type": "object",
|
|
437
|
-
"properties": {
|
|
438
|
-
"query": {"type": "string", "description": "Symbol name to search for (fuzzy match)"},
|
|
439
|
-
"directory": {"type": "string", "description": "Workspace directory", "default": "."},
|
|
440
|
-
},
|
|
441
|
-
"required": ["query"],
|
|
442
|
-
},
|
|
443
|
-
),
|
|
444
|
-
Tool(
|
|
445
|
-
name="lsp_prepare_rename",
|
|
446
|
-
description="Check if a symbol at position can be renamed. Use before lsp_rename.",
|
|
447
|
-
inputSchema={
|
|
448
|
-
"type": "object",
|
|
449
|
-
"properties": {
|
|
450
|
-
"file_path": {"type": "string", "description": "Absolute path to the file"},
|
|
451
|
-
"line": {"type": "integer", "description": "Line number (1-indexed)"},
|
|
452
|
-
"character": {"type": "integer", "description": "Character position (0-indexed)"},
|
|
453
|
-
},
|
|
454
|
-
"required": ["file_path", "line", "character"],
|
|
455
|
-
},
|
|
456
|
-
),
|
|
457
|
-
Tool(
|
|
458
|
-
name="lsp_rename",
|
|
459
|
-
description="Rename a symbol across the workspace. Use lsp_prepare_rename first to validate.",
|
|
460
|
-
inputSchema={
|
|
461
|
-
"type": "object",
|
|
462
|
-
"properties": {
|
|
463
|
-
"file_path": {"type": "string", "description": "Absolute path to the file"},
|
|
464
|
-
"line": {"type": "integer", "description": "Line number (1-indexed)"},
|
|
465
|
-
"character": {"type": "integer", "description": "Character position (0-indexed)"},
|
|
466
|
-
"new_name": {"type": "string", "description": "New name for the symbol"},
|
|
467
|
-
"dry_run": {"type": "boolean", "description": "Preview changes without applying", "default": True},
|
|
468
|
-
},
|
|
469
|
-
"required": ["file_path", "line", "character", "new_name"],
|
|
470
|
-
},
|
|
471
|
-
),
|
|
472
|
-
Tool(
|
|
473
|
-
name="lsp_code_actions",
|
|
474
|
-
description="Get available quick fixes and refactorings at a position.",
|
|
475
|
-
inputSchema={
|
|
476
|
-
"type": "object",
|
|
477
|
-
"properties": {
|
|
478
|
-
"file_path": {"type": "string", "description": "Absolute path to the file"},
|
|
479
|
-
"line": {"type": "integer", "description": "Line number (1-indexed)"},
|
|
480
|
-
"character": {"type": "integer", "description": "Character position (0-indexed)"},
|
|
481
|
-
},
|
|
482
|
-
"required": ["file_path", "line", "character"],
|
|
483
|
-
},
|
|
484
|
-
),
|
|
485
|
-
Tool(
|
|
486
|
-
name="lsp_servers",
|
|
487
|
-
description="List available LSP servers and their installation status.",
|
|
488
|
-
inputSchema={
|
|
489
|
-
"type": "object",
|
|
490
|
-
"properties": {},
|
|
491
|
-
},
|
|
492
|
-
),
|
|
493
|
-
Tool(
|
|
494
|
-
name="ast_grep_replace",
|
|
495
|
-
description="Replace code patterns using ast-grep's AST-aware replacement. More reliable than text-based replace for refactoring.",
|
|
496
|
-
inputSchema={
|
|
497
|
-
"type": "object",
|
|
498
|
-
"properties": {
|
|
499
|
-
"pattern": {"type": "string", "description": "ast-grep pattern to search (e.g., 'console.log($A)')"},
|
|
500
|
-
"replacement": {"type": "string", "description": "Replacement pattern (e.g., 'logger.debug($A)')"},
|
|
501
|
-
"directory": {"type": "string", "description": "Directory to search in", "default": "."},
|
|
502
|
-
"language": {"type": "string", "description": "Filter by language (typescript, python, etc.)"},
|
|
503
|
-
"dry_run": {"type": "boolean", "description": "Preview changes without applying", "default": True},
|
|
504
|
-
},
|
|
505
|
-
"required": ["pattern", "replacement"],
|
|
506
|
-
},
|
|
507
|
-
),
|
|
508
|
-
]
|
|
509
|
-
return tools
|
|
510
|
-
|
|
85
|
+
"""List available tools (metadata only)."""
|
|
86
|
+
from .server_tools import get_tool_definitions
|
|
87
|
+
return get_tool_definitions()
|
|
511
88
|
|
|
512
89
|
@server.call_tool()
|
|
513
90
|
async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
|
|
514
|
-
"""Handle tool
|
|
515
|
-
logger.info(f"Tool
|
|
91
|
+
"""Handle tool calls with deep lazy loading of implementations."""
|
|
92
|
+
logger.info(f"Tool call: {name}")
|
|
93
|
+
hook_manager = get_hook_manager_lazy()
|
|
94
|
+
token_store = get_token_store()
|
|
516
95
|
|
|
517
96
|
try:
|
|
97
|
+
# Pre-tool call hooks orchestration
|
|
98
|
+
arguments = await hook_manager.execute_pre_tool_call(name, arguments)
|
|
99
|
+
|
|
100
|
+
result_content = None
|
|
101
|
+
|
|
102
|
+
# --- MODEL DISPATCH ---
|
|
518
103
|
if name == "invoke_gemini":
|
|
519
|
-
|
|
104
|
+
from .tools.model_invoke import invoke_gemini
|
|
105
|
+
result_content = await invoke_gemini(
|
|
520
106
|
token_store=token_store,
|
|
521
107
|
prompt=arguments["prompt"],
|
|
522
|
-
model=arguments.get("model", "gemini-
|
|
108
|
+
model=arguments.get("model", "gemini-2.0-flash-exp"),
|
|
523
109
|
temperature=arguments.get("temperature", 0.7),
|
|
524
|
-
max_tokens=arguments.get("max_tokens",
|
|
110
|
+
max_tokens=arguments.get("max_tokens", 8192),
|
|
525
111
|
thinking_budget=arguments.get("thinking_budget", 0),
|
|
526
112
|
)
|
|
527
|
-
return [TextContent(type="text", text=result)]
|
|
528
113
|
|
|
529
114
|
elif name == "invoke_openai":
|
|
530
|
-
|
|
115
|
+
from .tools.model_invoke import invoke_openai
|
|
116
|
+
result_content = await invoke_openai(
|
|
531
117
|
token_store=token_store,
|
|
532
118
|
prompt=arguments["prompt"],
|
|
533
|
-
model=arguments.get("model", "gpt-
|
|
119
|
+
model=arguments.get("model", "gpt-4o"),
|
|
534
120
|
temperature=arguments.get("temperature", 0.7),
|
|
535
121
|
max_tokens=arguments.get("max_tokens", 4096),
|
|
536
122
|
thinking_budget=arguments.get("thinking_budget", 0),
|
|
537
123
|
)
|
|
538
|
-
return [TextContent(type="text", text=result)]
|
|
539
124
|
|
|
125
|
+
# --- CONTEXT DISPATCH ---
|
|
540
126
|
elif name == "get_project_context":
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
)
|
|
544
|
-
return [TextContent(type="text", text=result)]
|
|
127
|
+
from .tools.project_context import get_project_context
|
|
128
|
+
result_content = await get_project_context(project_path=arguments.get("project_path"))
|
|
545
129
|
|
|
546
130
|
elif name == "get_system_health":
|
|
547
|
-
|
|
548
|
-
|
|
131
|
+
from .tools.project_context import get_system_health
|
|
132
|
+
result_content = await get_system_health()
|
|
549
133
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
134
|
+
# --- SEARCH DISPATCH ---
|
|
135
|
+
elif name == "grep_search":
|
|
136
|
+
from .tools.code_search import grep_search
|
|
137
|
+
result_content = await grep_search(
|
|
138
|
+
pattern=arguments["pattern"],
|
|
139
|
+
directory=arguments.get("directory", "."),
|
|
140
|
+
file_pattern=arguments.get("file_pattern", ""),
|
|
554
141
|
)
|
|
555
|
-
return [TextContent(type="text", text=result)]
|
|
556
142
|
|
|
557
143
|
elif name == "ast_grep_search":
|
|
558
|
-
|
|
144
|
+
from .tools.code_search import ast_grep_search
|
|
145
|
+
result_content = await ast_grep_search(
|
|
559
146
|
pattern=arguments["pattern"],
|
|
560
147
|
directory=arguments.get("directory", "."),
|
|
561
148
|
language=arguments.get("language", ""),
|
|
562
149
|
)
|
|
563
|
-
return [TextContent(type="text", text=result)]
|
|
564
150
|
|
|
565
|
-
elif name == "
|
|
566
|
-
|
|
151
|
+
elif name == "ast_grep_replace":
|
|
152
|
+
from .tools.code_search import ast_grep_replace
|
|
153
|
+
result_content = await ast_grep_replace(
|
|
567
154
|
pattern=arguments["pattern"],
|
|
155
|
+
replacement=arguments["replacement"],
|
|
568
156
|
directory=arguments.get("directory", "."),
|
|
569
|
-
|
|
157
|
+
language=arguments.get("language", ""),
|
|
158
|
+
dry_run=arguments.get("dry_run", True),
|
|
570
159
|
)
|
|
571
|
-
return [TextContent(type="text", text=result)]
|
|
572
160
|
|
|
573
161
|
elif name == "glob_files":
|
|
574
|
-
|
|
162
|
+
from .tools.code_search import glob_files
|
|
163
|
+
result_content = await glob_files(
|
|
575
164
|
pattern=arguments["pattern"],
|
|
576
165
|
directory=arguments.get("directory", "."),
|
|
577
166
|
)
|
|
578
|
-
return [TextContent(type="text", text=result)]
|
|
579
167
|
|
|
168
|
+
# --- SESSION DISPATCH ---
|
|
580
169
|
elif name == "session_list":
|
|
581
|
-
|
|
170
|
+
from .tools.session_manager import list_sessions
|
|
171
|
+
result_content = list_sessions(
|
|
582
172
|
project_path=arguments.get("project_path"),
|
|
583
173
|
limit=arguments.get("limit", 20),
|
|
584
174
|
)
|
|
585
|
-
return [TextContent(type="text", text=result)]
|
|
586
175
|
|
|
587
176
|
elif name == "session_read":
|
|
588
|
-
|
|
177
|
+
from .tools.session_manager import read_session
|
|
178
|
+
result_content = read_session(
|
|
589
179
|
session_id=arguments["session_id"],
|
|
590
180
|
limit=arguments.get("limit"),
|
|
591
181
|
)
|
|
592
|
-
return [TextContent(type="text", text=result)]
|
|
593
182
|
|
|
594
183
|
elif name == "session_search":
|
|
595
|
-
|
|
184
|
+
from .tools.session_manager import search_sessions
|
|
185
|
+
result_content = search_sessions(
|
|
596
186
|
query=arguments["query"],
|
|
597
187
|
session_id=arguments.get("session_id"),
|
|
598
188
|
limit=arguments.get("limit", 20),
|
|
599
189
|
)
|
|
600
|
-
return [TextContent(type="text", text=result)]
|
|
601
190
|
|
|
191
|
+
# --- SKILL DISPATCH ---
|
|
602
192
|
elif name == "skill_list":
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
)
|
|
606
|
-
return [TextContent(type="text", text=result)]
|
|
193
|
+
from .tools.skill_loader import list_skills
|
|
194
|
+
result_content = list_skills(project_path=arguments.get("project_path"))
|
|
607
195
|
|
|
608
196
|
elif name == "skill_get":
|
|
609
|
-
|
|
197
|
+
from .tools.skill_loader import get_skill
|
|
198
|
+
result_content = get_skill(
|
|
610
199
|
name=arguments["name"],
|
|
611
200
|
project_path=arguments.get("project_path"),
|
|
612
201
|
)
|
|
613
|
-
return [TextContent(type="text", text=result)]
|
|
614
|
-
|
|
615
|
-
elif name == "task_spawn":
|
|
616
|
-
result = await task_spawn(
|
|
617
|
-
prompt=arguments["prompt"],
|
|
618
|
-
model=arguments.get("model", "gemini-3-flash"),
|
|
619
|
-
)
|
|
620
|
-
return [TextContent(type="text", text=result)]
|
|
621
202
|
|
|
622
|
-
|
|
623
|
-
result = await task_status(
|
|
624
|
-
task_id=arguments["task_id"],
|
|
625
|
-
)
|
|
626
|
-
return [TextContent(type="text", text=result)]
|
|
627
|
-
|
|
628
|
-
elif name == "task_list":
|
|
629
|
-
result = await task_list()
|
|
630
|
-
return [TextContent(type="text", text=result)]
|
|
631
|
-
|
|
632
|
-
# Agent tools with full tool access
|
|
203
|
+
# --- AGENT DISPATCH ---
|
|
633
204
|
elif name == "agent_spawn":
|
|
634
|
-
|
|
205
|
+
from .tools.agent_manager import get_agent_manager
|
|
206
|
+
manager = get_agent_manager()
|
|
207
|
+
result_content = await manager.spawn(
|
|
208
|
+
token_store=token_store,
|
|
635
209
|
prompt=arguments["prompt"],
|
|
636
210
|
agent_type=arguments.get("agent_type", "explore"),
|
|
637
211
|
description=arguments.get("description", ""),
|
|
212
|
+
parent_session_id=arguments.get("parent_session_id"),
|
|
213
|
+
system_prompt=arguments.get("system_prompt"),
|
|
638
214
|
model=arguments.get("model", "gemini-3-flash"),
|
|
639
215
|
thinking_budget=arguments.get("thinking_budget", 0),
|
|
216
|
+
timeout=arguments.get("timeout", 300),
|
|
640
217
|
)
|
|
641
|
-
return [TextContent(type="text", text=result)]
|
|
642
218
|
|
|
643
219
|
elif name == "agent_output":
|
|
644
|
-
|
|
220
|
+
from .tools.agent_manager import agent_output
|
|
221
|
+
result_content = await agent_output(
|
|
645
222
|
task_id=arguments["task_id"],
|
|
646
223
|
block=arguments.get("block", False),
|
|
647
224
|
)
|
|
648
|
-
return [TextContent(type="text", text=result)]
|
|
649
225
|
|
|
650
226
|
elif name == "agent_cancel":
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
)
|
|
654
|
-
return [TextContent(type="text", text=result)]
|
|
227
|
+
from .tools.agent_manager import agent_cancel
|
|
228
|
+
result_content = await agent_cancel(task_id=arguments["task_id"])
|
|
655
229
|
|
|
656
230
|
elif name == "agent_list":
|
|
657
|
-
|
|
658
|
-
|
|
231
|
+
from .tools.agent_manager import agent_list
|
|
232
|
+
result_content = await agent_list()
|
|
659
233
|
|
|
660
234
|
elif name == "agent_progress":
|
|
661
|
-
|
|
235
|
+
from .tools.agent_manager import agent_progress
|
|
236
|
+
result_content = await agent_progress(
|
|
662
237
|
task_id=arguments["task_id"],
|
|
663
238
|
lines=arguments.get("lines", 20),
|
|
664
239
|
)
|
|
665
|
-
return [TextContent(type="text", text=result)]
|
|
666
240
|
|
|
667
|
-
|
|
241
|
+
elif name == "agent_retry":
|
|
242
|
+
from .tools.agent_manager import agent_retry
|
|
243
|
+
result_content = await agent_retry(
|
|
244
|
+
task_id=arguments["task_id"],
|
|
245
|
+
new_prompt=arguments.get("new_prompt"),
|
|
246
|
+
new_timeout=arguments.get("new_timeout"),
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# --- BACKGROUND TASK DISPATCH ---
|
|
250
|
+
elif name == "task_spawn":
|
|
251
|
+
from .tools.background_tasks import task_spawn
|
|
252
|
+
result_content = await task_spawn(
|
|
253
|
+
prompt=arguments["prompt"],
|
|
254
|
+
model=arguments.get("model", "gemini-3-flash"),
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
elif name == "task_status":
|
|
258
|
+
from .tools.background_tasks import task_status
|
|
259
|
+
result_content = await task_status(task_id=arguments["task_id"])
|
|
260
|
+
|
|
261
|
+
elif name == "task_list":
|
|
262
|
+
from .tools.background_tasks import task_list
|
|
263
|
+
result_content = await task_list()
|
|
264
|
+
|
|
265
|
+
# --- LSP DISPATCH ---
|
|
668
266
|
elif name == "lsp_hover":
|
|
669
|
-
|
|
267
|
+
from .tools.lsp import lsp_hover
|
|
268
|
+
result_content = await lsp_hover(
|
|
670
269
|
file_path=arguments["file_path"],
|
|
671
270
|
line=arguments["line"],
|
|
672
271
|
character=arguments["character"],
|
|
673
272
|
)
|
|
674
|
-
return [TextContent(type="text", text=result)]
|
|
675
273
|
|
|
676
274
|
elif name == "lsp_goto_definition":
|
|
677
|
-
|
|
275
|
+
from .tools.lsp import lsp_goto_definition
|
|
276
|
+
result_content = await lsp_goto_definition(
|
|
678
277
|
file_path=arguments["file_path"],
|
|
679
278
|
line=arguments["line"],
|
|
680
279
|
character=arguments["character"],
|
|
681
280
|
)
|
|
682
|
-
return [TextContent(type="text", text=result)]
|
|
683
281
|
|
|
684
282
|
elif name == "lsp_find_references":
|
|
685
|
-
|
|
283
|
+
from .tools.lsp import lsp_find_references
|
|
284
|
+
result_content = await lsp_find_references(
|
|
686
285
|
file_path=arguments["file_path"],
|
|
687
286
|
line=arguments["line"],
|
|
688
287
|
character=arguments["character"],
|
|
689
288
|
include_declaration=arguments.get("include_declaration", True),
|
|
690
289
|
)
|
|
691
|
-
return [TextContent(type="text", text=result)]
|
|
692
290
|
|
|
693
291
|
elif name == "lsp_document_symbols":
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
)
|
|
697
|
-
return [TextContent(type="text", text=result)]
|
|
292
|
+
from .tools.lsp import lsp_document_symbols
|
|
293
|
+
result_content = await lsp_document_symbols(file_path=arguments["file_path"])
|
|
698
294
|
|
|
699
295
|
elif name == "lsp_workspace_symbols":
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
directory=arguments.get("directory", "."),
|
|
703
|
-
)
|
|
704
|
-
return [TextContent(type="text", text=result)]
|
|
296
|
+
from .tools.lsp import lsp_workspace_symbols
|
|
297
|
+
result_content = await lsp_workspace_symbols(query=arguments["query"])
|
|
705
298
|
|
|
706
299
|
elif name == "lsp_prepare_rename":
|
|
707
|
-
|
|
300
|
+
from .tools.lsp import lsp_prepare_rename
|
|
301
|
+
result_content = await lsp_prepare_rename(
|
|
708
302
|
file_path=arguments["file_path"],
|
|
709
303
|
line=arguments["line"],
|
|
710
304
|
character=arguments["character"],
|
|
711
305
|
)
|
|
712
|
-
return [TextContent(type="text", text=result)]
|
|
713
306
|
|
|
714
307
|
elif name == "lsp_rename":
|
|
715
|
-
|
|
308
|
+
from .tools.lsp import lsp_rename
|
|
309
|
+
result_content = await lsp_rename(
|
|
716
310
|
file_path=arguments["file_path"],
|
|
717
311
|
line=arguments["line"],
|
|
718
312
|
character=arguments["character"],
|
|
719
313
|
new_name=arguments["new_name"],
|
|
720
|
-
dry_run=arguments.get("dry_run", True),
|
|
721
314
|
)
|
|
722
|
-
return [TextContent(type="text", text=result)]
|
|
723
315
|
|
|
724
316
|
elif name == "lsp_code_actions":
|
|
725
|
-
|
|
317
|
+
from .tools.lsp import lsp_code_actions
|
|
318
|
+
result_content = await lsp_code_actions(
|
|
726
319
|
file_path=arguments["file_path"],
|
|
727
320
|
line=arguments["line"],
|
|
728
321
|
character=arguments["character"],
|
|
729
322
|
)
|
|
730
|
-
return [TextContent(type="text", text=result)]
|
|
731
323
|
|
|
732
324
|
elif name == "lsp_servers":
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
elif name == "ast_grep_replace":
|
|
737
|
-
result = await ast_grep_replace(
|
|
738
|
-
pattern=arguments["pattern"],
|
|
739
|
-
replacement=arguments["replacement"],
|
|
740
|
-
directory=arguments.get("directory", "."),
|
|
741
|
-
language=arguments.get("language", ""),
|
|
742
|
-
dry_run=arguments.get("dry_run", True),
|
|
743
|
-
)
|
|
744
|
-
return [TextContent(type="text", text=result)]
|
|
325
|
+
from .tools.lsp import lsp_servers
|
|
326
|
+
result_content = await lsp_servers()
|
|
745
327
|
|
|
746
328
|
else:
|
|
747
|
-
|
|
329
|
+
result_content = f"Unknown tool: {name}"
|
|
330
|
+
|
|
331
|
+
# Post-tool call hooks orchestration
|
|
332
|
+
if result_content is not None:
|
|
333
|
+
if isinstance(result_content, list) and len(result_content) > 0 and hasattr(result_content[0], "text"):
|
|
334
|
+
processed_text = await hook_manager.execute_post_tool_call(name, arguments, result_content[0].text)
|
|
335
|
+
result_content[0].text = processed_text
|
|
336
|
+
elif isinstance(result_content, str):
|
|
337
|
+
result_content = await hook_manager.execute_post_tool_call(name, arguments, result_content)
|
|
338
|
+
|
|
339
|
+
# Format final return as List[TextContent]
|
|
340
|
+
if isinstance(result_content, list):
|
|
341
|
+
return result_content
|
|
342
|
+
return [TextContent(type="text", text=str(result_content))]
|
|
748
343
|
|
|
749
344
|
except Exception as e:
|
|
750
|
-
logger.error(f"Error
|
|
345
|
+
logger.error(f"Error calling tool {name}: {e}")
|
|
751
346
|
return [TextContent(type="text", text=f"Error: {str(e)}")]
|
|
752
347
|
|
|
753
|
-
|
|
754
348
|
@server.list_prompts()
|
|
755
349
|
async def list_prompts() -> list[Prompt]:
|
|
756
|
-
"""List available
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
name="stravinsky",
|
|
760
|
-
description=(
|
|
761
|
-
"Stravinsky - Powerful AI orchestrator. "
|
|
762
|
-
"Plans obsessively with todos, assesses search complexity before "
|
|
763
|
-
"exploration, delegates strategically to specialized agents."
|
|
764
|
-
),
|
|
765
|
-
arguments=[],
|
|
766
|
-
),
|
|
767
|
-
Prompt(
|
|
768
|
-
name="delphi",
|
|
769
|
-
description=(
|
|
770
|
-
"Delphi - Strategic advisor using GPT for debugging, "
|
|
771
|
-
"architecture review, and complex problem solving."
|
|
772
|
-
),
|
|
773
|
-
arguments=[],
|
|
774
|
-
),
|
|
775
|
-
Prompt(
|
|
776
|
-
name="dewey",
|
|
777
|
-
description=(
|
|
778
|
-
"Dewey - Documentation and GitHub research specialist. "
|
|
779
|
-
"Finds implementation examples, official docs, and code patterns."
|
|
780
|
-
),
|
|
781
|
-
arguments=[],
|
|
782
|
-
),
|
|
783
|
-
Prompt(
|
|
784
|
-
name="explore",
|
|
785
|
-
description=(
|
|
786
|
-
"Explore - Fast codebase search specialist. "
|
|
787
|
-
"Answers 'Where is X?', finds files and code patterns."
|
|
788
|
-
),
|
|
789
|
-
arguments=[],
|
|
790
|
-
),
|
|
791
|
-
Prompt(
|
|
792
|
-
name="frontend",
|
|
793
|
-
description=(
|
|
794
|
-
"Frontend UI/UX Engineer - Designer-turned-developer for stunning visuals. "
|
|
795
|
-
"Excels at styling, layout, animation, typography."
|
|
796
|
-
),
|
|
797
|
-
arguments=[],
|
|
798
|
-
),
|
|
799
|
-
Prompt(
|
|
800
|
-
name="document_writer",
|
|
801
|
-
description=(
|
|
802
|
-
"Document Writer - Technical documentation specialist. "
|
|
803
|
-
"README files, API docs, architecture docs, user guides."
|
|
804
|
-
),
|
|
805
|
-
arguments=[],
|
|
806
|
-
),
|
|
807
|
-
Prompt(
|
|
808
|
-
name="multimodal",
|
|
809
|
-
description=(
|
|
810
|
-
"Multimodal Looker - Visual content analysis. "
|
|
811
|
-
"PDFs, images, diagrams - extracts and interprets visual data."
|
|
812
|
-
),
|
|
813
|
-
arguments=[],
|
|
814
|
-
),
|
|
815
|
-
]
|
|
816
|
-
|
|
350
|
+
"""List available prompts (metadata only)."""
|
|
351
|
+
from .server_tools import get_prompt_definitions
|
|
352
|
+
return get_prompt_definitions()
|
|
817
353
|
|
|
818
354
|
@server.get_prompt()
|
|
819
355
|
async def get_prompt(name: str, arguments: dict[str, str] | None) -> GetPromptResult:
|
|
820
|
-
"""Get a specific
|
|
356
|
+
"""Get a specific prompt content (lazy loaded)."""
|
|
357
|
+
from .prompts import stravinsky, delphi, dewey, explore, frontend, document_writer, multimodal
|
|
358
|
+
|
|
821
359
|
prompts_map = {
|
|
822
360
|
"stravinsky": ("Stravinsky orchestrator system prompt", stravinsky.get_stravinsky_prompt),
|
|
823
361
|
"delphi": ("Delphi advisor system prompt", delphi.get_delphi_prompt),
|
|
@@ -844,23 +382,42 @@ async def get_prompt(name: str, arguments: dict[str, str] | None) -> GetPromptRe
|
|
|
844
382
|
],
|
|
845
383
|
)
|
|
846
384
|
|
|
847
|
-
|
|
848
385
|
async def async_main():
|
|
849
|
-
"""
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
server.create_initialization_options(),
|
|
857
|
-
)
|
|
386
|
+
"""Server execution entry point."""
|
|
387
|
+
# Initialize hooks at runtime, not import time
|
|
388
|
+
try:
|
|
389
|
+
from .hooks import initialize_hooks
|
|
390
|
+
initialize_hooks()
|
|
391
|
+
except Exception as e:
|
|
392
|
+
logger.error(f"Failed to initialize hooks: {e}")
|
|
858
393
|
|
|
394
|
+
try:
|
|
395
|
+
async with stdio_server() as (read_stream, write_stream):
|
|
396
|
+
await server.run(
|
|
397
|
+
read_stream,
|
|
398
|
+
write_stream,
|
|
399
|
+
server.create_initialization_options(),
|
|
400
|
+
)
|
|
401
|
+
except Exception as e:
|
|
402
|
+
logger.critical("Server process crashed in async_main", exc_info=True)
|
|
403
|
+
sys.exit(1)
|
|
859
404
|
|
|
860
405
|
def main():
|
|
861
|
-
"""Synchronous
|
|
862
|
-
|
|
863
|
-
|
|
406
|
+
"""Synchronous entry point with CLI arg handling."""
|
|
407
|
+
import argparse
|
|
408
|
+
parser = argparse.ArgumentParser(description="Stravinsky MCP Bridge Server")
|
|
409
|
+
parser.add_argument("--version", action="version", version=f"stravinsky {__version__}")
|
|
410
|
+
|
|
411
|
+
# Check for CLI flags before starting async loop
|
|
412
|
+
args, unknown = parser.parse_known_args()
|
|
413
|
+
|
|
414
|
+
try:
|
|
415
|
+
asyncio.run(async_main())
|
|
416
|
+
except KeyboardInterrupt:
|
|
417
|
+
pass
|
|
418
|
+
except Exception as e:
|
|
419
|
+
logger.critical(f"Unhandled exception in main: {e}")
|
|
420
|
+
sys.exit(1)
|
|
864
421
|
|
|
865
422
|
if __name__ == "__main__":
|
|
866
423
|
main()
|