agentshim 0.6.1__tar.gz → 0.6.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.
Files changed (54) hide show
  1. {agentshim-0.6.1 → agentshim-0.6.2}/CHANGELOG.md +6 -0
  2. {agentshim-0.6.1 → agentshim-0.6.2}/PKG-INFO +1 -1
  3. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/__init__.py +1 -1
  4. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/core/mcp.py +20 -1
  5. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/claude/provider.py +3 -0
  6. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/codex/provider.py +12 -2
  7. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/copilot/provider.py +4 -0
  8. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/gemini/provider.py +3 -0
  9. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/opencode/provider.py +3 -0
  10. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/testing/__init__.py +2 -1
  11. {agentshim-0.6.1 → agentshim-0.6.2}/pyproject.toml +1 -1
  12. {agentshim-0.6.1 → agentshim-0.6.2}/.gitignore +0 -0
  13. {agentshim-0.6.1 → agentshim-0.6.2}/README.md +0 -0
  14. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/agent.py +0 -0
  15. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/core/__init__.py +0 -0
  16. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/core/_files.py +0 -0
  17. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/core/env.py +0 -0
  18. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/core/errors.py +0 -0
  19. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/core/events.py +0 -0
  20. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/core/profile.py +0 -0
  21. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/core/provider.py +0 -0
  22. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/core/schema.py +0 -0
  23. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/core/stream.py +0 -0
  24. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/core/turn.py +0 -0
  25. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/core/usage.py +0 -0
  26. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/execution/__init__.py +0 -0
  27. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/execution/executor.py +0 -0
  28. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/execution/host.py +0 -0
  29. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/execution/transform.py +0 -0
  30. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/__init__.py +0 -0
  31. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/claude/__init__.py +0 -0
  32. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/claude/events.py +0 -0
  33. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/claude/hooks/__init__.py +0 -0
  34. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/claude/hooks/confine_reads.py +0 -0
  35. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/claude/parser.py +0 -0
  36. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/claude/sandbox.py +0 -0
  37. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/claude/scripted.py +0 -0
  38. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/codex/__init__.py +0 -0
  39. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/codex/events.py +0 -0
  40. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/codex/parser.py +0 -0
  41. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/codex/scripted.py +0 -0
  42. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/copilot/__init__.py +0 -0
  43. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/copilot/events.py +0 -0
  44. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/copilot/parser.py +0 -0
  45. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/copilot/scripted.py +0 -0
  46. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/gemini/__init__.py +0 -0
  47. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/gemini/events.py +0 -0
  48. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/gemini/parser.py +0 -0
  49. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/gemini/scripted.py +0 -0
  50. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/opencode/__init__.py +0 -0
  51. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/opencode/events.py +0 -0
  52. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/opencode/parser.py +0 -0
  53. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/providers/opencode/scripted.py +0 -0
  54. {agentshim-0.6.1 → agentshim-0.6.2}/agentshim/py.typed +0 -0
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.2 (2026-09-26)
4
+
5
+ - Add optional positive finite `tool_timeout_s` to stdio and HTTP MCP server
6
+ specs. Codex renders it as an invocation-scoped `tool_timeout_sec` config
7
+ override; providers that cannot represent it reject the request explicitly.
8
+
3
9
  ## 0.6.1 (2026-09-10)
4
10
 
5
11
  Additive only: every new field defaults, so a 0.6.0 consumer's existing
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentshim
3
- Version: 0.6.1
3
+ Version: 0.6.2
4
4
  Summary: Provider-agnostic coding-agent CLI shims
5
5
  Requires-Python: >=3.10
6
6
  Provides-Extra: test
@@ -84,7 +84,7 @@ from .providers.copilot import CopilotProvider
84
84
  from .providers.gemini import GeminiProvider
85
85
  from .providers.opencode import OpencodeProvider
86
86
 
87
- __version__ = "0.6.0"
87
+ __version__ = "0.6.2"
88
88
 
89
89
  __all__ = [
90
90
  "AgentEvent",
@@ -14,6 +14,7 @@ each concurrent turn its own workspace.
14
14
  from __future__ import annotations
15
15
 
16
16
  import json
17
+ import math
17
18
  from dataclasses import dataclass, field
18
19
  from types import MappingProxyType
19
20
  from typing import TYPE_CHECKING, Any, Literal, cast
@@ -37,12 +38,18 @@ def _no_env() -> Mapping[str, str]:
37
38
 
38
39
  @dataclass(frozen=True)
39
40
  class StdioMcpServer:
40
- """MCP server launched as a subprocess over stdio."""
41
+ """MCP server launched as a subprocess over stdio.
42
+
43
+ ``tool_timeout_s`` is the maximum time one tool call may run. Providers
44
+ that cannot express a per-server tool timeout reject a non-``None`` value
45
+ rather than silently dropping it.
46
+ """
41
47
 
42
48
  name: str
43
49
  command: str
44
50
  args: Sequence[str] = ()
45
51
  env: Mapping[str, str] = field(default_factory=_no_env)
52
+ tool_timeout_s: float | None = None
46
53
 
47
54
  def __post_init__(self) -> None:
48
55
  """Reject a spec the provider could never launch.
@@ -56,6 +63,7 @@ class StdioMcpServer:
56
63
  if not self.command:
57
64
  msg = f"MCP server {self.name!r} must declare a command"
58
65
  raise ValueError(msg)
66
+ _validate_tool_timeout(self.name, self.tool_timeout_s)
59
67
 
60
68
 
61
69
  #: The two remote MCP transports the CLIs distinguish.
@@ -79,6 +87,7 @@ class HttpMcpServer:
79
87
  url: str
80
88
  headers: Mapping[str, str] = field(default_factory=_no_env)
81
89
  transport: McpTransport = "http"
90
+ tool_timeout_s: float | None = None
82
91
 
83
92
  def __post_init__(self) -> None:
84
93
  """Reject a spec the provider could never reach.
@@ -95,6 +104,16 @@ class HttpMcpServer:
95
104
  if self.transport not in _TRANSPORTS:
96
105
  msg = f"MCP server {self.name!r} transport must be one of {_TRANSPORTS}"
97
106
  raise ValueError(msg)
107
+ _validate_tool_timeout(self.name, self.tool_timeout_s)
108
+
109
+
110
+ def _validate_tool_timeout(name: str, timeout_s: float | None) -> None:
111
+ """Reject timeout values no provider can apply meaningfully."""
112
+ if timeout_s is None:
113
+ return
114
+ if isinstance(timeout_s, bool) or not math.isfinite(timeout_s) or timeout_s <= 0:
115
+ msg = f"MCP server {name!r} tool_timeout_s must be a positive finite number"
116
+ raise ValueError(msg)
98
117
 
99
118
 
100
119
  McpServer = StdioMcpServer | HttpMcpServer
@@ -162,6 +162,9 @@ def _resumed_session_id(argv: Sequence[str]) -> str | None:
162
162
 
163
163
  def mcp_entry(server: McpServer) -> dict[str, Any]:
164
164
  """Render one server the way ``.mcp.json`` describes it."""
165
+ if server.tool_timeout_s is not None:
166
+ msg = "claude cannot configure a per-server MCP tool timeout"
167
+ raise ProviderCapabilityError(msg)
165
168
  if isinstance(server, HttpMcpServer):
166
169
  # Claude validates the config strictly and needs an explicit type; it
167
170
  # names the two remote transports exactly as the spec does.
@@ -186,7 +186,8 @@ def _server_flags(server: McpServer) -> list[str]:
186
186
  raise ProviderCapabilityError(_HTTP_HEADERS_UNSUPPORTED)
187
187
  # A ``--config`` override carries the address and nothing else, so
188
188
  # Codex works the transport out from the endpoint itself.
189
- return ["--config", f"{prefix}.url={_toml_str(server.url)}"]
189
+ flags = ["--config", f"{prefix}.url={_toml_str(server.url)}"]
190
+ return flags + _tool_timeout_flags(prefix, server.tool_timeout_s)
190
191
  flags = [
191
192
  "--config",
192
193
  f"{prefix}.command={_toml_str(server.command)}",
@@ -195,7 +196,14 @@ def _server_flags(server: McpServer) -> list[str]:
195
196
  ]
196
197
  for env_key, env_value in server.env.items():
197
198
  flags += ["--config", f"{prefix}.env.{env_key}={_toml_str(env_value)}"]
198
- return flags
199
+ return flags + _tool_timeout_flags(prefix, server.tool_timeout_s)
200
+
201
+
202
+ def _tool_timeout_flags(prefix: str, timeout_s: float | None) -> list[str]:
203
+ """Render Codex's per-server tool timeout as an invocation override."""
204
+ if timeout_s is None:
205
+ return []
206
+ return ["--config", f"{prefix}.tool_timeout_sec={timeout_s}"]
199
207
 
200
208
 
201
209
  def _toml_str(value: str) -> str:
@@ -267,6 +275,8 @@ def _apply_mcp_override(entry: dict[str, Any], field: str, raw_value: str) -> No
267
275
  elif field == "url":
268
276
  entry["url"] = _parse_toml_str(raw_value)
269
277
  entry["transport"] = "http"
278
+ elif field == "tool_timeout_sec":
279
+ entry["tool_timeout_s"] = float(raw_value)
270
280
  elif field.startswith("env."):
271
281
  entry.setdefault("env", {})[field.removeprefix("env.")] = _parse_toml_str(raw_value)
272
282
 
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import json
6
6
  from typing import TYPE_CHECKING, Any
7
7
 
8
+ from agentshim.core.errors import ProviderCapabilityError
8
9
  from agentshim.core.mcp import FlagsInstallation, HttpMcpServer, NoopInstallation
9
10
  from agentshim.core.profile import McpMechanism, OutputSchemaStyle, ProviderProfile
10
11
 
@@ -134,6 +135,9 @@ class CopilotProvider:
134
135
 
135
136
  def mcp_entry(server: McpServer) -> dict[str, Any]:
136
137
  """Render one server the way ``--additional-mcp-config`` describes it."""
138
+ if server.tool_timeout_s is not None:
139
+ msg = "copilot cannot configure a per-server MCP tool timeout"
140
+ raise ProviderCapabilityError(msg)
137
141
  if isinstance(server, HttpMcpServer):
138
142
  # Copilot uses the spec's own transport names in ``type``.
139
143
  entry: dict[str, Any] = {"type": server.transport, "url": server.url}
@@ -154,6 +154,9 @@ def mcp_entry(server: McpServer) -> dict[str, Any]:
154
154
  ``trust: true`` skips Gemini's per-tool approval prompt for servers the
155
155
  caller installed on purpose.
156
156
  """
157
+ if server.tool_timeout_s is not None:
158
+ msg = "gemini cannot configure a per-server MCP tool timeout"
159
+ raise ProviderCapabilityError(msg)
157
160
  if isinstance(server, HttpMcpServer):
158
161
  # Gemini picks the transport by which key holds the address:
159
162
  # ``httpUrl`` is streamable HTTP, plain ``url`` is SSE.
@@ -159,6 +159,9 @@ def mcp_entry(server: McpServer) -> dict[str, Any]:
159
159
  non-interactive ``run`` mode auto-answers permission prompts, so no
160
160
  extra ``permission`` block is needed.
161
161
  """
162
+ if server.tool_timeout_s is not None:
163
+ msg = "opencode cannot configure a per-server MCP tool timeout"
164
+ raise ProviderCapabilityError(msg)
162
165
  if isinstance(server, HttpMcpServer):
163
166
  # opencode has one remote server type and negotiates the transport
164
167
  # itself, so ``transport`` has nothing to select here.
@@ -215,7 +215,8 @@ def installed_mcp_servers(
215
215
  Each entry is a plain dict in a canonical shape, regardless of how the
216
216
  provider itself renders it: ``command``, ``args`` and ``env`` for a
217
217
  server started over stdio, ``url`` and ``transport`` for one reached over
218
- HTTP.
218
+ HTTP. Either shape includes ``tool_timeout_s`` when the invocation set
219
+ one.
219
220
 
220
221
  Args:
221
222
  provider: A registered provider name.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentshim"
3
- version = "0.6.1"
3
+ version = "0.6.2"
4
4
  description = "Provider-agnostic coding-agent CLI shims"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
File without changes
File without changes
File without changes
File without changes