Metadata-Version: 2.4
Name: strands-meridian
Version: 0.1.1
Summary: Meridian model provider for Strands Agents - run your agents on your Claude Max subscription quota via the local Meridian proxy.
Author-email: Mert Özbaş <mertozbas@gmail.com>
Maintainer-email: Mert Özbaş <mertozbas@gmail.com>
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/mertozbas/strands-meridian
Project-URL: Repository, https://github.com/mertozbas/strands-meridian
Project-URL: Issues, https://github.com/mertozbas/strands-meridian/issues
Project-URL: Documentation, https://github.com/mertozbas/strands-meridian#readme
Project-URL: Meridian, https://github.com/rynfar/meridian
Keywords: meridian,strands,strands-agents,ai-agent,claude,claude-max,anthropic,model-provider,llm,claude-code
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: strands-agents>=1.0.0
Requires-Dist: anthropic>=0.40.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: black>=23.0; extra == "dev"
Requires-Dist: ruff>=0.1; extra == "dev"
Dynamic: license-file

# strands-meridian

[![PyPI version](https://img.shields.io/pypi/v/strands-meridian.svg)](https://pypi.org/project/strands-meridian/)
[![Python versions](https://img.shields.io/pypi/pyversions/strands-meridian.svg)](https://pypi.org/project/strands-meridian/)
[![CI](https://github.com/mertozbas/strands-meridian/actions/workflows/ci.yml/badge.svg)](https://github.com/mertozbas/strands-meridian/actions/workflows/ci.yml)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

A [Meridian](https://github.com/rynfar/meridian) model provider for [Strands Agents](https://github.com/strands-agents/sdk-python). Run your Strands agents on your **Claude Max subscription quota** instead of per-token API billing — by pointing the standard Anthropic model provider at a local Meridian proxy.

> A community model-provider extension for Strands Agents. Not affiliated with, sponsored by, or endorsed by Anthropic or AWS.

```python
from strands import Agent
from strands_meridian import MeridianModel

agent = Agent(model=MeridianModel())
agent("Hello from my Claude Max quota")
```

## How it works

[Meridian](https://github.com/rynfar/meridian) is a local proxy that bridges the **Claude Code SDK** to the standard **Anthropic Messages API** (`POST /v1/messages`). It runs on `http://127.0.0.1:3456` and authenticates through your Claude Code login, so requests draw from your Claude Max subscription — no API key required.

`strands-meridian` is a thin wrapper around Strands' `AnthropicModel`. The only thing it changes is the client transport: `base_url` points at Meridian and `api_key` is a harmless placeholder. Because Meridian speaks the native Anthropic protocol, **prompt caching, structured output, streaming, and extended thinking all keep working** — your Max quota is used efficiently.

```
Strands Agent ──► MeridianModel (AnthropicModel) ──► http://127.0.0.1:3456 ──► Claude Code SDK ──► Claude Max
```

`MeridianModel` is a real Strands **model provider** — it subclasses `AnthropicModel`, which itself implements `strands.models.model.Model`, so it satisfies the exact same provider contract (`stream`, `structured_output`, `format_request`, …) as a from-scratch provider like [`strands-mlx`](https://github.com/cagataycali/strands-mlx). The difference: MLX is local inference with no compatible endpoint, so it implements that contract by hand; Meridian *is* an Anthropic-compatible endpoint, so the correct path is to inherit the implementation and only override the transport — caching/streaming/structured-output come for free.

## Prerequisites

Install and start Meridian, and log in once:

```bash
npm install -g @rynfar/meridian
claude login      # one-time: stores Claude Code credentials
meridian          # starts the proxy on http://127.0.0.1:3456
```

Verify it's up: `curl http://127.0.0.1:3456/health` (or `python -m strands_meridian.meridian`).

## Installation

```bash
pip install strands-meridian
```

## Quick Start

```python
from strands import Agent
from strands_meridian import MeridianModel

# Defaults: base_url=http://127.0.0.1:3456, model=claude-opus-4-8
agent = Agent(model=MeridianModel())
print(agent("Summarize the theory of relativity in one sentence."))
```

### Pick a model

```python
# Opus with 1M context — included with Claude Max at no extra cost.
# model_id may be positional (like strands-mlx's MLXModel("...")):
agent = Agent(model=MeridianModel("opus[1m]"))

# A full model id (works through Meridian and in direct fallback)
agent = Agent(model=MeridianModel("claude-opus-4-6", max_tokens=8192))
```

Via Meridian you can use the aliases `sonnet`, `opus`, `haiku`, `sonnet[1m]`, `opus[1m]`, or any full Claude model id. The default is the full id `claude-opus-4-8` (Claude's most capable model; 1M context is included with Claude Max) so it remains valid in direct-Anthropic fallback mode too.

> **Billing note:** Opus 1M context is included with Claude Max. Sonnet 1M (`sonnet[1m]`) is billed as Extra Usage even on Max — this is [Anthropic's billing model](https://code.claude.com/docs/en/model-config#extended-context), enforced by Meridian, not this package.

### Extended thinking, caching, and other Anthropic options

Every `AnthropicModel` / `AnthropicConfig` option passes straight through:

```python
agent = Agent(
    model=MeridianModel(
        model_id="opus[1m]",
        max_tokens=8192,
        params={"thinking": {"type": "enabled", "budget_tokens": 4000}},
    )
)
```

## Configuration

`MeridianModel(...)` arguments take precedence over environment variables, which take precedence over the built-in defaults.

| Variable | Default | Description |
|----------|---------|-------------|
| `MERIDIAN_BASE_URL` | `http://127.0.0.1:3456` | Meridian proxy URL |
| `MERIDIAN_MODEL` | `claude-opus-4-8` | Default model id (or Meridian alias) |
| `MERIDIAN_MAX_TOKENS` | `4096` | Default max response tokens |
| `MERIDIAN_API_KEY` | _(placeholder)_ | Only needed if you enabled Meridian's own [API-key auth gate](https://github.com/rynfar/meridian#api-key-authentication) |
| `MERIDIAN_DIRECT` | _(unset)_ | When truthy, `use_meridian()` bypasses Meridian (see below) |
| `ANTHROPIC_API_KEY` | _(unset)_ | Real Anthropic key for the opt-in direct fallback |

```python
agent = Agent(
    model=MeridianModel(
        base_url="http://192.168.1.10:3456",   # remote Meridian host
        model_id="opus[1m]",
        timeout=120.0,
    )
)
```

## `use_meridian()` factory + optional fallback

`use_meridian()` is the factory form of `MeridianModel`, plus one extra capability: an **explicit, manual** escape hatch to the direct Anthropic API (per-token billed). There is **no automatic fallback** — switching to per-token billing is always your decision.

```python
from strands import Agent
from strands_meridian import use_meridian

# Default: Meridian (Claude Max quota)
agent = Agent(model=use_meridian(model_id="opus[1m]"))

# Opt-in: bypass Meridian, bill per token (needs ANTHROPIC_API_KEY)
agent = Agent(model=use_meridian(direct=True, model_id="claude-opus-4-6"))
```

You can also flip the default for a whole process with `MERIDIAN_DIRECT=1` (still requires `ANTHROPIC_API_KEY`). In direct mode, `model_id` must be a real Anthropic model id — not a Meridian alias like `opus`.

## Health check

```python
from strands_meridian import health_check, is_available

if not is_available():
    raise SystemExit("Start Meridian first: `meridian` (after `claude login`)")

info = health_check()
print(info["auth"]["loggedIn"], info["auth"].get("subscriptionType"))
```

`health_check()` returns Meridian's `/health` JSON (status, version, auth, mode, plugin) and raises `MeridianError` if the proxy is unreachable. `is_available()` is the never-raising boolean form.

## CLI

Installing the package adds a `strands-meridian` command:

```bash
strands-meridian health           # check the proxy + auth status
strands-meridian models           # list the models Meridian exposes (/v1/models)
strands-meridian chat "hello"     # one-shot agent call on your Max quota
strands-meridian chat "explain X" --model "opus[1m]" --max-tokens 2048
strands-meridian --base-url http://192.168.1.10:3456 health
```

## API reference

| Name | Description |
|------|-------------|
| `MeridianModel(model_id=None, **kwargs)` | A Strands `AnthropicModel` pre-wired to Meridian (`model_id` may be positional). Drop-in `model=` for `Agent`. |
| `use_meridian(direct=False, **kwargs)` | Factory returning a `MeridianModel`, or a direct Anthropic model when `direct=True`. |
| `health_check(base_url=None, timeout=5.0)` | Query `/health`; returns parsed JSON or raises `MeridianError`. |
| `is_available(base_url=None, timeout=5.0)` | `True`/`False` reachability check (never raises). |
| `MeridianError` | Raised when Meridian is unreachable or misconfigured. |

Common keyword arguments: `base_url`, `api_key`, `timeout`, `model_id`, `max_tokens`, `client_args`, plus any `AnthropicConfig` key your installed `strands-agents` supports (`params` everywhere; recent versions also accept `context_window_limit` and `use_native_token_count`). `use_meridian` additionally accepts `direct` and `anthropic_api_key`.

## Security

- Meridian binds to `127.0.0.1` by default; the `api_key` field is a placeholder because authentication happens through the Claude Code SDK.
- If you expose Meridian beyond loopback (LAN, Tailscale, Docker), enable Meridian's `MERIDIAN_API_KEY` auth gate and set the matching `MERIDIAN_API_KEY` here — an unprotected network-accessible proxy is a Claude Max credential leak.
- This package never logs credentials.

## Requirements

- Python 3.10+
- `strands-agents`
- `anthropic`
- A running [Meridian](https://github.com/rynfar/meridian) proxy + a Claude subscription (`claude login`)

## License

Apache License 2.0

## Links

- [PyPI](https://pypi.org/project/strands-meridian/)
- [GitHub](https://github.com/mertozbas/strands-meridian)
- [Meridian](https://github.com/rynfar/meridian)
- [Strands Agents](https://github.com/strands-agents/sdk-python)
