archery-sql-mcp 0.2.0__tar.gz → 0.2.2__tar.gz
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.
- {archery_sql_mcp-0.2.0 → archery_sql_mcp-0.2.2}/PKG-INFO +1 -1
- {archery_sql_mcp-0.2.0 → archery_sql_mcp-0.2.2}/pyproject.toml +1 -1
- {archery_sql_mcp-0.2.0 → archery_sql_mcp-0.2.2}/src/archery_sql_mcp/__init__.py +45 -15
- {archery_sql_mcp-0.2.0 → archery_sql_mcp-0.2.2}/.gitignore +0 -0
- {archery_sql_mcp-0.2.0 → archery_sql_mcp-0.2.2}/README.md +0 -0
|
@@ -28,7 +28,7 @@ from typing import Any, Optional
|
|
|
28
28
|
|
|
29
29
|
import httpx
|
|
30
30
|
|
|
31
|
-
VERSION = "0.2.
|
|
31
|
+
VERSION = "0.2.2"
|
|
32
32
|
|
|
33
33
|
# ── 配置文件与会话缓存位置(可用 ARCHERY_CONFIG_DIR 整体改根目录) ──────────
|
|
34
34
|
_CONFIG_DIR = Path(os.environ.get("ARCHERY_CONFIG_DIR", str(Path.home() / ".archery-mcp")))
|
|
@@ -166,15 +166,26 @@ class SessionManager:
|
|
|
166
166
|
},
|
|
167
167
|
headers={"Referer": f"{self.base}/login/"},
|
|
168
168
|
)
|
|
169
|
-
#
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
169
|
+
# /authenticate/ 是 ajax JSON 接口:恒为 HTTP 200,
|
|
170
|
+
# {"status": 0}=成功(sessionid 经 Set-Cookie 下发),status=1 时 msg 为可读错误。
|
|
171
|
+
# 兼容旧式表单行为(302 跳转)作为回退分支。
|
|
172
|
+
payload: Any = None
|
|
173
|
+
try:
|
|
174
|
+
payload = resp.json()
|
|
175
|
+
except Exception:
|
|
176
|
+
payload = None
|
|
177
|
+
if payload is not None:
|
|
178
|
+
if payload.get("status") != 0:
|
|
179
|
+
raise ArcheryError(f"Archery 登录失败: {payload.get('msg') or payload}")
|
|
180
|
+
else:
|
|
181
|
+
location = resp.headers.get("location", "")
|
|
182
|
+
if resp.status_code not in (301, 302) or "/login" in location:
|
|
183
|
+
reason = _extract_login_error(resp.text) or (
|
|
184
|
+
f"HTTP {resp.status_code}"
|
|
185
|
+
+ (f" → {location}" if location else "")
|
|
186
|
+
+ "(用户名/密码错误,或账号被禁用)"
|
|
187
|
+
)
|
|
188
|
+
raise ArcheryError(f"Archery 登录失败: {reason}")
|
|
178
189
|
|
|
179
190
|
self.sessionid = client.cookies.get("sessionid")
|
|
180
191
|
self.csrftoken = client.cookies.get("csrftoken") or cookie_csrf
|
|
@@ -248,13 +259,13 @@ class ArcheryClient:
|
|
|
248
259
|
for _attempt in range(2):
|
|
249
260
|
await self.sm.ensure()
|
|
250
261
|
async with httpx.AsyncClient(
|
|
251
|
-
timeout=self.cfg["request_timeout_s"], follow_redirects=False
|
|
262
|
+
timeout=self.cfg["request_timeout_s"], follow_redirects=False,
|
|
263
|
+
cookies={"csrftoken": self.sm.csrftoken or "",
|
|
264
|
+
"sessionid": self.sm.sessionid or ""},
|
|
252
265
|
) as client:
|
|
253
266
|
resp = await client.request(
|
|
254
267
|
method, f"{self.sm.base}{path}",
|
|
255
268
|
data=data, params=params,
|
|
256
|
-
cookies={"csrftoken": self.sm.csrftoken or "",
|
|
257
|
-
"sessionid": self.sm.sessionid or ""},
|
|
258
269
|
headers={"X-CSRFToken": self.sm.csrftoken or "",
|
|
259
270
|
"Referer": self.sm.base + "/"},
|
|
260
271
|
)
|
|
@@ -306,9 +317,28 @@ class ArcheryClient:
|
|
|
306
317
|
raise ArcheryError(f"/query/ 返回非 JSON (HTTP {resp.status_code}),请联系管理员确认平台版本兼容性")
|
|
307
318
|
if submitted.get("status") != 0:
|
|
308
319
|
raise ArcheryError(f"/query/ 提交失败: {submitted.get('msg') or submitted}")
|
|
309
|
-
|
|
320
|
+
data = submitted.get("data") or {}
|
|
321
|
+
|
|
322
|
+
def _finish(columns: Any, rows: Any) -> dict[str, Any]:
|
|
323
|
+
return {
|
|
324
|
+
"status": "finished",
|
|
325
|
+
"columns": columns or [],
|
|
326
|
+
"rows": rows or [],
|
|
327
|
+
"row_count": len(rows or []),
|
|
328
|
+
"elapsed_ms": int((time.monotonic() - started) * 1000),
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
# 同步模式(实测 2026-08-28,公司 Archery 版本):
|
|
332
|
+
# /query/ 直接返回 column_list / rows / error,无 query_id。
|
|
333
|
+
if data.get("column_list") is not None or data.get("rows") is not None:
|
|
334
|
+
if data.get("error"):
|
|
335
|
+
raise ArcheryError(f"SQL 执行错误: {data['error']}")
|
|
336
|
+
return _finish(data.get("column_list"), data.get("rows"))
|
|
337
|
+
|
|
338
|
+
# 异步模式(部分 Archery 版本/配置):提交返回 query_id → 轮询 /queryresult/。
|
|
339
|
+
query_id = data.get("query_id")
|
|
310
340
|
if not query_id:
|
|
311
|
-
raise ArcheryError(f"/query/
|
|
341
|
+
raise ArcheryError(f"/query/ 未返回结果也未返回 query_id: {submitted}")
|
|
312
342
|
|
|
313
343
|
# 轮询至终态
|
|
314
344
|
interval = float(self.cfg["poll_interval_s"])
|
|
File without changes
|
|
File without changes
|