S3 Reader MCP Server
README.md
# S3 Reader MCP Server (Streamable HTTP, Cloud Run)
A Model Context Protocol (MCP) server that exposes **read-only** tools for
browsing and reading Amazon S3 objects, served over the **streamable-http**
transport (MCP spec 2025-03-26), ready to deploy on **Google Cloud Run**.
## Tools exposed
| Tool | Description |
|---|---|
| `list_buckets` | List all S3 buckets visible to the AWS credentials in use |
| `list_objects` | List/paginate objects in a bucket, with optional prefix/delimiter |
| `get_object_metadata` | HEAD an object: size, content-type, last-modified, etag |
| `read_object_text` | Read an object's body as text (size-capped) |
| `read_object_base64` | Read an object's raw bytes as base64 (for binary files) |
| `read_pdf_text` | Extract readable text from a PDF (page-by-page, with page markers) |
The MCP endpoint is served at: `POST /mcp` (this is the default
`streamable_http_path` from the SDK).
All tools are read-only — nothing in this server can create, overwrite, or
delete S3 data.
## 1. Local setup
```bash
pip install -r requirements.txt
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_REGION=us-east-1
export PORT=8080
python server.py
```
You should see a log line like:
```
Starting S3 MCP server on 0.0.0.0:8080 (region=us-east-1)
```
You can also put these values in a local `.env` file instead of exporting
them — `server.py` loads it automatically via `python-dotenv`. Never commit
`.env` to source control (it's already excluded in `.dockerignore`).
## 2. Test it with MCP Inspector
Before wiring this server up to any AI client, test it directly with the
official **MCP Inspector** — a small web UI that lets you call MCP tools by
hand and see the raw request/response. No install needed beyond Node:
```bash
npx @modelcontextprotocol/inspector
```
This opens a local UI (usually at `http://localhost:6274`). In the
connection panel:
1. **Transport type**: `Streamable HTTP`
2. **URL**: `http://localhost:8080/mcp` (or your Cloud Run URL + `/mcp` once deployed)
3. If your deployment requires auth (see step 6 below), add an
`Authorization: Bearer <token>` header in the Inspector's headers section.
4. Click **Connect**, then open the **Tools** tab — you should see all six
tools listed (`list_buckets`, `list_objects`, `get_object_metadata`,
`read_object_text`, `read_object_base64`, `read_pdf_text`).
5. Pick a tool, fill in its arguments (e.g. `list_buckets` needs none;
`list_objects` needs a `bucket`), and click **Run Tool** to see the
live result against your actual AWS account.
This is the fastest way to confirm the server is reachable, credentials are
working, and each tool's schema/response shape looks right — before you
point a real MCP client (Claude, another agent, etc.) at it.
If you'd rather stay on the command line, `curl` also works for a basic
smoke test:
```bash
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
```
## 3. AWS credentials & permissions
The server uses boto3's standard credential chain (env vars → shared config
→ IAM role). Grant the identity used at minimum:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListAllMyBuckets", "s3:ListBucket", "s3:GetObject"],
"Resource": ["arn:aws:s3:::*"]
}
]
}
```
Scope `Resource` down to specific bucket ARNs in production.
**Recommended for Cloud Run**: store AWS credentials in **Secret Manager**
rather than plain environment variables (see step 5 below). If you're
running entirely within AWS-adjacent infra, you could alternatively use
workload identity federation between GCP and AWS, but the simplest path is
an IAM user with an access key stored as a Cloud Run secret.
## 4. Build and push the container
```bash
export PROJECT_ID=your-gcp-project
export REGION=us-central1
export REPO=s3-mcp-server
gcloud auth login
gcloud config set project "$PROJECT_ID"
# One-time: create an Artifact Registry repo
gcloud artifacts repositories create "$REPO" \
--repository-format=docker \
--location="$REGION"
# Build with Cloud Build (no local Docker needed) and push
gcloud builds submit \
--tag "$REGION-docker.pkg.dev/$PROJECT_ID/$REPO/s3-mcp-server:latest"
```
(Alternatively, build locally with `docker build -t ... .` and
`docker push ...` if you have Docker installed and are authenticated to
Artifact Registry via `gcloud auth configure-docker`.)
## 5. Store AWS credentials as secrets
```bash
printf '%s' "$AWS_ACCESS_KEY_ID" | gcloud secrets create aws-access-key-id --data-file=-
printf '%s' "$AWS_SECRET_ACCESS_KEY" | gcloud secrets create aws-secret-access-key --data-file=-
```
## 6. Deploy to Cloud Run
```bash
gcloud run deploy s3-mcp-server \
--image "$REGION-docker.pkg.dev/$PROJECT_ID/$REPO/s3-mcp-server:latest" \
--region "$REGION" \
--platform managed \
--port 8080 \
--set-env-vars AWS_REGION=us-east-1 \
--set-secrets AWS_ACCESS_KEY_ID=aws-access-key-id:latest,AWS_SECRET_ACCESS_KEY=aws-secret-access-key:latest \
--min-instances 0 \
--max-instances 5 \
--no-allow-unauthenticated
```
Notes:
- `--no-allow-unauthenticated` requires callers to send a valid Google
identity token (`Authorization: Bearer <token>`). Use
`--allow-unauthenticated` only if you're deploying your own auth in front,
since this server exposes read access to your S3 data.
- Cloud Run injects `PORT` automatically; `server.py` reads it via
`os.environ["PORT"]`, so `--port 8080` above must match `EXPOSE 8080` in
the Dockerfile and the container's listening port (already aligned).
- Because the server is stateless (`stateless_http=True`), it's safe for
Cloud Run to route consecutive requests to different container instances
— no sticky sessions required.
## 7. Connect an MCP client
Point any MCP client that supports the streamable-http transport (including
MCP Inspector — see step 2) at:
```
https://<your-cloud-run-url>/mcp
```
If the service requires authentication, include a Google-signed identity
token as a bearer token, e.g. for quick manual testing:
```bash
TOKEN=$(gcloud auth print-identity-token)
curl -X POST https://<your-cloud-run-url>/mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```
## Files
- `server.py` — MCP server implementation (FastMCP, streamable-http transport)
- `requirements.txt` — Python dependencies
- `Dockerfile` — container build for Cloud Run
- `.dockerignore` — build context excludes
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues