Metadata-Version: 2.4
Name: rotascale-mcp
Version: 0.1.0
Summary: Govern any agent that speaks the Model Context Protocol
Project-URL: Homepage, https://rotascale.com
Project-URL: Source, https://github.com/rotascale/rotascale-sdks
Project-URL: Issues, https://github.com/rotascale/rotascale-console/issues
Author-email: "Rota, Inc." <dev@rotascale.com>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: agent,authorization,governance,mcp,model-context-protocol
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 :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: mcp>=1.2
Requires-Dist: rotascale>=0.2.0
Description-Content-Type: text/markdown

# rotascale-mcp

Govern any agent that speaks the Model Context Protocol — including agents whose
code you cannot touch.

```bash
pip install rotascale-mcp
```

## Two surfaces, and the difference is the whole point

| | `rotascale-mcp` | `rotascale-mcp-proxy` |
|---|---|---|
| How it is reached | the agent decides to call a governance tool | every tool call passes through it |
| Can the agent avoid it | **yes** | **no** |
| Enforcement | advisory | in the tool path |

**Only the proxy is a control.** The server surface is genuinely useful — real
evidence, and a real gate for an agent that cooperates — but an agent that never
calls `authorize_action` is not governed by it. We would rather say that plainly
than let you find out during an incident.

## Rotascale as an MCP server

Adds governance tools to any MCP host. The agent chooses when to call them.

```json
{
  "mcpServers": {
    "rotascale": {
      "command": "rotascale-mcp",
      "env": {
        "ROTASCALE_URL": "https://rotascale.acme.internal",
        "ROTASCALE_API_KEY": "rota_live_…"
      }
    }
  }
}
```

| Tool | When the agent calls it |
|---|---|
| `open_trajectory` | once, at the start of a task |
| `authorize_action` | **before** any consequential action — moving money, changing a record, contacting a person |
| `witness_step` | as it reads and acts; `kind="retrieval"` is what carries taint |
| `check_authority` | to see what it may do and what budget remains |
| `close_trajectory` | when the task ends, success or failure |

### `authorize_action` returns an outcome, not a boolean

Six outcomes, each with a different remedy, and a `guidance` string written for
a model to act on:

| Outcome | What it means |
|---|---|
| `allow` | proceed |
| `deny` | outside the granted authority — do not retry, do not route around |
| `exhausted` | budget or call allowance spent — retrying cannot help |
| `gated` | the context is tainted and this authority needs a clean one |
| `review_sync` | a human must decide first |
| `review_async` | proceed, but it is queued for review |

A boolean would collapse these, and an agent that cannot tell `exhausted` from
`gated` will do the wrong thing about both — usually retrying, which is useless
for the first and a security problem for the second.

## Transport

stdio by default, because that is how MCP hosts launch a local server. Logging
goes to stderr, since stdout *is* the protocol channel.

```bash
ROTASCALE_MCP_TRANSPORT=streamable-http rotascale-mcp
```

## Why a separate package

`pip install rotascale` must never carry an MCP dependency, and the MCP spec
revises on its own schedule. Pinning them together would force pointless
releases of one to keep up with the other.

## Tracking MCP servers you already use

Separate from this package: the `rotascale` SDK's `watch_mcp` wraps an MCP
client session and reports each server's tool manifest, so a tool whose
*description* changes is caught — including between sessions. A description is
an instruction the model reads, so rewriting one changes what your agent does
without changing a line of your code.

```python
from rotascale.middleware import watch_mcp

session = watch_mcp(session, server="filesystem", transport="stdio")
```
