feat: Facebook Page integration

Four tools: facebook_get_page, facebook_get_posts, facebook_create_post
(text + optional link), facebook_create_photo_post (image URL + caption).

Uses Graph API v19.0 with Page access token. Credentials stored per-customer
in Redis under creds:{id}:facebook with pageId alongside the access token.
Env-var fallback: FACEBOOK_{ACCOUNT}_ACCESS_TOKEN + FACEBOOK_{ACCOUNT}_PAGE_ID.

Wired into Platform type, validPlatforms, /api/connections, manifest OpenAPI
spec, and manifest tool registry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Garfield
2026-05-08 12:47:20 -04:00
parent 6c7e56769e
commit ffb67560b9
5 changed files with 421 additions and 3 deletions

View File

@@ -11,6 +11,7 @@ import { getProfile as getInstagramProfile, getMedia as getInstagramMedia, creat
import { searchTweets, getUserProfile, getUserTweets, createTweet } from './clients/twitter.js';
import { getUserProfile as getTikTokProfile, getUserVideos, createVideo, getVideoStatus } from './clients/tiktok.js';
import { getMe as getSnapchatMe, createSnap, getAdAccounts } from './clients/snapchat.js';
import { getPage, getPosts, createPost as createFacebookPost, createPhotoPost } from './clients/facebook.js';
const ACCOUNT_PARAM = {
account: {
@@ -610,6 +611,59 @@ export const tools: Tool[] = [
},
},
},
// ── Facebook tools ───────────────────────────────────────────
{
name: 'facebook_get_page',
description:
'Get a Facebook Page profile including name, category, fan count, and follower count.',
inputSchema: {
type: 'object',
properties: {
account: { type: 'string', description: 'Which Facebook account to use (default: "default")' },
},
},
},
{
name: 'facebook_get_posts',
description:
'Get recent posts from a Facebook Page feed.',
inputSchema: {
type: 'object',
properties: {
limit: { type: 'number', description: 'Max posts to return (default: 10)' },
account: { type: 'string', description: 'Which Facebook account to use (default: "default")' },
},
},
},
{
name: 'facebook_create_post',
description:
'Publish a text post (optionally with a link) to a Facebook Page.',
inputSchema: {
type: 'object',
required: ['message'],
properties: {
message: { type: 'string', description: 'Post text content' },
link: { type: 'string', description: 'Optional URL to attach to the post' },
account: { type: 'string', description: 'Which Facebook account to use (default: "default")' },
},
},
},
{
name: 'facebook_create_photo_post',
description:
'Publish a photo to a Facebook Page using a publicly accessible image URL. Creates a photo post with optional caption.',
inputSchema: {
type: 'object',
required: ['image_url'],
properties: {
image_url: { type: 'string', description: 'Publicly accessible URL of the image to post' },
caption: { type: 'string', description: 'Post caption text' },
account: { type: 'string', description: 'Which Facebook account to use (default: "default")' },
},
},
},
];
function acct(args: Record<string, unknown>): Account {
@@ -924,6 +978,36 @@ export async function handleToolCall(
}, customer);
break;
// ── Facebook ────────────────────────────────────────────────
case 'facebook_get_page':
result = await getPage({
account: args.account as string | undefined,
}, customer);
break;
case 'facebook_get_posts':
result = await getPosts({
limit: (args.limit as number) ?? 10,
account: args.account as string | undefined,
}, customer);
break;
case 'facebook_create_post':
result = await createFacebookPost({
message: args.message as string,
link: args.link as string | undefined,
account: args.account as string | undefined,
}, customer);
break;
case 'facebook_create_photo_post':
result = await createPhotoPost({
image_url: args.image_url as string,
caption: args.caption as string | undefined,
account: args.account as string | undefined,
}, customer);
break;
// Legacy Yahoo-prefixed names — keep working for any cached Claude sessions
case 'yahoo_get_profile':
result = await getProfile('yahoo');