Metadata-Version: 2.4
Name: objectstore-mcp
Version: 1.0.1
Summary: Object storage (S3/MinIO/R2, Google Cloud Storage, Azure Blob, local filesystem) API + MCP Server + A2A Server for Agentic AI!
Author-email: Audel Rouhi <knucklessg1@gmail.com>
License: MIT
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: MIT License
Classifier: Environment :: Console
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Requires-Python: <3.15,>=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: agent-utilities>=1.0.0
Requires-Dist: python-dotenv>=1.0.0
Provides-Extra: mcp
Requires-Dist: agent-utilities[mcp]>=1.0.0; extra == "mcp"
Provides-Extra: agent
Requires-Dist: agent-utilities[agent,logfire]>=1.0.0; extra == "agent"
Provides-Extra: s3
Requires-Dist: boto3>=1.34.0; extra == "s3"
Provides-Extra: gcs
Requires-Dist: google-cloud-storage>=2.16.0; extra == "gcs"
Provides-Extra: azure
Requires-Dist: azure-storage-blob>=12.20.0; extra == "azure"
Provides-Extra: all
Requires-Dist: objectstore-mcp[agent,azure,gcs,logfire,mcp,s3]>=1.0.0; extra == "all"
Provides-Extra: test
Requires-Dist: pytest-xdist>=3.6.0; extra == "test"
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-asyncio; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: moto[s3]>=5.0.0; extra == "test"
Dynamic: license-file

# ObjectStore MCP
## Multi-Backend Object Storage | MCP Server

![PyPI - Version](https://img.shields.io/pypi/v/objectstore-mcp)
![MCP Server](https://badge.mcpx.dev?type=server 'MCP Server')
![PyPI - License](https://img.shields.io/pypi/l/objectstore-mcp)

The object-storage connector for the agent-utilities fleet: **one MCP tool
surface over S3 and S3-compatible stores (MinIO, Cloudflare R2), Google Cloud
Storage, Azure Blob Storage, and a zero-infra local-filesystem backend**.

*Version: 1.0.1*

> **Documentation** — Installation, deployment, and usage across the API, CLI, and
> MCP interfaces are maintained in [`docs/`](docs/index.md).

## Table of Contents

- [Overview](#overview)
- [What it provides](#what-it-provides)
- [Backend × capability matrix](#backend--capability-matrix)
- [Installation](#installation)
- [Configuration (environment)](#configuration-environment)
- [Usage](#usage)
- [MCP config](#mcp-config)
- [Docker deployment](#docker-deployment)
- [Development](#development)
- [License](#license)

## Overview

`objectstore-mcp` wraps heterogeneous object stores behind one typed,
deterministic MCP tool surface, plus an optional Pydantic-AI A2A agent server
(`objectstore-agent`). Safety caps, explicit buckets, and dry-run-by-default
batch deletes are enforced uniformly in the tool layer, regardless of backend.

## What it provides

- **A multi-backend store abstraction** (`objectstore_mcp.api`,
  CONCEPT:OBJ-1.0) — every provider implements the same
  `ObjectStoreBackend` protocol, validated by a single conformance test
  suite that runs for real against the filesystem backend.
- **Three consolidated, action-routed MCP tools** (`objectstore-mcp`
  console script):

  | Tool | Actions | Description |
  |---|---|---|
  | `objects` | `list` (prefix/delimiter pagination), `head`, `get` (text/base64, size-capped), `put` (text/base64, size-capped), `copy`, `move`, `delete`, `delete_batch` (capped, dry-run by default), `presign`, `metadata_get`, `metadata_set` | Single-object lifecycle and listing on any store |
  | `buckets` | `list`, `create`, `delete` (empty-only, opt-in), `exists`, `info`, `stores` | Bucket/container admin and store registry introspection |
  | `transfer` | `upload`, `download`, `upload_dir`, `download_prefix` (all size/batch-capped) | Local-filesystem ⇄ object-store transfer, single or by prefix |

  The whole tool set toggles with `OBJECTSTORETOOL`.

- **Named multi-store routing** — `OBJECTSTORE_STORES` JSON maps store names
  to `{backend, bucket?, endpoint?, profile?, ...}`; every tool takes an
  optional `store` argument. A zero-infra `local` filesystem store always
  exists, so the server works with no cloud credentials at all.
- **A safety governor** — size caps on get/put/transfer, list/batch key caps,
  deletes that demand an explicit bucket+key (no wildcards), batch deletes
  that are prefix-scoped, capped, and dry-run by default, and bucket deletes
  that are empty-only and disabled unless explicitly enabled.

## Available MCP Tools

<!-- This table is auto-generated by `python -m agent_utilities.mcp.readme_tools` — do not edit by hand. -->

<!-- MCP-TOOLS-TABLE:START -->

#### Condensed action-routed tools (default — `MCP_TOOL_MODE=condensed`)

| MCP Tool | Toggle Env Var | Description |
|----------|----------------|-------------|
| `buckets` | `OBJECTSTORETOOL` | Manage buckets/containers and inspect configured stores. |
| `objects` | `OBJECTSTORETOOL` | List, read, write, copy, move, delete, presign, and tag objects. |
| `transfer` | `OBJECTSTORETOOL` | Move data between the local filesystem and object storage. |

#### Verbose 1:1 API-mapped tools (`MCP_TOOL_MODE=verbose` or `both`)

<details>
<summary>15 per-operation tools — one per public API method (click to expand)</summary>

| MCP Tool | Toggle Env Var | Description |
|----------|----------------|-------------|
| `objectstore_bucket_exists` | `OBJECT_STORE_BACKENDTOOL` | Return True when the bucket exists. |
| `objectstore_bucket_info` | `OBJECT_STORE_BACKENDTOOL` | Describe one bucket. Raises NotFoundError when absent. |
| `objectstore_capabilities` | `OBJECT_STORE_BACKENDTOOL` | Advertise optional capabilities. |
| `objectstore_copy_object` | `OBJECT_STORE_BACKENDTOOL` | Server-side (where possible) copy of one object. |
| `objectstore_create_bucket` | `OBJECT_STORE_BACKENDTOOL` | Create a bucket. Raises AlreadyExistsError if it exists. |
| `objectstore_delete_bucket` | `OBJECT_STORE_BACKENDTOOL` | Delete an EMPTY bucket. Raises BucketNotEmptyError otherwise. |
| `objectstore_delete_object` | `OBJECT_STORE_BACKENDTOOL` | Delete exactly one object. Raises NotFoundError when absent. |
| `objectstore_get_object` | `OBJECT_STORE_BACKENDTOOL` | Download an object's bytes. Raises ObjectStoreError when the |
| `objectstore_get_object_metadata` | `OBJECT_STORE_BACKENDTOOL` | Return the user metadata of one object. |
| `objectstore_head_object` | `OBJECT_STORE_BACKENDTOOL` | Stat one object without downloading it. |
| `objectstore_list_buckets` | `OBJECT_STORE_BACKENDTOOL` | List all buckets/containers visible to the credentials. |
| `objectstore_list_objects` | `OBJECT_STORE_BACKENDTOOL` | List objects under ``prefix``, optionally folding at ``delimiter``. |
| `objectstore_presigned_url` | `OBJECT_STORE_BACKENDTOOL` | Mint a presigned URL. Raises UnsupportedOperationError when the |
| `objectstore_put_object` | `OBJECT_STORE_BACKENDTOOL` | Upload bytes to ``bucket/key`` (overwrites). |
| `objectstore_set_object_metadata` | `OBJECT_STORE_BACKENDTOOL` | Replace the user metadata of one object. |

</details>

_3 action-routed tool(s) (default) · 15 verbose 1:1 tool(s). Each is enabled unless its `<DOMAIN>TOOL` toggle is set false; `MCP_TOOL_MODE` selects the surface (`condensed` default · `verbose` 1:1 · `both`). Auto-generated — do not edit._
<!-- MCP-TOOLS-TABLE:END -->

## Backend × capability matrix

| Capability | filesystem | s3 / minio / r2 | gcs | azure |
|---|---|---|---|---|
| buckets (list/create/delete/exists/info) | yes | yes | yes | yes |
| objects (list/head/get/put/copy/move/delete) | yes | yes | yes | yes |
| prefix + delimiter listing, pagination | yes | yes | yes | yes |
| user metadata get/set | yes | yes | yes | yes |
| presigned URLs | no | yes | yes (needs service-account key) | yes (needs account key) |
| bucket location | no | yes | yes | no |

## Installation

Pick the extra that matches what you want to run (provider extras are additive):

| Extra | Installs | Use when |
|-------|----------|----------|
| `objectstore-mcp[mcp]` | Slim MCP server only (`agent-utilities[mcp]` — FastMCP/FastAPI) | You only run the **MCP server** (smallest install / image) |
| `objectstore-mcp[agent]` | Full agent runtime (`agent-utilities[agent,logfire]` — Pydantic AI + the epistemic-graph engine) | You run the **integrated A2A agent** |
| `objectstore-mcp[all]` | Everything (`mcp` + `agent` + `s3` + `gcs` + `azure` + `logfire`) | Development / both surfaces |

```bash
pip install objectstore-mcp            # core: local filesystem backend only
pip install objectstore-mcp[mcp]       # slim MCP server (FastMCP/FastAPI)
pip install objectstore-mcp[agent]     # full A2A agent runtime + epistemic-graph engine
pip install objectstore-mcp[mcp,s3]    # + boto3 (S3, MinIO, R2)
pip install objectstore-mcp[mcp,gcs]   # + google-cloud-storage
pip install objectstore-mcp[mcp,azure] # + azure-storage-blob
pip install objectstore-mcp[all]       # everything (MCP + agent + all providers)
```

### Container images (`:mcp` vs `:agent`)

One multi-stage `docker/Dockerfile` builds two right-sized images, selected by `--target`:

| Image tag | Build target | Contents | Entrypoint |
|-----------|--------------|----------|------------|
| `knucklessg1/objectstore-mcp:mcp` | `--target mcp` | `objectstore-mcp[mcp]` — **slim**, no engine/`pydantic-ai`/`dspy`/`llama-index`/`tree-sitter` | `objectstore-mcp` |
| `knucklessg1/objectstore-mcp:latest` | `--target agent` (default) | `objectstore-mcp[agent]` — **full** agent runtime + epistemic-graph engine | `objectstore-agent` |

```bash
docker build --target mcp   -t knucklessg1/objectstore-mcp:mcp    docker/   # slim MCP server
docker build --target agent -t knucklessg1/objectstore-mcp:latest docker/   # full agent
```

Or pull a prebuilt image:

```bash
docker pull knucklessg1/objectstore-mcp:mcp      # slim MCP server
docker pull knucklessg1/objectstore-mcp:latest   # full agent (default)
```

> The `:mcp` tag is the **slim MCP-server image** (built from
> `docker/Dockerfile --target mcp`, installing `objectstore-mcp[mcp]`). The default
> `:latest` tag is the **full agent image** (`--target agent`, `objectstore-mcp[agent]`)
> which also bundles the Pydantic AI agent and the epistemic-graph engine — use it
> when you run `objectstore-agent` (the agent), not just the MCP server.

### Knowledge-graph database (`epistemic-graph`)

The **full agent** (`[agent]` / `:latest`) embeds the **epistemic-graph** engine (pulled in
transitively via `agent-utilities[agent]`). For production — or to share one knowledge graph
across multiple agents — run **epistemic-graph as its own database container** and point the
agent at it instead of embedding it. Deployment recipes (single-node + Raft HA), connection
config, and the full database architecture (with diagrams) are documented in the
[epistemic-graph deployment guide](https://knuckles-team.github.io/epistemic-graph/deployment/).
The slim `[mcp]` server does **not** require the database.

## Configuration (environment)

| Var | Default | Meaning |
|---|---|---|
| `OBJECTSTORE_STORES` | _(empty)_ | JSON: store name → `{backend, bucket?, endpoint?, profile?, region?, root?, project?, connection_string?}` |
| `OBJECTSTORE_DEFAULT_STORE` | first configured store, else `local` | Store used when a tool call omits `store` |
| `OBJECTSTORE_FS_ROOT` | `~/.local/share/objectstore-mcp` | Root of the implicit `local` filesystem store |
| `OBJECTSTORE_MAX_GET_BYTES` | `10485760` (10 MiB) | Cap on `objects get` |
| `OBJECTSTORE_MAX_PUT_BYTES` | `10485760` (10 MiB) | Cap on `objects put` |
| `OBJECTSTORE_MAX_TRANSFER_BYTES` | `104857600` (100 MiB) | Cap per transfer (single or batch total) |
| `OBJECTSTORE_MAX_BATCH_KEYS` | `100` | Cap on batch delete / dir transfer key counts |
| `OBJECTSTORE_MAX_LIST_KEYS` | `1000` | Cap on one listing page |
| `OBJECTSTORE_ALLOW_DELETE` | `true` | Object deletes (delete/delete_batch/move) |
| `OBJECTSTORE_ALLOW_BUCKET_DELETE` | `false` | Bucket deletes (empty buckets only) |
| `OBJECTSTORETOOL` | `True` | Register the objectstore tool set |
| `HOST` / `PORT` / `TRANSPORT` | `0.0.0.0` / `8000` / `stdio` | MCP server bind + transport (`stdio`, `streamable-http`, `sse`) |
| `AUTH_TYPE` | `none` | MCP auth mode (container image) |
| `ENABLE_OTEL` | `True` | OTEL/Langfuse telemetry export |
| `EUNOMIA_TYPE` / `EUNOMIA_POLICY_FILE` | `none` / `mcp_policies.json` | Eunomia access-governance middleware |
| `DEFAULT_AGENT_NAME` / `AGENT_DESCRIPTION` / `AGENT_SYSTEM_PROMPT` | identity defaults | A2A agent server identity overrides |
| `MCP_URL` | _(empty)_ | MCP endpoint the A2A agent connects to |

Provider credentials resolve through each SDK's own chain — boto3's
resolution order for S3 (env keys, `~/.aws` profiles via the store's
`profile`, SSO, instance metadata), `GOOGLE_APPLICATION_CREDENTIALS` /
Application Default Credentials for GCS, and
`AZURE_STORAGE_CONNECTION_STRING` for Azure Blob.

### Example store registry

```json
{
  "media":   {"backend": "s3", "bucket": "media-prod", "profile": "prod", "region": "us-east-1"},
  "minio":   {"backend": "s3", "endpoint": "http://minio.arpa:9000"},
  "r2":      {"backend": "s3", "endpoint": "https://<account>.r2.cloudflarestorage.com"},
  "reports": {"backend": "gcs", "bucket": "acme-reports"},
  "archive": {"backend": "azure", "bucket": "archive"},
  "scratch": {"backend": "filesystem", "root": "~/scratch-store"}
}
```

## Usage

```bash
objectstore-mcp                                   # stdio
objectstore-mcp --transport streamable-http --host 0.0.0.0 --port 8000
```

Example tool calls (any MCP client):

```jsonc
// Write then read a text object on the zero-infra local store
{"tool": "buckets",  "arguments": {"action": "create", "params_json": "{\"bucket\": \"notes\"}"}}
{"tool": "objects",  "arguments": {"action": "put",    "params_json": "{\"bucket\": \"notes\", \"key\": \"todo.md\", \"text\": \"- ship it\"}"}}
{"tool": "objects",  "arguments": {"action": "get",    "params_json": "{\"bucket\": \"notes\", \"key\": \"todo.md\"}"}}

// Preview then execute a prefix-scoped batch delete on a named S3 store
{"tool": "objects", "arguments": {"store": "media", "action": "delete_batch", "params_json": "{\"bucket\": \"media-prod\", \"prefix\": \"tmp/\"}"}}
{"tool": "objects", "arguments": {"store": "media", "action": "delete_batch", "params_json": "{\"bucket\": \"media-prod\", \"prefix\": \"tmp/\", \"dry_run\": false}"}}
```

## MCP config

> **Install the slim `[mcp]` extra.** The example below installs
> `objectstore-mcp[mcp]` — the MCP-server extra that pulls only the FastMCP /
> FastAPI tooling (`agent-utilities[mcp]`). It deliberately **excludes** the heavy
> agent runtime (the epistemic-graph engine, `pydantic-ai`, `dspy`, `llama-index`,
> `tree-sitter`), so `uvx`/container installs are dramatically smaller and faster.
> Add the provider extras you need (`[mcp,s3]`, `[mcp,gcs]`, `[mcp,azure]`); use the
> full `[agent]` extra only when you need the integrated Pydantic AI agent
> (see [Installation](#installation)).

```json
{
  "mcpServers": {
    "objectstore-mcp": {
      "command": "uvx",
      "args": ["--from", "objectstore-mcp[mcp]", "objectstore-mcp"],
      "env": {
        "OBJECTSTORE_STORES": "{\"minio\": {\"backend\": \"s3\", \"endpoint\": \"http://minio.arpa:9000\"}}",
        "OBJECTSTORE_DEFAULT_STORE": "local"
      }
    }
  }
}
```

Run the A2A agent server against a live MCP server:

```bash
objectstore-agent --mcp-url http://localhost:8000/mcp --host 0.0.0.0 --port 9001
```

<!-- BEGIN GENERATED: additional-deployment-options -->
### Additional Deployment Options

`objectstore-mcp` can also run as a **local container** (Docker / Podman / `uv`) or be
consumed from a **remote deployment**. The
[Deployment guide](https://knuckles-team.github.io/objectstore-mcp/deployment/) has full, copy-paste
`mcp_config.json` for all four transports — **stdio**, **streamable-http**,
**local container / uv**, and **remote URL**:

- **Local container / uv** — launch the server from `mcp_config.json` via `uvx`,
  `docker run`, or `podman run`, or point at a local streamable-http container by `url`.
- **Remote URL** — connect to a server deployed behind Caddy at
  `http://objectstore-mcp.arpa/mcp` using the `"url"` key.
<!-- END GENERATED: additional-deployment-options -->

## Docker deployment

```bash
docker compose -f docker/mcp.compose.yml up -d      # MCP server only
docker compose -f docker/agent.compose.yml up -d    # MCP + A2A agent
curl -s http://localhost:8000/health                 # {"status":"OK"}
```

Both services read configuration from `../.env` (copy
[`.env.example`](.env.example)); see [`docs/deployment.md`](docs/deployment.md).

## Development

```bash
pip install -e .[test]
pytest                       # full suite (cloud SDKs not required)
pip install -e .[test-s3]    # adds boto3+moto integration tests
pre-commit run --all-files
```

See [`docs/`](docs/) for architecture, concepts, and deployment details.

## License

MIT — see [LICENSE](LICENSE).


<!-- BEGIN agent-os-genesis-deploy (generated; do not edit between markers) -->

## Deploy with `agent-os-genesis`

This package can be provisioned for you — skill-guided — by the **`agent-os-genesis`**
universal skill (its *single-package deploy mode*): it picks your install method, seeds
secrets to OpenBao/Vault (or `.env`), trusts your enterprise CA, registers the MCP
server, and verifies it — the same machinery that stands up the whole Agent OS, narrowed
to just this package. Ask your agent to **"deploy `objectstore-mcp` with agent-os-genesis"**.

| Install mode | Command |
|------|---------|
| Bare-metal, prod (PyPI) | `uvx objectstore-mcp` · or `uv tool install objectstore-mcp` |
| Bare-metal, dev (editable) | `uv pip install -e ".[all]"` · or `pip install -e ".[all]"` |
| Container, prod | deploy `knucklessg1/objectstore-mcp:latest` via docker-compose / swarm / podman / podman-compose / kubernetes |
| Container, dev (editable) | deploy `docker/compose.dev.yml` (source-mounted at `/src`; edits live on restart) |

Secrets are read-existing + seeded via `vault_sync` — you are only prompted for what's missing.

<!-- END agent-os-genesis-deploy -->

## Environment Variables

<!-- ENV-VARS-TABLE:START -->

#### Package environment variables

| Variable | Example | Description |
|----------|---------|-------------|
| `HOST` | `0.0.0.0` |  |
| `PORT` | `8000` |  |
| `TRANSPORT` | `stdio` | options: stdio, streamable-http, sse |
| `ENABLE_OTEL` | `True` |  |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:8080/api/public/otel` |  |
| `OTEL_EXPORTER_OTLP_PUBLIC_KEY` | `pk-...` |  |
| `OTEL_EXPORTER_OTLP_SECRET_KEY` | `sk-...` |  |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | `http/protobuf` |  |
| `EUNOMIA_TYPE` | `none` | options: none, embedded, remote |
| `EUNOMIA_POLICY_FILE` | `mcp_policies.json` |  |
| `EUNOMIA_REMOTE_URL` | `http://eunomia-server:8000` |  |
| `OBJECTSTORE_STORES` | `{"media": {"backend": "s3", "bucket": "media-prod", "profile": "prod"}, "minio": {"backend": "s3", "endpoint": "http://minio.arpa:9000"}, "reports": {"backend": "gcs", "bucket": "acme-reports"}, "archive": {"backend": "azure", "bucket": "archive"}}` | Named stores (JSON). The zero-infra "local" filesystem store always exists. |
| `OBJECTSTORE_DEFAULT_STORE` | `local` |  |
| `OBJECTSTORE_FS_ROOT` | `~/.local/share/objectstore-mcp` |  |
| `OBJECTSTORE_MAX_GET_BYTES` | `10485760` | Safety limits (bytes / keys) |
| `OBJECTSTORE_MAX_PUT_BYTES` | `10485760` |  |
| `OBJECTSTORE_MAX_TRANSFER_BYTES` | `104857600` |  |
| `OBJECTSTORE_MAX_BATCH_KEYS` | `100` |  |
| `OBJECTSTORE_MAX_LIST_KEYS` | `1000` |  |
| `OBJECTSTORE_ALLOW_DELETE` | `true` |  |
| `OBJECTSTORE_ALLOW_BUCKET_DELETE` | `false` |  |
| `AZURE_STORAGE_CONNECTION_STRING` | — | Azure is the exception — the code reads this connection string directly: |
| `DEFAULT_AGENT_NAME` | `ObjectStore Agent` |  |
| `AGENT_DESCRIPTION` | `AI agent for object-storage operations.` |  |
| `AGENT_SYSTEM_PROMPT` | — |  |
| `MCP_URL` | `http://localhost:8000/mcp` |  |
| `OBJECTSTORETOOL` | `True` |  |

#### Inherited agent-utilities variables (apply to every connector)

| Variable | Example | Description |
|----------|---------|-------------|
| `MCP_TOOL_MODE` | `condensed` | Tool surface: `condensed` | `verbose` | `both` |
| `MCP_ENABLED_TOOLS` | — | Comma-separated tool allow-list |
| `MCP_DISABLED_TOOLS` | — | Comma-separated tool deny-list |
| `MCP_ENABLED_TAGS` | — | Comma-separated tag allow-list |
| `MCP_DISABLED_TAGS` | — | Comma-separated tag deny-list |
| `MCP_CLIENT_AUTH` | — | Outbound MCP auth (`oidc-client-credentials` for fleet calls) |
| `OIDC_CLIENT_ID` | — | OIDC client id (service-account auth) |
| `OIDC_CLIENT_SECRET` | — | OIDC client secret (service-account auth) |
| `DEBUG` | `False` | Verbose logging |
| `PYTHONUNBUFFERED` | `1` | Unbuffered stdout (recommended in containers) |
| `PROVIDER` | `openai` | LLM provider for the agent |
| `MODEL_ID` | `gpt-4o` | Model id for the agent |
| `ENABLE_WEB_UI` | `True` | Serve the AG-UI web interface |

_27 package + 13 inherited variable(s). Auto-generated from `.env.example` + the shared agent-utilities set — do not edit._
<!-- ENV-VARS-TABLE:END -->
