Automates drafting and publishing articles to Substack and Medium with Apify and Notion integration
Recursos
2Instal·la
npx skillscat add oyi77/1ai-skills/content-publisher Instal·la des del registre de SkillsCat.
SKILL.md
Content Publisher Agent
Automates drafting and publishing articles to Substack and Medium with workflow automation.
Required Tools
MCP Servers
Apify MCP (Publishing Automation)
{
"mcpServers": {
"apify": {
"command": "npx",
"args": ["-y", "@apify/mcp-server"],
"env": { "APIFY_API_TOKEN": "${APIFY_API_TOKEN}" }
},
"notion": {
"command": "npx",
"args": ["-y", "@makenotion/mcp-server"],
"env": { "NOTION_API_KEY": "${NOTION_API_KEY}" }
},
"slack": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-slack"],
"env": { "SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}" }
}
}
}Tool Permissions
| Tool | Capabilities |
|---|---|
Bash(apify:*) |
Execute browser automation for publishing |
MCP(apify:*) |
Run web scraping/automation actors |
MCP(notion:*) |
Store editorial calendar, track drafts |
MCP(slack:*) |
Send approval notifications |
Authentication
Setup
Apify Token
export APIFY_API_TOKEN="your-token"Notion Integration
- Create integration at https://www.notion.so/my-integrations
- Share database with integration
Slack (optional)
export SLACK_BOT_TOKEN="xoxb-your-token"
Capabilities
- Drafting: Turn ideas into structured Markdown drafts
- Publishing: Navigate to platform editors and publish
- Cross-Posting: Sync content between platforms
- Scheduling: Schedule posts for future publication
- Editorial Calendar: Track in Notion
Pseudo Code
Example 1: Draft Article
// 1. Analyze request
const topic = "The impact of Agentic AI on coding";
const audience = "developers";
const tone = "professional";
// 2. Research if needed
const context = await apify.actor("apify/firecrawl-scraper", {
urls: [`https://news.ycombinator.com/?q=${topic}`]
});
// 3. Generate content
const draft = await generateArticle({ topic, audience, tone, context });
// 4. Save to drafts folder
const filename = `memory/drafts/${dateSlug(topic)}.md`;
await fs.write(filename, draft);
// 5. Log to Notion editorial calendar
await notion.createPage("Editorial Calendar", {
title: draft.title,
status: "Draft",
scheduledDate: null,
platform: "both"
});Example 2: Publish to Platform
// 1. Read draft
const content = await fs.read("memory/drafts/agentic-ai.md");
// 2. Navigate to platform
await browser.goto("https://medium.com/new-post");
// 3. Fill content
await browser.fill(".title-input", content.title);
await browser.fill(".body-editor", content.body);
// 4. Add tags
for (const tag of content.tags) {
await browser.click(".tag-input");
await browser.type(tag);
}
// 5. Publish or save draft
if (mode === "live") {
await browser.click(".publish-button");
await slack.notify("#content", `Published: ${content.title}`);
} else {
await browser.click(".save-draft");
}Example 3: Cross-Post to Both Platforms
// 1. Read content
const content = await fs.read(draftPath);
// 2. Convert for each platform
const mediumContent = convertToMedium(content);
const substackContent = convertToSubstack(content);
// 3. Publish to Medium
await publishToMedium(mediumContent);
// 4. Publish to Substack
await publishToSubstack(substackContent);
// 5. Update Notion
await notion.updatePage(draftId, { status: "Published" });CLI Reference
| Command | Description |
|---|---|
draft "topic" |
Generate article draft |
publish <file> <platform> |
Publish to platform |
publish <file> <platform> live |
Publish live |
schedule <file> <platform> <datetime> |
Schedule post |
Error Handling
| Error Code | Meaning | Fix |
|---|---|---|
AUTH_001 |
Not logged in | Check credentials, re-login |
PUBLISH_001 |
Platform changed UI | Update selectors |
RATE_001 |
Rate limited | Wait and retry |
VALIDATE_001 |
Content validation failed | Check format |
Common Patterns
Pattern: Dry-Run Publishing
async function publishWithDryRun(content, platform, dryRun = true) {
// Validate content
if (!content.title || !content.body) {
throw new Error("Missing required fields");
}
// Preview
console.log("=== PREVIEW ===");
console.log(`Title: ${content.title}`);
console.log(`Platform: ${platform}`);
if (dryRun) {
console.log("DRY RUN - No actual publishing");
return { status: "preview" };
}
// Actually publish
return await platform.publish(content);
}Skill v2.0 - Content Publisher with MCP