Skip to main content
Glama
ysh-fe

Content Automation MCP Server

by ysh-fe
README.md
# Content Automation

Automated content publishing for Pinterest and Instagram with AI-powered content generation and scheduling.

## Features

- āœ… **MCP Integration** ⭐ - Natural language control through AI assistants (primary way of working)
  - šŸ“– [Detailed MCP description](./MCP.md) - setup, prompt examples, troubleshooting
- āœ… **TypeScript** - Full type safety for reliability and better IDE support
- āœ… **Late API** - Publishing to Instagram and Pinterest through a unified API
- āœ… **Local Image Processing** - Automatic conversion (HEIC/PNG/GIF → JPG) and center-crop to platform aspect ratios via `sharp`
- āœ… **SCP Upload to CDN** - Processed images are automatically uploaded to your CDN server via `scp`
- āœ… Unique AI-powered content generation for Pinterest and Instagram (OpenAI with customizable prompts)
- āœ… Publication scheduling through Late API (no local scheduler needed)
- āœ… **Instagram Stories Support** - Publishing Stories through Late API
- āœ… **Instagram Carousel** - Publish 2–10 photos in one post (via MCP)
- āœ… Publishing to both platforms with a single request
- āœ… Processed image tracking
- āœ… OpenAI integration for content generation (optional)
- āœ… CLI commands - alternative way of working through terminal

## Installation

1. Install dependencies:
```bash
npm install
```

2. Build the TypeScript project:
```bash
npm run build
```

3. Copy `.env.example` to `.env` and fill it:
```bash
cp .env.example .env
```

4. Configure environment variables in `.env`:
   - **Late API** (required): Get API key at [getlate.dev](https://getlate.dev)
   - **CDN Base URL** (required): Specify the base CDN URL where images are hosted
   - **SCP Upload** (optional): Configure SCP to auto-upload processed local images to CDN
   - **OpenAI API** (optional): For AI-powered content generation
   - **Content Prompt** (optional): Customize content generation by copying `prompts/content-prompt.example.txt` to `prompts/content-prompt.txt` and editing it

## Getting API Keys

### Late API (Required) ⭐

The project uses **only Late API** for publishing to Instagram and Pinterest.

1. Register at [getlate.dev](https://getlate.dev)
2. Get API key
3. Connect Instagram and Pinterest accounts
4. Get Profile ID and Account IDs from the dashboard
5. Add to `.env`:
   ```
   LATE_API_KEY=your_key
   LATE_PROFILE_ID=your_profile_id
   LATE_INSTAGRAM_ACCOUNT_ID=your_instagram_account_id
   LATE_PINTEREST_ACCOUNT_ID=your_pinterest_account_id
   ```

**Late API Benefits:**
- āœ… No local scheduler needed - Late API publishes on schedule automatically
- āœ… Reliable scheduling on Late servers
- āœ… **Publishing to Instagram AND Pinterest with a single request** - saves API requests!
- āœ… Free plan available (60 requests/minute)
- āœ… Support for 13 platforms (Instagram, Pinterest, Facebook, LinkedIn, Twitter/X, TikTok, YouTube, Reddit, Bluesky, Threads, Google Business, Telegram, Snapchat)

**Supported Platforms:** Instagram, Pinterest, Facebook, LinkedIn, Twitter/X, TikTok, YouTube, Reddit, Bluesky, Threads, Google Business, Telegram, Snapchat

## Usage

The project supports **two ways of working**:

1. **Through MCP agent** (recommended) - Natural language control through AI assistant
2. **Through CLI commands** - Traditional way through terminal

### Setup Check

Before starting, check API connection:

```bash
npm run check
```

This will check:
- Late API connection (required)
- CDN configuration (required)
- SCP upload configuration (optional)
- OpenAI API availability (optional)

### CLI Commands

#### Publishing Images

```bash
npm run publish -- --image=IMG_5857.jpg [options]
```

**Image Input:**
- **Local file** (recommended): Place source images (HEIC, JPG, PNG, GIF) in the images directory. By default the system looks in `images/` (relative to project root); you can override with `IMAGES_SOURCE_DIR` in `.env` (e.g. an absolute path like `/path/to/story-matcher`). The system will convert to JPG, center-crop to platform aspect ratios, upload to CDN via SCP, and use the CDN URLs for publishing.
- **CDN URL**: If the image is already on CDN, pass the full URL — processing is skipped.
- **Filename only**: If SCP is not configured, the filename is resolved against `CDN_BASE_URL` directly (existing behavior).

**Examples:**
```bash
# Local image: auto-process + upload + publish to all platforms
npm run publish -- --image=IMG_5857.HEIC

# Publish in 1 hour
npm run publish -- --image=IMG_5857.jpg 1

# Draft with publication date
npm run publish -- --image=IMG_5857.jpg --date="tomorrow 18:00" --draft

# With context
npm run publish -- --image=IMG_5857.jpg 1 --context="City center"

# Instagram post only
npm run publish -- --image=IMG_5857.jpg --post

# Instagram story only
npm run publish -- --image=IMG_5857.jpg --story

# Instagram story + Pinterest pin (comma-separated)
npm run publish -- --image=IMG_5857.jpg --types=story,pin

# Instagram post + Pinterest pin
npm run publish -- --image=IMG_5857.jpg --types=post,pin

# Pinterest pin only
npm run publish -- --image=IMG_5857.jpg --pin

# With draft
npm run publish -- --image=IMG_5857.jpg --story --draft
```

**Options:**
- `--image=<filename>` - Image filename (required in CLI mode)
- `--context=<text>` - Context for content generation
- `--types=<list>` - Content types comma-separated: `post`, `story`, `pin` (e.g., `--types=story,pin`)
- `--post` - Instagram post only (short form for `--types=post`)
- `--story` - Instagram story only (short form for `--types=story`)
- `--pin` - Pinterest pin only (short form for `--types=pin`)
- (no flags) - All three types (default): Instagram post, Instagram story, Pinterest pin
- `--date=<date>` - Publication date (YYYY-MM-DD HH:MM or "tomorrow 18:00")
- `<hours>` - Alternative to --date: number of hours from current moment
- `--draft` or `-d` - Create as draft

**Content Types:**
- Default (nothing specified) - Publishes all three types: Instagram post, Instagram story, Pinterest pin
- `--types=post,story,pin` - Specify types comma-separated (e.g., `--types=story,pin`)
- `--post` - Publishes only Instagram post
- `--story` - Publishes only Instagram story
- `--pin` - Publishes only Pinterest pin (without Instagram)

**Detailed Documentation:**
- [CLI.md](./CLI.md) - Detailed CLI description with usage examples

### Building the Project

```bash
# Build TypeScript to JavaScript
npm run build

# Build and watch for changes (auto-recompiles on file changes)
npm run watch
```

### Using MCP in Dev Mode

When developing with MCP, use automatic recompilation:

```bash
# Terminal: Run watch for automatic recompilation
npm run watch
```

**How it works:**
1. `watch` automatically recompiles TypeScript when files change
2. Cursor automatically restarts MCP server when `dist/mcp/mcp-server.js` changes
3. You can immediately use updated MCP tools in Cursor chat

**Important:** After changing code in `src/mcp/` or related modules, changes will apply automatically through watch + MCP restart in Cursor.

**šŸ”§ Dev Mode (dry-run):**
- In `watch` mode, `DEV_MODE=true` is automatically enabled
- **Requests to Late API are NOT sent** - instead, JSON ready to send is output
- This allows testing logic without real publications
- For real requests, use `npm run watch:prod` or simply `npm run build` + commands

### Using MCP Agent (Recommended) ⭐

**MCP (Model Context Protocol)** - This is the primary and recommended way to work with the project. Manage publications through AI assistant using natural language directly in Cursor, Claude, or other AI tools.

#### Quick Start with MCP

1. **Build the project:**
   ```bash
   npm run build
   ```

2. **MCP server is automatically available in Cursor** (via `.cursor/mcp.json`)

3. **Use natural language:**
   ```
   "Publish IMG_0802.HEIC tomorrow at 18:00 with context 'Concert hall, evening'"
   "Show list of available images"
   "Generate content for IMG_1460.JPG"
   "Schedule Story for IMG_2228.JPG today at 20:00"
   ```

**MCP Benefits:**
- āœ… **Natural language** - No need to remember commands
- āœ… **AI Integration** - Works directly in Cursor/Claude
- āœ… **Smart automation** - AI can plan publications itself
- āœ… **Contextual understanding** - AI understands your intentions

**Detailed Documentation:**
- [MCP.md](./MCP.md) - Setup, prompt examples, troubleshooting

### Using CLI Commands

Alternative way of working through terminal. Detailed documentation: [CLI.md](./CLI.md)

## Local Image Processing

The system can process local source images automatically — convert formats, crop to platform aspect ratios, and upload to CDN.

### How It Works

```
IMG_123.HEIC  (source image from IMAGES_SOURCE_DIR)
        │
        ā”œā”€ sharp: read → center-crop 4:5  → .processed/post/IMG_123.jpg
        ā”œā”€ sharp: read → center-crop 9:16 → .processed/story/IMG_123.jpg
        ā”œā”€ sharp: read → center-crop 2:3  → .processed/pin/IMG_123.jpg
        │
        ā”œā”€ scp: upload to CDN server
        │
        └─ CDN URLs used for publishing via Late API
```

### Supported Source Formats

HEIC, JPG, JPEG, PNG, GIF — all converted to JPEG (quality 90).

### Aspect Ratios (center-crop)

| Content Type | Ratio | Example Resolution |
|---|---|---|
| Instagram Post (`post`) | 4:5 | 1080Ɨ1350 |
| Instagram Story (`story`) | 9:16 | 1080Ɨ1920 |
| Pinterest Pin (`pin`) | 2:3 | 1000Ɨ1500 |

### Setup

1. **Place source images** in the images directory (default: `images/`, override with `IMAGES_SOURCE_DIR` in `.env`).

2. **Configure SCP upload** in `.env` (all 4 variables required together):
   ```
   SCP_HOST=cdn.example.com
   SCP_USER=deploy
   SCP_KEY_PATH=~/.ssh/id_rsa
   SCP_REMOTE_BASE_PATH=/var/www/cdn/images
   ```

3. **Ensure CDN_BASE_URL is set** — used to build public URLs from uploaded files.

### Caching

Processed images are cached in `.processed/`. If the output file already exists, processing is skipped. Delete `.processed/` to force reprocessing.

### Without SCP (URL-only mode)

If SCP is not configured, the system works as before — pass image filenames or URLs directly, and CDN URLs are built from `CDN_BASE_URL`. Local image processing is only triggered when a local filename is provided and SCP is configured.

## Project Structure

The project is organized on a **modular principle** for better scalability and support.

### Main Modules:

- **`src/core/`** - Core infrastructure (cache with LRU eviction, retry with exponential backoff, rate limiting, logger, error definitions, config, validation schemas)
- **`src/services/`** - API services (Late API, CDN uploader)
- **`src/generators/`** - Content generators (AI-powered with customizable prompts)
- **`src/processors/`** - Data processors (image pipeline, sharp processor)
- **`src/utils/`** - Utilities (CDN URL builder, scheduling, date and shared helpers)
- **`src/cli/`** - CLI commands and entry points
- **`src/mcp/`** - MCP integration for AI assistants
- **`src/types/`** - TypeScript types and interfaces
- **`prompts/`** - Content generation prompts (customizable)
- **`.processed/`** - Processed image cache (gitignored)

### Modular Structure Benefits:

āœ… **Separation of concerns** - Each module is responsible for its area
āœ… **Singleton pattern** - Services are exported as singleton instances
āœ… **Direct imports** - Simplified structure without intermediate index.ts
āœ… **Scalability** - Easy to add new services
āœ… **Testability** - Each module can be tested separately


## Content Format

### Pinterest
- **Title**: SEO-optimized headline
- **Description**: Description with keywords
- **Keywords**: List of keywords
- **CTA**: Call to action (leads to Instagram)

### Instagram Posts
- **Caption**: Emotional story text
- **Hashtags**: 3-5 relevant hashtags (customizable via content prompt)

### Instagram Stories
- Stories do not support long captions
- Text overlays can be used (via API)
- User mentions (user_tags) are supported
- Automatically disappear after 24 hours

## Limitations and Capabilities

### Late API
- āœ… **Only Late API is used** for publishing to Instagram and Pinterest
- āœ… Native scheduling through Late API
- āœ… Does not require local scheduler
- āœ… Publishing to both platforms with a single request
- āœ… Support for media file upload or using public CDN URLs
- āœ… **Supports Stories** - Can publish Stories through Late API
- āš ļø **Stories limitations**: Can only schedule for the next 24 hours
- āš ļø Free plan: 60 requests/minute

### CDN for Images

Two modes of operation:

**1. Local processing + SCP upload (recommended):**
- Place source images in the images directory (default: `images/`, configurable via `IMAGES_SOURCE_DIR`)
- Configure SCP variables in `.env`
- System auto-converts, crops, uploads, and builds CDN URLs
- Supports HEIC, JPG, PNG, GIF input formats

**2. Direct CDN URLs (no local processing):**
- Images must already be on CDN in correct aspect ratios
- Specify `CDN_BASE_URL` in `.env`
- Pass filename or full URL in commands
- The system builds CDN URLs by content type:
  - `post` → `{CDN_BASE_URL}/post/{filename}`
  - `story` → `{CDN_BASE_URL}/story/{filename}`
  - `pin` → `{CDN_BASE_URL}/pin/{filename}`
- **Important:** Filenames are case-sensitive - use exact case as on CDN


## Content Generation Customization

The content generation prompt can be customized to match your brand voice and target audience:

1. **Copy the example prompt:**
   ```bash
   cp prompts/content-prompt.example.txt prompts/content-prompt.txt
   ```

2. **Edit `prompts/content-prompt.txt`** with your custom instructions:
   - Adjust tone and style
   - Modify hashtag requirements
   - Change content focus and goals
   - Update output format if needed

3. **The custom prompt is automatically loaded** - no code changes needed!

**Note:** `prompts/content-prompt.txt` is ignored by git, so your customizations remain private. The example file (`content-prompt.example.txt`) serves as a template and is version-controlled.

## Security

- Never commit `.env` file
- Never commit `prompts/content-prompt.txt` (already in `.gitignore`)
- Store API keys in a secure place
- Regularly check Late API key expiration
- Do not share API keys publicly
- `SCP_KEY_PATH` points to your SSH key — ensure it has proper file permissions (`chmod 600`)

## Troubleshooting

### Late API Connection Error
- Check API key correctness: `npm run check`
- Make sure both accounts (Instagram and Pinterest) are connected in Late
- Check API key expiration

### Publication Error
- Make sure Instagram and Pinterest accounts are connected in Late
- Check account access rights
- Make sure images are available on CDN at specified URLs

### Images Not Processing
- Check file formats (supported: HEIC, JPG, PNG, GIF)
- Make sure files are not corrupted
- **Check filename case** - Filename is case-sensitive (e.g., `IMG_5857.jpg` ≠ `img_5857.jpg`)
- Verify the source file exists in the images source directory (default: `images/`, or `IMAGES_SOURCE_DIR` if set)
- Check `.processed/` for cached output — delete to force reprocessing

### SCP Upload Failing
- Verify all 4 SCP variables are set in `.env`: `SCP_HOST`, `SCP_USER`, `SCP_KEY_PATH`, `SCP_REMOTE_BASE_PATH`
- Check that your SSH key exists at the specified `SCP_KEY_PATH` and has correct permissions (`chmod 600`)
- Test SSH connectivity manually: `ssh -i ~/.ssh/id_rsa user@host`
- Check that the remote base path exists and is writable

### CDN Not Working
- Make sure `CDN_BASE_URL` is specified correctly
- Check that images are available at specified URLs
- Make sure URLs are publicly accessible

## License

MIT

Maintenance

ActivityInactive
ResponsivenessNo issues