Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion pctx-py/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,21 @@ For changes to the underlying Rust crates and CLI, see the

## [UNRELEASED] - YYYY-MM-DD

## [v0.4.0] - 2026-06-08
### Added

- `Pctx(headers=...)`: an arbitrary `dict[str, str]` of headers applied to every
HTTP request and the WebSocket connection request. Use this to authenticate
against deployments that expect custom headers (e.g. a
`{"authorization": "Bearer <token>"}` for GCP IAM-protected services).

### Removed

- **Breaking**: the deprecated `Pctx(api_key=...)` parameter and the
`x-pctx-api-key` header it set. The system that validated this key is no
longer maintained. Pass credentials via `headers=` instead — e.g.
`Pctx(api_key="k")` → `Pctx(headers={"x-pctx-api-key": "k"})`.

## [v0.4.1] - 2026-06-08

### Added
- `py.typed` for mypy
Expand Down
10 changes: 6 additions & 4 deletions pctx-py/src/pctx_client/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def __init__(
tools: list[Tool | AsyncTool] | None = None,
servers: list[ServerConfig] | None = None,
url: str = "http://localhost:8080",
api_key: str | None = None,
headers: dict[str, str] | None = None,
execute_timeout: float = 30.0,
):
"""
Expand All @@ -77,6 +77,8 @@ def __init__(
- HTTP server: {"name": "...", "url": "...", "auth": {...}}
- stdio server: {"name": "...", "command": "...", "args": [...], "env": {...}}
url: PCTX server URL (default: http://localhost:8080)
headers: Additional headers applied to every request and the
WebSocket connection request
execute_timeout: Timeout for code execution in seconds (default: 30.0)
"""

Expand All @@ -100,14 +102,14 @@ def __init__(
ws_scheme = "wss" if http_scheme == "https" else "ws"

self._ws_client = WebSocketClient(
url=f"{ws_scheme}://{host}{parsed.path}/ws", api_key=api_key, tools=tools
url=f"{ws_scheme}://{host}{parsed.path}/ws", headers=headers, tools=tools
)
self._client = AsyncClient(
base_url=f"{http_scheme}://{host}{parsed.path}",
headers={"x-pctx-api-key": api_key or ""},
headers=headers or {},
)
self._session_id: str | None = None
self._api_key = api_key
self._headers = headers or {}

self._tools = tools or []
self._servers = servers or []
Expand Down
7 changes: 4 additions & 3 deletions pctx-py/src/pctx_client/_websocket_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,20 @@ class WebSocketClient:
def __init__(
self,
url: str,
api_key: str | None = None,
headers: dict[str, str] | None = None,
tools: list[Tool | AsyncTool] | None = None,
):
"""
Initialize the WebSocket client.

Args:
url: WebSocket server URL (e.g., "ws://localhost:8080/ws")
headers: Additional headers to send with the connection request
"""
self.url = url
self.ws: ClientConnection | None = None
self.tools = tools or []
self._api_key = api_key
self._headers = headers or {}
self._pending_executions: dict[str | int, asyncio.Future] = {}
self._request_counter = 0

Expand All @@ -77,8 +78,8 @@ async def _connect(self, code_mode_session: str):
"""
try:
headers = {
**self._headers,
"x-code-mode-session": code_mode_session,
"x-pctx-api-key": self._api_key or "",
}
self.ws = await websockets.connect(self.url, additional_headers=headers)
except Exception as e:
Expand Down
2 changes: 1 addition & 1 deletion pctx-py/tests/scripts/manual_code_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def multiply(a: float, b: float) -> MultiplyOutput:
async def main():
async with Pctx(
# url="https://....",
# api_key="pctx_xxxx",
# headers={"authorization": "Bearer xxxx"},
tools=[add, subtract, multiply, now_timestamp, search_logs],
servers=[
{
Expand Down
Loading