feat: Discord + Instagram integrations

Discord Bot API
- New client: src/clients/discord.ts
- Tools: discord_get_me, discord_get_guilds, discord_get_channels, discord_send_message, discord_get_messages
- REST endpoints: GET /api/discord/me, /api/discord/guilds, /api/discord/channels, /api/discord/messages, POST /api/discord/message
- Multi-account env var: DISCORD_{ACCOUNT}_BOT_TOKEN

Instagram Graph API
- New client: src/clients/instagram.ts
- Tools: instagram_get_profile, instagram_get_media, instagram_create_post
- REST endpoints: GET /api/instagram/profile, /api/instagram/media, POST /api/instagram/post
- Multi-account env vars: INSTAGRAM_{ACCOUNT}_ACCESS_TOKEN, INSTAGRAM_{ACCOUNT}_BUSINESS_ACCOUNT_ID

Total tools: 32
This commit is contained in:
Garfield
2026-05-05 22:01:21 -04:00
parent 385f91de4d
commit e1e7d88c8a
6 changed files with 724 additions and 0 deletions

View File

@@ -5,6 +5,8 @@ import { searchNotes, getNote, appendToNote, updateNote, getSyncStatus } from '.
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 { 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';
const ACCOUNT_PARAM = {
account: {
@@ -353,6 +355,110 @@ export const tools: Tool[] = [
},
},
},
// ── Discord tools ────────────────────────────────────────────
{
name: 'discord_get_me',
description:
'Get information about the Discord bot. Use to verify the bot is connected and get its username.',
inputSchema: {
type: 'object',
properties: {
account: { type: 'string', description: 'Which Discord account to use (default: "default")' },
},
},
},
{
name: 'discord_get_guilds',
description:
'List Discord servers (guilds) the bot is a member of. Use to find server IDs for sending messages.',
inputSchema: {
type: 'object',
properties: {
account: { type: 'string', description: 'Which Discord account to use (default: "default")' },
},
},
},
{
name: 'discord_get_channels',
description:
'List channels in a Discord server. Use to find channel IDs for sending messages.',
inputSchema: {
type: 'object',
required: ['guild_id'],
properties: {
guild_id: { type: 'string', description: 'Discord server (guild) ID' },
account: { type: 'string', description: 'Which Discord account to use (default: "default")' },
},
},
},
{
name: 'discord_send_message',
description:
'Send a message to a Discord channel. Use when the user wants to post in a Discord server.',
inputSchema: {
type: 'object',
required: ['channel_id', 'content'],
properties: {
channel_id: { type: 'string', description: 'Discord channel ID' },
content: { type: 'string', description: 'Message content (up to 2000 chars)' },
account: { type: 'string', description: 'Which Discord account to use (default: "default")' },
},
},
},
{
name: 'discord_get_messages',
description:
'Get recent messages from a Discord channel. Use to read chat history.',
inputSchema: {
type: 'object',
required: ['channel_id'],
properties: {
channel_id: { type: 'string', description: 'Discord channel ID' },
limit: { type: 'number', description: 'Max messages to return (default: 10, max: 100)' },
account: { type: 'string', description: 'Which Discord account to use (default: "default")' },
},
},
},
// ── Instagram tools ──────────────────────────────────────────
{
name: 'instagram_get_profile',
description:
'Get Instagram Business/Creator account profile info. Use when the user asks about their Instagram stats, followers, or account details.',
inputSchema: {
type: 'object',
properties: {
account: { type: 'string', description: 'Which Instagram account to use (default: "default")' },
},
},
},
{
name: 'instagram_get_media',
description:
'Get recent posts from an Instagram Business/Creator account. Use when the user wants to see their recent content.',
inputSchema: {
type: 'object',
properties: {
limit: { type: 'number', description: 'Max posts to return (default: 10)' },
account: { type: 'string', description: 'Which Instagram account to use (default: "default")' },
},
},
},
{
name: 'instagram_create_post',
description:
'Create a post on Instagram. [REQUIRES BUSINESS ACCOUNT] Only works with Instagram Business/Creator accounts connected to a Facebook Page.',
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 Instagram account to use (default: "default")' },
},
},
},
];
function acct(args: Record<string, unknown>): Account {
@@ -525,6 +631,64 @@ export async function handleToolCall(
});
break;
// ── Discord ─────────────────────────────────────────────────
case 'discord_get_me':
result = await getDiscordMe({
account: args.account as string | undefined,
});
break;
case 'discord_get_guilds':
result = await getGuilds({
account: args.account as string | undefined,
});
break;
case 'discord_get_channels':
result = await getChannels({
guild_id: args.guild_id as string,
account: args.account as string | undefined,
});
break;
case 'discord_send_message':
result = await sendDiscordMessage({
channel_id: args.channel_id as string,
content: args.content as string,
account: args.account as string | undefined,
});
break;
case 'discord_get_messages':
result = await getDiscordMessages({
channel_id: args.channel_id as string,
limit: (args.limit as number) ?? 10,
account: args.account as string | undefined,
});
break;
// ── Instagram ───────────────────────────────────────────────
case 'instagram_get_profile':
result = await getInstagramProfile({
account: args.account as string | undefined,
});
break;
case 'instagram_get_media':
result = await getInstagramMedia({
limit: (args.limit as number) ?? 10,
account: args.account as string | undefined,
});
break;
case 'instagram_create_post':
result = await createInstagramPost({
image_url: args.image_url as string,
caption: args.caption as string | undefined,
account: args.account as string | undefined,
});
break;
// Legacy Yahoo-prefixed names — keep working for any cached Claude sessions
case 'yahoo_get_profile':
result = await getProfile('yahoo');