cade-cli 0.4.0__tar.gz → 0.5.0__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.
Files changed (55) hide show
  1. cade_cli-0.5.0/LICENSE +21 -0
  2. {cade_cli-0.4.0 → cade_cli-0.5.0}/PKG-INFO +25 -4
  3. {cade_cli-0.4.0 → cade_cli-0.5.0}/pyproject.toml +4 -4
  4. cade_cli-0.5.0/src/cadecoder/__init__.py +1 -0
  5. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/cli/auth.py +0 -30
  6. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/cli/commands/context.py +11 -1
  7. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/cli/commands/mcp.py +11 -2
  8. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/cli/commands/thread.py +9 -1
  9. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/tools/manager/mcp.py +38 -5
  10. cade_cli-0.4.0/src/cadecoder/__init__.py +0 -1
  11. {cade_cli-0.4.0 → cade_cli-0.5.0}/.gitignore +0 -0
  12. {cade_cli-0.4.0 → cade_cli-0.5.0}/README.md +0 -0
  13. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/ai/__init__.py +0 -0
  14. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/ai/prompts.py +0 -0
  15. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/cli/__init__.py +0 -0
  16. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/cli/app.py +0 -0
  17. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/cli/commands/__init__.py +0 -0
  18. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/cli/commands/auth.py +0 -0
  19. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/cli/commands/chat.py +0 -0
  20. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/cli/commands/model.py +0 -0
  21. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/cli/commands/tools.py +0 -0
  22. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/core/__init__.py +0 -0
  23. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/core/config.py +0 -0
  24. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/core/constants.py +0 -0
  25. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/core/errors.py +0 -0
  26. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/core/logging.py +0 -0
  27. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/core/names.py +0 -0
  28. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/core/types.py +0 -0
  29. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/execution/__init__.py +0 -0
  30. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/execution/context_window.py +0 -0
  31. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/execution/orchestrator.py +0 -0
  32. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/execution/parallel.py +0 -0
  33. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/providers/__init__.py +0 -0
  34. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/providers/anthropic.py +0 -0
  35. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/providers/base.py +0 -0
  36. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/providers/ollama.py +0 -0
  37. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/providers/openai.py +0 -0
  38. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/storage/__init__.py +0 -0
  39. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/storage/threads.py +0 -0
  40. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/templates/login_failed.html +0 -0
  41. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/templates/login_success.html +0 -0
  42. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/templates/styles.css +0 -0
  43. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/tools/__init__.py +0 -0
  44. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/tools/local/__init__.py +0 -0
  45. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/tools/local/filesystem.py +0 -0
  46. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/tools/local/git.py +0 -0
  47. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/tools/local/search.py +0 -0
  48. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/tools/local/shell.py +0 -0
  49. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/tools/manager/__init__.py +0 -0
  50. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/tools/manager/base.py +0 -0
  51. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/tools/manager/composite.py +0 -0
  52. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/tools/manager/config.py +0 -0
  53. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/ui/__init__.py +0 -0
  54. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/ui/display.py +0 -0
  55. {cade_cli-0.4.0 → cade_cli-0.5.0}/src/cadecoder/ui/session.py +0 -0
cade_cli-0.5.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Arcade AI Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cade-cli
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Cade - The CLI Agent from Arcade.dev
5
5
  Project-URL: Homepage, https://arcade.dev
6
6
  Project-URL: Documentation, https://docs.arcade.dev
@@ -8,7 +8,28 @@ Project-URL: Repository, https://github.com/arcadeai-labs/cade
8
8
  Project-URL: Issues, https://github.com/arcadeai-labs/cade/issues
9
9
  Project-URL: Changelog, https://github.com/arcadeai-labs/cade/releases
10
10
  Author-email: "Arcade AI Inc." <dev@arcade.dev>
11
- License: MIT
11
+ License: MIT License
12
+
13
+ Copyright (c) 2024 Arcade AI Inc.
14
+
15
+ Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ of this software and associated documentation files (the "Software"), to deal
17
+ in the Software without restriction, including without limitation the rights
18
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ copies of the Software, and to permit persons to whom the Software is
20
+ furnished to do so, subject to the following conditions:
21
+
22
+ The above copyright notice and this permission notice shall be included in all
23
+ copies or substantial portions of the Software.
24
+
25
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
+ SOFTWARE.
32
+ License-File: LICENSE
12
33
  Keywords: agent,ai,arcade,cli,coding-assistant,llm,mcp
13
34
  Classifier: Development Status :: 4 - Beta
14
35
  Classifier: Environment :: Console
@@ -29,12 +50,12 @@ Requires-Dist: arcade-tdk>=2.0.0
29
50
  Requires-Dist: authlib<2.0.0,>=1.6.0
30
51
  Requires-Dist: httpx<1.0.0,>=0.27.0
31
52
  Requires-Dist: openai<2.0.0,>=1.0.0
32
- Requires-Dist: prompt-toolkit>=3.0.52
53
+ Requires-Dist: prompt-toolkit<4.0.0,>=3.0.52
33
54
  Requires-Dist: pydantic[email]<3.0.0,>=2.0.0
34
55
  Requires-Dist: pyperclip<2.0.0,>=1.8.0
35
56
  Requires-Dist: pyyaml<7.0.0,>=6.0
36
57
  Requires-Dist: rich<14.0.0,>=13.0.0
37
- Requires-Dist: tiktoken>=0.11.0
58
+ Requires-Dist: tiktoken<1.0.0,>=0.11.0
38
59
  Requires-Dist: toml<1.0.0,>=0.10.0
39
60
  Requires-Dist: typer>0.10.0
40
61
  Requires-Dist: ulid==1.1
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "cade-cli"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Cade - The CLI Agent from Arcade.dev"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
7
- license = {text = "MIT"}
7
+ license = {file = "LICENSE"}
8
8
  authors = [
9
9
  { name = "Arcade AI Inc.", email = "dev@arcade.dev" },
10
10
  ]
@@ -36,8 +36,8 @@ dependencies = [
36
36
  "arcade-core>=4.1.0,<5.0.0",
37
37
  "authlib>=1.6.0,<2.0.0",
38
38
  "pyperclip>=1.8.0,<2.0.0",
39
- "prompt-toolkit>=3.0.52",
40
- "tiktoken>=0.11.0",
39
+ "prompt-toolkit>=3.0.52,<4.0.0",
40
+ "tiktoken>=0.11.0,<1.0.0",
41
41
  "httpx>=0.27.0,<1.0.0",
42
42
  ]
43
43
 
@@ -0,0 +1 @@
1
+ __version__ = "0.5.0"
@@ -10,7 +10,6 @@ import threading
10
10
  import uuid
11
11
  import webbrowser
12
12
  from http.server import BaseHTTPRequestHandler, HTTPServer
13
- from pathlib import Path
14
13
  from typing import Any
15
14
  from urllib.parse import parse_qs
16
15
 
@@ -452,32 +451,3 @@ def get_access_token(coordinator_url: str | None = None) -> str:
452
451
  ValueError: If not logged in or token refresh fails
453
452
  """
454
453
  return get_valid_access_token(coordinator_url)
455
-
456
-
457
- # For backward compatibility with legacy API key format
458
- def _check_legacy_credentials() -> tuple[str, str] | None:
459
- """Check for legacy API key credentials.
460
-
461
- Returns:
462
- Tuple of (api_key, email) if found, None otherwise.
463
- """
464
- import yaml
465
-
466
- creds_path = Path.home() / ".arcade" / "credentials.yaml"
467
- if not creds_path.exists():
468
- return None
469
-
470
- try:
471
- with creds_path.open() as f:
472
- data = yaml.safe_load(f) or {}
473
-
474
- cloud = data.get("cloud", {})
475
- if isinstance(cloud, dict) and "api" in cloud:
476
- api_key = cloud.get("api", {}).get("key")
477
- email = cloud.get("user", {}).get("email")
478
- if api_key and email:
479
- return api_key, email
480
- except Exception:
481
- pass
482
-
483
- return None
@@ -14,7 +14,17 @@ from rich.table import Table
14
14
  from cadecoder.core.logging import log
15
15
 
16
16
  console = Console(stderr=True)
17
- context_app = typer.Typer(help="Manage organization and project context", no_args_is_help=True)
17
+ context_app = typer.Typer(
18
+ help="Manage organization and project context", invoke_without_command=True
19
+ )
20
+
21
+
22
+ @context_app.callback()
23
+ def context_callback(ctx: typer.Context) -> None:
24
+ """Manage organization and project context"""
25
+ if ctx.invoked_subcommand is None:
26
+ console.print(ctx.get_help())
27
+ raise typer.Exit(0)
18
28
 
19
29
 
20
30
  @context_app.command(name="show")
@@ -17,17 +17,26 @@ from cadecoder.tools.manager import (
17
17
  MCPServerStore,
18
18
  MCPToolManager,
19
19
  )
20
+ from cadecoder.tools.manager.mcp import MCP_PROTOCOL_VERSION
20
21
 
21
22
  # Create MCP command group
22
23
  mcp_app = typer.Typer(
23
24
  name="mcp",
24
25
  help="Manage MCP (Model Context Protocol) servers",
25
- no_args_is_help=True,
26
+ invoke_without_command=True,
26
27
  )
27
28
 
28
29
  console = Console()
29
30
 
30
31
 
32
+ @mcp_app.callback()
33
+ def mcp_callback(ctx: typer.Context) -> None:
34
+ """Manage MCP (Model Context Protocol) servers"""
35
+ if ctx.invoked_subcommand is None:
36
+ console.print(ctx.get_help())
37
+ raise typer.Exit(0)
38
+
39
+
31
40
  @mcp_app.command("list")
32
41
  def list_servers() -> None:
33
42
  """List all configured MCP servers."""
@@ -379,7 +388,7 @@ def authorize_server(
379
388
  await manager._send_request(
380
389
  "initialize",
381
390
  {
382
- "protocolVersion": "2024-11-05",
391
+ "protocolVersion": MCP_PROTOCOL_VERSION,
383
392
  "capabilities": {},
384
393
  "clientInfo": {"name": "cade", "version": "1.0.0"},
385
394
  },
@@ -11,12 +11,20 @@ from cadecoder.storage.threads import get_thread_history
11
11
  thread_app = typer.Typer(
12
12
  name="thread",
13
13
  help="Manage chat threads",
14
- no_args_is_help=True,
14
+ invoke_without_command=True,
15
15
  )
16
16
 
17
17
  console = Console()
18
18
 
19
19
 
20
+ @thread_app.callback()
21
+ def thread_callback(ctx: typer.Context) -> None:
22
+ """Manage chat threads"""
23
+ if ctx.invoked_subcommand is None:
24
+ console.print(ctx.get_help())
25
+ raise typer.Exit(0)
26
+
27
+
20
28
  @thread_app.command("list")
21
29
  def list_threads(
22
30
  limit: Annotated[
@@ -21,6 +21,9 @@ from cadecoder.tools.manager.config import (
21
21
  MCPTransportType,
22
22
  )
23
23
 
24
+ # MCP protocol version - client's preferred version for negotiation
25
+ MCP_PROTOCOL_VERSION = "2025-11-25"
26
+
24
27
 
25
28
  class StdioMCPConnection:
26
29
  """Manages stdio subprocess for MCP server."""
@@ -110,7 +113,10 @@ class MCPOAuthHandler:
110
113
  async def _ensure_client(self) -> httpx.AsyncClient:
111
114
  """Ensure HTTP client exists."""
112
115
  if self._client is None:
113
- self._client = httpx.AsyncClient(timeout=httpx.Timeout(30.0))
116
+ self._client = httpx.AsyncClient(
117
+ timeout=httpx.Timeout(30.0),
118
+ follow_redirects=True,
119
+ )
114
120
  return self._client
115
121
 
116
122
  def parse_www_authenticate(self, header: str) -> dict[str, str]:
@@ -428,6 +434,7 @@ class MCPToolManager(ToolManager):
428
434
  self._client: httpx.AsyncClient | None = None
429
435
 
430
436
  self._session_id: str | None = None
437
+ self._negotiated_protocol_version: str | None = None
431
438
  self._oauth_handler = MCPOAuthHandler(server_config)
432
439
  self._auth_server_metadata: dict[str, Any] | None = None
433
440
 
@@ -441,6 +448,9 @@ class MCPToolManager(ToolManager):
441
448
  if self._session_id:
442
449
  headers["Mcp-Session-Id"] = self._session_id
443
450
 
451
+ if self._negotiated_protocol_version:
452
+ headers["MCP-Protocol-Version"] = self._negotiated_protocol_version
453
+
444
454
  if self.config.auth_type == MCPAuthType.BEARER and self.config.auth_value:
445
455
  headers["Authorization"] = f"Bearer {self.config.auth_value}"
446
456
  elif self.config.auth_type == MCPAuthType.API_KEY and self.config.auth_value:
@@ -456,6 +466,7 @@ class MCPToolManager(ToolManager):
456
466
  if self._client is None:
457
467
  self._client = httpx.AsyncClient(
458
468
  timeout=httpx.Timeout(60.0, connect=10.0),
469
+ follow_redirects=True,
459
470
  )
460
471
  return self._client
461
472
 
@@ -609,7 +620,9 @@ class MCPToolManager(ToolManager):
609
620
  log.error(f"MCP stdio request failed for {self.config.name}: {e}")
610
621
  raise
611
622
 
612
- async def _send_http_request(self, method: str, params: dict[str, Any] | None = None) -> Any:
623
+ async def _send_http_request(
624
+ self, method: str, params: dict[str, Any] | None = None, _retry: bool = False
625
+ ) -> Any:
613
626
  """Send a JSON-RPC request via HTTP transport."""
614
627
  await self._maybe_refresh_oauth_token()
615
628
 
@@ -634,6 +647,19 @@ class MCPToolManager(ToolManager):
634
647
  f"Captured MCP session ID for '{self.config.name}': {self._session_id[:8]}..."
635
648
  )
636
649
 
650
+ # Per MCP spec: 404 with active session means session expired, reinitialize
651
+ if response.status_code == 404 and self._session_id and not _retry:
652
+ log.warning(f"MCP server '{self.config.name}' session expired, reinitializing")
653
+ self._session_id = None
654
+ self._negotiated_protocol_version = None
655
+ self._initialized = False
656
+ self._tools_cache = None
657
+ if await self.initialize():
658
+ return await self._send_http_request(method, params, _retry=True)
659
+ raise Exception(
660
+ f"MCP server '{self.config.name}' session expired and reinitialization failed"
661
+ )
662
+
637
663
  if response.status_code == 401:
638
664
  auth_url = await self._handle_401_response(response)
639
665
  raise ToolAuthorizationRequired(
@@ -696,16 +722,22 @@ class MCPToolManager(ToolManager):
696
722
  result = await self._send_request(
697
723
  "initialize",
698
724
  {
699
- "protocolVersion": "2024-11-05",
725
+ "protocolVersion": MCP_PROTOCOL_VERSION,
700
726
  "capabilities": {"tools": {}},
701
727
  "clientInfo": {"name": "cade", "version": "1.0.0"},
702
728
  },
703
729
  )
704
730
 
705
731
  if result:
732
+ self._negotiated_protocol_version = result.get(
733
+ "protocolVersion", MCP_PROTOCOL_VERSION
734
+ )
706
735
  self._initialized = True
707
736
  await self._send_notification("notifications/initialized")
708
- log.info(f"MCP server '{self.config.name}' initialized")
737
+ log.info(
738
+ f"MCP server '{self.config.name}' initialized "
739
+ f"(protocol: {self._negotiated_protocol_version})"
740
+ )
709
741
  return True
710
742
 
711
743
  return False
@@ -789,7 +821,6 @@ class MCPToolManager(ToolManager):
789
821
 
790
822
  def _parse_arcade_error(self, error_text: str) -> str:
791
823
  """Parse arcade-mcp-server error object string to extract clean error message."""
792
- import re
793
824
 
794
825
  # Try to extract message field: message="..."
795
826
  message_match = re.search(r'message="([^"]*)"', error_text)
@@ -861,6 +892,7 @@ class MCPToolManager(ToolManager):
861
892
  try:
862
893
  self._initialized = False
863
894
  self._session_id = None
895
+ self._negotiated_protocol_version = None
864
896
 
865
897
  success = await self.initialize()
866
898
  if success:
@@ -923,5 +955,6 @@ class MCPToolManager(ToolManager):
923
955
  await self._client.aclose()
924
956
  self._client = None
925
957
  self._session_id = None
958
+ self._negotiated_protocol_version = None
926
959
  self._initialized = False
927
960
  await self._oauth_handler.close()
@@ -1 +0,0 @@
1
- __version__ = "0.4.0"
File without changes
File without changes