intentkit-storage
v1.0.1
Published
S3-compatible file storage adapter for IntentKit
Maintainers
Readme
intentkit-storage
S3-compatible file storage adapter for IntentKit — upload, download, list, and generate signed URLs via the provider system.
Works with AWS S3, MinIO, Cloudflare R2, DigitalOcean Spaces, and any S3-compatible storage service.
Install
npm install intentkit-storageQuick Start
import { defineFunction, IntentRegistry, createContext, serve, z } from 'intentkit';
import { createStorageProvider, type StorageClient } from 'intentkit-storage';
// Register your functions
const registry = new IntentRegistry().register(uploadFile, listFiles, deleteFile, getFileUrl);
// Create context (no database needed for storage-only projects)
const context = await createContext({ events: true });
// Boot MCP server with storage provider
await serve({
name: 'my-storage-agent',
registry,
context,
providers: [
createStorageProvider({
bucket: 'my-bucket',
region: 'us-east-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
}),
],
});Configuration
| Option | Default | Description |
|--------|---------|-------------|
| bucket | — | S3 bucket name (required) |
| region | 'us-east-1' | AWS region |
| endpoint | — | Custom endpoint for S3-compatible services |
| accessKeyId | — | AWS access key ID (omit for IAM role / instance profile) |
| secretAccessKey | — | AWS secret access key |
| forcePathStyle | false | Path-style addressing (required for MinIO) |
| signedUrlExpiry | 3600 | Default signed URL expiry in seconds |
| name | 'storage' | Provider name in ctx.providers |
Common Provider Configs
AWS S3:
createStorageProvider({
bucket: 'my-bucket',
region: 'us-east-1',
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
})AWS S3 with IAM Role (EC2 / ECS / Lambda — no credentials needed):
createStorageProvider({
bucket: 'my-bucket',
region: 'us-east-1',
})MinIO (local development):
createStorageProvider({
bucket: 'my-bucket',
endpoint: 'http://localhost:9000',
accessKeyId: 'minioadmin',
secretAccessKey: 'minioadmin',
forcePathStyle: true, // Required for MinIO
})Cloudflare R2:
createStorageProvider({
bucket: 'my-bucket',
endpoint: `https://${process.env.CF_ACCOUNT_ID}.r2.cloudflarestorage.com`,
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
region: 'auto',
})DigitalOcean Spaces:
createStorageProvider({
bucket: 'my-space',
endpoint: 'https://nyc3.digitaloceanspaces.com',
accessKeyId: process.env.DO_SPACES_KEY!,
secretAccessKey: process.env.DO_SPACES_SECRET!,
region: 'nyc3',
})Using in Functions
Access the storage client via ctx.providers.storage:
import { defineFunction, z } from 'intentkit';
import type { StorageClient } from 'intentkit-storage';
export const saveReport = defineFunction({
name: 'save_report',
intent: 'Save a generated report to file storage',
permissions: ['storage:write'],
requires: ['storage'], // Validates provider exists at startup
input: z.object({
name: z.string(),
content: z.string(),
}),
output: z.object({
key: z.string(),
url: z.string(),
}),
execute: async (input, ctx) => {
const storage = ctx.providers.storage as StorageClient;
const key = `reports/${Date.now()}-${input.name}.txt`;
await storage.upload(key, input.content, { contentType: 'text/plain' });
const url = await storage.getSignedUrl(key, 86400); // 24h link
return { key, url };
},
});Example Functions
The package includes 4 ready-to-use functions in functions/files.ts:
| Function | Intent | Permission |
|----------|--------|------------|
| upload_file | Upload a file to storage | storage:write |
| list_files | List files with optional prefix filter | storage:read |
| delete_file | Delete a file by its key | storage:write |
| get_file_url | Generate a temporary signed URL | storage:read |
Import and register them:
import { uploadFile, listFiles, deleteFile, getFileUrl } from 'intentkit-storage/functions';
const registry = new IntentRegistry()
.register(uploadFile, listFiles, deleteFile, getFileUrl);StorageClient API
The full client interface for custom function implementations:
interface StorageClient {
// Upload
upload(key: string, body: string | Buffer | ReadableStream, options?: UploadOptions): Promise<{ key: string; etag?: string }>;
// Download
download(key: string): Promise<{ body: ReadableStream; contentType?: string; size?: number }>;
// Delete
delete(key: string): Promise<void>;
// List
list(options?: ListOptions): Promise<ListResult>;
// Signed URLs
getSignedUrl(key: string, expiresIn?: number): Promise<string>;
// Utilities
exists(key: string): Promise<boolean>;
copy(sourceKey: string, destinationKey: string): Promise<void>;
ping(): Promise<boolean>;
}Claude Desktop Config
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"storage": {
"command": "node",
"args": ["path/to/your/serve.js"],
"env": {
"AWS_ACCESS_KEY_ID": "your-access-key",
"AWS_SECRET_ACCESS_KEY": "your-secret-key"
}
}
}
}Architecture
Claude (Dispatch / Desktop)
| MCP tool call
IntentKit (serve + permissions + hooks)
| ctx.providers.storage
intentkit-storage (StorageClientImpl)
|
@aws-sdk/client-s3
|
S3-Compatible Storage
(AWS S3 / MinIO / R2 / DO Spaces)The S3 client is created once at server startup and reused across requests. healthCheck() sends a HeadBucket command. The client is cleanly destroyed on shutdown via IntentKit's lifecycle manager.
License
MIT
