superqode 0.1.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 (288) hide show
  1. superqode/__init__.py +33 -0
  2. superqode/acp/__init__.py +23 -0
  3. superqode/acp/client.py +913 -0
  4. superqode/acp/permission_screen.py +457 -0
  5. superqode/acp/types.py +480 -0
  6. superqode/acp_discovery.py +856 -0
  7. superqode/agent/__init__.py +22 -0
  8. superqode/agent/edit_strategies.py +334 -0
  9. superqode/agent/loop.py +892 -0
  10. superqode/agent/qe_report_templates.py +39 -0
  11. superqode/agent/system_prompts.py +353 -0
  12. superqode/agent_output.py +721 -0
  13. superqode/agent_stream.py +953 -0
  14. superqode/agents/__init__.py +59 -0
  15. superqode/agents/acp_registry.py +305 -0
  16. superqode/agents/client.py +249 -0
  17. superqode/agents/data/augmentcode.com.toml +51 -0
  18. superqode/agents/data/cagent.dev.toml +51 -0
  19. superqode/agents/data/claude.com.toml +60 -0
  20. superqode/agents/data/codeassistant.dev.toml +51 -0
  21. superqode/agents/data/codex.openai.com.toml +57 -0
  22. superqode/agents/data/fastagent.ai.toml +66 -0
  23. superqode/agents/data/geminicli.com.toml +77 -0
  24. superqode/agents/data/goose.block.xyz.toml +54 -0
  25. superqode/agents/data/junie.jetbrains.com.toml +56 -0
  26. superqode/agents/data/kimi.moonshot.cn.toml +57 -0
  27. superqode/agents/data/llmlingagent.dev.toml +51 -0
  28. superqode/agents/data/molt.bot.toml +49 -0
  29. superqode/agents/data/opencode.ai.toml +60 -0
  30. superqode/agents/data/stakpak.dev.toml +51 -0
  31. superqode/agents/data/vtcode.dev.toml +51 -0
  32. superqode/agents/discovery.py +266 -0
  33. superqode/agents/messaging.py +160 -0
  34. superqode/agents/persona.py +166 -0
  35. superqode/agents/registry.py +421 -0
  36. superqode/agents/schema.py +72 -0
  37. superqode/agents/unified.py +367 -0
  38. superqode/app/__init__.py +111 -0
  39. superqode/app/constants.py +314 -0
  40. superqode/app/css.py +366 -0
  41. superqode/app/models.py +118 -0
  42. superqode/app/suggester.py +125 -0
  43. superqode/app/widgets.py +1591 -0
  44. superqode/app_enhanced.py +399 -0
  45. superqode/app_main.py +17187 -0
  46. superqode/approval.py +312 -0
  47. superqode/atomic.py +296 -0
  48. superqode/commands/__init__.py +1 -0
  49. superqode/commands/acp.py +965 -0
  50. superqode/commands/agents.py +180 -0
  51. superqode/commands/auth.py +278 -0
  52. superqode/commands/config.py +374 -0
  53. superqode/commands/init.py +826 -0
  54. superqode/commands/providers.py +819 -0
  55. superqode/commands/qe.py +1145 -0
  56. superqode/commands/roles.py +380 -0
  57. superqode/commands/serve.py +172 -0
  58. superqode/commands/suggestions.py +127 -0
  59. superqode/commands/superqe.py +460 -0
  60. superqode/config/__init__.py +51 -0
  61. superqode/config/loader.py +812 -0
  62. superqode/config/schema.py +498 -0
  63. superqode/core/__init__.py +111 -0
  64. superqode/core/roles.py +281 -0
  65. superqode/danger.py +386 -0
  66. superqode/data/superqode-template.yaml +1522 -0
  67. superqode/design_system.py +1080 -0
  68. superqode/dialogs/__init__.py +6 -0
  69. superqode/dialogs/base.py +39 -0
  70. superqode/dialogs/model.py +130 -0
  71. superqode/dialogs/provider.py +870 -0
  72. superqode/diff_view.py +919 -0
  73. superqode/enterprise.py +21 -0
  74. superqode/evaluation/__init__.py +25 -0
  75. superqode/evaluation/adapters.py +93 -0
  76. superqode/evaluation/behaviors.py +89 -0
  77. superqode/evaluation/engine.py +209 -0
  78. superqode/evaluation/scenarios.py +96 -0
  79. superqode/execution/__init__.py +36 -0
  80. superqode/execution/linter.py +538 -0
  81. superqode/execution/modes.py +347 -0
  82. superqode/execution/resolver.py +283 -0
  83. superqode/execution/runner.py +642 -0
  84. superqode/file_explorer.py +811 -0
  85. superqode/file_viewer.py +471 -0
  86. superqode/flash.py +183 -0
  87. superqode/guidance/__init__.py +58 -0
  88. superqode/guidance/config.py +203 -0
  89. superqode/guidance/prompts.py +71 -0
  90. superqode/harness/__init__.py +54 -0
  91. superqode/harness/accelerator.py +291 -0
  92. superqode/harness/config.py +319 -0
  93. superqode/harness/validator.py +147 -0
  94. superqode/history.py +279 -0
  95. superqode/integrations/superopt_runner.py +124 -0
  96. superqode/logging/__init__.py +49 -0
  97. superqode/logging/adapters.py +219 -0
  98. superqode/logging/formatter.py +923 -0
  99. superqode/logging/integration.py +341 -0
  100. superqode/logging/sinks.py +170 -0
  101. superqode/logging/unified_log.py +417 -0
  102. superqode/lsp/__init__.py +26 -0
  103. superqode/lsp/client.py +544 -0
  104. superqode/main.py +1069 -0
  105. superqode/mcp/__init__.py +89 -0
  106. superqode/mcp/auth_storage.py +380 -0
  107. superqode/mcp/client.py +1236 -0
  108. superqode/mcp/config.py +319 -0
  109. superqode/mcp/integration.py +337 -0
  110. superqode/mcp/oauth.py +436 -0
  111. superqode/mcp/oauth_callback.py +385 -0
  112. superqode/mcp/types.py +290 -0
  113. superqode/memory/__init__.py +31 -0
  114. superqode/memory/feedback.py +342 -0
  115. superqode/memory/store.py +522 -0
  116. superqode/notifications.py +369 -0
  117. superqode/optimization/__init__.py +5 -0
  118. superqode/optimization/config.py +33 -0
  119. superqode/permissions/__init__.py +25 -0
  120. superqode/permissions/rules.py +488 -0
  121. superqode/plan.py +323 -0
  122. superqode/providers/__init__.py +33 -0
  123. superqode/providers/gateway/__init__.py +165 -0
  124. superqode/providers/gateway/base.py +228 -0
  125. superqode/providers/gateway/litellm_gateway.py +1170 -0
  126. superqode/providers/gateway/openresponses_gateway.py +436 -0
  127. superqode/providers/health.py +297 -0
  128. superqode/providers/huggingface/__init__.py +74 -0
  129. superqode/providers/huggingface/downloader.py +472 -0
  130. superqode/providers/huggingface/endpoints.py +442 -0
  131. superqode/providers/huggingface/hub.py +531 -0
  132. superqode/providers/huggingface/inference.py +394 -0
  133. superqode/providers/huggingface/transformers_runner.py +516 -0
  134. superqode/providers/local/__init__.py +100 -0
  135. superqode/providers/local/base.py +438 -0
  136. superqode/providers/local/discovery.py +418 -0
  137. superqode/providers/local/lmstudio.py +256 -0
  138. superqode/providers/local/mlx.py +457 -0
  139. superqode/providers/local/ollama.py +486 -0
  140. superqode/providers/local/sglang.py +268 -0
  141. superqode/providers/local/tgi.py +260 -0
  142. superqode/providers/local/tool_support.py +477 -0
  143. superqode/providers/local/vllm.py +258 -0
  144. superqode/providers/manager.py +1338 -0
  145. superqode/providers/models.py +1016 -0
  146. superqode/providers/models_dev.py +578 -0
  147. superqode/providers/openresponses/__init__.py +87 -0
  148. superqode/providers/openresponses/converters/__init__.py +17 -0
  149. superqode/providers/openresponses/converters/messages.py +343 -0
  150. superqode/providers/openresponses/converters/tools.py +268 -0
  151. superqode/providers/openresponses/schema/__init__.py +56 -0
  152. superqode/providers/openresponses/schema/models.py +585 -0
  153. superqode/providers/openresponses/streaming/__init__.py +5 -0
  154. superqode/providers/openresponses/streaming/parser.py +338 -0
  155. superqode/providers/openresponses/tools/__init__.py +21 -0
  156. superqode/providers/openresponses/tools/apply_patch.py +352 -0
  157. superqode/providers/openresponses/tools/code_interpreter.py +290 -0
  158. superqode/providers/openresponses/tools/file_search.py +333 -0
  159. superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
  160. superqode/providers/registry.py +716 -0
  161. superqode/providers/usage.py +332 -0
  162. superqode/pure_mode.py +384 -0
  163. superqode/qr/__init__.py +23 -0
  164. superqode/qr/dashboard.py +781 -0
  165. superqode/qr/generator.py +1018 -0
  166. superqode/qr/templates.py +135 -0
  167. superqode/safety/__init__.py +41 -0
  168. superqode/safety/sandbox.py +413 -0
  169. superqode/safety/warnings.py +256 -0
  170. superqode/server/__init__.py +33 -0
  171. superqode/server/lsp_server.py +775 -0
  172. superqode/server/web.py +250 -0
  173. superqode/session/__init__.py +25 -0
  174. superqode/session/persistence.py +580 -0
  175. superqode/session/sharing.py +477 -0
  176. superqode/session.py +475 -0
  177. superqode/sidebar.py +2991 -0
  178. superqode/stream_view.py +648 -0
  179. superqode/styles/__init__.py +3 -0
  180. superqode/superqe/__init__.py +184 -0
  181. superqode/superqe/acp_runner.py +1064 -0
  182. superqode/superqe/constitution/__init__.py +62 -0
  183. superqode/superqe/constitution/evaluator.py +308 -0
  184. superqode/superqe/constitution/loader.py +432 -0
  185. superqode/superqe/constitution/schema.py +250 -0
  186. superqode/superqe/events.py +591 -0
  187. superqode/superqe/frameworks/__init__.py +65 -0
  188. superqode/superqe/frameworks/base.py +234 -0
  189. superqode/superqe/frameworks/e2e.py +263 -0
  190. superqode/superqe/frameworks/executor.py +237 -0
  191. superqode/superqe/frameworks/javascript.py +409 -0
  192. superqode/superqe/frameworks/python.py +373 -0
  193. superqode/superqe/frameworks/registry.py +92 -0
  194. superqode/superqe/mcp_tools/__init__.py +47 -0
  195. superqode/superqe/mcp_tools/core_tools.py +418 -0
  196. superqode/superqe/mcp_tools/registry.py +230 -0
  197. superqode/superqe/mcp_tools/testing_tools.py +167 -0
  198. superqode/superqe/noise.py +89 -0
  199. superqode/superqe/orchestrator.py +778 -0
  200. superqode/superqe/roles.py +609 -0
  201. superqode/superqe/session.py +713 -0
  202. superqode/superqe/skills/__init__.py +57 -0
  203. superqode/superqe/skills/base.py +106 -0
  204. superqode/superqe/skills/core_skills.py +899 -0
  205. superqode/superqe/skills/registry.py +90 -0
  206. superqode/superqe/verifier.py +101 -0
  207. superqode/superqe_cli.py +76 -0
  208. superqode/tool_call.py +358 -0
  209. superqode/tools/__init__.py +93 -0
  210. superqode/tools/agent_tools.py +496 -0
  211. superqode/tools/base.py +324 -0
  212. superqode/tools/batch_tool.py +133 -0
  213. superqode/tools/diagnostics.py +311 -0
  214. superqode/tools/edit_tools.py +653 -0
  215. superqode/tools/enhanced_base.py +515 -0
  216. superqode/tools/file_tools.py +269 -0
  217. superqode/tools/file_tracking.py +45 -0
  218. superqode/tools/lsp_tools.py +610 -0
  219. superqode/tools/network_tools.py +350 -0
  220. superqode/tools/permissions.py +400 -0
  221. superqode/tools/question_tool.py +324 -0
  222. superqode/tools/search_tools.py +598 -0
  223. superqode/tools/shell_tools.py +259 -0
  224. superqode/tools/todo_tools.py +121 -0
  225. superqode/tools/validation.py +80 -0
  226. superqode/tools/web_tools.py +639 -0
  227. superqode/tui.py +1152 -0
  228. superqode/tui_integration.py +875 -0
  229. superqode/tui_widgets/__init__.py +27 -0
  230. superqode/tui_widgets/widgets/__init__.py +18 -0
  231. superqode/tui_widgets/widgets/progress.py +185 -0
  232. superqode/tui_widgets/widgets/tool_display.py +188 -0
  233. superqode/undo_manager.py +574 -0
  234. superqode/utils/__init__.py +5 -0
  235. superqode/utils/error_handling.py +323 -0
  236. superqode/utils/fuzzy.py +257 -0
  237. superqode/widgets/__init__.py +477 -0
  238. superqode/widgets/agent_collab.py +390 -0
  239. superqode/widgets/agent_store.py +936 -0
  240. superqode/widgets/agent_switcher.py +395 -0
  241. superqode/widgets/animation_manager.py +284 -0
  242. superqode/widgets/code_context.py +356 -0
  243. superqode/widgets/command_palette.py +412 -0
  244. superqode/widgets/connection_status.py +537 -0
  245. superqode/widgets/conversation_history.py +470 -0
  246. superqode/widgets/diff_indicator.py +155 -0
  247. superqode/widgets/enhanced_status_bar.py +385 -0
  248. superqode/widgets/enhanced_toast.py +476 -0
  249. superqode/widgets/file_browser.py +809 -0
  250. superqode/widgets/file_reference.py +585 -0
  251. superqode/widgets/issue_timeline.py +340 -0
  252. superqode/widgets/leader_key.py +264 -0
  253. superqode/widgets/mode_switcher.py +445 -0
  254. superqode/widgets/model_picker.py +234 -0
  255. superqode/widgets/permission_preview.py +1205 -0
  256. superqode/widgets/prompt.py +358 -0
  257. superqode/widgets/provider_connect.py +725 -0
  258. superqode/widgets/pty_shell.py +587 -0
  259. superqode/widgets/qe_dashboard.py +321 -0
  260. superqode/widgets/resizable_sidebar.py +377 -0
  261. superqode/widgets/response_changes.py +218 -0
  262. superqode/widgets/response_display.py +528 -0
  263. superqode/widgets/rich_tool_display.py +613 -0
  264. superqode/widgets/sidebar_panels.py +1180 -0
  265. superqode/widgets/slash_complete.py +356 -0
  266. superqode/widgets/split_view.py +612 -0
  267. superqode/widgets/status_bar.py +273 -0
  268. superqode/widgets/superqode_display.py +786 -0
  269. superqode/widgets/thinking_display.py +815 -0
  270. superqode/widgets/throbber.py +87 -0
  271. superqode/widgets/toast.py +206 -0
  272. superqode/widgets/unified_output.py +1073 -0
  273. superqode/workspace/__init__.py +75 -0
  274. superqode/workspace/artifacts.py +472 -0
  275. superqode/workspace/coordinator.py +353 -0
  276. superqode/workspace/diff_tracker.py +429 -0
  277. superqode/workspace/git_guard.py +373 -0
  278. superqode/workspace/git_snapshot.py +526 -0
  279. superqode/workspace/manager.py +750 -0
  280. superqode/workspace/snapshot.py +357 -0
  281. superqode/workspace/watcher.py +535 -0
  282. superqode/workspace/worktree.py +440 -0
  283. superqode-0.1.5.dist-info/METADATA +204 -0
  284. superqode-0.1.5.dist-info/RECORD +288 -0
  285. superqode-0.1.5.dist-info/WHEEL +5 -0
  286. superqode-0.1.5.dist-info/entry_points.txt +3 -0
  287. superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
  288. superqode-0.1.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,385 @@
1
+ """
2
+ MCP OAuth Callback Server - Local HTTP Server for OAuth Redirects.
3
+
4
+ Implements a lightweight HTTP server to handle OAuth callbacks
5
+ during the authorization flow.
6
+
7
+ Features:
8
+ - Single-use callback handling
9
+ - Timeout support
10
+ - Success/error page display
11
+ - Thread-safe operation
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import asyncio
17
+ import logging
18
+ import urllib.parse
19
+ from dataclasses import dataclass, field
20
+ from http.server import HTTPServer, BaseHTTPRequestHandler
21
+ from typing import Dict, Optional
22
+ import threading
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ # HTML templates for callback responses
28
+ SUCCESS_HTML = """<!DOCTYPE html>
29
+ <html>
30
+ <head>
31
+ <title>Authorization Successful</title>
32
+ <style>
33
+ body {
34
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
35
+ display: flex;
36
+ justify-content: center;
37
+ align-items: center;
38
+ height: 100vh;
39
+ margin: 0;
40
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
41
+ }
42
+ .container {
43
+ text-align: center;
44
+ padding: 40px;
45
+ background: white;
46
+ border-radius: 12px;
47
+ box-shadow: 0 4px 20px rgba(0,0,0,0.15);
48
+ max-width: 400px;
49
+ }
50
+ .icon {
51
+ font-size: 64px;
52
+ margin-bottom: 20px;
53
+ }
54
+ h1 {
55
+ color: #22c55e;
56
+ margin: 0 0 10px 0;
57
+ font-size: 24px;
58
+ }
59
+ p {
60
+ color: #666;
61
+ margin: 0;
62
+ font-size: 14px;
63
+ }
64
+ .close-msg {
65
+ margin-top: 20px;
66
+ color: #999;
67
+ font-size: 12px;
68
+ }
69
+ </style>
70
+ </head>
71
+ <body>
72
+ <div class="container">
73
+ <div class="icon">✓</div>
74
+ <h1>Authorization Successful</h1>
75
+ <p>You have been authenticated with the MCP server.</p>
76
+ <p class="close-msg">You can close this window and return to SuperQode.</p>
77
+ </div>
78
+ <script>
79
+ // Try to close the window after a delay
80
+ setTimeout(function() {
81
+ window.close();
82
+ }, 3000);
83
+ </script>
84
+ </body>
85
+ </html>"""
86
+
87
+ ERROR_HTML = """<!DOCTYPE html>
88
+ <html>
89
+ <head>
90
+ <title>Authorization Failed</title>
91
+ <style>
92
+ body {
93
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
94
+ display: flex;
95
+ justify-content: center;
96
+ align-items: center;
97
+ height: 100vh;
98
+ margin: 0;
99
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
100
+ }
101
+ .container {
102
+ text-align: center;
103
+ padding: 40px;
104
+ background: white;
105
+ border-radius: 12px;
106
+ box-shadow: 0 4px 20px rgba(0,0,0,0.15);
107
+ max-width: 400px;
108
+ }
109
+ .icon {
110
+ font-size: 64px;
111
+ margin-bottom: 20px;
112
+ }
113
+ h1 {
114
+ color: #ef4444;
115
+ margin: 0 0 10px 0;
116
+ font-size: 24px;
117
+ }
118
+ p {
119
+ color: #666;
120
+ margin: 0;
121
+ font-size: 14px;
122
+ }
123
+ .error-detail {
124
+ margin-top: 15px;
125
+ padding: 10px;
126
+ background: #fef2f2;
127
+ border-radius: 6px;
128
+ color: #b91c1c;
129
+ font-size: 12px;
130
+ font-family: monospace;
131
+ }
132
+ </style>
133
+ </head>
134
+ <body>
135
+ <div class="container">
136
+ <div class="icon">✕</div>
137
+ <h1>Authorization Failed</h1>
138
+ <p>There was a problem authenticating with the MCP server.</p>
139
+ <div class="error-detail">{error}</div>
140
+ </div>
141
+ </body>
142
+ </html>"""
143
+
144
+
145
+ @dataclass
146
+ class CallbackResult:
147
+ """Result from an OAuth callback."""
148
+
149
+ code: Optional[str] = None
150
+ state: Optional[str] = None
151
+ error: Optional[str] = None
152
+ error_description: Optional[str] = None
153
+
154
+
155
+ class OAuthCallbackHandler(BaseHTTPRequestHandler):
156
+ """HTTP request handler for OAuth callbacks."""
157
+
158
+ # Class-level storage for callback results
159
+ callback_results: Dict[str, asyncio.Future] = {}
160
+ server_lock = threading.Lock()
161
+
162
+ def log_message(self, format: str, *args) -> None:
163
+ """Suppress default logging."""
164
+ logger.debug(format % args)
165
+
166
+ def do_GET(self) -> None:
167
+ """Handle GET request (OAuth callback)."""
168
+ # Parse the URL
169
+ parsed = urllib.parse.urlparse(self.path)
170
+
171
+ # Only handle the callback path
172
+ if parsed.path != "/mcp/oauth/callback":
173
+ self.send_error(404, "Not Found")
174
+ return
175
+
176
+ # Parse query parameters
177
+ params = urllib.parse.parse_qs(parsed.query)
178
+
179
+ # Extract OAuth parameters
180
+ code = params.get("code", [None])[0]
181
+ state = params.get("state", [None])[0]
182
+ error = params.get("error", [None])[0]
183
+ error_description = params.get("error_description", [None])[0]
184
+
185
+ # Create result
186
+ result = CallbackResult(
187
+ code=code,
188
+ state=state,
189
+ error=error,
190
+ error_description=error_description,
191
+ )
192
+
193
+ # Store result and notify waiting future
194
+ with self.server_lock:
195
+ if state and state in self.callback_results:
196
+ future = self.callback_results[state]
197
+ if not future.done():
198
+ # Use call_soon_threadsafe to set result from HTTP thread
199
+ try:
200
+ loop = future.get_loop()
201
+ loop.call_soon_threadsafe(future.set_result, result)
202
+ except Exception as e:
203
+ logger.error(f"Error setting callback result: {e}")
204
+
205
+ # Send response
206
+ if error:
207
+ self._send_error_page(error, error_description)
208
+ else:
209
+ self._send_success_page()
210
+
211
+ def _send_success_page(self) -> None:
212
+ """Send success HTML page."""
213
+ content = SUCCESS_HTML.encode("utf-8")
214
+ self.send_response(200)
215
+ self.send_header("Content-Type", "text/html; charset=utf-8")
216
+ self.send_header("Content-Length", str(len(content)))
217
+ self.end_headers()
218
+ self.wfile.write(content)
219
+
220
+ def _send_error_page(self, error: str, description: Optional[str]) -> None:
221
+ """Send error HTML page."""
222
+ error_msg = error
223
+ if description:
224
+ error_msg = f"{error}: {description}"
225
+
226
+ content = ERROR_HTML.format(error=error_msg).encode("utf-8")
227
+ self.send_response(400)
228
+ self.send_header("Content-Type", "text/html; charset=utf-8")
229
+ self.send_header("Content-Length", str(len(content)))
230
+ self.end_headers()
231
+ self.wfile.write(content)
232
+
233
+
234
+ class OAuthCallbackServer:
235
+ """
236
+ Local HTTP server for OAuth callbacks.
237
+
238
+ Runs a lightweight HTTP server on localhost to receive OAuth
239
+ authorization callbacks.
240
+
241
+ Usage:
242
+ server = OAuthCallbackServer()
243
+ await server.start()
244
+
245
+ # Start OAuth flow with state parameter
246
+ state = "random_state_value"
247
+
248
+ # Wait for callback
249
+ result = await server.wait_for_callback(state, timeout=300)
250
+
251
+ # result.code contains the authorization code
252
+
253
+ await server.stop()
254
+ """
255
+
256
+ DEFAULT_PORT = 19876
257
+
258
+ def __init__(self, port: int = DEFAULT_PORT):
259
+ self.port = port
260
+ self._server: Optional[HTTPServer] = None
261
+ self._server_thread: Optional[threading.Thread] = None
262
+ self._running = False
263
+
264
+ async def start(self) -> None:
265
+ """Start the callback server."""
266
+ if self._running:
267
+ return
268
+
269
+ # Create HTTP server
270
+ try:
271
+ self._server = HTTPServer(
272
+ ("localhost", self.port),
273
+ OAuthCallbackHandler,
274
+ )
275
+ self._server.timeout = 1 # Allow periodic checks
276
+
277
+ # Start server in background thread
278
+ self._running = True
279
+ self._server_thread = threading.Thread(
280
+ target=self._serve_forever,
281
+ daemon=True,
282
+ )
283
+ self._server_thread.start()
284
+
285
+ logger.info(f"OAuth callback server started on port {self.port}")
286
+
287
+ except OSError as e:
288
+ if "Address already in use" in str(e):
289
+ # Port in use, try next port
290
+ self.port += 1
291
+ await self.start()
292
+ else:
293
+ raise
294
+
295
+ def _serve_forever(self) -> None:
296
+ """Server loop running in background thread."""
297
+ while self._running:
298
+ self._server.handle_request()
299
+
300
+ async def stop(self) -> None:
301
+ """Stop the callback server."""
302
+ self._running = False
303
+
304
+ if self._server:
305
+ self._server.shutdown()
306
+ self._server = None
307
+
308
+ if self._server_thread:
309
+ self._server_thread.join(timeout=5)
310
+ self._server_thread = None
311
+
312
+ logger.info("OAuth callback server stopped")
313
+
314
+ async def wait_for_callback(
315
+ self,
316
+ state: str,
317
+ timeout: float = 300,
318
+ ) -> CallbackResult:
319
+ """
320
+ Wait for an OAuth callback with the given state.
321
+
322
+ Args:
323
+ state: The state parameter to wait for
324
+ timeout: Maximum time to wait in seconds
325
+
326
+ Returns:
327
+ CallbackResult with the authorization code or error
328
+ """
329
+ if not self._running:
330
+ await self.start()
331
+
332
+ # Create future for this state
333
+ loop = asyncio.get_event_loop()
334
+ future: asyncio.Future[CallbackResult] = loop.create_future()
335
+
336
+ with OAuthCallbackHandler.server_lock:
337
+ OAuthCallbackHandler.callback_results[state] = future
338
+
339
+ try:
340
+ result = await asyncio.wait_for(future, timeout=timeout)
341
+ return result
342
+ except asyncio.TimeoutError:
343
+ return CallbackResult(
344
+ error="timeout",
345
+ error_description="OAuth callback timed out",
346
+ )
347
+ finally:
348
+ # Clean up
349
+ with OAuthCallbackHandler.server_lock:
350
+ OAuthCallbackHandler.callback_results.pop(state, None)
351
+
352
+ def get_redirect_uri(self) -> str:
353
+ """Get the redirect URI for this server."""
354
+ return f"http://localhost:{self.port}/mcp/oauth/callback"
355
+
356
+ @property
357
+ def is_running(self) -> bool:
358
+ """Check if the server is running."""
359
+ return self._running
360
+
361
+
362
+ # Global server instance for shared use
363
+ _global_server: Optional[OAuthCallbackServer] = None
364
+
365
+
366
+ async def get_callback_server() -> OAuthCallbackServer:
367
+ """Get or create the global callback server."""
368
+ global _global_server
369
+
370
+ if _global_server is None:
371
+ _global_server = OAuthCallbackServer()
372
+
373
+ if not _global_server.is_running:
374
+ await _global_server.start()
375
+
376
+ return _global_server
377
+
378
+
379
+ async def shutdown_callback_server() -> None:
380
+ """Shutdown the global callback server."""
381
+ global _global_server
382
+
383
+ if _global_server is not None:
384
+ await _global_server.stop()
385
+ _global_server = None
superqode/mcp/types.py ADDED
@@ -0,0 +1,290 @@
1
+ """MCP type definitions for SuperQode.
2
+
3
+ This module defines the data types used for MCP tool, resource, and prompt
4
+ representations within SuperQode. Aligned with MCP protocol version 2025-03-26.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from enum import Enum
9
+ from typing import Any, Literal
10
+
11
+
12
+ class ServerCapability(Enum):
13
+ """Server capabilities that can be checked."""
14
+
15
+ EXPERIMENTAL = "experimental"
16
+ LOGGING = "logging"
17
+ PROMPTS = "prompts"
18
+ RESOURCES = "resources"
19
+ TOOLS = "tools"
20
+ COMPLETIONS = "completions"
21
+
22
+
23
+ class LoggingLevel(Enum):
24
+ """MCP logging levels."""
25
+
26
+ DEBUG = "debug"
27
+ INFO = "info"
28
+ NOTICE = "notice"
29
+ WARNING = "warning"
30
+ ERROR = "error"
31
+ CRITICAL = "critical"
32
+ ALERT = "alert"
33
+ EMERGENCY = "emergency"
34
+
35
+
36
+ @dataclass
37
+ class ToolAnnotations:
38
+ """Annotations providing hints about tool behavior.
39
+
40
+ Attributes:
41
+ title: Human-readable title for the tool
42
+ read_only_hint: If true, the tool does not modify its environment
43
+ destructive_hint: If true, the tool may perform destructive updates
44
+ idempotent_hint: If true, calling repeatedly has no additional effect
45
+ open_world_hint: If true, tool may interact with external entities
46
+ """
47
+
48
+ title: str | None = None
49
+ read_only_hint: bool | None = None
50
+ destructive_hint: bool | None = None
51
+ idempotent_hint: bool | None = None
52
+ open_world_hint: bool | None = None
53
+
54
+
55
+ @dataclass
56
+ class MCPTool:
57
+ """Represents an MCP tool available from a server.
58
+
59
+ Attributes:
60
+ name: The tool's unique name
61
+ description: Human-readable description of what the tool does
62
+ input_schema: JSON Schema defining the tool's input parameters
63
+ output_schema: Optional JSON Schema for tool output
64
+ server_id: ID of the server providing this tool
65
+ annotations: Optional hints about tool behavior
66
+ """
67
+
68
+ name: str
69
+ description: str
70
+ input_schema: dict[str, Any]
71
+ server_id: str
72
+ output_schema: dict[str, Any] | None = None
73
+ annotations: ToolAnnotations | None = None
74
+
75
+
76
+ @dataclass
77
+ class MCPResource:
78
+ """Represents an MCP resource available from a server.
79
+
80
+ Attributes:
81
+ uri: The resource's unique URI
82
+ name: Human-readable name for the resource
83
+ description: Optional description of the resource
84
+ mime_type: Optional MIME type of the resource content
85
+ server_id: ID of the server providing this resource
86
+ """
87
+
88
+ uri: str
89
+ name: str
90
+ description: str | None = None
91
+ mime_type: str | None = None
92
+ server_id: str = ""
93
+
94
+
95
+ @dataclass
96
+ class MCPResourceTemplate:
97
+ """Represents an MCP resource template for dynamic resources.
98
+
99
+ Attributes:
100
+ uri_template: URI template with placeholders
101
+ name: Human-readable name for the template
102
+ description: Optional description
103
+ mime_type: Optional MIME type of generated resources
104
+ server_id: ID of the server providing this template
105
+ """
106
+
107
+ uri_template: str
108
+ name: str
109
+ description: str | None = None
110
+ mime_type: str | None = None
111
+ server_id: str = ""
112
+
113
+
114
+ @dataclass
115
+ class MCPPromptArgument:
116
+ """Argument definition for an MCP prompt.
117
+
118
+ Attributes:
119
+ name: Argument name
120
+ description: Optional description
121
+ required: Whether the argument is required
122
+ """
123
+
124
+ name: str
125
+ description: str | None = None
126
+ required: bool = False
127
+
128
+
129
+ @dataclass
130
+ class MCPPrompt:
131
+ """Represents an MCP prompt template from a server.
132
+
133
+ Attributes:
134
+ name: The prompt's unique name
135
+ description: Human-readable description of the prompt
136
+ arguments: List of argument definitions for the prompt
137
+ server_id: ID of the server providing this prompt
138
+ """
139
+
140
+ name: str
141
+ description: str | None = None
142
+ arguments: list[MCPPromptArgument] = field(default_factory=list)
143
+ server_id: str = ""
144
+
145
+
146
+ @dataclass
147
+ class MCPToolResult:
148
+ """Result from executing an MCP tool.
149
+
150
+ Attributes:
151
+ content: The tool's output content (text, images, etc.)
152
+ is_error: Whether the tool execution resulted in an error
153
+ error_message: Error message if is_error is True
154
+ structured_content: Optional structured output data
155
+ """
156
+
157
+ content: list[dict[str, Any]]
158
+ is_error: bool = False
159
+ error_message: str | None = None
160
+ structured_content: dict[str, Any] | None = None
161
+
162
+
163
+ @dataclass
164
+ class MCPResourceContent:
165
+ """Content retrieved from an MCP resource.
166
+
167
+ Attributes:
168
+ uri: The resource URI
169
+ mime_type: MIME type of the content
170
+ text: Text content (if text-based)
171
+ blob: Binary content as base64 (if binary)
172
+ """
173
+
174
+ uri: str
175
+ mime_type: str | None = None
176
+ text: str | None = None
177
+ blob: str | None = None
178
+
179
+
180
+ @dataclass
181
+ class MCPPromptMessage:
182
+ """A message from an MCP prompt.
183
+
184
+ Attributes:
185
+ role: The message role (user, assistant)
186
+ content: The message content
187
+ """
188
+
189
+ role: Literal["user", "assistant"]
190
+ content: str | dict[str, Any]
191
+
192
+
193
+ @dataclass
194
+ class MCPPromptResult:
195
+ """Result from getting an MCP prompt.
196
+
197
+ Attributes:
198
+ description: Optional description of the prompt
199
+ messages: List of messages in the prompt
200
+ """
201
+
202
+ description: str | None = None
203
+ messages: list[MCPPromptMessage] = field(default_factory=list)
204
+
205
+
206
+ @dataclass
207
+ class MCPCompletionResult:
208
+ """Result from a completion request.
209
+
210
+ Attributes:
211
+ values: List of completion values
212
+ total: Total number of completions (if known)
213
+ has_more: Whether more completions are available
214
+ """
215
+
216
+ values: list[str]
217
+ total: int | None = None
218
+ has_more: bool | None = None
219
+
220
+
221
+ @dataclass
222
+ class MCPRoot:
223
+ """Represents a root directory for the MCP client.
224
+
225
+ Attributes:
226
+ uri: The root URI
227
+ name: Optional human-readable name
228
+ """
229
+
230
+ uri: str
231
+ name: str | None = None
232
+
233
+
234
+ @dataclass
235
+ class MCPServerCapabilities:
236
+ """Server capabilities reported during initialization.
237
+
238
+ Attributes:
239
+ experimental: Experimental capabilities
240
+ logging: Logging capability
241
+ prompts: Prompts capability with list_changed support
242
+ resources: Resources capability with subscribe and list_changed
243
+ tools: Tools capability with list_changed support
244
+ completions: Completions capability
245
+ """
246
+
247
+ experimental: dict[str, Any] | None = None
248
+ logging: bool = False
249
+ prompts: bool = False
250
+ prompts_list_changed: bool = False
251
+ resources: bool = False
252
+ resources_subscribe: bool = False
253
+ resources_list_changed: bool = False
254
+ tools: bool = False
255
+ tools_list_changed: bool = False
256
+ completions: bool = False
257
+
258
+
259
+ @dataclass
260
+ class MCPServerInfo:
261
+ """Information about an MCP server.
262
+
263
+ Attributes:
264
+ name: Server name
265
+ version: Server version
266
+ capabilities: Server capabilities
267
+ protocol_version: MCP protocol version
268
+ """
269
+
270
+ name: str
271
+ version: str
272
+ capabilities: MCPServerCapabilities
273
+ protocol_version: str = ""
274
+
275
+
276
+ @dataclass
277
+ class MCPProgress:
278
+ """Progress notification data.
279
+
280
+ Attributes:
281
+ progress_token: Token identifying the operation
282
+ progress: Current progress value (0.0 to 1.0 or absolute)
283
+ total: Optional total value for absolute progress
284
+ message: Optional progress message
285
+ """
286
+
287
+ progress_token: str | int
288
+ progress: float
289
+ total: float | None = None
290
+ message: str | None = None
@@ -0,0 +1,31 @@
1
+ """
2
+ Memory Package - Persistent learning from QE sessions.
3
+
4
+ Provides:
5
+ - QEMemory store for project-specific learnings
6
+ - Issue pattern tracking
7
+ - False positive suppressions
8
+ - File risk scoring
9
+ - Role effectiveness metrics
10
+ """
11
+
12
+ from .store import (
13
+ QEMemory,
14
+ IssuePattern,
15
+ Suppression,
16
+ FixPattern,
17
+ RoleMetrics,
18
+ MemoryStore,
19
+ )
20
+ from .feedback import FeedbackCollector, FindingFeedback
21
+
22
+ __all__ = [
23
+ "QEMemory",
24
+ "IssuePattern",
25
+ "Suppression",
26
+ "FixPattern",
27
+ "RoleMetrics",
28
+ "MemoryStore",
29
+ "FeedbackCollector",
30
+ "FindingFeedback",
31
+ ]