From 6604ab5d2baaa08b60c2d4596824ee2bf72c7512 Mon Sep 17 00:00:00 2001 From: Garfield Date: Fri, 12 Jun 2026 14:55:36 -0400 Subject: [PATCH] feat(connect): dedicated Claude.ai / ChatGPT browser connect picker - Replace single 'Connect to Claude / ChatGPT' button with a modal picker offering Claude.ai web, Claude Desktop, Codex CLI, and ChatGPT/GPT Actions. - Add /oauth/connect-claude-ai backend route that redirects to Anthropic's official https://claude.ai/api/mcp/auth_callback OAuth callback. - Update MCP callback result page with browser-specific instructions for Claude.ai web, Claude Desktop, ChatGPT/GPT Actions, and Codex CLI. - Deploy new app and hermes images to K8s. --- hermes-k8s.yaml | 6 ++-- product/app/app-k8s.yaml | 2 +- product/app/app.js | 66 ++++++++++++++++++++++++++++++++++++++-- product/app/index.html | 2 +- product/app/styles.css | 63 ++++++++++++++++++++++++++++++++++++++ src/index.ts | 55 ++++++++++++++++++++++++++++----- 6 files changed, 180 insertions(+), 14 deletions(-) diff --git a/hermes-k8s.yaml b/hermes-k8s.yaml index 45762ca..68e6f46 100644 --- a/hermes-k8s.yaml +++ b/hermes-k8s.yaml @@ -22,7 +22,7 @@ spec: fsGroup: 1000 containers: - name: hermes-mcp - image: localhost:32000/hermes-mcp@sha256:d282f50a2409b322ffbf5a7e10511493e3fcf7346a9fb47ecfac10b8cbe02660 + image: localhost:32000/hermes-mcp@sha256:e5dec7b23a64f2f2d7977d24ca789dbcc077bcffb86a5b623d6ec64ff2436e06 imagePullPolicy: Always securityContext: allowPrivilegeEscalation: false @@ -158,11 +158,11 @@ spec: - name: PILOT_CUSTOMER_ID value: "9a3f1a23-3080-4f9f-932c-02dae813ee96" - name: FACEBOOK_DEFAULT_ACCESS_TOKEN - value: "EAAYG3FLDWzMBRgOmCM5GX7E3L6zk5utoZCn9eZAVvk0Ein6NaYtDZCtD5aMP3yMDnB0X2EoqvIYeOU77PhCCNaCve9LwX8iyQ2UsxsCajeHc7SXQL4EYWB7UEsDbcRA2gRF8GITYgbhBKKRlE3ehlwWBySwfxVexzMDgkGgz3ctzK4144hgJnE3LZB8EHP2FvolqNpXPVitexunWN0hxRwVXUSDgZCiOfzXfa1t0smxDs5wZDZD" + value: "EAAYG3FLDWzMBRmZBDhn1rePtuKDCLUkzHLyJHNJA7yXXdcNUPXmyZA36BwLp7vXHhOxguCIGZB3JfJIhgX2ZBRZBTmZCDfdAYeZBrFAye2L5cIUKvYdjYYA3mlT3ZAacEQgmbhYuKBp4eCOQz0rrNUwLZB2qspvO9wczZAM3tWqFctYBP10oGfgOJIQ8ITweRU2Bgdte2hod66" - name: FACEBOOK_DEFAULT_PAGE_ID value: "1152192567968569" - name: INSTAGRAM_DEFAULT_ACCESS_TOKEN - value: "EAAYG3FLDWzMBRgOmCM5GX7E3L6zk5utoZCn9eZAVvk0Ein6NaYtDZCtD5aMP3yMDnB0X2EoqvIYeOU77PhCCNaCve9LwX8iyQ2UsxsCajeHc7SXQL4EYWB7UEsDbcRA2gRF8GITYgbhBKKRlE3ehlwWBySwfxVexzMDgkGgz3ctzK4144hgJnE3LZB8EHP2FvolqNpXPVitexunWN0hxRwVXUSDgZCiOfzXfa1t0smxDs5wZDZD" + value: "EAAYG3FLDWzMBRmZBDhn1rePtuKDCLUkzHLyJHNJA7yXXdcNUPXmyZA36BwLp7vXHhOxguCIGZB3JfJIhgX2ZBRZBTmZCDfdAYeZBrFAye2L5cIUKvYdjYYA3mlT3ZAacEQgmbhYuKBp4eCOQz0rrNUwLZB2qspvO9wczZAM3tWqFctYBP10oGfgOJIQ8ITweRU2Bgdte2hod66" - name: INSTAGRAM_DEFAULT_BUSINESS_ACCOUNT_ID value: "17841422623735880" - name: WHATSAPP_DEFAULT_ACCESS_TOKEN diff --git a/product/app/app-k8s.yaml b/product/app/app-k8s.yaml index c7ebe31..b514297 100644 --- a/product/app/app-k8s.yaml +++ b/product/app/app-k8s.yaml @@ -15,7 +15,7 @@ spec: spec: containers: - name: squaremcp-app - image: localhost:32000/squaremcp-app@sha256:88c9163b49a881b2d8e59f46d66297902109026d138e9562b9fcc1bb0f4ab4d6 + image: localhost:32000/squaremcp-app@sha256:9c2601dd74bfca9f22350a38dc616eb8a76580090587803911bb2e5633ace361 imagePullPolicy: Always ports: - containerPort: 8080 diff --git a/product/app/app.js b/product/app/app.js index 24571e1..fc973fc 100644 --- a/product/app/app.js +++ b/product/app/app.js @@ -252,9 +252,71 @@ logoutBtn.addEventListener('click', async () => { showLogin(); }); -// Connect MCP Client — start the browser OAuth flow +// Connect MCP Client — show picker for Claude.ai / ChatGPT / desktop / CLI document.getElementById('connect-mcp-btn')?.addEventListener('click', () => { - window.open(`${API_BASE}/oauth/connect-mcp`, '_blank', 'width=560,height=600,noopener'); + openModal(renderMcpClientPicker()); +}); + +function renderMcpClientPicker() { + return ` +
+

Connect an AI client

+

Choose where you want to use SquareMCP tools.

+ +
+
+
Claude.ai (web)
+
Use SquareMCP directly in your browser at claude.ai.
+
+ Connect +
+ +
+
+
Claude Desktop
+
macOS / Windows app with local MCP config.
+
+ +
+ +
+
+
Codex CLI / OpenCode
+
Terminal-based agents (OpenAI, opencode).
+
+ +
+ +
+
+
ChatGPT (web)
+
Copy the OpenAPI spec URL for GPT Actions.
+
+ +
+
+ `; +} + +window.closeMcpPicker = closeModal; + +modalBody.addEventListener('click', (e) => { + const btn = e.target.closest('[data-connect]'); + if (!btn) return; + const type = btn.dataset.connect; + if (type === 'claude-desktop' || type === 'codex') { + window.open(`${API_BASE}/oauth/connect-mcp`, '_blank', 'width=560,height=600,noopener'); + } else if (type === 'chatgpt') { + openModal(` +
+

ChatGPT / GPT Actions

+

ChatGPT browser does not yet support native MCP. Use this OpenAPI spec URL in a GPT Action:

+
${API_BASE}/openapi.json
+

Set authentication to Bearer token and paste your API key from Settings → API Keys.

+ +
+ `); + } }); // Password reset request diff --git a/product/app/index.html b/product/app/index.html index 27e2277..82fd346 100644 --- a/product/app/index.html +++ b/product/app/index.html @@ -94,7 +94,7 @@

Connect your accounts

Connect once. Then ask Claude or ChatGPT to post, search your notes, or send email — without touching any of these apps.

- +
diff --git a/product/app/styles.css b/product/app/styles.css index 9d54671..c2c853e 100644 --- a/product/app/styles.css +++ b/product/app/styles.css @@ -751,3 +751,66 @@ body { text-align: center; min-height: 18px; } + + +/* MCP client picker modal */ +.mcp-picker h3 { + margin: 0 0 6px; + font-size: 1.25rem; +} + +.picker-subtitle { + color: var(--text-secondary); + font-size: 0.9rem; + margin: 0 0 20px; + line-height: 1.5; +} + +.picker-option { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 16px; + border: 1px solid var(--border); + border-radius: var(--radius); + margin-bottom: 12px; + background: var(--background); +} + +.picker-option:last-child { + margin-bottom: 0; +} + +.picker-meta { + flex: 1; + min-width: 0; +} + +.picker-title { + font-weight: 600; + font-size: 0.95rem; + margin-bottom: 4px; +} + +.picker-desc { + color: var(--text-secondary); + font-size: 0.8rem; + line-height: 1.4; +} + +.picker-option .btn { + white-space: nowrap; + padding: 8px 14px; + font-size: 0.85rem; +} + +.token-box { + background: var(--background); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 12px; + font-family: 'SF Mono', monospace; + font-size: 0.85rem; + word-break: break-all; +} diff --git a/src/index.ts b/src/index.ts index 65b37d6..569a52f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -724,6 +724,23 @@ app.get('/oauth/connect-mcp', (req, res) => { res.redirect(`/oauth/authorize?${params}`); }); +// Dedicated entry point for the Claude.ai web MCP client. It uses the official +// Anthropic redirect_uri so Claude.ai receives the authorization code directly. +app.get('/oauth/connect-claude-ai', (req, res) => { + const clientId = process.env.OAUTH_CLIENT_ID; + if (!clientId) { + res.status(503).send('MCP OAuth app not configured (OAUTH_CLIENT_ID missing)'); + return; + } + const params = new URLSearchParams({ + client_id: clientId, + redirect_uri: 'https://claude.ai/api/mcp/auth_callback', + response_type: 'code', + scope: 'mcp', + }); + res.redirect(`/oauth/authorize?${params}`); +}); + // Callback — exchange code for token and render the config snippet page app.get('/oauth/mcp-callback', async (req, res) => { const code = req.query.code as string | undefined; @@ -762,11 +779,12 @@ h1{color:#dc2626;margin:0 0 12px}p{color:#888;margin:0} } const { token, serverUrl } = opts; + const mcpUrl = `${serverUrl}/mcp`; const claudeConfig = JSON.stringify({ - mcpServers: { 'hermes-mcp': { type: 'http', url: `${serverUrl}/mcp`, headers: { Authorization: `Bearer ${token}` } } } + mcpServers: { 'hermes-mcp': { type: 'http', url: mcpUrl, headers: { Authorization: `Bearer ${token}` } } } }, null, 2); const codexConfig = JSON.stringify({ - mcpServers: { 'hermes-mcp': { type: 'http', url: `${serverUrl}/mcp`, headers: { Authorization: `Bearer ${token}` } } } + mcpServers: { 'hermes-mcp': { type: 'http', url: mcpUrl, headers: { Authorization: `Bearer ${token}` } } } }, null, 2); const esc = (s: string) => s.replace(/&/g, '&').replace(//g, '>'); @@ -781,12 +799,18 @@ body{font-family:system-ui,sans-serif;background:#0f0f10;color:#e5e5e5;margin:0; .card{background:#1a1a1b;border:1px solid #2a2a2b;border-radius:12px;padding:32px;max-width:680px;margin:0 auto} h1{font-size:22px;margin:0 0 8px;color:#10a37f} .subtitle{color:#888;margin:0 0 28px;font-size:14px} -h2{font-size:14px;font-weight:600;color:#888;text-transform:uppercase;letter-spacing:.05em;margin:20px 0 8px} +h2{font-size:14px;font-weight:600;color:#888;text-transform:uppercase;letter-spacing:.05em;margin:24px 0 10px} pre{background:#0f0f10;border:1px solid #2a2a2b;border-radius:8px;padding:16px;font-size:12px;overflow-x:auto;position:relative} .copy-btn{position:absolute;top:8px;right:8px;background:#2a2a2b;border:none;color:#888;padding:4px 10px;border-radius:6px;cursor:pointer;font-size:11px} .copy-btn:hover{color:#e5e5e5} .token-box{background:#0f0f10;border:1px solid #2a2a2b;border-radius:8px;padding:12px 16px;font-family:monospace;font-size:13px;word-break:break-all;margin-bottom:8px} .warn{color:#888;font-size:12px;margin:4px 0 20px} +.instruct{color:#a1a1aa;font-size:13px;line-height:1.6;margin:8px 0} +.instruct code{background:#0f0f10;border:1px solid #2a2a2b;border-radius:4px;padding:2px 5px;font-size:12px} +.instruct ol{margin:8px 0;padding-left:20px} +.instruct li{margin:6px 0} +.client-section{border-top:1px solid #2a2a2b;padding-top:18px;margin-top:18px} +.client-section:first-of-type{border-top:none;padding-top:0;margin-top:0} @@ -798,11 +822,28 @@ pre{background:#0f0f10;border:1px solid #2a2a2b;border-radius:8px;padding:16px;f
${esc(token!)}

Store this securely — it won't be shown again.

-

Claude Desktop claude_desktop_config.json

-
${esc(claudeConfig)}
+
+

Claude.ai (browser)

+

In claude.ai go to Settings → Integrations → Add MCP server and paste:

+
${esc(mcpUrl)}
+

When prompted, use the access token above.

+
-

Codex CLI / opencode config

-
${esc(codexConfig)}
+
+

Claude Desktop

+

Paste this into claude_desktop_config.json:

+
${esc(claudeConfig)}
+
+ +
+

ChatGPT / GPT Actions

+

For ChatGPT, use the OpenAPI spec at ${esc(serverUrl!)}/openapi.json and add a Bearer token header with the token above. Native MCP support in chatgpt.com is not yet available.

+
+ +
+

Codex CLI / OpenCode

+
${esc(codexConfig)}
+