Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Give agents more agency

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    vanman2024

    spaces-storage

    vanman2024/spaces-storage
    DevOps
    2

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    DigitalOcean Spaces (S3-compatible) object storage patterns

    SKILL.md

    Spaces Object Storage Skill

    Overview

    DigitalOcean Spaces provides S3-compatible object storage. Use it for:

    • File uploads (images, documents, media)
    • Static asset hosting
    • Application backups
    • Data lake storage

    Setup

    Create Space

    doctl spaces create my-space --region nyc3
    

    CDN (Built-in)

    Enable CDN for faster global access to your assets.

    # CDN endpoint
    https://my-space.nyc3.cdn.digitaloceanspaces.com/file.jpg
    
    # Direct endpoint
    https://my-space.nyc3.digitaloceanspaces.com/file.jpg
    

    SDK Integration

    Node.js (@aws-sdk/client-s3)

    import {
      S3Client,
      PutObjectCommand,
      GetObjectCommand,
      DeleteObjectCommand,
      ListObjectsV2Command,
    } from '@aws-sdk/client-s3';
    import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
    
    const s3 = new S3Client({
      endpoint: 'https://nyc3.digitaloceanspaces.com',
      region: 'nyc3',
      credentials: {
        accessKeyId: process.env.DIGITALOCEAN_SPACES_ACCESS_KEY!,
        secretAccessKey: process.env.DIGITALOCEAN_SPACES_SECRET_KEY!,
      },
    });
    
    const BUCKET = 'my-space';
    
    // Upload file
    async function uploadFile(key: string, body: Buffer, contentType: string) {
      await s3.send(
        new PutObjectCommand({
          Bucket: BUCKET,
          Key: key,
          Body: body,
          ContentType: contentType,
          ACL: 'public-read', // or "private"
        })
      );
    
      return `https://${BUCKET}.nyc3.cdn.digitaloceanspaces.com/${key}`;
    }
    
    // Download file
    async function downloadFile(key: string) {
      const response = await s3.send(
        new GetObjectCommand({
          Bucket: BUCKET,
          Key: key,
        })
      );
    
      return response.Body;
    }
    
    // Generate presigned URL (for private uploads)
    async function getPresignedUploadUrl(key: string, expiresIn = 3600) {
      const command = new PutObjectCommand({
        Bucket: BUCKET,
        Key: key,
      });
    
      return getSignedUrl(s3, command, { expiresIn });
    }
    
    // Generate presigned download URL (for private files)
    async function getPresignedDownloadUrl(key: string, expiresIn = 3600) {
      const command = new GetObjectCommand({
        Bucket: BUCKET,
        Key: key,
      });
    
      return getSignedUrl(s3, command, { expiresIn });
    }
    
    // List files
    async function listFiles(prefix = '') {
      const response = await s3.send(
        new ListObjectsV2Command({
          Bucket: BUCKET,
          Prefix: prefix,
        })
      );
    
      return response.Contents || [];
    }
    
    // Delete file
    async function deleteFile(key: string) {
      await s3.send(
        new DeleteObjectCommand({
          Bucket: BUCKET,
          Key: key,
        })
      );
    }
    

    Python (boto3)

    import boto3
    from botocore.config import Config
    
    session = boto3.session.Session()
    s3 = session.client(
        's3',
        region_name='nyc3',
        endpoint_url='https://nyc3.digitaloceanspaces.com',
        aws_access_key_id=os.environ['DIGITALOCEAN_SPACES_ACCESS_KEY'],
        aws_secret_access_key=os.environ['DIGITALOCEAN_SPACES_SECRET_KEY']
    )
    
    BUCKET = 'my-space'
    
    # Upload file
    def upload_file(key: str, file_path: str, content_type: str = None):
        extra_args = {'ACL': 'public-read'}
        if content_type:
            extra_args['ContentType'] = content_type
    
        s3.upload_file(file_path, BUCKET, key, ExtraArgs=extra_args)
        return f'https://{BUCKET}.nyc3.cdn.digitaloceanspaces.com/{key}'
    
    # Upload from memory
    def upload_bytes(key: str, data: bytes, content_type: str):
        s3.put_object(
            Bucket=BUCKET,
            Key=key,
            Body=data,
            ContentType=content_type,
            ACL='public-read'
        )
        return f'https://{BUCKET}.nyc3.cdn.digitaloceanspaces.com/{key}'
    
    # Download file
    def download_file(key: str, file_path: str):
        s3.download_file(BUCKET, key, file_path)
    
    # Generate presigned URL
    def get_presigned_url(key: str, expires_in: int = 3600):
        return s3.generate_presigned_url(
            'get_object',
            Params={'Bucket': BUCKET, 'Key': key},
            ExpiresIn=expires_in
        )
    
    # List files
    def list_files(prefix: str = ''):
        response = s3.list_objects_v2(Bucket=BUCKET, Prefix=prefix)
        return response.get('Contents', [])
    
    # Delete file
    def delete_file(key: str):
        s3.delete_object(Bucket=BUCKET, Key=key)
    

    Next.js Integration

    API Route for File Upload

    // app/api/upload/route.ts
    import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
    import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
    import { nanoid } from 'nanoid';
    
    const s3 = new S3Client({
      endpoint: 'https://nyc3.digitaloceanspaces.com',
      region: 'nyc3',
      credentials: {
        accessKeyId: process.env.DIGITALOCEAN_SPACES_ACCESS_KEY!,
        secretAccessKey: process.env.DIGITALOCEAN_SPACES_SECRET_KEY!,
      },
    });
    
    export async function POST(request: Request) {
      const { filename, contentType } = await request.json();
    
      const key = `uploads/${nanoid()}-${filename}`;
    
      const command = new PutObjectCommand({
        Bucket: process.env.DIGITALOCEAN_SPACES_BUCKET!,
        Key: key,
        ContentType: contentType,
        ACL: 'public-read',
      });
    
      const uploadUrl = await getSignedUrl(s3, command, { expiresIn: 3600 });
      const publicUrl = `https://${process.env.DIGITALOCEAN_SPACES_BUCKET}.nyc3.cdn.digitaloceanspaces.com/${key}`;
    
      return Response.json({ uploadUrl, publicUrl, key });
    }
    

    Client Upload Component

    "use client";
    
    import { useState } from "react";
    
    export function FileUpload({ onUpload }: { onUpload: (url: string) => void }) {
      const [uploading, setUploading] = useState(false);
    
      async function handleUpload(e: React.ChangeEvent<HTMLInputElement>) {
        const file = e.target.files?.[0];
        if (!file) return;
    
        setUploading(true);
    
        try {
          // Get presigned URL
          const response = await fetch("/api/upload", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
              filename: file.name,
              contentType: file.type,
            }),
          });
    
          const { uploadUrl, publicUrl } = await response.json();
    
          // Upload directly to Spaces
          await fetch(uploadUrl, {
            method: "PUT",
            headers: { "Content-Type": file.type },
            body: file,
          });
    
          onUpload(publicUrl);
        } finally {
          setUploading(false);
        }
      }
    
      return (
        <input
          type="file"
          onChange={handleUpload}
          disabled={uploading}
        />
      );
    }
    

    FastAPI Integration

    from fastapi import FastAPI, UploadFile
    import boto3
    from nanoid import generate
    
    app = FastAPI()
    
    s3 = boto3.client(
        's3',
        region_name='nyc3',
        endpoint_url='https://nyc3.digitaloceanspaces.com',
        aws_access_key_id=os.environ['DIGITALOCEAN_SPACES_ACCESS_KEY'],
        aws_secret_access_key=os.environ['DIGITALOCEAN_SPACES_SECRET_KEY']
    )
    
    BUCKET = os.environ['DIGITALOCEAN_SPACES_BUCKET']
    
    @app.post("/upload")
    async def upload_file(file: UploadFile):
        key = f"uploads/{generate()}-{file.filename}"
    
        s3.upload_fileobj(
            file.file,
            BUCKET,
            key,
            ExtraArgs={
                'ACL': 'public-read',
                'ContentType': file.content_type
            }
        )
    
        return {
            "url": f"https://{BUCKET}.nyc3.cdn.digitaloceanspaces.com/{key}",
            "key": key
        }
    

    Environment Variables

    # .env
    DIGITALOCEAN_SPACES_ACCESS_KEY=your_access_key
    DIGITALOCEAN_SPACES_SECRET_KEY=your_secret_key
    DIGITALOCEAN_SPACES_BUCKET=my-space
    DIGITALOCEAN_SPACES_REGION=nyc3
    DIGITALOCEAN_SPACES_ENDPOINT=https://nyc3.digitaloceanspaces.com
    

    CORS Configuration

    Configure CORS via the DigitalOcean console or API:

    {
      "CORSRules": [
        {
          "AllowedOrigins": ["https://myapp.com"],
          "AllowedMethods": ["GET", "PUT", "POST"],
          "AllowedHeaders": ["*"],
          "MaxAgeSeconds": 3000
        }
      ]
    }
    
    Recommended Servers
    GENESIS ProofRelay MCP Verifier
    GENESIS ProofRelay MCP Verifier
    Memory Tool
    Memory Tool
    Data Compliance Classifier MCP
    Data Compliance Classifier MCP
    Repository
    vanman2024/ai-dev-marketplace
    Files