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.
+
+
+
+
+
+
+
+
+
+ `;
+}
+
+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 .
+
Open spec
+
+ `);
+ }
});
// 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.
- Connect to Claude / ChatGPT
+ Connect AI Client
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)}Copy
+
+
Claude.ai (browser)
+
In claude.ai go to Settings → Integrations → Add MCP server and paste:
+
${esc(mcpUrl)}Copy
+
When prompted, use the access token above.
+
- Codex CLI / opencode config
- ${esc(codexConfig)}Copy
+
+
Claude Desktop
+
Paste this into claude_desktop_config.json:
+
${esc(claudeConfig)}Copy
+
+
+
+
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)}Copy
+