feat: social video uploads + hero page video + TikTok content

Hero page:
- Replace GIF with squaremcp-hero-loop.mp4 (autoplay, muted, loop)
- Update styles, scripts, tests, Dockerfile, baselines
- Deployed and verified

Social video uploads:
- Twitter/X: uploadVideoAndTweet via v1.1 media/upload + v2 tweets
- Facebook: createVideoPost via Graph API /{pageId}/videos
- Instagram: createReel via Graph API (container → poll → publish)
- TikTok: REST endpoints + OpenAPI schema for video upload

Marketing:
- TikTok content prompts, scripts, and posting schedule

Note: Remotion not mentioned in any user-facing content
This commit is contained in:
Garfield
2026-05-11 13:55:58 -04:00
parent de9d74bb2b
commit ecdf332b78
17 changed files with 854 additions and 54 deletions

View File

@@ -565,6 +565,29 @@ export function getOpenApiSpec(serverUrl: string) {
responses: { '200': { description: 'Photo posted' } },
},
},
'/api/facebook/video': {
post: {
operationId: 'facebook_create_video_post',
summary: 'Publish video post to Facebook Page',
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['video_url'],
properties: {
video_url: { type: 'string', description: 'Publicly accessible URL of the video' },
description: { type: 'string', description: 'Video description text' },
account: { type: 'string' },
},
},
},
},
},
responses: { '200': { description: 'Video posted' } },
},
},
// ── Twitter/X ───────────────────────────────────────────────
'/api/twitter/search': {
@@ -602,6 +625,97 @@ export function getOpenApiSpec(serverUrl: string) {
responses: { '200': { description: 'Tweet list' } },
},
},
'/api/twitter/video': {
post: {
operationId: 'twitter_upload_video',
summary: 'Upload video and post tweet',
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['video_url', 'text'],
properties: {
video_url: { type: 'string', description: 'Publicly accessible URL of the MP4 video' },
text: { type: 'string', description: 'Tweet text content' },
account: { type: 'string' },
},
},
},
},
},
responses: { '200': { description: 'Video tweet posted' } },
},
},
'/api/instagram/reel': {
post: {
operationId: 'instagram_create_reel',
summary: 'Create Instagram Reel',
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['video_url'],
properties: {
video_url: { type: 'string', description: 'Publicly accessible URL of the MP4 video' },
caption: { type: 'string', description: 'Reel caption text' },
account: { type: 'string' },
},
},
},
},
},
responses: { '200': { description: 'Reel created' } },
},
},
'/api/tiktok/profile': {
get: {
operationId: 'tiktok_get_profile',
summary: 'Get TikTok profile',
parameters: [
{ name: 'account', in: 'query', schema: { type: 'string' } },
],
responses: { '200': { description: 'Profile info' } },
},
},
'/api/tiktok/videos': {
get: {
operationId: 'tiktok_get_videos',
summary: 'Get TikTok videos',
parameters: [
{ name: 'max_count', in: 'query', schema: { type: 'integer' } },
{ name: 'account', in: 'query', schema: { type: 'string' } },
],
responses: { '200': { description: 'Video list' } },
},
},
'/api/tiktok/video': {
post: {
operationId: 'tiktok_create_video',
summary: 'Upload video to TikTok',
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['video_url'],
properties: {
video_url: { type: 'string', description: 'Publicly accessible URL of the video' },
title: { type: 'string', description: 'Video title (max 150 chars)' },
description: { type: 'string', description: 'Video description / caption' },
account: { type: 'string' },
},
},
},
},
},
responses: { '200': { description: 'Video upload initiated' } },
},
},
},
};
}
@@ -1326,6 +1440,30 @@ export function getManifest(serverUrl: string, authEnabled: boolean) {
},
examples: [{ image_url: 'https://example.com/photo.jpg', caption: 'New post!', account: 'default' }],
},
{
name: 'instagram_create_reel',
category: 'instagram',
description: 'Upload a video as an Instagram Reel',
when_to_use:
'User wants to publish a video to their Instagram Business/Creator account as a Reel.',
input_schema: {
type: 'object',
required: ['video_url'],
properties: {
video_url: { type: 'string', description: 'Publicly accessible URL of the MP4 video' },
caption: { type: 'string', description: 'Reel caption' },
account: { type: 'string', description: 'Which Instagram account to use (default: "default")' },
},
},
returns: {
type: 'object',
properties: {
success: { type: 'boolean' },
media_id: { type: 'string' },
},
},
examples: [{ video_url: 'https://example.com/video.mp4', caption: 'New reel!', account: 'default' }],
},
// ── Facebook tools ─────────────────────────────────────────────────────
{
@@ -1427,6 +1565,30 @@ export function getManifest(serverUrl: string, authEnabled: boolean) {
},
examples: [{ image_url: 'https://example.com/image.jpg', caption: 'New post!', account: 'default' }],
},
{
name: 'facebook_create_video_post',
category: 'facebook',
description: 'Publish a video post to a Facebook Page using a public video URL',
when_to_use: 'User wants to post a video to their Facebook Page.',
input_schema: {
type: 'object',
required: ['video_url'],
properties: {
video_url: { type: 'string', description: 'Publicly accessible URL of the video' },
description: { type: 'string', description: 'Video description text' },
account: { type: 'string', description: 'Which Facebook account to use (default: "default")' },
},
},
returns: {
type: 'object',
properties: {
success: { type: 'boolean' },
video_id: { type: 'string' },
post_id: { type: 'string' },
},
},
examples: [{ video_url: 'https://example.com/video.mp4', description: 'New video!', account: 'default' }],
},
// ── Twitter/X tools ────────────────────────────────────────────────────
{
@@ -1536,6 +1698,129 @@ export function getManifest(serverUrl: string, authEnabled: boolean) {
},
examples: [{ text: 'Hello from Hermes MCP!', account: 'default' }],
},
{
name: 'twitter_upload_video',
category: 'twitter',
description: 'Upload a video and post it as a tweet on Twitter/X',
when_to_use:
'User wants to post a video to Twitter/X. NOTE: Free tier is read-only. Paid upgrade required.',
input_schema: {
type: 'object',
required: ['video_url', 'text'],
properties: {
video_url: { type: 'string', description: 'Publicly accessible URL of the MP4 video' },
text: { type: 'string', description: 'Tweet text content' },
account: { type: 'string', description: 'Which Twitter account to use (default: "default")' },
},
},
returns: {
type: 'object',
properties: {
success: { type: 'boolean' },
tweet_id: { type: 'string' },
url: { type: 'string', description: 'Direct link to the tweet' },
},
},
examples: [{ video_url: 'https://example.com/video.mp4', text: 'Check this out!', account: 'default' }],
},
{
name: 'tiktok_get_profile',
category: 'tiktok',
description: 'Get the TikTok user profile including follower count, following count, likes, and video count',
when_to_use: 'User asks about their TikTok profile stats or account details.',
input_schema: {
type: 'object',
properties: {
account: { type: 'string', description: 'Which TikTok account to use (default: "default")' },
},
},
returns: {
type: 'object',
properties: {
open_id: { type: 'string' },
display_name: { type: 'string' },
follower_count: { type: 'number' },
following_count: { type: 'number' },
likes_count: { type: 'number' },
video_count: { type: 'number' },
},
},
examples: [{ account: 'default' }],
},
{
name: 'tiktok_get_videos',
category: 'tiktok',
description: 'List recent videos from the authenticated TikTok account',
when_to_use: 'User wants to see their recent TikTok videos and performance stats.',
input_schema: {
type: 'object',
properties: {
max_count: { type: 'number', description: 'Max videos to return (default: 10, max: 20)' },
account: { type: 'string', description: 'Which TikTok account to use (default: "default")' },
},
},
returns: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string' },
title: { type: 'string' },
view_count: { type: 'number' },
like_count: { type: 'number' },
share_url: { type: 'string' },
},
},
},
examples: [{ max_count: 10, account: 'default' }],
},
{
name: 'tiktok_create_video',
category: 'tiktok',
description: 'Post a video to TikTok by providing a publicly accessible video URL',
when_to_use: 'User wants to upload a video to TikTok.',
input_schema: {
type: 'object',
required: ['video_url'],
properties: {
video_url: { type: 'string', description: 'Publicly accessible URL of the video to post' },
title: { type: 'string', description: 'Video title (max 150 chars)' },
description: { type: 'string', description: 'Video description / caption' },
account: { type: 'string', description: 'Which TikTok account to use (default: "default")' },
},
},
returns: {
type: 'object',
properties: {
publish_id: { type: 'string' },
status: { type: 'string' },
},
},
examples: [{ video_url: 'https://example.com/video.mp4', title: 'My video', account: 'default' }],
},
{
name: 'tiktok_get_video_status',
category: 'tiktok',
description: 'Check the processing status of a TikTok video upload',
when_to_use: 'User wants to check if their TikTok video upload is complete.',
input_schema: {
type: 'object',
required: ['publish_id'],
properties: {
publish_id: { type: 'string', description: 'Publish ID returned by tiktok_create_video' },
account: { type: 'string', description: 'Which TikTok account to use (default: "default")' },
},
},
returns: {
type: 'object',
properties: {
publish_id: { type: 'string' },
status: { type: 'string' },
fail_reason: { type: 'string' },
},
},
examples: [{ publish_id: 'v123456', account: 'default' }],
},
// ── Obsidian tools ──────────────────────────────────────────────────────
{