feat(saas): SquareMCP v2 — multi-tenant MCP platform complete
Steps 0–10 of the v2 plan, 194 tests passing. Core infrastructure - Shared Redis client (src/redis.ts); all four Redis consumers migrated - Vitest test harness with vitest.config.ts and npm test/test:watch scripts Billing & invoicing (Steps 1–2) - Monthly invoice generation with idempotency (MySQL uq_customer_period unique key) - Cron job with Redis distributed lock (Lua compare-delete, 1-hr TTL) - Invoice emailer via nodemailer (FETCHERPAY SMTP) - Billing middleware: checkLimit gate in handleToolCall; platform attribution fix Email multi-tenancy (Step 3) - EmailCtx = Account | EmailCredentials; imap.ts + smtp.ts accept both - resolveEmailCtx helper in tools.ts; all email tools use customer credentials Analytics + platform health (Steps 4–5) - Chart.js bar charts for platform breakdown and daily activity - Token expiry check in getCredential with dynamic import refresh - platform-health.ts: per-platform health probe with 10-min Redis cache - GET /api/health/platforms; "Token expired" amber badge in dashboard Tool schema filtering (Step 6) - stripAccountParam deep-clones tool schemas; multi-tenant sessions never see the internal account enum OAuth hardening (Step 7) - Atomic auth code consumption: UPDATE SET used=TRUE, check affectedRows - customer_id threaded through oauth_auth_codes → oauth_tokens - getTokenCustomer(); requireAuth resolves req.customer from Bearer token - Consent page requires authenticated session; redirect_uri validated against registered URIs; http://localhost:* loopback wildcard DCR browser flow (Step 8) - ensureOAuthAppRegistered() upserts pre-registered SquareMCP OAuth app on startup with redirect URIs for mcp-callback, localhost:*, claude-desktop, opencode - GET /oauth/connect-mcp → server-side redirect (client_id off frontend) - GET /oauth/mcp-callback → exchanges code, renders config snippet page with copy buttons for Claude Desktop and Codex CLI Webhooks (Step 9) - webhook_url + webhook_secret columns on customers - deliverWebhook(): HMAC-SHA256 signing, 3× exponential retry (1s/4s/16s), Redis DLQ with 7-day TTL on total failure - isValidWebhookUrl(): SSRF protection (blocks RFC-1918, localhost, .local) - POST /api/webhooks/config (secret returned once), GET, DELETE - GET /api/admin/webhooks/dlq/:customerId - WhatsApp POST route uses express.raw() for raw body preservation - Dashboard Webhooks tab with secret-once display and copy button Developer docs (Step 10) - docs/ static HTML site (GitHub Pages, no build pipeline) - index.html: landing page with client + platform overview - getting-started.html: tabbed MCP config for Claude Desktop, Codex CLI, opencode - platforms.html: LinkedIn, TikTok, WhatsApp, Instagram, Twitter, Telegram guides - agent-tutorial.html: complete Node.js agent (Anthropic SDK + MCP SDK), LinkedIn posting loop, extensions for multi-platform + inbound webhook reaction Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
0
docs/.nojekyll
Normal file
0
docs/.nojekyll
Normal file
231
docs/agent-tutorial.html
Normal file
231
docs/agent-tutorial.html
Normal file
@@ -0,0 +1,231 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Agent Tutorial — SquareMCP Docs</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="site-nav">
|
||||
<div class="nav-inner">
|
||||
<a href="index.html" class="nav-logo"><span class="nav-logo-mark">S</span> SquareMCP</a>
|
||||
<div class="nav-links">
|
||||
<a href="getting-started.html">Getting started</a>
|
||||
<a href="platforms.html">Platform guides</a>
|
||||
<a href="agent-tutorial.html" class="active">Agent tutorial</a>
|
||||
<a href="https://hermes.squaremcp.com/openapi-social.json" target="_blank">API reference ↗</a>
|
||||
</div>
|
||||
<a href="https://squaremcp.com" class="nav-cta" target="_blank">Open app</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="page">
|
||||
<div class="hero">
|
||||
<h1>Build a LinkedIn posting agent</h1>
|
||||
<p>Real code: a Claude agent that researches a topic, drafts a post, and publishes it to LinkedIn — fully automated.</p>
|
||||
</div>
|
||||
|
||||
<div class="callout">
|
||||
<strong>What you'll build</strong>
|
||||
A Node.js script that (1) takes a topic from the command line, (2) uses Claude to research and draft a LinkedIn post, (3) calls the SquareMCP <code>linkedin_create_post</code> tool to publish — all in one agentic loop.
|
||||
</div>
|
||||
|
||||
<h2>Prerequisites</h2>
|
||||
<ul>
|
||||
<li>Node.js 18+ with ESM support</li>
|
||||
<li>An <a href="https://console.anthropic.com" target="_blank">Anthropic API key</a></li>
|
||||
<li>A SquareMCP Bearer token with LinkedIn connected (see <a href="getting-started.html">Getting started</a>)</li>
|
||||
</ul>
|
||||
|
||||
<h2>Step 1 — Install dependencies</h2>
|
||||
<pre><code>npm init -y
|
||||
npm install @anthropic-ai/sdk @modelcontextprotocol/sdk</code></pre>
|
||||
|
||||
<h2>Step 2 — Create the agent</h2>
|
||||
|
||||
<p>Create <code>linkedin-agent.mjs</code>:</p>
|
||||
|
||||
<pre><code><span class="kw">import</span> Anthropic <span class="kw">from</span> <span class="str">'@anthropic-ai/sdk'</span>;
|
||||
<span class="kw">import</span> { Client } <span class="kw">from</span> <span class="str">'@modelcontextprotocol/sdk/client/index.js'</span>;
|
||||
<span class="kw">import</span> { StreamableHTTPClientTransport } <span class="kw">from</span> <span class="str">'@modelcontextprotocol/sdk/client/streamableHttp.js'</span>;
|
||||
|
||||
<span class="kw">const</span> SQUAREMCP_URL = <span class="str">'https://hermes.squaremcp.com/mcp'</span>;
|
||||
<span class="kw">const</span> SQUAREMCP_TOKEN = process.env.<span class="cls">SQUAREMCP_TOKEN</span>; <span class="cmt">// your Bearer token</span>
|
||||
<span class="kw">const</span> topic = process.argv[<span class="num">2</span>] ?? <span class="str">'AI trends in 2026'</span>;
|
||||
|
||||
<span class="cmt">// ── 1. Connect to SquareMCP ──────────────────────────────────────</span>
|
||||
<span class="kw">const</span> transport = <span class="kw">new</span> <span class="cls">StreamableHTTPClientTransport</span>(<span class="kw">new</span> <span class="cls">URL</span>(SQUAREMCP_URL), {
|
||||
requestInit: { headers: { Authorization: <span class="str">`Bearer <span class="kw">${</span>SQUAREMCP_TOKEN<span class="kw">}</span>`</span> } },
|
||||
});
|
||||
|
||||
<span class="kw">const</span> mcpClient = <span class="kw">new</span> <span class="cls">Client</span>({ name: <span class="str">'linkedin-agent'</span>, version: <span class="str">'1.0.0'</span> });
|
||||
<span class="kw">await</span> mcpClient.connect(transport);
|
||||
|
||||
<span class="cmt">// Fetch available tools from SquareMCP</span>
|
||||
<span class="kw">const</span> { tools: mcpTools } = <span class="kw">await</span> mcpClient.<span class="fn">listTools</span>();
|
||||
|
||||
<span class="cmt">// Convert MCP tool descriptors to Anthropic tool format</span>
|
||||
<span class="kw">const</span> anthropicTools = mcpTools.<span class="fn">map</span>(t => ({
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
input_schema: t.inputSchema,
|
||||
}));
|
||||
|
||||
<span class="cmt">// ── 2. Run the agentic loop ──────────────────────────────────────</span>
|
||||
<span class="kw">const</span> anthropic = <span class="kw">new</span> <span class="cls">Anthropic</span>();
|
||||
<span class="kw">const</span> messages = [
|
||||
{
|
||||
role: <span class="str">'user'</span>,
|
||||
content: <span class="str">`You are a LinkedIn content strategist. Your job:
|
||||
1. Think about the topic: "<span class="kw">${</span>topic<span class="kw">}</span>"
|
||||
2. Draft a compelling LinkedIn post (150-250 words, professional tone, 3-5 hashtags)
|
||||
3. Call linkedin_create_post to publish it
|
||||
4. Report back what was posted.
|
||||
|
||||
Write the post now and publish it.`</span>,
|
||||
},
|
||||
];
|
||||
|
||||
console.<span class="fn">log</span>(<span class="str">`\n🤖 Agent starting — topic: "<span class="kw">${</span>topic<span class="kw">}</span>"\n`</span>);
|
||||
|
||||
<span class="kw">while</span> (<span class="kw">true</span>) {
|
||||
<span class="kw">const</span> response = <span class="kw">await</span> anthropic.messages.<span class="fn">create</span>({
|
||||
model: <span class="str">'claude-opus-4-7'</span>,
|
||||
max_tokens: <span class="num">1024</span>,
|
||||
tools: anthropicTools,
|
||||
messages,
|
||||
});
|
||||
|
||||
<span class="kmt">// Append assistant turn</span>
|
||||
messages.<span class="fn">push</span>({ role: <span class="str">'assistant'</span>, content: response.content });
|
||||
|
||||
<span class="kw">if</span> (response.stop_reason === <span class="str">'end_turn'</span>) {
|
||||
<span class="kw">const</span> text = response.content
|
||||
.<span class="fn">filter</span>(b => b.type === <span class="str">'text'</span>)
|
||||
.<span class="fn">map</span>(b => b.text)
|
||||
.<span class="fn">join</span>(<span class="str">'\n'</span>);
|
||||
console.<span class="fn">log</span>(<span class="str">'\n✅ Agent finished:\n'</span>, text);
|
||||
<span class="kw">break</span>;
|
||||
}
|
||||
|
||||
<span class="kw">if</span> (response.stop_reason !== <span class="str">'tool_use'</span>) <span class="kw">break</span>;
|
||||
|
||||
<span class="cmt">// Execute each tool call against SquareMCP</span>
|
||||
<span class="kw">const</span> toolResults = [];
|
||||
<span class="kw">for</span> (<span class="kw">const</span> block <span class="kw">of</span> response.content) {
|
||||
<span class="kw">if</span> (block.type !== <span class="str">'tool_use'</span>) <span class="kw">continue</span>;
|
||||
|
||||
console.<span class="fn">log</span>(<span class="str">`🔧 Calling <span class="kw">${</span>block.name<span class="kw">}</span>...`</span>);
|
||||
<span class="kw">let</span> result;
|
||||
<span class="kw">try</span> {
|
||||
result = <span class="kw">await</span> mcpClient.<span class="fn">callTool</span>({ name: block.name, arguments: block.input });
|
||||
console.<span class="fn">log</span>(<span class="str">` ✓ <span class="kw">${</span>JSON.<span class="fn">stringify</span>(result.content[<span class="num">0</span>]).<span class="fn">slice</span>(<span class="num">0</span>, <span class="num">120</span>)<span class="kw">}</span>...`</span>);
|
||||
} <span class="kw">catch</span> (err) {
|
||||
result = { content: [{ type: <span class="str">'text'</span>, text: <span class="str">`Error: <span class="kw">${</span>err.message<span class="kw">}</span>`</span> }] };
|
||||
console.<span class="fn">error</span>(<span class="str">` ✗ <span class="kw">${</span>err.message<span class="kw">}</span>`</span>);
|
||||
}
|
||||
|
||||
toolResults.<span class="fn">push</span>({
|
||||
type: <span class="str">'tool_result'</span>,
|
||||
tool_use_id: block.id,
|
||||
content: result.content,
|
||||
});
|
||||
}
|
||||
|
||||
messages.<span class="fn">push</span>({ role: <span class="str">'user'</span>, content: toolResults });
|
||||
}
|
||||
|
||||
<span class="kw">await</span> mcpClient.<span class="fn">close</span>();</code></pre>
|
||||
|
||||
<h2>Step 3 — Run it</h2>
|
||||
|
||||
<pre><code>export ANTHROPIC_API_KEY=sk-ant-...
|
||||
export SQUAREMCP_TOKEN=your-bearer-token-here
|
||||
|
||||
node linkedin-agent.mjs "The future of AI agents in enterprise software"</code></pre>
|
||||
|
||||
<p>Expected output:</p>
|
||||
|
||||
<pre><code>🤖 Agent starting — topic: "The future of AI agents in enterprise software"
|
||||
|
||||
🔧 Calling linkedin_create_post...
|
||||
✓ {"id":"urn:li:share:7194...","success":true}...
|
||||
|
||||
✅ Agent finished:
|
||||
I've published the following LinkedIn post:
|
||||
|
||||
"Enterprise software is undergoing a quiet revolution..."
|
||||
[full post text]
|
||||
|
||||
The post has been published successfully to your LinkedIn feed.</code></pre>
|
||||
|
||||
<h2>Step 4 — Extend it</h2>
|
||||
|
||||
<p>Now that you have the agentic loop working, you can extend it:</p>
|
||||
|
||||
<h3>Post to multiple platforms at once</h3>
|
||||
<pre><code><span class="cmt">// Replace the user message with:</span>
|
||||
content: <span class="str">`Draft a post about "<span class="kw">${</span>topic<span class="kw">}</span>" and publish it on both
|
||||
LinkedIn (professional tone) and Twitter (punchy, max 280 chars, 2 hashtags).
|
||||
Use linkedin_create_post and twitter_create_tweet.`</span></code></pre>
|
||||
|
||||
<h3>Schedule with a cron job</h3>
|
||||
<pre><code><span class="cmt"># Post every weekday at 9am</span>
|
||||
0 9 * * 1-5 SQUAREMCP_TOKEN=... ANTHROPIC_API_KEY=... \
|
||||
node /path/to/linkedin-agent.mjs "$(cat /path/to/topics.txt | shuf -n1)"</code></pre>
|
||||
|
||||
<h3>React to inbound WhatsApp messages</h3>
|
||||
<p>Configure a webhook in the SquareMCP dashboard. When a WhatsApp message arrives, SquareMCP POSTs to your server. Run the same agent loop but start with the inbound message as context:</p>
|
||||
|
||||
<pre><code><span class="kw">import</span> express <span class="kw">from</span> <span class="str">'express'</span>;
|
||||
<span class="kw">import</span> crypto <span class="kw">from</span> <span class="str">'crypto'</span>;
|
||||
|
||||
<span class="kw">const</span> app = <span class="fn">express</span>();
|
||||
app.<span class="fn">use</span>(express.<span class="fn">json</span>());
|
||||
|
||||
app.<span class="fn">post</span>(<span class="str">'/webhook'</span>, <span class="kw">async</span> (req, res) => {
|
||||
<span class="cmt">// Verify signature</span>
|
||||
<span class="kw">const</span> sig = req.headers[<span class="str">'x-squaremcp-signature'</span>];
|
||||
<span class="kw">const</span> expected = <span class="str">`sha256=<span class="kw">${</span>crypto
|
||||
.<span class="fn">createHmac</span>(<span class="str">'sha256'</span>, process.env.<span class="cls">WEBHOOK_SECRET</span>)
|
||||
.<span class="fn">update</span>(JSON.<span class="fn">stringify</span>(req.body))
|
||||
.<span class="fn">digest</span>(<span class="str">'hex'</span>)<span class="kw">}</span>`</span>;
|
||||
<span class="kw">if</span> (sig !== expected) { res.<span class="fn">status</span>(<span class="num">401</span>).<span class="fn">end</span>(); <span class="kw">return</span>; }
|
||||
|
||||
res.<span class="fn">status</span>(<span class="num">200</span>).<span class="fn">end</span>(); <span class="cmt">// acknowledge immediately</span>
|
||||
|
||||
<span class="kw">const</span> { platform, data } = req.body;
|
||||
console.<span class="fn">log</span>(<span class="str">`Inbound from <span class="kw">${</span>platform<span class="kw">}</span>: <span class="kw">${</span>data.text<span class="kw">}</span>`</span>);
|
||||
|
||||
<span class="cmt">// Run agent in background...</span>
|
||||
<span class="fn">runAgent</span>(data).catch(console.error);
|
||||
});
|
||||
|
||||
app.<span class="fn">listen</span>(<span class="num">3000</span>);</code></pre>
|
||||
|
||||
<h2>Going further</h2>
|
||||
|
||||
<div class="card-grid">
|
||||
<a href="getting-started.html" class="card" style="text-decoration:none;color:inherit;">
|
||||
<h4>← Getting started</h4>
|
||||
<p>Configure Claude Desktop, Codex CLI, or opencode.</p>
|
||||
</a>
|
||||
<a href="platforms.html" class="card" style="text-decoration:none;color:inherit;">
|
||||
<h4>Platform guides</h4>
|
||||
<p>Connect TikTok, WhatsApp, Instagram, and more.</p>
|
||||
</a>
|
||||
<a href="https://hermes.squaremcp.com/openapi-social.json" target="_blank" class="card" style="text-decoration:none;color:inherit;">
|
||||
<h4>API reference ↗</h4>
|
||||
<p>Full tool schemas for every platform.</p>
|
||||
</a>
|
||||
<a href="https://docs.anthropic.com/en/api/tool-use" target="_blank" class="card" style="text-decoration:none;color:inherit;">
|
||||
<h4>Anthropic tool use ↗</h4>
|
||||
<p>Deep dive into Claude's tool-use API.</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
185
docs/getting-started.html
Normal file
185
docs/getting-started.html
Normal file
@@ -0,0 +1,185 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Getting Started — SquareMCP Docs</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="site-nav">
|
||||
<div class="nav-inner">
|
||||
<a href="index.html" class="nav-logo"><span class="nav-logo-mark">S</span> SquareMCP</a>
|
||||
<div class="nav-links">
|
||||
<a href="getting-started.html" class="active">Getting started</a>
|
||||
<a href="platforms.html">Platform guides</a>
|
||||
<a href="agent-tutorial.html">Agent tutorial</a>
|
||||
<a href="https://hermes.squaremcp.com/openapi-social.json" target="_blank">API reference ↗</a>
|
||||
</div>
|
||||
<a href="https://squaremcp.com" class="nav-cta" target="_blank">Open app</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="page">
|
||||
<div class="hero">
|
||||
<h1>Getting started</h1>
|
||||
<p>Connect your AI assistant to SquareMCP in five minutes. Choose your client below.</p>
|
||||
</div>
|
||||
|
||||
<h2>Step 1 — Create your account</h2>
|
||||
<ol class="steps">
|
||||
<li>
|
||||
<div>
|
||||
<strong>Sign up at squaremcp.com</strong>
|
||||
Open the <a href="https://squaremcp.com" target="_blank">SquareMCP dashboard</a>, create an account, and verify your email.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Get your access token</strong>
|
||||
Click <strong>Connect MCP Client</strong> in the dashboard. This opens a short OAuth flow that issues a Bearer token bound to your account.
|
||||
Copy the token shown on the confirmation page — it won't be displayed again.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Connect at least one platform</strong>
|
||||
Go to <strong>Platforms</strong> and connect LinkedIn, TikTok, WhatsApp, or any other service. See <a href="platforms.html">Platform guides</a> for step-by-step instructions per platform.
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h2>Step 2 — Configure your MCP client</h2>
|
||||
|
||||
<div class="tabs">
|
||||
<button class="tab active" onclick="switchTab(this,'claude')">Claude Desktop</button>
|
||||
<button class="tab" onclick="switchTab(this,'codex')">Codex CLI</button>
|
||||
<button class="tab" onclick="switchTab(this,'opencode')">opencode</button>
|
||||
</div>
|
||||
|
||||
<div id="tab-claude" class="tab-content tab-panel active">
|
||||
<pre><code><span class="cmt">// ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)</span>
|
||||
<span class="cmt">// %APPDATA%\Claude\claude_desktop_config.json (Windows)</span>
|
||||
{
|
||||
<span class="str">"mcpServers"</span>: {
|
||||
<span class="str">"squaremcp"</span>: {
|
||||
<span class="str">"type"</span>: <span class="str">"http"</span>,
|
||||
<span class="str">"url"</span>: <span class="str">"https://hermes.squaremcp.com/mcp"</span>,
|
||||
<span class="str">"headers"</span>: {
|
||||
<span class="str">"Authorization"</span>: <span class="str">"Bearer YOUR_TOKEN_HERE"</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
<p>Restart Claude Desktop after saving. You should see SquareMCP tools in the tool picker (hammer icon).</p>
|
||||
</div>
|
||||
|
||||
<div id="tab-codex" class="tab-content tab-panel">
|
||||
<pre><code><span class="cmt"># ~/.codex/config.json</span>
|
||||
{
|
||||
<span class="str">"mcpServers"</span>: {
|
||||
<span class="str">"squaremcp"</span>: {
|
||||
<span class="str">"type"</span>: <span class="str">"http"</span>,
|
||||
<span class="str">"url"</span>: <span class="str">"https://hermes.squaremcp.com/mcp"</span>,
|
||||
<span class="str">"headers"</span>: {
|
||||
<span class="str">"Authorization"</span>: <span class="str">"Bearer YOUR_TOKEN_HERE"</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
<p>Or pass inline per command:</p>
|
||||
<pre><code>codex --mcp-server squaremcp=https://hermes.squaremcp.com/mcp \
|
||||
--mcp-header squaremcp:Authorization="Bearer YOUR_TOKEN_HERE" \
|
||||
"Post a LinkedIn update about today's product launch"</code></pre>
|
||||
<div class="callout">
|
||||
<strong>PKCE flow (optional)</strong>
|
||||
Codex CLI supports the full OAuth PKCE flow. Run <code>codex auth squaremcp</code> and follow the browser prompt — no token copy-paste required.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tab-opencode" class="tab-content tab-panel">
|
||||
<pre><code><span class="cmt"># ~/.config/opencode/config.json</span>
|
||||
{
|
||||
<span class="str">"mcp"</span>: {
|
||||
<span class="str">"servers"</span>: {
|
||||
<span class="str">"squaremcp"</span>: {
|
||||
<span class="str">"type"</span>: <span class="str">"http"</span>,
|
||||
<span class="str">"url"</span>: <span class="str">"https://hermes.squaremcp.com/mcp"</span>,
|
||||
<span class="str">"headers"</span>: {
|
||||
<span class="str">"Authorization"</span>: <span class="str">"Bearer YOUR_TOKEN_HERE"</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
<p>Save the file and restart opencode. The SquareMCP tools will appear in the tool list automatically.</p>
|
||||
</div>
|
||||
|
||||
<h2>Step 3 — Verify the connection</h2>
|
||||
<p>Ask your AI assistant:</p>
|
||||
<pre><code>What social platforms do I have connected?</code></pre>
|
||||
<p>It should call <code>get_profile</code> or <code>linkedin_get_profile</code> and return your account details. If you see a "Platform not connected" error, revisit the <a href="platforms.html">Platform guides</a>.</p>
|
||||
|
||||
<h2>Available tools</h2>
|
||||
<p>SquareMCP exposes tools across every connected platform. A few highlights:</p>
|
||||
|
||||
<div class="card-grid">
|
||||
<div class="card">
|
||||
<h4>linkedin_create_post</h4>
|
||||
<p>Publish text, image, or video to your LinkedIn feed.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h4>tiktok_create_video</h4>
|
||||
<p>Upload a video file and publish it to TikTok.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h4>whatsapp_send_message</h4>
|
||||
<p>Send a WhatsApp message to any number via Business API.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h4>twitter_create_tweet</h4>
|
||||
<p>Post a tweet with optional media attachment.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h4>instagram_create_reel</h4>
|
||||
<p>Publish a reel to your Instagram Business account.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h4>send_email</h4>
|
||||
<p>Send email from any connected IMAP/SMTP account.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>See the full list in the <a href="https://hermes.squaremcp.com/openapi-social.json" target="_blank">API reference</a>.</p>
|
||||
|
||||
<h2>Troubleshooting</h2>
|
||||
|
||||
<h3>Tools not appearing in Claude</h3>
|
||||
<p>Restart Claude Desktop after editing <code>claude_desktop_config.json</code>. If tools still don't appear, open the Claude Desktop developer console and look for MCP connection errors.</p>
|
||||
|
||||
<h3>"Platform not connected" errors</h3>
|
||||
<p>The tool was called but the platform isn't linked to your account. Open the dashboard and connect the platform under <strong>Platforms</strong>.</p>
|
||||
|
||||
<h3>"Token expired" badge in dashboard</h3>
|
||||
<p>OAuth tokens for LinkedIn, TikTok, and Instagram expire. SquareMCP attempts an automatic refresh — if that fails, reconnect the platform. WhatsApp, Telegram, and Discord use long-lived bot tokens that don't expire.</p>
|
||||
|
||||
<h3>Rate limit errors</h3>
|
||||
<p>Each SquareMCP plan has a monthly tool-call limit. Check <strong>Usage</strong> in the dashboard. Upgrade your plan if you're consistently hitting the limit.</p>
|
||||
|
||||
<div class="callout callout-warn">
|
||||
<strong>Keep your Bearer token secret</strong>
|
||||
Your Bearer token has full access to every connected platform. Treat it like a password. Rotate it from the dashboard if you suspect it's been exposed.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function switchTab(btn, id) {
|
||||
btn.closest('.page').querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||||
btn.closest('.page').querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
document.getElementById('tab-' + id).classList.add('active');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
94
docs/index.html
Normal file
94
docs/index.html
Normal file
@@ -0,0 +1,94 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SquareMCP Docs — AI Social Media Gateway</title>
|
||||
<meta name="description" content="Connect Claude, Codex CLI, and opencode to LinkedIn, TikTok, WhatsApp, and more via the Model Context Protocol.">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="site-nav">
|
||||
<div class="nav-inner">
|
||||
<a href="index.html" class="nav-logo"><span class="nav-logo-mark">S</span> SquareMCP</a>
|
||||
<div class="nav-links">
|
||||
<a href="getting-started.html">Getting started</a>
|
||||
<a href="platforms.html">Platform guides</a>
|
||||
<a href="agent-tutorial.html">Agent tutorial</a>
|
||||
<a href="https://hermes.squaremcp.com/openapi-social.json" target="_blank">API reference ↗</a>
|
||||
</div>
|
||||
<a href="https://squaremcp.com" class="nav-cta" target="_blank">Open app</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="page">
|
||||
<div class="hero">
|
||||
<h1>Build AI agents that<br>talk to the world</h1>
|
||||
<p>SquareMCP connects Claude, Codex CLI, and opencode to LinkedIn, TikTok, WhatsApp, Instagram, Twitter, and more — through a single MCP server.</p>
|
||||
</div>
|
||||
|
||||
<div class="card-grid">
|
||||
<a href="getting-started.html" class="card" style="text-decoration:none;color:inherit;">
|
||||
<h4>🚀 Getting started</h4>
|
||||
<p>Add SquareMCP to Claude Desktop, Codex CLI, or opencode in five minutes.</p>
|
||||
</a>
|
||||
<a href="platforms.html" class="card" style="text-decoration:none;color:inherit;">
|
||||
<h4>🔌 Platform guides</h4>
|
||||
<p>Connect LinkedIn, TikTok, and WhatsApp. Step-by-step setup for each platform.</p>
|
||||
</a>
|
||||
<a href="agent-tutorial.html" class="card" style="text-decoration:none;color:inherit;">
|
||||
<h4>🤖 Agent tutorial</h4>
|
||||
<p>Real code: a Claude agent that researches news and posts to LinkedIn automatically.</p>
|
||||
</a>
|
||||
<a href="https://hermes.squaremcp.com/openapi-social.json" target="_blank" class="card" style="text-decoration:none;color:inherit;">
|
||||
<h4>📖 API reference ↗</h4>
|
||||
<p>Full OpenAPI spec for every social tool. Mail API available separately.</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Why MCP?</h2>
|
||||
<p>The Model Context Protocol lets AI assistants call real tools without custom integrations. Instead of writing a bespoke connector for every model and platform, you configure SquareMCP once and every MCP-compatible client gains the same 50+ tools.</p>
|
||||
<p>SquareMCP is multi-tenant by design: each customer's credentials are encrypted at rest, isolated per account, and never shared across sessions. Bearer tokens issued through OAuth are bound to your customer record so every tool call is attributable and rate-limited.</p>
|
||||
|
||||
<h2>Supported clients</h2>
|
||||
<div class="card-grid">
|
||||
<div class="card">
|
||||
<h4>Claude Desktop</h4>
|
||||
<p>Add a <code>mcpServers</code> entry to <code>claude_desktop_config.json</code>. No extra software needed.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h4>Codex CLI</h4>
|
||||
<p>Pass <code>--mcp-server</code> or configure in <code>~/.codex/config.json</code>. Full PKCE OAuth flow supported.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h4>opencode</h4>
|
||||
<p>Add SquareMCP to your opencode MCP providers list. HTTP Bearer transport.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h4>Custom agents</h4>
|
||||
<p>Any MCP client that supports Streamable HTTP transport works. See the agent tutorial for a from-scratch example.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Supported platforms</h2>
|
||||
<div class="card-grid">
|
||||
<div class="card"><h4><span class="icon" style="background:#0a66c2;">in</span> LinkedIn</h4><p>Post text, images, and video. Search connections, send messages.</p></div>
|
||||
<div class="card"><h4><span class="icon" style="background:#000;">🎵</span> TikTok</h4><p>Upload and publish videos. View creator analytics.</p></div>
|
||||
<div class="card"><h4><span class="icon" style="background:#25d366;">💬</span> WhatsApp</h4><p>Send messages and templates. Receive inbound via webhook.</p></div>
|
||||
<div class="card"><h4><span class="icon" style="background:linear-gradient(45deg,#f09433,#dc2743,#bc1888);">📷</span> Instagram</h4><p>Publish reels and image posts via Business API.</p></div>
|
||||
<div class="card"><h4><span class="icon" style="background:#000;">𝕏</span> Twitter / X</h4><p>Tweet with media. Search and read timeline.</p></div>
|
||||
<div class="card"><h4><span class="icon" style="background:#1877f2;">f</span> Facebook</h4><p>Post to pages, share photos and video.</p></div>
|
||||
<div class="card"><h4><span class="icon" style="background:#0088cc;">✈️</span> Telegram</h4><p>Send messages and photos via bot token.</p></div>
|
||||
<div class="card"><h4><span class="icon" style="background:#5865f2;">🎮</span> Discord</h4><p>Send messages to channels via bot.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Highlight active nav link
|
||||
document.querySelectorAll('.nav-links a').forEach(a => {
|
||||
if (a.href === location.href) a.classList.add('active');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
288
docs/platforms.html
Normal file
288
docs/platforms.html
Normal file
@@ -0,0 +1,288 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Platform Guides — SquareMCP Docs</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="site-nav">
|
||||
<div class="nav-inner">
|
||||
<a href="index.html" class="nav-logo"><span class="nav-logo-mark">S</span> SquareMCP</a>
|
||||
<div class="nav-links">
|
||||
<a href="getting-started.html">Getting started</a>
|
||||
<a href="platforms.html" class="active">Platform guides</a>
|
||||
<a href="agent-tutorial.html">Agent tutorial</a>
|
||||
<a href="https://hermes.squaremcp.com/openapi-social.json" target="_blank">API reference ↗</a>
|
||||
</div>
|
||||
<a href="https://squaremcp.com" class="nav-cta" target="_blank">Open app</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="page">
|
||||
<div class="hero">
|
||||
<h1>Platform guides</h1>
|
||||
<p>Step-by-step instructions for connecting each platform. Click a platform to jump to its guide.</p>
|
||||
</div>
|
||||
|
||||
<div class="card-grid">
|
||||
<a href="#linkedin" class="card" style="text-decoration:none;color:inherit;"><h4><span class="icon" style="background:#0a66c2;color:#fff;">in</span> LinkedIn</h4><p>OAuth access token, post text and video.</p></a>
|
||||
<a href="#tiktok" class="card" style="text-decoration:none;color:inherit;"><h4><span class="icon" style="background:#000;color:#fff;">🎵</span> TikTok</h4><p>Login Kit OAuth flow, upload video.</p></a>
|
||||
<a href="#whatsapp" class="card" style="text-decoration:none;color:inherit;"><h4><span class="icon" style="background:#25d366;color:#fff;">💬</span> WhatsApp</h4><p>Business API, templates, inbound webhooks.</p></a>
|
||||
<a href="#instagram" class="card" style="text-decoration:none;color:inherit;"><h4><span class="icon" style="background:linear-gradient(45deg,#f09433,#dc2743,#bc1888);color:#fff;">📷</span> Instagram</h4><p>Business account, Graph API token.</p></a>
|
||||
<a href="#twitter" class="card" style="text-decoration:none;color:inherit;"><h4><span class="icon" style="background:#000;color:#fff;">𝕏</span> Twitter / X</h4><p>API v2 credentials, tweet with media.</p></a>
|
||||
<a href="#telegram" class="card" style="text-decoration:none;color:inherit;"><h4><span class="icon" style="background:#0088cc;color:#fff;">✈️</span> Telegram</h4><p>Bot token from BotFather.</p></a>
|
||||
</div>
|
||||
|
||||
<!-- ── LinkedIn ──────────────────────────────────────────────────── -->
|
||||
<h2 id="linkedin"><span class="icon" style="background:#0a66c2;color:#fff;margin-right:8px;">in</span> LinkedIn</h2>
|
||||
|
||||
<h3>What you can do</h3>
|
||||
<ul>
|
||||
<li>Post text updates, articles, images, and videos</li>
|
||||
<li>Search connections and send direct messages</li>
|
||||
<li>Retrieve your profile and network info</li>
|
||||
</ul>
|
||||
|
||||
<h3>Connecting LinkedIn</h3>
|
||||
<ol class="steps">
|
||||
<li>
|
||||
<div>
|
||||
<strong>Create a LinkedIn app</strong>
|
||||
Go to <a href="https://developer.linkedin.com/apps" target="_blank">developer.linkedin.com/apps</a> and create a new app. Add your company page and request the <code>w_member_social</code> and <code>r_liteprofile</code> products.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Generate an access token</strong>
|
||||
Use the LinkedIn OAuth 2.0 token generator in your app dashboard, or use the 3-legged OAuth flow. Copy the access token (valid 60 days; refresh tokens extend to ~12 months).
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Paste into SquareMCP dashboard</strong>
|
||||
Open the SquareMCP dashboard → LinkedIn → Connect → paste your access token → Save.
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h3>Available tools</h3>
|
||||
<p class="code-label">Example prompts</p>
|
||||
<pre><code>Post a LinkedIn update: "Excited to announce our new product launch!"
|
||||
|
||||
Post a LinkedIn video from /tmp/demo.mp4 with caption "Watch our product demo"
|
||||
|
||||
Search my LinkedIn connections for people at Anthropic
|
||||
|
||||
Send a LinkedIn message to John Smith saying "Great meeting you at the conference"</code></pre>
|
||||
|
||||
<div class="callout">
|
||||
<strong>Token refresh</strong>
|
||||
LinkedIn access tokens expire after 60 days. SquareMCP will automatically attempt a refresh using the refresh token. If refresh fails (e.g., the user revokes access), reconnect from the dashboard.
|
||||
</div>
|
||||
|
||||
<!-- ── TikTok ──────────────────────────────────────────────────────── -->
|
||||
<h2 id="tiktok"><span class="icon" style="background:#000;color:#fff;margin-right:8px;">🎵</span> TikTok</h2>
|
||||
|
||||
<h3>What you can do</h3>
|
||||
<ul>
|
||||
<li>Upload and publish videos with captions</li>
|
||||
<li>Set privacy level (public, friends, private)</li>
|
||||
<li>View creator info and video status</li>
|
||||
</ul>
|
||||
|
||||
<h3>Connecting TikTok</h3>
|
||||
<ol class="steps">
|
||||
<li>
|
||||
<div>
|
||||
<strong>OAuth via SquareMCP dashboard</strong>
|
||||
Open the SquareMCP dashboard → TikTok → Connect. You'll be redirected to TikTok to authorise the app. SquareMCP uses TikTok Login Kit with <code>video.publish</code> and <code>user.info.basic</code> scopes.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Allow the requested permissions</strong>
|
||||
TikTok shows the requested scopes. Click <strong>Authorize</strong>. You'll be redirected back to the dashboard with a "Connected" badge.
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h3>Uploading a video</h3>
|
||||
<p>Videos must be hosted at a publicly accessible URL. The tool uploads the video to TikTok's servers asynchronously — use <code>tiktok_get_video_status</code> to poll for completion.</p>
|
||||
|
||||
<p class="code-label">Example prompts</p>
|
||||
<pre><code>Upload the video at https://cdn.example.com/demo.mp4 to TikTok
|
||||
with caption "Check out our new feature! #AI #productlaunch"
|
||||
and set privacy to PUBLIC_TO_EVERYONE
|
||||
|
||||
Check the status of my last TikTok upload</code></pre>
|
||||
|
||||
<div class="callout callout-warn">
|
||||
<strong>TikTok sandbox vs. production</strong>
|
||||
TikTok's Content Posting API requires app approval for production use. New apps start in sandbox mode (videos published to a test account only). Apply for production access through the TikTok developer portal.
|
||||
</div>
|
||||
|
||||
<!-- ── WhatsApp ──────────────────────────────────────────────────── -->
|
||||
<h2 id="whatsapp"><span class="icon" style="background:#25d366;color:#fff;margin-right:8px;">💬</span> WhatsApp</h2>
|
||||
|
||||
<h3>What you can do</h3>
|
||||
<ul>
|
||||
<li>Send freeform messages and approved template messages</li>
|
||||
<li>Send images, documents, and media</li>
|
||||
<li>Receive inbound messages via webhook and forward to your AI agent</li>
|
||||
<li>List all approved message templates</li>
|
||||
</ul>
|
||||
|
||||
<h3>Prerequisites</h3>
|
||||
<p>You need a <strong>WhatsApp Business Account</strong> and a phone number registered through the <a href="https://business.facebook.com/settings/whatsapp-business-accounts" target="_blank">Meta Business Manager</a>.</p>
|
||||
|
||||
<h3>Connecting WhatsApp</h3>
|
||||
<ol class="steps">
|
||||
<li>
|
||||
<div>
|
||||
<strong>Get your credentials from Meta</strong>
|
||||
In Meta for Developers → Your App → WhatsApp → API Setup:
|
||||
<ul>
|
||||
<li><strong>Phone Number ID</strong> — the ID of your registered number</li>
|
||||
<li><strong>Business Account ID</strong> — your WABA ID</li>
|
||||
<li><strong>Permanent Access Token</strong> — generate from System Users in Business Manager</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Enter credentials in dashboard</strong>
|
||||
Open SquareMCP dashboard → WhatsApp → Connect → paste all three values → Save.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Configure the webhook (optional — for inbound messages)</strong>
|
||||
In Meta for Developers → Webhooks, set the callback URL to <code>https://hermes.squaremcp.com/webhook/whatsapp</code> and the verify token to your <code>WA_VERIFY_TOKEN</code> (ask support for this). Subscribe to the <code>messages</code> field.
|
||||
Then configure a forwarding URL in SquareMCP dashboard → Webhooks so inbound messages reach your server.
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h3>Sending messages</h3>
|
||||
<p class="code-label">Freeform message (within 24-hour session window)</p>
|
||||
<pre><code>Send a WhatsApp message to +447911123456 saying:
|
||||
"Hi! Your order #12345 has shipped and will arrive tomorrow."</code></pre>
|
||||
|
||||
<p class="code-label">Template message (anytime)</p>
|
||||
<pre><code>List my WhatsApp message templates
|
||||
|
||||
Send the "order_confirmation" template to +447911123456
|
||||
with variables: order_id=12345, delivery_date="14 May 2026"</code></pre>
|
||||
|
||||
<div class="callout">
|
||||
<strong>24-hour rule</strong>
|
||||
WhatsApp only allows freeform messages within 24 hours of a customer contacting you first. Outside that window, use approved template messages.
|
||||
</div>
|
||||
|
||||
<!-- ── Instagram ─────────────────────────────────────────────────── -->
|
||||
<h2 id="instagram"><span class="icon" style="background:linear-gradient(45deg,#f09433,#dc2743,#bc1888);color:#fff;margin-right:8px;">📷</span> Instagram</h2>
|
||||
|
||||
<h3>What you can do</h3>
|
||||
<ul>
|
||||
<li>Publish image posts and reels</li>
|
||||
<li>View profile and recent media</li>
|
||||
</ul>
|
||||
|
||||
<h3>Connecting Instagram</h3>
|
||||
<p>Instagram posting requires a <strong>Business or Creator account</strong> connected to a Facebook Page.</p>
|
||||
|
||||
<ol class="steps">
|
||||
<li>
|
||||
<div>
|
||||
<strong>Get a Graph API access token</strong>
|
||||
Open the <a href="https://developers.facebook.com/tools/explorer/" target="_blank">Graph API Explorer</a>, select your app, and request <code>instagram_basic</code>, <code>instagram_content_publish</code>, and <code>pages_read_engagement</code> permissions. Generate a User Access Token and exchange it for a long-lived token.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Find your Business Account ID</strong>
|
||||
Call <code>GET /me/accounts</code> then <code>GET /{page_id}?fields=instagram_business_account</code> to find your Instagram Business Account ID.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Enter credentials in dashboard</strong>
|
||||
SquareMCP dashboard → Instagram → Connect → paste the access token and Business Account ID.
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p class="code-label">Example prompts</p>
|
||||
<pre><code>Post an Instagram reel from https://cdn.example.com/reel.mp4
|
||||
with caption "Behind the scenes of our product shoot 🎬 #startup"
|
||||
|
||||
Get my last 10 Instagram posts</code></pre>
|
||||
|
||||
<!-- ── Twitter ────────────────────────────────────────────────────── -->
|
||||
<h2 id="twitter"><span class="icon" style="background:#000;color:#fff;margin-right:8px;">𝕏</span> Twitter / X</h2>
|
||||
|
||||
<h3>Connecting Twitter / X</h3>
|
||||
<ol class="steps">
|
||||
<li>
|
||||
<div>
|
||||
<strong>Create a developer app</strong>
|
||||
Go to <a href="https://developer.twitter.com/en/portal/dashboard" target="_blank">developer.twitter.com</a> and create a project and app. Enable <strong>OAuth 1.0a</strong> with Read and Write permissions.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Generate all four credentials</strong>
|
||||
In your app → Keys and tokens: copy the <strong>API Key</strong>, <strong>API Secret</strong>, <strong>Access Token</strong>, and <strong>Access Token Secret</strong> (all four are required).
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Enter credentials in dashboard</strong>
|
||||
SquareMCP dashboard → Twitter / X → Connect → paste all four values.
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p class="code-label">Example prompts</p>
|
||||
<pre><code>Tweet: "We just shipped our v2 release 🚀 Read the changelog at squaremcp.com"
|
||||
|
||||
Upload the video at /tmp/demo.mp4 and tweet "Watch our new feature demo!"
|
||||
|
||||
Search Twitter for tweets mentioning "SquareMCP"</code></pre>
|
||||
|
||||
<!-- ── Telegram ───────────────────────────────────────────────────── -->
|
||||
<h2 id="telegram"><span class="icon" style="background:#0088cc;color:#fff;margin-right:8px;">✈️</span> Telegram</h2>
|
||||
|
||||
<h3>Connecting Telegram</h3>
|
||||
<ol class="steps">
|
||||
<li>
|
||||
<div>
|
||||
<strong>Create a bot with BotFather</strong>
|
||||
Open Telegram and message <code>@BotFather</code>. Send <code>/newbot</code>, choose a name and username, and copy the <strong>API token</strong> it provides (format: <code>123456789:ABCdef...</code>).
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Enter the token in dashboard</strong>
|
||||
SquareMCP dashboard → Telegram → Connect → paste the bot token.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>Add your bot to a chat or group</strong>
|
||||
Bots can only send messages to chats they're a member of. Add your bot to the target chat, then use <code>telegram_get_updates</code> to find the chat ID.
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p class="code-label">Example prompts</p>
|
||||
<pre><code>Send a Telegram message to chat ID -1001234567890 saying "Daily report ready!"
|
||||
|
||||
Get the latest Telegram messages in my channel</code></pre>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
293
docs/styles.css
Normal file
293
docs/styles.css
Normal file
@@ -0,0 +1,293 @@
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root {
|
||||
--bg: #0f0f10;
|
||||
--surface: #1a1a1b;
|
||||
--surface-hover: #222223;
|
||||
--border: #2a2a2b;
|
||||
--text: #e5e5e5;
|
||||
--text-secondary: #888;
|
||||
--accent: #10a37f;
|
||||
--accent-hover: #0d8c6d;
|
||||
--code-bg: #161617;
|
||||
--radius: 10px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
a { color: var(--accent); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
/* ── Nav ── */
|
||||
.site-nav {
|
||||
background: var(--surface);
|
||||
border-bottom: 1px solid var(--border);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.nav-inner {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.nav-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-logo-mark {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: linear-gradient(135deg, var(--accent), #0d8c6d);
|
||||
border-radius: 7px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.nav-links a:hover { color: var(--text); background: var(--surface-hover); text-decoration: none; }
|
||||
.nav-links a.active { color: var(--text); background: var(--bg); }
|
||||
|
||||
.nav-cta {
|
||||
background: var(--accent);
|
||||
color: #fff !important;
|
||||
padding: 7px 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.nav-cta:hover { background: var(--accent-hover) !important; text-decoration: none !important; }
|
||||
|
||||
/* ── Layout ── */
|
||||
.page {
|
||||
max-width: 860px;
|
||||
margin: 0 auto;
|
||||
padding: 56px 24px 96px;
|
||||
}
|
||||
|
||||
.hero {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
line-height: 1.25;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 18px;
|
||||
color: var(--text-secondary);
|
||||
max-width: 620px;
|
||||
}
|
||||
|
||||
/* ── Section headers ── */
|
||||
h2 {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
margin: 48px 0 16px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
margin: 28px 0 10px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
p { margin-bottom: 14px; color: var(--text); }
|
||||
|
||||
ul, ol { padding-left: 20px; margin-bottom: 14px; }
|
||||
li { margin-bottom: 6px; }
|
||||
|
||||
/* ── Code ── */
|
||||
pre {
|
||||
background: var(--code-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 20px 24px;
|
||||
overflow-x: auto;
|
||||
margin: 16px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
p code, li code {
|
||||
background: #2a2a2b;
|
||||
padding: 2px 7px;
|
||||
border-radius: 5px;
|
||||
font-size: 12.5px;
|
||||
color: #e5e5e5;
|
||||
}
|
||||
|
||||
.code-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 6px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* Syntax colours */
|
||||
.kw { color: #569cd6; }
|
||||
.str { color: #ce9178; }
|
||||
.cmt { color: #6a9955; }
|
||||
.fn { color: #dcdcaa; }
|
||||
.num { color: #b5cea8; }
|
||||
.cls { color: #4ec9b0; }
|
||||
|
||||
/* ── Callout ── */
|
||||
.callout {
|
||||
background: rgba(16, 163, 127, 0.08);
|
||||
border: 1px solid rgba(16, 163, 127, 0.25);
|
||||
border-radius: var(--radius);
|
||||
padding: 16px 20px;
|
||||
margin: 20px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.callout-warn {
|
||||
background: rgba(245, 158, 11, 0.08);
|
||||
border-color: rgba(245, 158, 11, 0.25);
|
||||
}
|
||||
|
||||
.callout strong { display: block; margin-bottom: 4px; }
|
||||
|
||||
/* ── Steps ── */
|
||||
.steps { counter-reset: step; margin: 0; padding: 0; list-style: none; }
|
||||
|
||||
.steps li {
|
||||
counter-increment: step;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.steps li::before {
|
||||
content: counter(step);
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.steps li > div { flex: 1; }
|
||||
.steps li strong { display: block; margin-bottom: 4px; font-size: 15px; }
|
||||
|
||||
/* ── Cards ── */
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
gap: 14px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 20px;
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
|
||||
.card:hover { border-color: #3a3a3b; }
|
||||
.card h4 { font-size: 15px; margin-bottom: 6px; }
|
||||
.card p { font-size: 13px; color: var(--text-secondary); margin: 0; }
|
||||
|
||||
/* ── Tab switcher ── */
|
||||
.tabs { display: flex; gap: 4px; margin-bottom: -1px; }
|
||||
.tab {
|
||||
padding: 8px 16px;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
border-bottom: none;
|
||||
border-radius: 8px 8px 0 0;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.tab.active { background: var(--code-bg); border-color: var(--border); color: var(--text); }
|
||||
.tab-content { display: none; }
|
||||
.tab-content.active { display: block; }
|
||||
.tab-panel { border: 1px solid var(--border); border-radius: 0 var(--radius) var(--radius) var(--radius); }
|
||||
.tab-panel pre { border: none; border-radius: 0 var(--radius) var(--radius) var(--radius); margin: 0; }
|
||||
|
||||
/* ── Badge ── */
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
.badge-green { background: rgba(16,163,127,0.15); color: var(--accent); }
|
||||
.badge-blue { background: rgba(59,130,246,0.15); color: #60a5fa; }
|
||||
.badge-amber { background: rgba(245,158,11,0.15); color: #f59e0b; }
|
||||
|
||||
/* ── Platform icon ── */
|
||||
.platform-icon { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; }
|
||||
.icon { width: 24px; height: 24px; border-radius: 6px; display: inline-flex; align-items: center; justify-content: center; font-size: 13px; }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.hero h1 { font-size: 26px; }
|
||||
.nav-links { display: none; }
|
||||
pre { padding: 14px 16px; }
|
||||
}
|
||||
Reference in New Issue
Block a user