@oneciel-ai/claude-any 0.1.91 → 0.1.93
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.
- package/claude_any.py +119 -12
- package/package.json +1 -1
package/claude_any.py
CHANGED
|
@@ -105,7 +105,7 @@ OFFICIAL_CHANNEL_PLUGINS = {
|
|
|
105
105
|
"fakechat": "plugin:fakechat@claude-plugins-official",
|
|
106
106
|
}
|
|
107
107
|
APP_NAME = "Claude Any"
|
|
108
|
-
VERSION = "0.1.
|
|
108
|
+
VERSION = "0.1.93"
|
|
109
109
|
CREDITS = "Credits: One Ciel LLC"
|
|
110
110
|
|
|
111
111
|
LOG_LEVELS = {"SILENT": 0, "ERROR": 1, "WARN": 2, "INFO": 3, "DEBUG": 4, "TRACE": 5}
|
|
@@ -1675,6 +1675,11 @@ def executable_candidates(name: str) -> list[str]:
|
|
|
1675
1675
|
|
|
1676
1676
|
def executable_extra_dirs() -> list[Path]:
|
|
1677
1677
|
dirs = [HOME / ".local" / "bin"]
|
|
1678
|
+
for env_name in ("UV_INSTALL_DIR", "CARGO_HOME"):
|
|
1679
|
+
root = os.environ.get(env_name)
|
|
1680
|
+
if root:
|
|
1681
|
+
path = Path(root)
|
|
1682
|
+
dirs.append(path if path.name == "bin" else path / "bin")
|
|
1678
1683
|
if os.name == "nt":
|
|
1679
1684
|
pyver = f"Python{sys.version_info.major}{sys.version_info.minor}"
|
|
1680
1685
|
for env_name in ("APPDATA", "LOCALAPPDATA"):
|
|
@@ -1688,7 +1693,27 @@ def executable_extra_dirs() -> list[Path]:
|
|
|
1688
1693
|
except Exception:
|
|
1689
1694
|
pass
|
|
1690
1695
|
dirs.append(Path(sys.executable).resolve().parent / "Scripts")
|
|
1691
|
-
|
|
1696
|
+
else:
|
|
1697
|
+
dirs.extend(
|
|
1698
|
+
[
|
|
1699
|
+
HOME / ".cargo" / "bin",
|
|
1700
|
+
HOME / ".npm-global" / "bin",
|
|
1701
|
+
HOME / ".bun" / "bin",
|
|
1702
|
+
Path(sys.executable).resolve().parent,
|
|
1703
|
+
Path("/usr/local/bin"),
|
|
1704
|
+
Path("/usr/bin"),
|
|
1705
|
+
Path("/bin"),
|
|
1706
|
+
Path("/opt/homebrew/bin"),
|
|
1707
|
+
]
|
|
1708
|
+
)
|
|
1709
|
+
out: list[Path] = []
|
|
1710
|
+
seen: set[str] = set()
|
|
1711
|
+
for directory in dirs:
|
|
1712
|
+
key = str(directory)
|
|
1713
|
+
if key and key not in seen:
|
|
1714
|
+
seen.add(key)
|
|
1715
|
+
out.append(directory)
|
|
1716
|
+
return out
|
|
1692
1717
|
|
|
1693
1718
|
|
|
1694
1719
|
def find_executable(name: str) -> str | None:
|
|
@@ -1704,6 +1729,29 @@ def find_executable(name: str) -> str | None:
|
|
|
1704
1729
|
return None
|
|
1705
1730
|
|
|
1706
1731
|
|
|
1732
|
+
def resolve_executable_for_subprocess(command: str) -> str:
|
|
1733
|
+
command = str(command or "").strip()
|
|
1734
|
+
if not command:
|
|
1735
|
+
return command
|
|
1736
|
+
pathish = Path(command).is_absolute() or os.sep in command or bool(os.altsep and os.altsep in command)
|
|
1737
|
+
if pathish:
|
|
1738
|
+
return command
|
|
1739
|
+
return find_executable(command) or command
|
|
1740
|
+
|
|
1741
|
+
|
|
1742
|
+
def resolve_mcp_server_process(command: str, args: list[str]) -> tuple[str, list[str]]:
|
|
1743
|
+
command = str(command or "").strip()
|
|
1744
|
+
resolved = resolve_executable_for_subprocess(command)
|
|
1745
|
+
name = Path(command).name.lower()
|
|
1746
|
+
if resolved == command and name in ("uvx", "uvx.exe", "uvx.cmd", "uvx.bat"):
|
|
1747
|
+
uv = find_executable("uv")
|
|
1748
|
+
if uv:
|
|
1749
|
+
return uv, ["tool", "run", *args]
|
|
1750
|
+
if importlib.util.find_spec("uv") is not None:
|
|
1751
|
+
return sys.executable, ["-m", "uv", "tool", "run", *args]
|
|
1752
|
+
return resolved, args
|
|
1753
|
+
|
|
1754
|
+
|
|
1707
1755
|
def shell_command_string(args: list[str]) -> str:
|
|
1708
1756
|
if os.name == "nt":
|
|
1709
1757
|
# Claude Code on Windows runs hook commands through sh/bash, which treats
|
|
@@ -5035,6 +5083,45 @@ def _channel_mcp_update_cursor(last_id: int) -> None:
|
|
|
5035
5083
|
router_log("WARN", f"channel_mcp_cursor_write_failed error={type(exc).__name__}: {exc}")
|
|
5036
5084
|
|
|
5037
5085
|
|
|
5086
|
+
def _channel_mcp_parse_event_id(value: Any) -> int | None:
|
|
5087
|
+
text = str(value or "").strip()
|
|
5088
|
+
if not text:
|
|
5089
|
+
return None
|
|
5090
|
+
try:
|
|
5091
|
+
return max(0, int(text))
|
|
5092
|
+
except Exception:
|
|
5093
|
+
return None
|
|
5094
|
+
|
|
5095
|
+
|
|
5096
|
+
def _channel_mcp_client_last_event_id(handler: BaseHTTPRequestHandler) -> int | None:
|
|
5097
|
+
try:
|
|
5098
|
+
event_id = _channel_mcp_parse_event_id(handler.headers.get("Last-Event-ID"))
|
|
5099
|
+
if event_id is not None:
|
|
5100
|
+
return event_id
|
|
5101
|
+
except Exception:
|
|
5102
|
+
pass
|
|
5103
|
+
try:
|
|
5104
|
+
params = _query_params(handler)
|
|
5105
|
+
for key in ("lastEventId", "last_event_id", "last_id"):
|
|
5106
|
+
event_id = _channel_mcp_parse_event_id(_first_param(params, key))
|
|
5107
|
+
if event_id is not None:
|
|
5108
|
+
return event_id
|
|
5109
|
+
except Exception:
|
|
5110
|
+
pass
|
|
5111
|
+
return None
|
|
5112
|
+
|
|
5113
|
+
|
|
5114
|
+
def _channel_mcp_session_start_last_id(handler: BaseHTTPRequestHandler) -> int:
|
|
5115
|
+
cursor_last_id = _channel_mcp_ensure_cursor_initialized()
|
|
5116
|
+
client_last_id = _channel_mcp_client_last_event_id(handler)
|
|
5117
|
+
if client_last_id is None:
|
|
5118
|
+
return cursor_last_id
|
|
5119
|
+
if client_last_id > cursor_last_id:
|
|
5120
|
+
_channel_mcp_update_cursor(client_last_id)
|
|
5121
|
+
router_log("INFO", f"channel_mcp_resume client_last_id={client_last_id} cursor_last_id={cursor_last_id}")
|
|
5122
|
+
return client_last_id
|
|
5123
|
+
|
|
5124
|
+
|
|
5038
5125
|
def _channel_mcp_notifications_for_messages(
|
|
5039
5126
|
messages: list[dict[str, Any]],
|
|
5040
5127
|
session: str = "",
|
|
@@ -5054,7 +5141,7 @@ def _channel_mcp_notifications_for_messages(
|
|
|
5054
5141
|
events.append((last_id, _channel_mcp_notification(message)))
|
|
5055
5142
|
router_log(
|
|
5056
5143
|
"INFO",
|
|
5057
|
-
f"
|
|
5144
|
+
f"channel_mcp_notification_prepared session={session or '-'} message_id={message.get('id')} channel={message.get('channel')}",
|
|
5058
5145
|
)
|
|
5059
5146
|
return last_id, events
|
|
5060
5147
|
|
|
@@ -5066,7 +5153,7 @@ def handle_channel_mcp_get(handler: BaseHTTPRequestHandler, path: str) -> bool:
|
|
|
5066
5153
|
if path != "/ca/mcp/sse":
|
|
5067
5154
|
return False
|
|
5068
5155
|
session = _channel_mcp_session_id()
|
|
5069
|
-
last_id =
|
|
5156
|
+
last_id = _channel_mcp_session_start_last_id(handler)
|
|
5070
5157
|
with _CHANNEL_MCP_LOCK:
|
|
5071
5158
|
_CHANNEL_MCP_SESSIONS[session] = {"created_at": time.time(), "last_id": last_id, "initialized": False, "outbox": []}
|
|
5072
5159
|
router_log("INFO", f"channel_mcp_session_started session={session} last_id={last_id}")
|
|
@@ -5101,7 +5188,9 @@ def handle_channel_mcp_get(handler: BaseHTTPRequestHandler, path: str) -> bool:
|
|
|
5101
5188
|
last_id = max(last_id, delivered_last_id)
|
|
5102
5189
|
for event_id, notification in events:
|
|
5103
5190
|
_write_sse_event(handler, "message", notification, event_id)
|
|
5104
|
-
|
|
5191
|
+
router_log("INFO", f"channel_mcp_notification_written session={session} message_id={event_id}")
|
|
5192
|
+
if not events:
|
|
5193
|
+
_channel_mcp_update_cursor(last_id)
|
|
5105
5194
|
with _CHANNEL_MCP_LOCK:
|
|
5106
5195
|
state = _CHANNEL_MCP_SESSIONS.get(session)
|
|
5107
5196
|
if state:
|
|
@@ -9392,7 +9481,7 @@ def _mcp_server_is_stdio(server: dict[str, Any]) -> bool:
|
|
|
9392
9481
|
server_type = str(server.get("type") or "").strip().lower()
|
|
9393
9482
|
if server_type and server_type not in ("stdio", "command"):
|
|
9394
9483
|
return False
|
|
9395
|
-
command = str(server.get("command") or "").strip()
|
|
9484
|
+
command = resolve_executable_for_subprocess(str(server.get("command") or "").strip())
|
|
9396
9485
|
if not command:
|
|
9397
9486
|
return False
|
|
9398
9487
|
args = [str(item) for item in server.get("args", []) if item is not None] if isinstance(server.get("args", []), list) else []
|
|
@@ -13854,7 +13943,6 @@ def write_web_tools_mcp_config(cfg: dict[str, Any]) -> Path:
|
|
|
13854
13943
|
web = cfg.get("web_search", {})
|
|
13855
13944
|
package = web.get("package") or "ddg-mcp-search"
|
|
13856
13945
|
npx = find_executable("npx") or ("npx.cmd" if os.name == "nt" else "npx")
|
|
13857
|
-
uvx = find_executable("uvx") or "uvx"
|
|
13858
13946
|
servers: dict[str, Any] = {
|
|
13859
13947
|
"duckduckgo": {
|
|
13860
13948
|
"command": npx,
|
|
@@ -13867,11 +13955,29 @@ def write_web_tools_mcp_config(cfg: dict[str, Any]) -> Path:
|
|
|
13867
13955
|
fetch_args.extend(["--user-agent", str(web["fetch_user_agent"])])
|
|
13868
13956
|
if web.get("fetch_ignore_robots_txt", False):
|
|
13869
13957
|
fetch_args.append("--ignore-robots-txt")
|
|
13870
|
-
|
|
13871
|
-
|
|
13872
|
-
|
|
13873
|
-
|
|
13874
|
-
|
|
13958
|
+
fetch_command = find_executable("uvx")
|
|
13959
|
+
fetch_command_args = fetch_args
|
|
13960
|
+
if not fetch_command:
|
|
13961
|
+
uv = find_executable("uv")
|
|
13962
|
+
if uv:
|
|
13963
|
+
fetch_command = uv
|
|
13964
|
+
fetch_command_args = ["tool", "run", *fetch_args]
|
|
13965
|
+
elif importlib.util.find_spec("uv") is not None:
|
|
13966
|
+
fetch_command = sys.executable
|
|
13967
|
+
fetch_command_args = ["-m", "uv", "tool", "run", *fetch_args]
|
|
13968
|
+
else:
|
|
13969
|
+
pipx = find_executable("pipx")
|
|
13970
|
+
if pipx:
|
|
13971
|
+
fetch_command = pipx
|
|
13972
|
+
fetch_command_args = ["run", *fetch_args]
|
|
13973
|
+
if fetch_command:
|
|
13974
|
+
servers["web_fetch"] = {
|
|
13975
|
+
"command": fetch_command,
|
|
13976
|
+
"args": fetch_command_args,
|
|
13977
|
+
"claude_any_stdio": "jsonl",
|
|
13978
|
+
}
|
|
13979
|
+
else:
|
|
13980
|
+
router_log("WARN", "web_fetch_disabled_missing_runner install=uvx_or_uv")
|
|
13875
13981
|
data = {"mcpServers": servers}
|
|
13876
13982
|
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
13877
13983
|
WEB_TOOLS_MCP_CONFIG.write_text(json.dumps(data, indent=2) + "\n")
|
|
@@ -14600,6 +14706,7 @@ def run_mcp_stdio_proxy(server_name: str, server_config_path: Path) -> int:
|
|
|
14600
14706
|
return 2
|
|
14601
14707
|
command = str(server.get("command") or "").strip()
|
|
14602
14708
|
args = [str(item) for item in server.get("args", [])] if isinstance(server.get("args"), list) else []
|
|
14709
|
+
command, args = resolve_mcp_server_process(command, args)
|
|
14603
14710
|
env = os.environ.copy()
|
|
14604
14711
|
raw_env = server.get("env")
|
|
14605
14712
|
if isinstance(raw_env, dict):
|
package/package.json
CHANGED