Skip to main content
Glama
sanjibani

storedge-mcp

by sanjibani
README.md
# storedge-mcp

[![Tests](https://img.shields.io/badge/tests-64%20passed-brightgreen)]()
[![Python](https://img.shields.io/badge/python-3.10%2B-blue)]()
[![License](https://img.shields.io/badge/license-MIT-blue)]()
[![MCP](https://img.shields.io/badge/MCP-1.0-purple)]()

**The first MCP server for StorEdge (Storable Edge) - the self-storage management platform that 5,000+ storage facilities rely on.**

Read and write tenants, units, leads, ledgers, move-ins/outs, insurance, tasks, and surcharge calculations for a self-storage facility - straight from Claude, Cursor, or any MCP client.

---

## Why this exists

StorEdge is the operations backbone for tens of thousands of self-storage facilities (under the Storable / Red Nova Labs umbrella). Its REST API at `api.storedgefms.com` powers tenant management, lead tracking, billing, insurance enrollment, task workflows, and move-in/out pipelines.

The existing integration options all require building OAuth 1.0 signing + paginated list handling + typed-error mapping from scratch every time:

- **Tray.ai connector** - locked inside Tray's walled garden, no MCP, no local-first.
- **Twilio integration** - single-purpose, voice-channel only.
- **Java/PHP/.NET sample clients** (`rednovalabs/storedge-api-client-*`) - boilerplate code, no Claude tool surface.
- **No first-party MCP server** - StorEdge has not shipped one.

`storedge-mcp` is the open MCP surface. One stdio call away, agents can answer "what 10x10 units are available right now?", "create a reservation for unit A-12", "show me who's past due this week", and "calculate the surcharge for a $125 Visa payment".

---

## Quick start

```bash
pip install -e .
export STOREDGE_CONSUMER_KEY=...      # from StorEdge dashboard
export STOREDGE_CONSUMER_SECRET=...   # from StorEdge dashboard
export STOREDGE_FACILITY_ID=<uuid>    # optional; tools accept it as arg too
storedge-mcp
```

In Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "storedge": {
      "command": "storedge-mcp",
      "env": {
        "STOREDGE_CONSUMER_KEY": "...",
        "STOREDGE_CONSUMER_SECRET": "...",
        "STOREDGE_FACILITY_ID": "..."
      }
    }
  }
}
```

---

## Tools (26)

### Diagnostic

- `health_check(facility_id?)` - verify credentials by listing one tenant. Returns fast-fail if OAuth signing is wrong.

### Tenants

- `list_tenants(facility_id?, per_page?, page?, created_after?, created_before?, updated_since?, only_ids?)`
- `list_current_tenants(facility_id?, per_page?, page?)` - only currently-active tenants
- `search_tenants(account_code?, phone_number?, facility_id?)` - by account code OR phone (exactly one)
- `get_tenant(tenant_id, facility_id?)`
- `get_tenant_ledgers(tenant_id, facility_id?)` - list of a tenant's rental agreements
- `get_tenant_notes(tenant_id, facility_id?)` - operator notes attached to a tenant

### Units

- `list_units(facility_id?, per_page?, page?, only_ids?)` - every unit (rented + vacant + offline)
- `list_available_units(facility_id?, per_page?, page?)` - only units available for move-in
- `get_unit(unit_id, facility_id?)`
- `list_unit_groups(facility_id?, per_page?, page?)` - size buckets (5x5, 10x10, etc.)
- `list_unit_types(facility_id?, per_page?, page?)` - Parking, Climate, Drive-Up, etc.

### Leads

- `list_leads(view="current", facility_id?, per_page?, page?)` - view ∈ {current, reservations, inquiries, follow_up, waitlist, missed_move_ins}
- `get_lead_counts(facility_id?)` - open lead counts by status
- `get_lead(lead_id, facility_id?)`
- `create_lead(lead, facility_id?)` - create a reservation or inquiry
- `update_lead(lead_id, lead, facility_id?)` - PATCH an existing lead

### Ledgers (rental agreements)

- `list_current_ledgers(facility_id?, per_page?, page?)`
- `get_ledger(ledger_id, facility_id?)`
- `get_ledger_delinquency(ledger_id, facility_id?)` - overdue balance + days late
- `get_ledger_invoices(ledger_id, facility_id?)`

### Insurance

- `get_insurance_summary(facility_id?)` - counts + revenue rollup
- `get_insurance_activity(facility_id?, per_page?, page?)`

### Tasks

- `list_tasks(category_id?, facility_id?, per_page?, page?)`
- `list_task_categories(facility_id?)`
- `create_task(task, facility_id?)`
- `complete_task(task_id, facility_id?)`

### Move-in / move-out pipeline

- `list_move_ins(facility_id?, per_page?, page?)` - in-progress move-ins
- `list_move_outs(facility_id?, per_page?, page?)` - scheduled move-outs

### Surcharge + invoices

- `calculate_surcharge(amount_cents, card_type?, facility_id?)` - credit-card convenience fee quote
- `list_invoices_and_ledgers(facility_id?, per_page?, page?)`

---

## Auth

OAuth 1.0 one-legged (RFC 5849). Get your consumer key + secret from
**StorEdge dashboard → Corporate → Settings → API**.

`storedge-mcp` ships its own OAuth 1.0 signer - not authlib's - because authlib's stock `OAuth1Auth` strips JSON request bodies during signing (silently breaking `POST`/`PATCH`/`PUT`). Our signer computes `oauth_body_hash` for non-form bodies and includes it in the signature per RFC 5849 §3.4.1.3.

---

## Architecture

Built on industry-leading patterns from `encode/httpx`, `stripe-python`, `lepture/authlib`, `boto3`, `pytest-dev/pytest`, and `astral-sh/ruff`:

- **Shared `httpx.AsyncClient`** with pooling + transport-level retries.
- **Typed exception hierarchy** (`StoredgeAuthError`, `StoredgeNotFoundError`, `StoredgeRateLimitError`, `StoredgeAPIError`, `StoredgeConnectionError`) carrying structured `error_code` (from StorEdge's `meta.error_code` envelope) and `request_id`.
- **Application-level retry** with exponential backoff + full jitter on 429/5xx, honoring `Retry-After`.
- **isError-compliance** - every tool re-raises on failure so FastMCP sets `isError: true` on the wire (Blackwell audit / MCPTox).
- **JSONL audit logging** via `STOREDGE_AUDIT_LOG` env var (or stderr default) - every tool call emits a structured record with redaction for `password`/`api_key`/`token` fields.
- **Property-based tests** (Hypothesis) for JSON round-trips.
- **respx** for httpx mocking - no live API calls in tests.

See `engineering-playbook.md` for the full pattern reference.

---

## Development

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

# Lint + type check + test
ruff check src tests
mypy src tests
pytest

# Coverage
pytest --cov=storedge_mcp --cov-report=term-missing
```

CI matrix (Python 3.10-3.13 on ubuntu/macos/windows) lives in `.github/workflows/ci.yml`.

---

## Sister projects

sanjibani publishes MCP servers for one niche vertical SaaS per day. See the [full list](https://github.com/sanjibani?q=-mcp) for the rest:

- `hawksoft-mcp` - independent insurance agencies
- `open-dental-mcp` - dental practice mgmt
- `ezyvet-mcp` - veterinary
- `jobber-mcp` - home service / field service
- `practicepanther-mcp` - legal practice (solo/small)
- `realm-mp-mcp` - church / nonprofit
- `fieldroutes-mcp` - pest control / lawn care
- `cox-automotive-mcp` - auto dealership
- `qualia-mcp` - title and escrow
- `campspot-mcp` - outdoor hospitality (campgrounds)
- `cleancloud-mcp` - laundry / dry cleaning
- `playmetrics-mcp` - youth sports clubs
- `foreup-mcp` - golf course management

Need a custom MCP for your vertical SaaS stack? → [mcp-services.landing](https://sanjibani.github.io/mcp-services/).

---

## License

MIT

---

## References

- Storable Edge API docs: <https://api.storedgefms.com/>
- Storable Edge Swagger JSON: <https://api.storedgefms.com/apidoc/schema_swagger_json_v1.json>
- OAuth 1.0 RFC: <https://datatracker.ietf.org/doc/html/rfc5849>

TDQS

B3.3/5.0

Scored across 31 tools

Disambiguation5/5

Each tool targets a distinct entity or action: leads, tenants, units, ledgers, invoices, tasks, insurance, moves, and health. There is no ambiguity; even similar tools like list_current_tenants vs list_tenants vs search_tenants serve different scopes.

Naming Consistency5/5

All tools follow a consistent verb_noun snake_case pattern (create_lead, get_ledger, list_units, etc.). Even health_check and calculate_surcharge fit the pattern. No mixing of conventions.

Tool Count4/5

31 tools cover a broad storage facility management domain. While slightly above the typical ideal range (3-15), each tool serves a clear purpose and the count is justified by the complexity of the domain.

Completeness3/5

The set covers many read operations and a few creates/updates, but lacks update/delete for several entities (tenants, units, leads) and misses write endpoints for insurance and moves. Some obvious lifecycle gaps exist.

Maintenance

ActivityInactive
ResponsivenessNo issues