Skip to main content
Glama
FerhatDundar

s3-mcp-connector

by FerhatDundar

๐Ÿชฃ s3-mcp-connector

Talk to Amazon S3 โ€” or LocalStack, MinIO, any S3-compatible store โ€” from an MCP-speaking agent.

CI CodeQL Latest release MCP Registry Go Reference License: MIT Tested with LocalStack MCP Conventional Commits PRs Welcome


A single static Go binary that speaks the Model Context Protocol and exposes 8 tools for working with S3: list buckets, list/read/write/delete objects, create/delete buckets. Point it at real AWS or at a local LocalStack/MinIO instance with one environment variable โ€” same binary, same tools, zero code changes.

No Python, no uv, no runtime dependency to install โ€” just a binary and an .mcp.json.

โœจ Why this exists

An agent that can only talk about your S3 buckets isn't that useful. This gives it hands: it can look inside a bucket, read a config file out of it, drop a report back in, or clean up a stale prefix โ€” safely, with guardrails on size and destructive actions built in.

๐Ÿงฐ Tools

Tool

What it does

Write?

s3_list_buckets

List buckets visible to the credentials

s3_list_objects

List objects in a bucket, optional prefix, paginated

s3_head_object

Object metadata (size, type, ETag) without downloading

s3_get_object

Read an object's content โ€” text or base64, size-capped

s3_put_object

Write a small text/base64 object (โ‰ค 5 MB)

โœ๏ธ

s3_delete_object

Delete one object

๐Ÿ—‘๏ธ destructive

s3_create_bucket

Create a bucket

โœ๏ธ

s3_delete_bucket

Delete an empty bucket

๐Ÿ—‘๏ธ destructive

Every tool accepts an optional response_format: markdown (default, pretty tables for a chat UI) or json (for programmatic use).

s3_get_object auto-detects text vs. binary content and caps output at 200,000 bytes by default (raise via max_bytes, hard cap 5,000,000) โ€” it's built for reading configs, logs, and small data files, not bulk transfer. Reach for the AWS CLI or SDK directly for large objects.

๐Ÿš€ Quickstart

Fastest path: grab a prebuilt bundle from the latest release โ€” download s3-mcp-connector-plugin-<version>-<os>-<arch>.zip, unzip it, and point Cowork/Claude at the plugin/ folder inside (see step 4 of SETUP.md). No Go toolchain required.

From source:

# 1. Build
cd go-server
go mod tidy
go build -o s3-connector-server .
cp s3-connector-server ../plugin/servers/go/

# 2. Spin up LocalStack to test against (no AWS account needed)
cd ..
docker compose up -d

# 3. Point the connector at it
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_REGION=us-east-1
export S3_ENDPOINT_URL=http://localhost:4566
export S3_FORCE_PATH_STYLE=true

./go-server/s3-connector-server   # serves MCP over stdio

Or make build && make localstack-up โ€” see the Makefile for every shortcut (test, vet, fmt, lint, tidy, localstack-down).

Full walkthrough โ€” including wiring this up as a Claude/Cowork plugin and switching from LocalStack to real AWS โ€” is in SETUP.md.

๐Ÿ” Configuration

Everything is environment variables, passed through by the plugin's .mcp.json:

Variable

Purpose

Default

AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY

AWS credentials. Any non-empty values work against LocalStack.

โ€”

AWS_REGION

Region to use.

us-east-1

S3_ENDPOINT_URL

Custom endpoint. Leave unset for real AWS.

(unset)

S3_FORCE_PATH_STYLE

true for LocalStack/MinIO (path-style addressing).

false

๐Ÿงช Quality bar

This isn't a toy script โ€” it's got the same checks you'd expect from a production Go service:

  • โœ… Unit tests for every input-validation path (go test ./...)

  • โœ… go vet + gofmt clean

  • โœ… golangci-lint (govet, staticcheck, errcheck, gosec, and more)

  • โœ… govulncheck โ€” no known vulnerabilities in the dependency graph

  • โœ… CodeQL static security analysis on every push

  • โœ… End-to-end verified against real LocalStack โ€” every tool (create/delete bucket, put/get/head/list/delete object, and the 404 error path) was exercised against a live S3-compatible service, not mocks

  • โœ… Dependabot keeps Go modules and Actions current

All of it runs in CI on every push and PR.

๐Ÿท๏ธ Releases & versioning

Versions follow semver and are cut automatically by release-please from Conventional Commits on main:

  • fix: ... โ†’ patch (v0.1.0 โ†’ v0.1.1)

  • feat: ... โ†’ minor (v0.1.1 โ†’ v0.2.0)

  • feat!: ... / BREAKING CHANGE: footer โ†’ major (v0.2.0 โ†’ v1.0.0)

Every merged PR updates a standing "chore(main): release vX.Y.Z" PR with an auto-generated CHANGELOG.md. Merging that PR:

  1. tags the release and publishes it on GitHub

  2. builds and attaches zipped, ready-to-install plugin bundles for linux/darwin/windows ร— amd64/arm64

  3. regenerates server.json from those exact assets (fresh version + SHA-256 hashes) and publishes it to the official MCP Registry via mcp-publisher, authenticated with GitHub OIDC โ€” no stored secrets

See .github/workflows/release-please.yml and .github/workflows/publish-mcp-registry.yml (also runnable by hand for an existing tag via workflow_dispatch).

๐Ÿ“ Layout

s3-mcp-connector/
โ”œโ”€โ”€ README.md                  โ† you are here
โ”œโ”€โ”€ SETUP.md                   โ† step-by-step setup guide (LocalStack + real AWS)
โ”œโ”€โ”€ CONTRIBUTING.md             โ† how to contribute
โ”œโ”€โ”€ CODE_OF_CONDUCT.md
โ”œโ”€โ”€ SECURITY.md                 โ† vulnerability reporting
โ”œโ”€โ”€ CODEOWNERS
โ”œโ”€โ”€ LICENSE                     โ† MIT
โ”œโ”€โ”€ Makefile                    โ† build / test / lint / localstack shortcuts
โ”œโ”€โ”€ docker-compose.yml          โ† LocalStack, for local testing
โ”œโ”€โ”€ .golangci.yml                โ† lint rules
โ”œโ”€โ”€ release-please-config.json  โ† semver/changelog automation config
โ”œโ”€โ”€ .release-please-manifest.json
โ”œโ”€โ”€ server.json                  โ† MCP Registry manifest (regenerated fresh per release by CI)
โ”œโ”€โ”€ scripts/
โ”‚   โ””โ”€โ”€ render-server-json.sh    โ† rebuilds server.json from a release's zip assets
โ”œโ”€โ”€ .github/
โ”‚   โ”œโ”€โ”€ workflows/
โ”‚   โ”‚   โ”œโ”€โ”€ ci.yml                     โ† build, vet, test, lint, govulncheck
โ”‚   โ”‚   โ”œโ”€โ”€ codeql.yml                 โ† security scanning
โ”‚   โ”‚   โ”œโ”€โ”€ pr-title.yml               โ† Conventional Commits PR title check
โ”‚   โ”‚   โ”œโ”€โ”€ release-please.yml         โ† version PRs, tagging, GitHub releases
โ”‚   โ”‚   โ”œโ”€โ”€ publish-mcp-registry.yml   โ† publishes server.json to the MCP Registry
โ”‚   โ”‚   โ””โ”€โ”€ rebuild-release-assets.yml โ† manual re-attach fallback
โ”‚   โ”œโ”€โ”€ ISSUE_TEMPLATE/
โ”‚   โ”œโ”€โ”€ PULL_REQUEST_TEMPLATE.md
โ”‚   โ””โ”€โ”€ dependabot.yml
โ”œโ”€โ”€ go-server/                  โ† the MCP server source
โ”‚   โ”œโ”€โ”€ main.go
โ”‚   โ”œโ”€โ”€ main_test.go
โ”‚   โ”œโ”€โ”€ go.mod / go.sum
โ”‚   โ””โ”€โ”€ README.md
โ””โ”€โ”€ plugin/                     โ† installable Cowork/Claude plugin
    โ”œโ”€โ”€ .claude-plugin/plugin.json
    โ”œโ”€โ”€ .mcp.json                โ† holds credentials locally โ€” never commit real ones
    โ””โ”€โ”€ servers/go/              โ† compiled binary goes here

๐Ÿค Contributing

PRs and issues are very welcome โ€” see CONTRIBUTING.md for the full guide (setup, coding conventions, how to add a new tool) and the Code of Conduct.

main is protected: every change, including the maintainer's, lands via pull request with CI green. PR titles must follow Conventional Commits โ€” that's what drives the automatic versioning above.

Found a security issue? Please follow SECURITY.md instead of opening a public issue.

๐Ÿ“„ License

MIT ยฉ Ferhat Dundar