From fbd14ed6300fa6788f20539000365da3c10bfffe Mon Sep 17 00:00:00 2001 From: David Sourivong Date: Wed, 11 Mar 2026 08:57:14 +0100 Subject: [PATCH 1/4] feat: add High Story MCP server --- src/highstory/README.md | 2 ++ src/highstory/index.ts | 58 ++++++++++++++++++++++++++++++++++++++ src/highstory/package.json | 21 ++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 src/highstory/README.md create mode 100644 src/highstory/index.ts create mode 100644 src/highstory/package.json diff --git a/src/highstory/README.md b/src/highstory/README.md new file mode 100644 index 0000000000..c66b66cf4d --- /dev/null +++ b/src/highstory/README.md @@ -0,0 +1,2 @@ +# High Story MCP Server +This server allows Claude to interact with [High Story](https://highstory.io). diff --git a/src/highstory/index.ts b/src/highstory/index.ts new file mode 100644 index 0000000000..33cdc000a9 --- /dev/null +++ b/src/highstory/index.ts @@ -0,0 +1,58 @@ +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/http.js"; +import { z } from "zod"; +import { createClient } from "@supabase/supabase-js"; + +const server = new McpServer({ + name: "High Story", + version: "1.0.0" +}); + +async function authenticate(authHeader: string | null) { + const supabaseUrl = process.env.SUPABASE_URL; + const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY; + if (!supabaseUrl || !supabaseServiceKey) { + throw new Error("Missing SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY environment variables."); + } + + const supabaseAdmin = createClient(supabaseUrl, supabaseServiceKey); + const token = authHeader?.replace(/^Bearer\s+/i, '').trim(); + + if (!token) throw new Error("Missing Authorization token"); + + if (token.startsWith('hs_live_')) { + const hashBuffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(token)); + const hash = Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join(''); + + const { data: keyRecord, error } = await supabaseAdmin + .from('user_api_keys') + .select('user_id') + .eq('key_hash', hash) + .is('revoked_at', null) + .single(); + + if (error || !keyRecord) throw new Error("Invalid or revoked API key"); + + return { userId: keyRecord.user_id, authHeader }; + } + + const { data: { user }, error } = await supabaseAdmin.auth.getUser(token); + if (error || !user) throw new Error("Invalid session token"); + + return { userId: user.id, authHeader }; +} + +server.tool( + "execute_campaign", + "Create and execute an AI content campaign for a brand.", + { + brand_url: z.string().optional(), + campaign_objective: z.string(), + workspace_id: z.string().optional() + }, + async (args) => { + return { content: [{ type: "text", text: "Campaign execution logic triggered via High Story Cloud." }] }; + } +); + +export default server; diff --git a/src/highstory/package.json b/src/highstory/package.json new file mode 100644 index 0000000000..ad6cd2f22e --- /dev/null +++ b/src/highstory/package.json @@ -0,0 +1,21 @@ +{ + "name": "@modelcontextprotocol/server-highstory", + "version": "1.0.0", + "description": "High Story MCP server for AI-driven social media automation", + "type": "module", + "bin": { + "mcp-server-highstory": "dist/index.js" + }, + "scripts": { + "build": "tsc && chmod +x dist/index.js", + "prepare": "npm run build" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.26.0", + "@supabase/supabase-js": "^2.39.3", + "zod": "^3.25.0" + }, + "devDependencies": { + "typescript": "^5.6.2" + } +} From 644d0140f5a19c6d3ff8596b174f4e7afe39b4b0 Mon Sep 17 00:00:00 2001 From: David Sourivong Date: Wed, 11 Mar 2026 09:05:47 +0100 Subject: [PATCH 2/4] refactor: simplify auth for public use --- src/highstory/index.ts | 72 ++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/src/highstory/index.ts b/src/highstory/index.ts index 33cdc000a9..93a29fb9b0 100644 --- a/src/highstory/index.ts +++ b/src/highstory/index.ts @@ -8,50 +8,54 @@ const server = new McpServer({ version: "1.0.0" }); -async function authenticate(authHeader: string | null) { - const supabaseUrl = process.env.SUPABASE_URL; - const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY; - if (!supabaseUrl || !supabaseServiceKey) { - throw new Error("Missing SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY environment variables."); +const API_BASE_URL = "https://jeprtikkylotvcddrqvm.supabase.co/functions/v1"; + +/** + * Helper to call High Story Edge Functions + */ +async function callHighStoryAPI(endpoint: string, payload: any) { + const apiKey = process.env.HIGHSTORY_API_KEY; + if (!apiKey) { + throw new Error("Missing HIGHSTORY_API_KEY environment variable. Please generate one in your High Story settings."); } - - const supabaseAdmin = createClient(supabaseUrl, supabaseServiceKey); - const token = authHeader?.replace(/^Bearer\s+/i, '').trim(); - - if (!token) throw new Error("Missing Authorization token"); - - if (token.startsWith('hs_live_')) { - const hashBuffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(token)); - const hash = Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join(''); - - const { data: keyRecord, error } = await supabaseAdmin - .from('user_api_keys') - .select('user_id') - .eq('key_hash', hash) - .is('revoked_at', null) - .single(); - - if (error || !keyRecord) throw new Error("Invalid or revoked API key"); - - return { userId: keyRecord.user_id, authHeader }; + + const response = await fetch(`${API_BASE_URL}/${endpoint}`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${apiKey}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.error || `API Error: ${response.statusText}`); } - const { data: { user }, error } = await supabaseAdmin.auth.getUser(token); - if (error || !user) throw new Error("Invalid session token"); - - return { userId: user.id, authHeader }; + return await response.json(); } +// --- Tools Implementation --- + server.tool( "execute_campaign", - "Create and execute an AI content campaign for a brand.", + "Create and execute an AI content campaign for a brand. Generates social media posts based on brand analysis.", { - brand_url: z.string().optional(), - campaign_objective: z.string(), - workspace_id: z.string().optional() + brand_url: z.string().optional().describe("The brand website URL (optional)"), + campaign_objective: z.string().describe("The campaign goal"), + workspace_id: z.string().optional().describe("High Story workspace ID (optional)"), }, async (args) => { - return { content: [{ type: "text", text: "Campaign execution logic triggered via High Story Cloud." }] }; + try { + const data = await callHighStoryAPI('execute-campaign', { + ...args, + is_onboarding: false + }); + return { content: [{ type: "text", text: `Campaign started successfully! View it in your dashboard.` }] }; + } catch (e: any) { + return { isError: true, content: [{ type: "text", text: `Error: ${e.message}` }] }; + } } ); From d36c30947e07460ccd35360dea28d62619d17cf2 Mon Sep 17 00:00:00 2001 From: David Sourivong Date: Wed, 11 Mar 2026 09:11:26 +0100 Subject: [PATCH 3/4] fix: add stdio transport and entry point --- src/highstory/index.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/highstory/index.ts b/src/highstory/index.ts index 93a29fb9b0..1e93e8e16d 100644 --- a/src/highstory/index.ts +++ b/src/highstory/index.ts @@ -1,7 +1,6 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/http.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; -import { createClient } from "@supabase/supabase-js"; const server = new McpServer({ name: "High Story", @@ -59,4 +58,7 @@ server.tool( } ); -export default server; +// --- Entry Point --- + +const transport = new StdioServerTransport(); +await server.connect(transport); From b6b46a273276bf54369fbe966b9e89bb2f0f8164 Mon Sep 17 00:00:00 2001 From: David Sourivong Date: Wed, 11 Mar 2026 09:20:31 +0100 Subject: [PATCH 4/4] chore: standardize highstory module for official repo --- src/highstory/package.json | 6 ++++-- src/highstory/tsconfig.json | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 src/highstory/tsconfig.json diff --git a/src/highstory/package.json b/src/highstory/package.json index ad6cd2f22e..1f37d9f25c 100644 --- a/src/highstory/package.json +++ b/src/highstory/package.json @@ -2,20 +2,22 @@ "name": "@modelcontextprotocol/server-highstory", "version": "1.0.0", "description": "High Story MCP server for AI-driven social media automation", + "license": "MIT", "type": "module", "bin": { "mcp-server-highstory": "dist/index.js" }, "scripts": { - "build": "tsc && chmod +x dist/index.js", + "build": "tsc && shx chmod +x dist/index.js", "prepare": "npm run build" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.26.0", - "@supabase/supabase-js": "^2.39.3", "zod": "^3.25.0" }, "devDependencies": { + "@types/node": "^22.10.2", + "shx": "^0.3.4", "typescript": "^5.6.2" } } diff --git a/src/highstory/tsconfig.json b/src/highstory/tsconfig.json new file mode 100644 index 0000000000..ec5da15825 --- /dev/null +++ b/src/highstory/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "." + }, + "include": [ + "./**/*.ts" + ] +}