Content
# 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
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available