import Anthropic from '@anthropic-ai/sdk'; import { tools as allTools, handleToolCall } from './tools.js'; const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }); const SYSTEM_PROMPT = `You are a live demo assistant for SquareMCP — an AI Social Media Gateway that lets AI agents post to social platforms via the Model Context Protocol (MCP). You have real tools connected to live platforms. When a visitor asks "can it send a Slack message?" or "show me how it works" — actually do it. Send a demo message, read channels, show real results. What SquareMCP does: - Connects AI coding assistants to social platforms: LinkedIn, TikTok, WhatsApp, Instagram, Twitter/X, Facebook, Telegram, Discord, Slack, and Email - Works with any MCP-compatible client: Claude Desktop, Claude Code, Cursor, Windsurf, opencode, Codex CLI - Also works with ChatGPT via Custom GPT (uses the REST API + OAuth, not MCP protocol directly) - Provides a multi-tenant SaaS platform where each customer securely stores their own platform credentials - Plans: Free (100 calls/month), Pro, Business How to connect — by AI client: CHATGPT (chatgpt.com): SquareMCP works with ChatGPT via a Custom GPT with Actions. Steps: 1. Go to chatgpt.com → Explore GPTs → Create → Actions → Import from URL 2. Import the OpenAPI schema: https://hermes.squaremcp.com/openapi.json 3. Set Authentication to OAuth: - Authorization URL: https://hermes.squaremcp.com/oauth/authorize - Token URL: https://hermes.squaremcp.com/oauth/token - Register your OAuth client first at: https://hermes.squaremcp.com/oauth/register 4. Connect your social accounts in the SquareMCP dashboard at https://app.squaremcp.com 5. Ask your Custom GPT: "Send a WhatsApp to my top 3 clients" — it just works. Full guide: email support@squaremcp.com for the ChatGPT setup walkthrough. CLAUDE (claude.ai or Claude Desktop): 1. In claude.ai → Settings → Integrations → Add MCP server 2. Server URL: https://hermes.squaremcp.com/mcp 3. Authenticate via OAuth 4. Connect social accounts at https://app.squaremcp.com CURSOR / WINDSURF / CODEX CLI / opencode: Add to your MCP config: { "mcpServers": { "squaremcp": { "url": "https://hermes.squaremcp.com/mcp", "apiKey": "your-api-key-from-dashboard" } } } For mortgage brokers specifically: - ChatGPT Custom GPT is often the easiest starting point — no install required, works in the browser - WhatsApp rate blast: one command sends updates to hundreds of clients - Multi-channel: handle WhatsApp, email, LinkedIn, and Facebook from a single AI conversation - Compliance: we can scope the setup to stay within RESPA/TILA guidelines When using tools: - For demo sends (Slack, Telegram, Discord), send a short, friendly message that explains this is a live SquareMCP demo - After a tool succeeds, explain what just happened and how easy it was - If a tool fails (missing credentials), explain what credential the customer would set up instead - Never expose tokens or internal IDs in your reply Keep replies concise and practical. If you don't know something, say so and suggest emailing support@squaremcp.com.`; // Tools the public demo agent is allowed to use (no customer auth needed — uses env var creds) const DEMO_TOOL_NAMES = new Set([ 'slack_get_me', 'slack_get_channels', 'slack_send_message', 'slack_get_messages', 'discord_get_me', 'discord_get_guilds', 'discord_send_message', 'telegram_get_me', 'telegram_send_message', ]); const demoTools = allTools .filter(t => DEMO_TOOL_NAMES.has(t.name)) .map(t => ({ name: t.name, description: t.description, input_schema: t.inputSchema as Anthropic.Tool['input_schema'], })); export interface ChatMessage { role: 'user' | 'assistant'; content: string; } export interface ChatResult { reply: string; toolsUsed: string[]; } export async function handleChat(messages: ChatMessage[]): Promise { if (!process.env.ANTHROPIC_API_KEY) { throw new Error('ANTHROPIC_API_KEY not configured'); } const apiMessages: Anthropic.MessageParam[] = messages.map(m => ({ role: m.role, content: m.content, })); const toolsUsed: string[] = []; const MAX_ITERATIONS = 5; for (let i = 0; i < MAX_ITERATIONS; i++) { const response = await client.messages.create({ model: 'claude-haiku-4-5-20251001', max_tokens: 1024, system: SYSTEM_PROMPT, tools: demoTools, messages: apiMessages, }); if (response.stop_reason === 'end_turn') { const text = response.content.find(b => b.type === 'text'); return { reply: text?.text ?? 'Done.', toolsUsed }; } if (response.stop_reason === 'tool_use') { // Add Claude's response (with tool_use blocks) to the message history apiMessages.push({ role: 'assistant', content: response.content }); // Execute each tool call and collect results const toolResults: Anthropic.ToolResultBlockParam[] = []; for (const block of response.content) { if (block.type !== 'tool_use') continue; toolsUsed.push(block.name); try { const result = await handleToolCall( block.name, block.input as Record, undefined // no customer — uses env var credentials ); toolResults.push({ type: 'tool_result', tool_use_id: block.id, content: result.content[0]?.text ?? '', is_error: result.isError, }); } catch (err) { toolResults.push({ type: 'tool_result', tool_use_id: block.id, content: `Error: ${(err as Error).message}`, is_error: true, }); } } apiMessages.push({ role: 'user', content: toolResults }); continue; } // Unexpected stop reason — return whatever text we have const text = response.content.find(b => b.type === 'text'); return { reply: text?.text ?? 'Something unexpected happened.', toolsUsed }; } return { reply: 'I ran too many steps. Please try a simpler request.', toolsUsed }; }