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

@@ -679,6 +679,96 @@ app.get('/api/telegram/chat', requireAuth, async (req, res) => {
}
});
// ── Discord REST endpoints ──────────────────────────────────────
app.get('/api/discord/me', requireAuth, async (req, res) => {
const account = req.query.account as string | undefined;
try {
const result = await handleToolCall('discord_get_me', { account });
res.json(parseToolResult(result));
} catch (err) {
res.status(500).json({ error: (err as Error).message });
}
});
app.get('/api/discord/guilds', requireAuth, async (req, res) => {
const account = req.query.account as string | undefined;
try {
const result = await handleToolCall('discord_get_guilds', { account });
res.json(parseToolResult(result));
} catch (err) {
res.status(500).json({ error: (err as Error).message });
}
});
app.get('/api/discord/channels', requireAuth, async (req, res) => {
const guild_id = req.query.guild_id as string | undefined;
const account = req.query.account as string | undefined;
if (!guild_id) { res.status(400).json({ error: 'guild_id is required' }); return; }
try {
const result = await handleToolCall('discord_get_channels', { guild_id, account });
res.json(parseToolResult(result));
} catch (err) {
res.status(500).json({ error: (err as Error).message });
}
});
app.post('/api/discord/message', requireAuth, async (req, res) => {
const { channel_id, content, account } = req.body as Record<string, unknown>;
if (!channel_id || !content) { res.status(400).json({ error: 'channel_id and content are required' }); return; }
try {
const result = await handleToolCall('discord_send_message', { channel_id, content, account });
res.json(parseToolResult(result));
} catch (err) {
res.status(500).json({ error: (err as Error).message });
}
});
app.get('/api/discord/messages', requireAuth, async (req, res) => {
const channel_id = req.query.channel_id as string | undefined;
const limit = req.query.limit ? parseInt(req.query.limit as string, 10) : undefined;
const account = req.query.account as string | undefined;
if (!channel_id) { res.status(400).json({ error: 'channel_id is required' }); return; }
try {
const result = await handleToolCall('discord_get_messages', { channel_id, limit, account });
res.json(parseToolResult(result));
} catch (err) {
res.status(500).json({ error: (err as Error).message });
}
});
// ── Instagram REST endpoints ────────────────────────────────────
app.get('/api/instagram/profile', requireAuth, async (req, res) => {
const account = req.query.account as string | undefined;
try {
const result = await handleToolCall('instagram_get_profile', { account });
res.json(parseToolResult(result));
} catch (err) {
res.status(500).json({ error: (err as Error).message });
}
});
app.get('/api/instagram/media', requireAuth, async (req, res) => {
const limit = req.query.limit ? parseInt(req.query.limit as string, 10) : undefined;
const account = req.query.account as string | undefined;
try {
const result = await handleToolCall('instagram_get_media', { limit, account });
res.json(parseToolResult(result));
} catch (err) {
res.status(500).json({ error: (err as Error).message });
}
});
app.post('/api/instagram/post', requireAuth, async (req, res) => {
const { image_url, caption, account } = req.body as Record<string, unknown>;
if (!image_url) { res.status(400).json({ error: 'image_url is required' }); return; }
try {
const result = await handleToolCall('instagram_create_post', { image_url, caption, account });
res.json(parseToolResult(result));
} catch (err) {
res.status(500).json({ error: (err as Error).message });
}
});
app.post('/api/pilot-request', async (req, res) => {
const origin = req.get('origin');
if (origin && !SQUAREMCP_ALLOWED_ORIGINS.has(origin)) {