Metadata-Version: 2.5
Name: visceral-consignment-mcp
Version: 0.1.0
Summary: Official MCP server for the Visceral Consignment external API (v1, read-only)
Project-URL: Homepage, https://visceralapps.com
Author-email: Visceral Apps <brian@bitfoundation.io>
License: Proprietary
Keywords: claude,consignment,mcp,model-context-protocol,shopify
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: End Users/Desktop
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Office/Business
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: mcp<3,>=2
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == 'dev'
Description-Content-Type: text/markdown

# Visceral Consignment MCP Server

Official [MCP](https://modelcontextprotocol.io) server for the Visceral
Consignment **external API v1** — lets Claude (or any MCP client) answer
questions about a shop's consignors, payouts, and payout line items in plain
English.

**Read-only.** Every tool call is an authenticated HTTPS request to the
external REST API (`/api/external/v1/`), so the shop's API-key scoping,
tenant isolation, rate limits, usage metering, and secure-field masking all
apply unchanged. Bank details and other secure fields are always masked and
cannot be revealed through this server.

## Prerequisites

1. The external API enabled for the shop (pilot: enabled by Visceral support).
2. A **read-only API key**: in Visceral, Settings → API → Create key. Name it
   for the tool that will use it (e.g. "Claude assistant") — usage is tracked
   per key and you can revoke it independently at any time.

## Configuration

| Environment variable | Required | Meaning |
|---|---|---|
| `VISCERAL_API_KEY` | yes | The `vsk_...` key from Settings → API |
| `VISCERAL_API_URL` | no | API base URL. Default: `https://b.visceralapps.com/api/external/v1` |

## Install & run

With [uv](https://docs.astral.sh/uv/) (recommended — installs in one command and
manages its own Python, so no Python setup is needed):

```bash
uvx visceral-consignment-mcp
```

Or with pip:

```bash
pip install visceral-consignment-mcp
visceral-mcp
```

The server speaks MCP over stdio (the standard transport for desktop clients).

### Claude Desktop

Add to `claude_desktop_config.json` (Settings → Developer → Edit Config):

```json
{
  "mcpServers": {
    "visceral-consignment": {
      "command": "uvx",
      "args": ["visceral-consignment-mcp"],
      "env": {
        "VISCERAL_API_KEY": "vsk_your_key_here"
      }
    }
  }
}
```

### Claude Code

```bash
claude mcp add visceral-consignment \
  --env VISCERAL_API_KEY=vsk_your_key_here \
  -- uvx visceral-consignment-mcp
```

Then ask things like:

> "Which consignors haven't had a completed payout since July?"
> "Summarize payout #918 — what sold and what did the consignor earn?"
> "How much did we pay out in total last month?"

## Tools

| Tool | Purpose |
|---|---|
| `get_shop_info` | Confirm the connection: shop, key scopes, rate-limit headroom |
| `list_consignors` | Search/filter consignors (`search`, `email`, `active`, `updated_since`) |
| `get_consignor` | One consignor's full record (secure fields masked) |
| `list_payouts` | Filter payouts (`status`, `consignor_id`, `processed_since`, …) |
| `get_payout` | One payout's summary |
| `get_payout_line_items` | The sold items behind a payout, matching the CSV export |

## Behavior notes

- Money values are decimal **strings** (`"184.50"`) — the server instructs
  clients never to treat them as floats.
- On a short rate-limit (`429` with `Retry-After` ≤ 15s) the server waits and
  retries once; longer waits surface as a readable error telling the model
  how long to pause.
- Versioned with the external API: these tools track **v1** and follow the
  same early-access stability contract (see the External API v1 guide).

## Development

Source lives in `src/visceral_mcp/server.py` — one `@mcp.tool()` per
endpoint, a shared `_get()` helper for auth/errors/rate-limit handling.

```bash
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest tests/ -q
```

Tests inject an `httpx.MockTransport` via `server._transport` and call the
tool functions directly (the `@mcp.tool()` decorator returns the plain
function) — no network, no real key needed.
