Skip to content

Commit 162c40e

Browse files
authored
refactor: update references to CapiscioMCPServer.connect() API (#10)
* refactor: update references to CapiscioMCPServer.connect() API - README.md: MCPServerIdentity.from_env() → CapiscioMCPServer.connect() - guard.py: update from_env() docstring to reference connect() pattern * fix: clarify from_env() docstring — env vars resolved lazily * fix: clarify from_env() description in README Reword to avoid implying from_env() mirrors connect() — it's an env-var convenience initializer, not a connect/handshake call. * feat: add CapiscioGuard.connect() for ecosystem consistency Add connect() classmethod that eagerly connects to the CapiscIO registry, matching the CapiscIO.connect() and CapiscioMCPServer.connect() patterns used across the ecosystem. - connect() reads env vars as fallbacks, validates api_key upfront - from_env() now delegates to connect() (kept as convenience alias) - README updated to feature connect() as the primary API - 6 new unit tests for connect() and from_env() delegation * style: fix ruff format in test_guard.py * fix: dev_mode precedence, README signature, test env isolation - Change dev_mode param from bool=False to bool|None=None so explicit False overrides CAPISCIO_DEV_MODE env var - Fix from_env() docstring to mention kwargs override env vars - Fix README constructor signature to show keyword-only args - Isolate tests from runner env vars using patch.dict(os.environ) - Add test for dev_mode=False overriding env var
1 parent 2132283 commit 162c40e

4 files changed

Lines changed: 292 additions & 35 deletions

File tree

README.md

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ Turn any LangChain agent into a trust-verified agent in 2 lines:
2121
```python
2222
from langchain_capiscio import CapiscioGuard
2323

24-
# Zero-config — reads CAPISCIO_API_KEY from env, connects to registry
25-
secured = CapiscioGuard() | my_chain
24+
# Reads CAPISCIO_API_KEY from env, connects to registry
25+
secured = CapiscioGuard.connect() | my_chain
2626
result = secured.invoke({"input": "Summarize this ticket"})
2727
```
2828

29-
`CapiscioGuard` reads your environment, connects to the CapiscIO registry on first use, and verifies caller trust badges before every invocation.
29+
`CapiscioGuard.connect()` reads your environment, connects to the CapiscIO registry, and returns a guard that verifies caller trust badges before every invocation — consistent with `CapiscIO.connect()` and `CapiscioMCPServer.connect()` across the ecosystem.
3030

3131
## Why LangChain Guard?
3232

@@ -51,9 +51,9 @@ LangChain Guard solves this with:
5151
Control enforcement behavior per guard instance:
5252

5353
```python
54-
guard = CapiscioGuard(mode="block") # Fail closed (production default)
55-
guard = CapiscioGuard(mode="monitor") # Warn but continue
56-
guard = CapiscioGuard(mode="log") # Log only
54+
guard = CapiscioGuard.connect(mode="block") # Fail closed (production default)
55+
guard = CapiscioGuard.connect(mode="monitor") # Warn but continue
56+
guard = CapiscioGuard.connect(mode="log") # Log only
5757
```
5858

5959
## LCEL Pipe Composition
@@ -68,7 +68,7 @@ from langchain_capiscio import CapiscioGuard
6868
from langgraph.prebuilt import create_react_agent
6969

7070
agent = create_react_agent(llm, tools)
71-
secured = CapiscioGuard(mode="log") | agent
71+
secured = CapiscioGuard.connect(mode="log") | agent
7272
result = secured.invoke({"input": "What's 42 * 17?"})
7373
```
7474

@@ -94,7 +94,7 @@ Events emitted: `task_started`, `task_completed`, `task_failed`, `tool_call`, `t
9494
from langchain_capiscio import CapiscioGuard, capiscio_guard
9595

9696
# Option 1: Runnable as graph node
97-
graph.add_node("verify", CapiscioGuard())
97+
graph.add_node("verify", CapiscioGuard.connect())
9898

9999
# Option 2: Decorator
100100
@capiscio_guard(mode="block")
@@ -106,7 +106,7 @@ def call_agent(state: dict) -> dict:
106106

107107
### Zero-config (recommended)
108108

109-
Set environment variables and create a guard with no arguments:
109+
Set environment variables and connect with no arguments:
110110

111111
```bash
112112
export CAPISCIO_API_KEY="cap_..."
@@ -116,41 +116,39 @@ export CAPISCIO_DEV_MODE="true" # optional
116116
```
117117

118118
```python
119-
guard = CapiscioGuard() # reads env vars, connects on first invoke()
119+
guard = CapiscioGuard.connect() # reads env vars, connects eagerly
120120
```
121121

122122
### Explicit configuration
123123

124124
```python
125-
guard = CapiscioGuard(
126-
mode="block",
125+
guard = CapiscioGuard.connect(
127126
api_key="cap_...",
127+
mode="block",
128128
name="my-agent",
129129
server_url="https://dev.registry.capisc.io",
130130
)
131131
```
132132

133-
### `connect_kwargs`
133+
### Extra connect kwargs
134134

135-
Pass extra keyword arguments through to `CapiscIO.connect()`:
135+
Pass additional keyword arguments through to `CapiscIO.connect()`:
136136

137137
```python
138-
guard = CapiscioGuard(
138+
guard = CapiscioGuard.connect(
139139
mode="log",
140-
connect_kwargs={
141-
"dev_mode": True,
142-
"keys_dir": "capiscio_keys/",
143-
"agent_card": my_card_dict,
144-
},
140+
dev_mode=True,
141+
keys_dir="capiscio_keys/",
142+
agent_card=my_card_dict,
145143
)
146144
```
147145

148146
## Using Environment Variables
149147

150-
`CapiscioGuard.from_env()` mirrors the `CapiscIO.from_env()` / `MCPServerIdentity.from_env()` pattern used across CapiscIO packages:
148+
`CapiscioGuard.connect()` reads environment variables automatically. `from_env()` is kept as a convenience alias:
151149

152150
```python
153-
guard = CapiscioGuard.from_env(mode="log")
151+
guard = CapiscioGuard.connect(mode="log")
154152
```
155153

156154
| Variable | Required | Description | Default |
@@ -200,8 +198,8 @@ services:
200198
```
201199
202200
```python
203-
# No code changes needed — CapiscioGuard reads env vars automatically
204-
secured = CapiscioGuard(mode="block") | my_agent
201+
# No code changes needed — CapiscioGuard.connect() reads env vars automatically
202+
secured = CapiscioGuard.connect(mode="block") | my_agent
205203
```
206204

207205
> **Warning:** Never bake private keys into container images. Inject them at runtime via environment variables or mounted secrets.
@@ -241,10 +239,11 @@ set_capiscio_context(CapiscioRequestContext(
241239

242240
### Guard
243241

244-
- `CapiscioGuard(mode, api_key, name, server_url, connect_kwargs, identity, config)` — LCEL-composable trust enforcement Runnable
242+
- `CapiscioGuard.connect(api_key, *, mode, name, server_url, dev_mode, **kwargs)` — Connect to registry and return a ready-to-use guard (recommended)
243+
- `CapiscioGuard.from_env(mode, **kwargs)` — Alias for `connect()` (reads env vars)
244+
- `CapiscioGuard(*, identity, config, mode, api_key, name, server_url, connect_kwargs)` — Low-level constructor (keyword-only, lazy init on first invoke)
245245
- `CapiscioGuard.invoke(input, config)` — Verify badge and pass through to downstream
246246
- `CapiscioGuard.ainvoke(input, config)` — Async version
247-
- `CapiscioGuard.from_env(mode, **kwargs)` — Create guard from environment variables
248247

249248
### Callbacks
250249

langchain_capiscio/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from langchain_capiscio import CapiscioGuard
44
from langchain_openai import ChatOpenAI
55
6-
secured = CapiscioGuard() | ChatOpenAI()
6+
secured = CapiscioGuard.connect() | ChatOpenAI()
77
result = secured.invoke("Summarise quarterly earnings")
88
"""
99

langchain_capiscio/guard.py

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,19 +101,100 @@ def __init__(
101101
self._wire_badge_renewal(identity)
102102
self._initialized = True
103103

104+
@classmethod
105+
def connect(
106+
cls,
107+
api_key: str | None = None,
108+
*,
109+
mode: str = "block",
110+
name: str | None = None,
111+
server_url: str | None = None,
112+
dev_mode: bool | None = None,
113+
**kwargs: Any,
114+
) -> CapiscioGuard:
115+
"""Connect to CapiscIO and return a ready-to-use guard.
116+
117+
This is the recommended entry point — consistent with
118+
``CapiscIO.connect()`` and ``CapiscioMCPServer.connect()``
119+
across the CapiscIO ecosystem.
120+
121+
Eagerly connects to the registry so errors surface immediately
122+
rather than on first ``invoke()``.
123+
124+
Args:
125+
api_key: Registry API key. Falls back to ``CAPISCIO_API_KEY``
126+
env var when ``None``.
127+
mode: Enforcement mode — ``"block"``, ``"monitor"``, or ``"log"``.
128+
name: Agent name for registration. Falls back to
129+
``CAPISCIO_AGENT_NAME`` env var.
130+
server_url: Registry URL override. Falls back to
131+
``CAPISCIO_SERVER_URL`` env var.
132+
dev_mode: Enable dev mode (relaxed validation). When ``None``,
133+
falls back to ``CAPISCIO_DEV_MODE`` env var. Explicit
134+
``True``/``False`` overrides the env var.
135+
**kwargs: Extra keyword arguments forwarded to
136+
``CapiscIO.connect()`` (e.g. ``keys_dir``, ``agent_card``).
137+
138+
Returns:
139+
A fully-initialized ``CapiscioGuard`` ready for use in LCEL
140+
pipes or as a standalone Runnable.
141+
142+
Raises:
143+
CapiscioConfigError: If no API key is available.
144+
145+
Example::
146+
147+
guard = CapiscioGuard.connect(mode="block")
148+
secured = guard | my_chain
149+
"""
150+
import os
151+
152+
effective_api_key = api_key or os.environ.get("CAPISCIO_API_KEY")
153+
if not effective_api_key:
154+
raise CapiscioConfigError(
155+
"No API key provided. Set CAPISCIO_API_KEY env var"
156+
" or pass api_key= to CapiscioGuard.connect()."
157+
)
158+
159+
from capiscio_sdk import CapiscIO
160+
161+
connect_kwargs: dict[str, Any] = {**kwargs}
162+
163+
effective_name = name or os.environ.get("CAPISCIO_AGENT_NAME")
164+
if effective_name:
165+
connect_kwargs["name"] = effective_name
166+
167+
effective_url = server_url or os.environ.get("CAPISCIO_SERVER_URL")
168+
if effective_url:
169+
connect_kwargs["server_url"] = effective_url
170+
171+
if dev_mode is None:
172+
dev_mode = os.environ.get("CAPISCIO_DEV_MODE", "").lower() in (
173+
"true",
174+
"1",
175+
"yes",
176+
)
177+
if dev_mode:
178+
connect_kwargs["dev_mode"] = True
179+
180+
identity = CapiscIO.connect(effective_api_key, **connect_kwargs)
181+
config = cls._make_config(mode)
182+
183+
return cls(identity=identity, config=config, mode=mode)
184+
104185
@classmethod
105186
def from_env(cls, *, mode: str = "block", **kwargs: Any) -> CapiscioGuard:
106-
"""Create a CapiscioGuard using environment variables for configuration.
187+
"""Create a guard from environment variables.
107188
108-
Reads CAPISCIO_API_KEY, CAPISCIO_SERVER_URL, CAPISCIO_AGENT_NAME,
109-
and CAPISCIO_DEV_MODE from the environment. Mirrors the ``from_env()``
110-
pattern used by ``CapiscIO.from_env()`` and
111-
``MCPServerIdentity.from_env()``.
189+
Convenience alias for ``CapiscioGuard.connect()`` — reads API key,
190+
agent name, server URL, and dev mode from environment variables.
191+
Explicit ``**kwargs`` override env vars when provided.
112192
113-
Any explicit keyword arguments are forwarded to the constructor and
114-
take precedence over environment variables.
193+
.. deprecated::
194+
Prefer ``CapiscioGuard.connect()`` for consistency with the
195+
rest of the CapiscIO ecosystem.
115196
"""
116-
return cls(mode=mode, **kwargs)
197+
return cls.connect(mode=mode, **kwargs)
117198

118199
@staticmethod
119200
def _make_config(mode: str) -> Any:

0 commit comments

Comments
 (0)