feat: LinkedIn video upload support (linkedin_upload_video tool + REST route)

Implements full 4-step LinkedIn Videos API flow: download from public URL,
initialize upload, 4MB chunk PUT with ETag collection, finalize, poll until
AVAILABLE, then publish via POST /rest/posts reading post ID from x-restli-id.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
garfieldheron
2026-05-11 12:26:42 -04:00
parent e5994312bc
commit c2eabd8e66
5 changed files with 226 additions and 1 deletions

View File

@@ -4,7 +4,7 @@ import { searchMessages, readMessage, getProfile, listFolders, type Account } fr
import { sendEmail, createDraft } from './smtp.js';
import { searchNotes, getNote, appendToNote, updateNote, getSyncStatus } from './clients/obsidian.js';
import { sendMessage, sendTemplate, getMessageStatus, listTemplates } from './clients/whatsapp.js';
import { getProfile as getLinkedInProfile, createPost as createLinkedInPost, searchConnections, sendMessage as sendLinkedInMessage } from './clients/linkedin.js';
import { getProfile as getLinkedInProfile, createPost as createLinkedInPost, createVideoPost as createLinkedInVideoPost, searchConnections, sendMessage as sendLinkedInMessage } from './clients/linkedin.js';
import { getMe as getTelegramMe, sendMessage as sendTelegramMessage, sendPhoto as sendTelegramPhoto, getUpdates as getTelegramUpdates, getChat as getTelegramChat } from './clients/telegram.js';
import { getMe as getDiscordMe, getGuilds, getChannels, sendMessage as sendDiscordMessage, getMessages as getDiscordMessages } from './clients/discord.js';
import { getProfile as getInstagramProfile, getMedia as getInstagramMedia, createPost as createInstagramPost } from './clients/instagram.js';
@@ -266,6 +266,21 @@ export const tools: Tool[] = [
required: ['text'],
},
},
{
name: 'linkedin_upload_video',
description:
'Upload a video and create a LinkedIn post. Accepts a publicly accessible video URL, downloads it server-side, uploads it to LinkedIn via the Videos API, and publishes the post. Video must be publicly reachable (not localhost or private network). Large videos may take 3090 seconds.',
inputSchema: {
type: 'object',
properties: {
video_url: { type: 'string', description: 'Publicly accessible URL of the MP4 video to upload' },
text: { type: 'string', description: 'Post caption / commentary text' },
visibility: { type: 'string', enum: ['PUBLIC', 'CONNECTIONS'], description: 'Post visibility. Default: PUBLIC' },
account: { type: 'string', description: 'Which LinkedIn account to use (default: "default")' },
},
required: ['video_url', 'text'],
},
},
{
name: 'linkedin_search_connections',
description:
@@ -783,6 +798,18 @@ export async function handleToolCall(
}, customer);
break;
case 'linkedin_upload_video':
result = await createLinkedInVideoPost(
{
video_url: args.video_url as string,
text: args.text as string,
visibility: (args.visibility as 'PUBLIC' | 'CONNECTIONS') ?? 'PUBLIC',
account: args.account as string | undefined,
},
customer
);
break;
case 'linkedin_search_connections':
result = await searchConnections({
keywords: args.keywords as string | undefined,