mcp-server-s3 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +121 -0
- package/dist/index.js +346 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ofer Shapira
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# mcp-server-s3
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/mcp-server-s3)
|
|
4
|
+
[](https://www.npmjs.com/package/mcp-server-s3)
|
|
5
|
+
[](https://github.com/ofershap/mcp-server-s3/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
Manage AWS S3 buckets and objects from your AI assistant. Browse files, upload and download content, and generate presigned URLs.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx mcp-server-s3
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
> Works with Claude Desktop, Cursor, VS Code Copilot, and any MCP client. Uses your existing AWS credentials (`~/.aws/credentials` or environment variables).
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
<sub>Demo built with <a href="https://github.com/ofershap/remotion-readme-kit">remotion-readme-kit</a></sub>
|
|
20
|
+
|
|
21
|
+
## Why
|
|
22
|
+
|
|
23
|
+
S3 is the most widely used cloud storage service, but managing it from the command line means remembering `aws s3 ls`, `aws s3 cp`, presigned URL syntax, and various flags. Google has an official MCP for GCS, Cloudflare has one for R2, but AWS S3 doesn't have a polished standalone MCP server on npm. This one lets you ask your assistant to list buckets, download a config file, upload content, or generate a temporary sharing link. It uses the standard AWS credential chain, so if your CLI already works, this works too.
|
|
24
|
+
|
|
25
|
+
## Tools
|
|
26
|
+
|
|
27
|
+
| Tool | What it does |
|
|
28
|
+
| --------------- | ------------------------------------------------------ |
|
|
29
|
+
| `list_buckets` | List all S3 buckets in your AWS account. |
|
|
30
|
+
| `list_objects` | List objects in a bucket, with optional prefix filter. |
|
|
31
|
+
| `get_object` | Download and read an object's content as text. |
|
|
32
|
+
| `put_object` | Upload text content to an S3 object. |
|
|
33
|
+
| `delete_object` | Delete an object from a bucket. |
|
|
34
|
+
| `presigned_url` | Generate a temporary presigned URL for an object. |
|
|
35
|
+
| `bucket_info` | Check if a bucket exists and get basic info. |
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
### Cursor
|
|
40
|
+
|
|
41
|
+
Add to `.cursor/mcp.json`:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"mcpServers": {
|
|
46
|
+
"s3": {
|
|
47
|
+
"command": "npx",
|
|
48
|
+
"args": ["mcp-server-s3"],
|
|
49
|
+
"env": {
|
|
50
|
+
"AWS_REGION": "us-east-1",
|
|
51
|
+
"AWS_ACCESS_KEY_ID": "your-access-key",
|
|
52
|
+
"AWS_SECRET_ACCESS_KEY": "your-secret-key"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Claude Desktop
|
|
60
|
+
|
|
61
|
+
Add to `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"s3": {
|
|
67
|
+
"command": "npx",
|
|
68
|
+
"args": ["mcp-server-s3"],
|
|
69
|
+
"env": {
|
|
70
|
+
"AWS_REGION": "us-east-1",
|
|
71
|
+
"AWS_ACCESS_KEY_ID": "your-access-key",
|
|
72
|
+
"AWS_SECRET_ACCESS_KEY": "your-secret-key"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### VS Code
|
|
80
|
+
|
|
81
|
+
Configure the MCP server to run `npx mcp-server-s3` with `AWS_REGION`, `AWS_ACCESS_KEY_ID`, and `AWS_SECRET_ACCESS_KEY` in the environment.
|
|
82
|
+
|
|
83
|
+
## Authentication
|
|
84
|
+
|
|
85
|
+
The server uses the standard AWS credential chain:
|
|
86
|
+
|
|
87
|
+
1. **Environment variables**: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`
|
|
88
|
+
2. **Shared credentials file**: `~/.aws/credentials`
|
|
89
|
+
3. **IAM roles**: when running on EC2, ECS, Lambda, or similar
|
|
90
|
+
|
|
91
|
+
Set `AWS_REGION` (defaults to `us-east-1`) and make sure your credentials have the necessary S3 permissions: `s3:ListBuckets`, `s3:ListBucket`, `s3:GetObject`, `s3:PutObject`, `s3:DeleteObject`, `s3:HeadBucket`.
|
|
92
|
+
|
|
93
|
+
## Example prompts
|
|
94
|
+
|
|
95
|
+
- "List all my S3 buckets"
|
|
96
|
+
- "Show me the files in my-bucket/uploads/"
|
|
97
|
+
- "Download the config.json from my-bucket"
|
|
98
|
+
- "Upload this content to my-bucket/notes.txt"
|
|
99
|
+
- "Generate a presigned URL for this file that expires in 1 hour"
|
|
100
|
+
|
|
101
|
+
## Development
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install
|
|
105
|
+
npm run typecheck
|
|
106
|
+
npm run build
|
|
107
|
+
npm test
|
|
108
|
+
npm run format
|
|
109
|
+
npm run lint
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Author
|
|
113
|
+
|
|
114
|
+
**Ofer Shapira**
|
|
115
|
+
|
|
116
|
+
[](https://linkedin.com/in/ofershap)
|
|
117
|
+
[](https://github.com/ofershap)
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT © 2026 Ofer Shapira
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
// src/s3.ts
|
|
9
|
+
import {
|
|
10
|
+
S3Client,
|
|
11
|
+
ListBucketsCommand,
|
|
12
|
+
ListObjectsV2Command,
|
|
13
|
+
GetObjectCommand,
|
|
14
|
+
PutObjectCommand,
|
|
15
|
+
DeleteObjectCommand,
|
|
16
|
+
HeadBucketCommand
|
|
17
|
+
} from "@aws-sdk/client-s3";
|
|
18
|
+
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
19
|
+
var defaultRegion = process.env.AWS_REGION ?? "us-east-1";
|
|
20
|
+
function createClient() {
|
|
21
|
+
return new S3Client({ region: defaultRegion });
|
|
22
|
+
}
|
|
23
|
+
async function listBuckets() {
|
|
24
|
+
const client = createClient();
|
|
25
|
+
const result = await client.send(new ListBucketsCommand({}));
|
|
26
|
+
const buckets = result.Buckets ?? [];
|
|
27
|
+
return buckets.map((b) => ({
|
|
28
|
+
name: b.Name ?? "",
|
|
29
|
+
creationDate: b.CreationDate
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
async function listObjects(bucket, prefix, maxKeys = 100) {
|
|
33
|
+
const client = createClient();
|
|
34
|
+
const result = await client.send(
|
|
35
|
+
new ListObjectsV2Command({
|
|
36
|
+
Bucket: bucket,
|
|
37
|
+
Prefix: prefix,
|
|
38
|
+
MaxKeys: maxKeys,
|
|
39
|
+
Delimiter: "/"
|
|
40
|
+
})
|
|
41
|
+
);
|
|
42
|
+
const items = [];
|
|
43
|
+
for (const cp of result.CommonPrefixes ?? []) {
|
|
44
|
+
if (cp.Prefix) {
|
|
45
|
+
items.push({ key: cp.Prefix, isPrefix: true });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (const obj of result.Contents ?? []) {
|
|
49
|
+
if (obj.Key) {
|
|
50
|
+
items.push({
|
|
51
|
+
key: obj.Key,
|
|
52
|
+
size: obj.Size,
|
|
53
|
+
lastModified: obj.LastModified,
|
|
54
|
+
isPrefix: false
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return items;
|
|
59
|
+
}
|
|
60
|
+
async function getObject(bucket, key) {
|
|
61
|
+
const client = createClient();
|
|
62
|
+
const result = await client.send(
|
|
63
|
+
new GetObjectCommand({ Bucket: bucket, Key: key })
|
|
64
|
+
);
|
|
65
|
+
const body = result.Body;
|
|
66
|
+
if (!body) {
|
|
67
|
+
throw new Error(`Object ${key} has no body`);
|
|
68
|
+
}
|
|
69
|
+
const chunks = [];
|
|
70
|
+
for await (const chunk of body) {
|
|
71
|
+
chunks.push(chunk);
|
|
72
|
+
}
|
|
73
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
74
|
+
}
|
|
75
|
+
async function putObject(bucket, key, content, contentType) {
|
|
76
|
+
const client = createClient();
|
|
77
|
+
await client.send(
|
|
78
|
+
new PutObjectCommand({
|
|
79
|
+
Bucket: bucket,
|
|
80
|
+
Key: key,
|
|
81
|
+
Body: Buffer.from(content, "utf-8"),
|
|
82
|
+
ContentType: contentType ?? "text/plain"
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
async function deleteObject(bucket, key) {
|
|
87
|
+
const client = createClient();
|
|
88
|
+
await client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
|
|
89
|
+
}
|
|
90
|
+
async function presignedUrl(bucket, key, expiresIn = 3600) {
|
|
91
|
+
const client = createClient();
|
|
92
|
+
const command = new GetObjectCommand({ Bucket: bucket, Key: key });
|
|
93
|
+
return getSignedUrl(client, command, { expiresIn });
|
|
94
|
+
}
|
|
95
|
+
async function bucketInfo(bucket) {
|
|
96
|
+
const client = createClient();
|
|
97
|
+
try {
|
|
98
|
+
await client.send(new HeadBucketCommand({ Bucket: bucket }));
|
|
99
|
+
return {
|
|
100
|
+
name: bucket,
|
|
101
|
+
exists: true,
|
|
102
|
+
region: defaultRegion
|
|
103
|
+
};
|
|
104
|
+
} catch {
|
|
105
|
+
return { name: bucket, exists: false };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/index.ts
|
|
110
|
+
function formatBuckets(buckets) {
|
|
111
|
+
if (buckets.length === 0) return "No buckets found.";
|
|
112
|
+
return buckets.map(
|
|
113
|
+
(b) => ` \u2022 ${b.name}${b.creationDate ? ` (created: ${b.creationDate.toISOString()})` : ""}`
|
|
114
|
+
).join("\n");
|
|
115
|
+
}
|
|
116
|
+
function formatObjects(items) {
|
|
117
|
+
if (items.length === 0) return "No objects found.";
|
|
118
|
+
return items.map((o) => {
|
|
119
|
+
if (o.isPrefix) return ` \u{1F4C1} ${o.key}`;
|
|
120
|
+
const size = o.size != null ? ` (${o.size} B)` : "";
|
|
121
|
+
const modified = o.lastModified != null ? ` \u2014 ${o.lastModified.toISOString()}` : "";
|
|
122
|
+
return ` \u{1F4C4} ${o.key}${size}${modified}`;
|
|
123
|
+
}).join("\n");
|
|
124
|
+
}
|
|
125
|
+
var server = new McpServer({
|
|
126
|
+
name: "mcp-server-s3",
|
|
127
|
+
version: "1.0.0"
|
|
128
|
+
});
|
|
129
|
+
server.tool(
|
|
130
|
+
"list_buckets",
|
|
131
|
+
"List all S3 buckets in your AWS account.",
|
|
132
|
+
{},
|
|
133
|
+
async () => {
|
|
134
|
+
try {
|
|
135
|
+
const buckets = await listBuckets();
|
|
136
|
+
const text = `Buckets (${buckets.length}):
|
|
137
|
+
|
|
138
|
+
${formatBuckets(buckets)}`;
|
|
139
|
+
return { content: [{ type: "text", text }] };
|
|
140
|
+
} catch (err) {
|
|
141
|
+
return {
|
|
142
|
+
content: [
|
|
143
|
+
{
|
|
144
|
+
type: "text",
|
|
145
|
+
text: `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
isError: true
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
server.tool(
|
|
154
|
+
"list_objects",
|
|
155
|
+
"List objects in an S3 bucket. Optionally filter by prefix and limit count.",
|
|
156
|
+
{
|
|
157
|
+
bucket: z.string().describe("Bucket name"),
|
|
158
|
+
prefix: z.string().optional().describe("Key prefix (e.g. 'uploads/')"),
|
|
159
|
+
maxKeys: z.number().int().min(1).max(1e3).default(100).describe("Max objects to return")
|
|
160
|
+
},
|
|
161
|
+
async ({ bucket, prefix, maxKeys }) => {
|
|
162
|
+
try {
|
|
163
|
+
const items = await listObjects(bucket, prefix, maxKeys);
|
|
164
|
+
const header = prefix ? `Objects in s3://${bucket}/${prefix} (max ${maxKeys}):
|
|
165
|
+
|
|
166
|
+
` : `Objects in s3://${bucket}/ (max ${maxKeys}):
|
|
167
|
+
|
|
168
|
+
`;
|
|
169
|
+
const text = header + formatObjects(items);
|
|
170
|
+
return { content: [{ type: "text", text }] };
|
|
171
|
+
} catch (err) {
|
|
172
|
+
return {
|
|
173
|
+
content: [
|
|
174
|
+
{
|
|
175
|
+
type: "text",
|
|
176
|
+
text: `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
isError: true
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
server.tool(
|
|
185
|
+
"get_object",
|
|
186
|
+
"Download and read the contents of an S3 object as text.",
|
|
187
|
+
{
|
|
188
|
+
bucket: z.string().describe("Bucket name"),
|
|
189
|
+
key: z.string().describe("Object key")
|
|
190
|
+
},
|
|
191
|
+
async ({ bucket, key }) => {
|
|
192
|
+
try {
|
|
193
|
+
const content = await getObject(bucket, key);
|
|
194
|
+
return {
|
|
195
|
+
content: [
|
|
196
|
+
{
|
|
197
|
+
type: "text",
|
|
198
|
+
text: `Content of s3://${bucket}/${key}:
|
|
199
|
+
|
|
200
|
+
${content}`
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
};
|
|
204
|
+
} catch (err) {
|
|
205
|
+
return {
|
|
206
|
+
content: [
|
|
207
|
+
{
|
|
208
|
+
type: "text",
|
|
209
|
+
text: `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
210
|
+
}
|
|
211
|
+
],
|
|
212
|
+
isError: true
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
server.tool(
|
|
218
|
+
"put_object",
|
|
219
|
+
"Upload text content to an S3 object.",
|
|
220
|
+
{
|
|
221
|
+
bucket: z.string().describe("Bucket name"),
|
|
222
|
+
key: z.string().describe("Object key"),
|
|
223
|
+
content: z.string().describe("Content to upload"),
|
|
224
|
+
contentType: z.string().optional().describe("Content-Type header (default: text/plain)")
|
|
225
|
+
},
|
|
226
|
+
async ({ bucket, key, content, contentType }) => {
|
|
227
|
+
try {
|
|
228
|
+
await putObject(bucket, key, content, contentType);
|
|
229
|
+
return {
|
|
230
|
+
content: [
|
|
231
|
+
{
|
|
232
|
+
type: "text",
|
|
233
|
+
text: `\u2705 Uploaded ${content.length} bytes to s3://${bucket}/${key}`
|
|
234
|
+
}
|
|
235
|
+
]
|
|
236
|
+
};
|
|
237
|
+
} catch (err) {
|
|
238
|
+
return {
|
|
239
|
+
content: [
|
|
240
|
+
{
|
|
241
|
+
type: "text",
|
|
242
|
+
text: `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
243
|
+
}
|
|
244
|
+
],
|
|
245
|
+
isError: true
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
server.tool(
|
|
251
|
+
"delete_object",
|
|
252
|
+
"Delete an object from an S3 bucket.",
|
|
253
|
+
{
|
|
254
|
+
bucket: z.string().describe("Bucket name"),
|
|
255
|
+
key: z.string().describe("Object key")
|
|
256
|
+
},
|
|
257
|
+
async ({ bucket, key }) => {
|
|
258
|
+
try {
|
|
259
|
+
await deleteObject(bucket, key);
|
|
260
|
+
return {
|
|
261
|
+
content: [
|
|
262
|
+
{
|
|
263
|
+
type: "text",
|
|
264
|
+
text: `\u2705 Deleted s3://${bucket}/${key}`
|
|
265
|
+
}
|
|
266
|
+
]
|
|
267
|
+
};
|
|
268
|
+
} catch (err) {
|
|
269
|
+
return {
|
|
270
|
+
content: [
|
|
271
|
+
{
|
|
272
|
+
type: "text",
|
|
273
|
+
text: `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
274
|
+
}
|
|
275
|
+
],
|
|
276
|
+
isError: true
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
server.tool(
|
|
282
|
+
"presigned_url",
|
|
283
|
+
"Generate a presigned URL for temporary access to an S3 object.",
|
|
284
|
+
{
|
|
285
|
+
bucket: z.string().describe("Bucket name"),
|
|
286
|
+
key: z.string().describe("Object key"),
|
|
287
|
+
expiresIn: z.number().int().min(60).max(604800).default(3600).describe("URL expiry in seconds (default: 1 hour)")
|
|
288
|
+
},
|
|
289
|
+
async ({ bucket, key, expiresIn }) => {
|
|
290
|
+
try {
|
|
291
|
+
const url = await presignedUrl(bucket, key, expiresIn);
|
|
292
|
+
const hrs = Math.round(expiresIn / 3600);
|
|
293
|
+
const text = `Presigned URL for s3://${bucket}/${key} (valid ~${hrs}h):
|
|
294
|
+
|
|
295
|
+
${url}`;
|
|
296
|
+
return { content: [{ type: "text", text }] };
|
|
297
|
+
} catch (err) {
|
|
298
|
+
return {
|
|
299
|
+
content: [
|
|
300
|
+
{
|
|
301
|
+
type: "text",
|
|
302
|
+
text: `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
303
|
+
}
|
|
304
|
+
],
|
|
305
|
+
isError: true
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
server.tool(
|
|
311
|
+
"bucket_info",
|
|
312
|
+
"Check if a bucket exists and get basic info.",
|
|
313
|
+
{
|
|
314
|
+
bucket: z.string().describe("Bucket name")
|
|
315
|
+
},
|
|
316
|
+
async ({ bucket }) => {
|
|
317
|
+
try {
|
|
318
|
+
const info = await bucketInfo(bucket);
|
|
319
|
+
const status = info.exists ? "\u2705 Exists" : "\u274C Not found / no access";
|
|
320
|
+
const region = info.region ? `
|
|
321
|
+
Region: ${info.region}` : "";
|
|
322
|
+
const text = `Bucket: ${bucket}
|
|
323
|
+
${status}${region}`;
|
|
324
|
+
return { content: [{ type: "text", text }] };
|
|
325
|
+
} catch (err) {
|
|
326
|
+
return {
|
|
327
|
+
content: [
|
|
328
|
+
{
|
|
329
|
+
type: "text",
|
|
330
|
+
text: `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
331
|
+
}
|
|
332
|
+
],
|
|
333
|
+
isError: true
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
);
|
|
338
|
+
async function main() {
|
|
339
|
+
const transport = new StdioServerTransport();
|
|
340
|
+
await server.connect(transport);
|
|
341
|
+
}
|
|
342
|
+
main().catch((err) => {
|
|
343
|
+
console.error("Fatal error:", err);
|
|
344
|
+
process.exit(1);
|
|
345
|
+
});
|
|
346
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/s3.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport {\n listBuckets,\n listObjects,\n getObject,\n putObject,\n deleteObject,\n presignedUrl,\n bucketInfo,\n type ObjectInfo,\n} from \"./s3.js\";\n\nfunction formatBuckets(\n buckets: { name: string; creationDate?: Date }[],\n): string {\n if (buckets.length === 0) return \"No buckets found.\";\n return buckets\n .map(\n (b) =>\n ` • ${b.name}${b.creationDate ? ` (created: ${b.creationDate.toISOString()})` : \"\"}`,\n )\n .join(\"\\n\");\n}\n\nfunction formatObjects(items: ObjectInfo[]): string {\n if (items.length === 0) return \"No objects found.\";\n return items\n .map((o) => {\n if (o.isPrefix) return ` 📁 ${o.key}`;\n const size = o.size != null ? ` (${o.size} B)` : \"\";\n const modified =\n o.lastModified != null ? ` — ${o.lastModified.toISOString()}` : \"\";\n return ` 📄 ${o.key}${size}${modified}`;\n })\n .join(\"\\n\");\n}\n\nconst server = new McpServer({\n name: \"mcp-server-s3\",\n version: \"1.0.0\",\n});\n\nserver.tool(\n \"list_buckets\",\n \"List all S3 buckets in your AWS account.\",\n {},\n async () => {\n try {\n const buckets = await listBuckets();\n const text = `Buckets (${buckets.length}):\\n\\n${formatBuckets(buckets)}`;\n return { content: [{ type: \"text\", text }] };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n },\n);\n\nserver.tool(\n \"list_objects\",\n \"List objects in an S3 bucket. Optionally filter by prefix and limit count.\",\n {\n bucket: z.string().describe(\"Bucket name\"),\n prefix: z.string().optional().describe(\"Key prefix (e.g. 'uploads/')\"),\n maxKeys: z\n .number()\n .int()\n .min(1)\n .max(1000)\n .default(100)\n .describe(\"Max objects to return\"),\n },\n async ({ bucket, prefix, maxKeys }) => {\n try {\n const items = await listObjects(bucket, prefix, maxKeys);\n const header = prefix\n ? `Objects in s3://${bucket}/${prefix} (max ${maxKeys}):\\n\\n`\n : `Objects in s3://${bucket}/ (max ${maxKeys}):\\n\\n`;\n const text = header + formatObjects(items);\n return { content: [{ type: \"text\", text }] };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n },\n);\n\nserver.tool(\n \"get_object\",\n \"Download and read the contents of an S3 object as text.\",\n {\n bucket: z.string().describe(\"Bucket name\"),\n key: z.string().describe(\"Object key\"),\n },\n async ({ bucket, key }) => {\n try {\n const content = await getObject(bucket, key);\n return {\n content: [\n {\n type: \"text\",\n text: `Content of s3://${bucket}/${key}:\\n\\n${content}`,\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n },\n);\n\nserver.tool(\n \"put_object\",\n \"Upload text content to an S3 object.\",\n {\n bucket: z.string().describe(\"Bucket name\"),\n key: z.string().describe(\"Object key\"),\n content: z.string().describe(\"Content to upload\"),\n contentType: z\n .string()\n .optional()\n .describe(\"Content-Type header (default: text/plain)\"),\n },\n async ({ bucket, key, content, contentType }) => {\n try {\n await putObject(bucket, key, content, contentType);\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Uploaded ${content.length} bytes to s3://${bucket}/${key}`,\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n },\n);\n\nserver.tool(\n \"delete_object\",\n \"Delete an object from an S3 bucket.\",\n {\n bucket: z.string().describe(\"Bucket name\"),\n key: z.string().describe(\"Object key\"),\n },\n async ({ bucket, key }) => {\n try {\n await deleteObject(bucket, key);\n return {\n content: [\n {\n type: \"text\",\n text: `✅ Deleted s3://${bucket}/${key}`,\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n },\n);\n\nserver.tool(\n \"presigned_url\",\n \"Generate a presigned URL for temporary access to an S3 object.\",\n {\n bucket: z.string().describe(\"Bucket name\"),\n key: z.string().describe(\"Object key\"),\n expiresIn: z\n .number()\n .int()\n .min(60)\n .max(604800)\n .default(3600)\n .describe(\"URL expiry in seconds (default: 1 hour)\"),\n },\n async ({ bucket, key, expiresIn }) => {\n try {\n const url = await presignedUrl(bucket, key, expiresIn);\n const hrs = Math.round(expiresIn / 3600);\n const text = `Presigned URL for s3://${bucket}/${key} (valid ~${hrs}h):\\n\\n${url}`;\n return { content: [{ type: \"text\", text }] };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n },\n);\n\nserver.tool(\n \"bucket_info\",\n \"Check if a bucket exists and get basic info.\",\n {\n bucket: z.string().describe(\"Bucket name\"),\n },\n async ({ bucket }) => {\n try {\n const info = await bucketInfo(bucket);\n const status = info.exists ? \"✅ Exists\" : \"❌ Not found / no access\";\n const region = info.region ? `\\nRegion: ${info.region}` : \"\";\n const text = `Bucket: ${bucket}\\n${status}${region}`;\n return { content: [{ type: \"text\", text }] };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n },\n);\n\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((err) => {\n console.error(\"Fatal error:\", err);\n process.exit(1);\n});\n","import {\n S3Client,\n ListBucketsCommand,\n ListObjectsV2Command,\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n HeadBucketCommand,\n} from \"@aws-sdk/client-s3\";\nimport { getSignedUrl } from \"@aws-sdk/s3-request-presigner\";\n\nconst defaultRegion = process.env.AWS_REGION ?? \"us-east-1\";\n\nfunction createClient(): S3Client {\n return new S3Client({ region: defaultRegion });\n}\n\nexport interface BucketInfo {\n name: string;\n creationDate?: Date;\n}\n\nexport interface ObjectInfo {\n key: string;\n size?: number;\n lastModified?: Date;\n isPrefix?: boolean;\n}\n\nexport interface BucketDetails {\n name: string;\n exists: boolean;\n region?: string;\n}\n\nexport async function listBuckets(): Promise<BucketInfo[]> {\n const client = createClient();\n const result = await client.send(new ListBucketsCommand({}));\n const buckets = result.Buckets ?? [];\n return buckets.map((b) => ({\n name: b.Name ?? \"\",\n creationDate: b.CreationDate,\n }));\n}\n\nexport async function listObjects(\n bucket: string,\n prefix?: string,\n maxKeys = 100,\n): Promise<ObjectInfo[]> {\n const client = createClient();\n const result = await client.send(\n new ListObjectsV2Command({\n Bucket: bucket,\n Prefix: prefix,\n MaxKeys: maxKeys,\n Delimiter: \"/\",\n }),\n );\n const items: ObjectInfo[] = [];\n\n for (const cp of result.CommonPrefixes ?? []) {\n if (cp.Prefix) {\n items.push({ key: cp.Prefix, isPrefix: true });\n }\n }\n for (const obj of result.Contents ?? []) {\n if (obj.Key) {\n items.push({\n key: obj.Key,\n size: obj.Size,\n lastModified: obj.LastModified,\n isPrefix: false,\n });\n }\n }\n return items;\n}\n\nexport async function getObject(bucket: string, key: string): Promise<string> {\n const client = createClient();\n const result = await client.send(\n new GetObjectCommand({ Bucket: bucket, Key: key }),\n );\n const body = result.Body;\n if (!body) {\n throw new Error(`Object ${key} has no body`);\n }\n const chunks: Uint8Array[] = [];\n for await (const chunk of body as AsyncIterable<Uint8Array>) {\n chunks.push(chunk);\n }\n return Buffer.concat(chunks).toString(\"utf-8\");\n}\n\nexport async function putObject(\n bucket: string,\n key: string,\n content: string,\n contentType?: string,\n): Promise<void> {\n const client = createClient();\n await client.send(\n new PutObjectCommand({\n Bucket: bucket,\n Key: key,\n Body: Buffer.from(content, \"utf-8\"),\n ContentType: contentType ?? \"text/plain\",\n }),\n );\n}\n\nexport async function deleteObject(bucket: string, key: string): Promise<void> {\n const client = createClient();\n await client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));\n}\n\nexport async function presignedUrl(\n bucket: string,\n key: string,\n expiresIn = 3600,\n): Promise<string> {\n const client = createClient();\n const command = new GetObjectCommand({ Bucket: bucket, Key: key });\n return getSignedUrl(client, command, { expiresIn });\n}\n\nexport async function bucketInfo(bucket: string): Promise<BucketDetails> {\n const client = createClient();\n try {\n await client.send(new HeadBucketCommand({ Bucket: bucket }));\n return {\n name: bucket,\n exists: true,\n region: defaultRegion,\n };\n } catch {\n return { name: bucket, exists: false };\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-server-s3",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for AWS S3 — list buckets, browse objects, upload/download files, and generate presigned URLs from your IDE",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mcp-server-s3": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"test:watch": "vitest",
|
|
17
|
+
"test:coverage": "vitest run --coverage",
|
|
18
|
+
"lint": "eslint . && prettier --check .",
|
|
19
|
+
"format": "prettier --write .",
|
|
20
|
+
"prepare": "husky"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"mcp-server",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"aws",
|
|
27
|
+
"s3",
|
|
28
|
+
"storage",
|
|
29
|
+
"cloud",
|
|
30
|
+
"files",
|
|
31
|
+
"bucket",
|
|
32
|
+
"presigned-url",
|
|
33
|
+
"ai",
|
|
34
|
+
"llm",
|
|
35
|
+
"claude",
|
|
36
|
+
"cursor"
|
|
37
|
+
],
|
|
38
|
+
"author": "Ofer Shapira",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/ofershap/mcp-server-s3.git"
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/ofershap/mcp-server-s3/issues"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/ofershap/mcp-server-s3#readme",
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@aws-sdk/client-s3": "^3.700.0",
|
|
50
|
+
"@aws-sdk/s3-request-presigner": "^3.700.0",
|
|
51
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
52
|
+
"zod": "^3.25.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@eslint/js": "^9.0.0",
|
|
56
|
+
"@types/node": "^22.0.0",
|
|
57
|
+
"eslint": "^9.0.0",
|
|
58
|
+
"eslint-config-prettier": "^10.0.0",
|
|
59
|
+
"husky": "^9.0.0",
|
|
60
|
+
"lint-staged": "^15.0.0",
|
|
61
|
+
"prettier": "^3.0.0",
|
|
62
|
+
"tsup": "^8.0.0",
|
|
63
|
+
"typescript": "^5.7.0",
|
|
64
|
+
"typescript-eslint": "^8.0.0",
|
|
65
|
+
"vitest": "^3.2.0"
|
|
66
|
+
},
|
|
67
|
+
"lint-staged": {
|
|
68
|
+
"*.{ts,tsx,js}": "eslint --fix",
|
|
69
|
+
"*.{json,md,yml,yaml}": "prettier --write"
|
|
70
|
+
}
|
|
71
|
+
}
|