brandguideai-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # brandguideai-mcp
2
+
3
+ Brand strategy knowledge base for Claude Code, Cursor, Windsurf, and any MCP-compatible tool.
4
+
5
+ Search 1,000+ pages of brand methodology, design guidelines, and expert video transcripts — directly from your development environment.
6
+
7
+ ## Quick start
8
+
9
+ ```bash
10
+ npx brandguideai-mcp
11
+ ```
12
+
13
+ No signup needed. 8 free queries included.
14
+
15
+ ## Setup
16
+
17
+ ### Claude Code
18
+
19
+ ```bash
20
+ claude mcp add brandguide -- npx brandguideai-mcp
21
+ ```
22
+
23
+ ### Claude Desktop
24
+
25
+ Add to `claude_desktop_config.json`:
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "brandguide": {
31
+ "command": "npx",
32
+ "args": ["-y", "brandguideai-mcp"]
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ ### Cursor / Windsurf / Other MCP clients
39
+
40
+ Add to your MCP configuration:
41
+
42
+ ```json
43
+ {
44
+ "command": "npx",
45
+ "args": ["-y", "brandguideai-mcp"]
46
+ }
47
+ ```
48
+
49
+ ## What you get
50
+
51
+ **`search_brand_knowledge`** — one tool that searches the brandguide/AI knowledge base.
52
+
53
+ Ask anything about brand strategy:
54
+ - "What is brand identity?"
55
+ - "How do I choose brand colors?"
56
+ - "What makes a good brand name?"
57
+ - "Mi a brand pozicionálás?" (works in Hungarian too)
58
+
59
+ Responses include expert answers with source citations from brand strategy books and video transcripts.
60
+
61
+ ## Free tier
62
+
63
+ Every user gets 8 free queries. No account or API key required — a temporary session is created automatically on first use.
64
+
65
+ After 8 queries, the tool returns upgrade links:
66
+ - **Creator** — 300 queries/month
67
+ - **Pro** — 1,000 queries/month
68
+ - **Mentor+** — unlimited
69
+
70
+ ## For existing brandguide/AI subscribers
71
+
72
+ Your subscription quota works automatically. The MCP server uses the same account system as the web app at [ai.brandguide.hu](https://ai.brandguide.hu).
73
+
74
+ ## How it works
75
+
76
+ The MCP server connects to the brandguide/AI knowledge base through the partner API. Queries are processed by the same RAG pipeline that powers the web app — Voyage AI embeddings, Supabase vector search, and Claude for response generation.
77
+
78
+ Credentials are stored locally at `~/.brandguide/credentials.json`.
79
+
80
+ ## Links
81
+
82
+ - [brandguide/AI web app](https://ai.brandguide.hu)
83
+ - [Agents & MCP info](https://ai.brandguide.hu/agents)
84
+ - [brandguide/AI Skill](https://ai.brandguide.hu/agents)
85
+
86
+ ## License
87
+
88
+ MIT
package/dist/auth.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function getAccessToken(): Promise<string>;
2
+ export declare function getStoredEmail(): string;
package/dist/auth.js ADDED
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAccessToken = getAccessToken;
4
+ exports.getStoredEmail = getStoredEmail;
5
+ const supabase_js_1 = require("@supabase/supabase-js");
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ const node_os_1 = require("node:os");
9
+ const constants_js_1 = require("./constants.js");
10
+ const CREDENTIALS_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), '.brandguide');
11
+ const CREDENTIALS_FILE = (0, node_path_1.join)(CREDENTIALS_DIR, 'credentials.json');
12
+ let cachedSession = null;
13
+ function loadCredentials() {
14
+ try {
15
+ if (!(0, node_fs_1.existsSync)(CREDENTIALS_FILE))
16
+ return null;
17
+ const raw = (0, node_fs_1.readFileSync)(CREDENTIALS_FILE, 'utf-8');
18
+ const data = JSON.parse(raw);
19
+ if (data.email && data.password && data.userId)
20
+ return data;
21
+ return null;
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ function saveCredentials(creds) {
28
+ try {
29
+ if (!(0, node_fs_1.existsSync)(CREDENTIALS_DIR)) {
30
+ (0, node_fs_1.mkdirSync)(CREDENTIALS_DIR, { recursive: true });
31
+ }
32
+ (0, node_fs_1.writeFileSync)(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), 'utf-8');
33
+ }
34
+ catch (err) {
35
+ console.error('[auth] Failed to save credentials:', err);
36
+ }
37
+ }
38
+ async function createTempAccount() {
39
+ const response = await fetch(constants_js_1.CREATE_TEMP_SESSION_URL, {
40
+ method: 'POST',
41
+ headers: {
42
+ Authorization: `Bearer ${constants_js_1.SUPABASE_ANON_KEY}`,
43
+ apikey: constants_js_1.SUPABASE_ANON_KEY,
44
+ 'Content-Type': 'application/json',
45
+ ...constants_js_1.BOT_SHIELD_HEADERS,
46
+ },
47
+ body: JSON.stringify({
48
+ landingVariant: 'business',
49
+ uiLang: 'en',
50
+ sessionFingerprint: `mcp-${Date.now()}`,
51
+ }),
52
+ });
53
+ if (!response.ok) {
54
+ const text = await response.text();
55
+ throw new Error(`Failed to create temp account: ${response.status} ${text}`);
56
+ }
57
+ const data = await response.json();
58
+ if (!data.email || !data.password || !data.userId) {
59
+ throw new Error('Invalid response from create-temp-session');
60
+ }
61
+ return { email: data.email, password: data.password, userId: data.userId };
62
+ }
63
+ async function signIn(email, password) {
64
+ const supabase = (0, supabase_js_1.createClient)(constants_js_1.SUPABASE_URL, constants_js_1.SUPABASE_ANON_KEY, {
65
+ auth: { persistSession: false, autoRefreshToken: false },
66
+ });
67
+ const { data, error } = await supabase.auth.signInWithPassword({ email, password });
68
+ if (error || !data.session) {
69
+ throw new Error(error?.message || 'Sign-in failed');
70
+ }
71
+ return data.session;
72
+ }
73
+ async function getAccessToken() {
74
+ // Check if cached session is still valid (5 min buffer)
75
+ if (cachedSession) {
76
+ const expiresAt = (cachedSession.expires_at ?? 0) * 1000;
77
+ if (expiresAt > Date.now() + 5 * 60 * 1000) {
78
+ return cachedSession.access_token;
79
+ }
80
+ cachedSession = null;
81
+ }
82
+ // Try stored credentials
83
+ let creds = loadCredentials();
84
+ if (!creds) {
85
+ // Create new temp account
86
+ creds = await createTempAccount();
87
+ saveCredentials(creds);
88
+ }
89
+ try {
90
+ cachedSession = await signIn(creds.email, creds.password);
91
+ return cachedSession.access_token;
92
+ }
93
+ catch {
94
+ // Credentials might be stale (account deleted), create new account
95
+ creds = await createTempAccount();
96
+ saveCredentials(creds);
97
+ cachedSession = await signIn(creds.email, creds.password);
98
+ return cachedSession.access_token;
99
+ }
100
+ }
101
+ function getStoredEmail() {
102
+ const creds = loadCredentials();
103
+ return creds?.email ?? '';
104
+ }
@@ -0,0 +1,15 @@
1
+ interface Source {
2
+ title?: string;
3
+ type?: string;
4
+ url?: string;
5
+ page?: number;
6
+ }
7
+ interface SearchResult {
8
+ answer: string;
9
+ sources: Source[];
10
+ remaining: number | null;
11
+ total: number | null;
12
+ quotaExceeded: boolean;
13
+ }
14
+ export declare function searchBrandKnowledge(query: string): Promise<SearchResult>;
15
+ export {};
package/dist/client.js ADDED
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.searchBrandKnowledge = searchBrandKnowledge;
4
+ const constants_js_1 = require("./constants.js");
5
+ const auth_js_1 = require("./auth.js");
6
+ const PARTNER_API_URL = `${constants_js_1.SUPABASE_URL}/functions/v1/partner-api`;
7
+ function formatSources(sources) {
8
+ if (!sources.length)
9
+ return '';
10
+ const lines = sources.map((s) => {
11
+ const parts = [s.title || 'Untitled'];
12
+ if (s.type)
13
+ parts.push(s.type.toUpperCase());
14
+ if (s.page)
15
+ parts.push(`p.${s.page}`);
16
+ return `- ${parts.join(' | ')}`;
17
+ });
18
+ return '\nSources:\n' + lines.join('\n');
19
+ }
20
+ function formatQuotaWall(email) {
21
+ const emailParam = email ? `?prefilled_email=${encodeURIComponent(email)}` : '';
22
+ return [
23
+ "You've used all your free brandguide/AI responses.",
24
+ '',
25
+ 'Upgrade to continue:',
26
+ ` Creator (300/month): ${constants_js_1.STRIPE_LINKS.creator}${emailParam}`,
27
+ ` Pro (1,000/month): ${constants_js_1.STRIPE_LINKS.pro}${emailParam}`,
28
+ ` Mentor+ (unlimited): ${constants_js_1.STRIPE_LINKS.mentorPlus}${emailParam}`,
29
+ '',
30
+ 'Or visit https://ai.brandguide.hu to manage your account.',
31
+ ].join('\n');
32
+ }
33
+ async function searchBrandKnowledge(query) {
34
+ let accessToken = await (0, auth_js_1.getAccessToken)();
35
+ const makeRequest = async (token) => fetch(`${PARTNER_API_URL}?apikey=${constants_js_1.SUPABASE_ANON_KEY}`, {
36
+ method: 'POST',
37
+ headers: {
38
+ Authorization: `Bearer ${token}`,
39
+ 'Content-Type': 'application/json',
40
+ ...constants_js_1.BOT_SHIELD_HEADERS,
41
+ },
42
+ body: JSON.stringify({ query }),
43
+ });
44
+ let response = await makeRequest(accessToken);
45
+ // 401 retry with fresh token
46
+ if (response.status === 401) {
47
+ accessToken = await (0, auth_js_1.getAccessToken)();
48
+ response = await makeRequest(accessToken);
49
+ }
50
+ // Quota exceeded
51
+ if (response.status === 402 || response.status === 429) {
52
+ const email = (0, auth_js_1.getStoredEmail)();
53
+ return {
54
+ answer: formatQuotaWall(email),
55
+ sources: [],
56
+ remaining: 0,
57
+ total: null,
58
+ quotaExceeded: true,
59
+ };
60
+ }
61
+ if (!response.ok) {
62
+ const text = await response.text().catch(() => '');
63
+ throw new Error(`brandguide/AI API error: ${response.status} ${text.slice(0, 200)}`);
64
+ }
65
+ const data = await response.json();
66
+ const answer = data.answer || data.response || '';
67
+ const sources = Array.isArray(data.sources)
68
+ ? data.sources
69
+ : [];
70
+ const usage = data.usage || {};
71
+ return {
72
+ answer: answer + formatSources(sources),
73
+ sources,
74
+ remaining: typeof usage.remaining === 'number' ? usage.remaining : null,
75
+ total: typeof usage.limit === 'number' ? usage.limit : null,
76
+ quotaExceeded: false,
77
+ };
78
+ }
@@ -0,0 +1,15 @@
1
+ export declare const SUPABASE_URL = "https://udqiowvplrkdrviahylk.supabase.co";
2
+ export declare const SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InVkcWlvd3ZwbHJrZHJ2aWFoeWxrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTI4MzEwNDYsImV4cCI6MjA2ODQwNzA0Nn0.GFEyIVbnl5oTKL8FnyelU04RyS5fGDvpNm3AjFqDCtQ";
3
+ export declare const CREATE_TEMP_SESSION_URL = "https://udqiowvplrkdrviahylk.supabase.co/functions/v1/create-temp-session";
4
+ export declare const CONVERSATIONAL_RAG_URL = "https://udqiowvplrkdrviahylk.supabase.co/functions/v1/conversational-rag";
5
+ export declare const STRIPE_PAYMENT_LINK_ID = "5kQ8wO1xs0rS8q15NE2sM03";
6
+ export declare const STRIPE_LINKS: {
7
+ creator: string;
8
+ pro: string;
9
+ mentorPlus: string;
10
+ };
11
+ export declare const BOT_SHIELD_HEADERS: {
12
+ 'User-Agent': string;
13
+ Origin: string;
14
+ 'Accept-Language': string;
15
+ };
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BOT_SHIELD_HEADERS = exports.STRIPE_LINKS = exports.STRIPE_PAYMENT_LINK_ID = exports.CONVERSATIONAL_RAG_URL = exports.CREATE_TEMP_SESSION_URL = exports.SUPABASE_ANON_KEY = exports.SUPABASE_URL = void 0;
4
+ exports.SUPABASE_URL = 'https://udqiowvplrkdrviahylk.supabase.co';
5
+ exports.SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InVkcWlvd3ZwbHJrZHJ2aWFoeWxrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTI4MzEwNDYsImV4cCI6MjA2ODQwNzA0Nn0.GFEyIVbnl5oTKL8FnyelU04RyS5fGDvpNm3AjFqDCtQ';
6
+ exports.CREATE_TEMP_SESSION_URL = `${exports.SUPABASE_URL}/functions/v1/create-temp-session`;
7
+ exports.CONVERSATIONAL_RAG_URL = `${exports.SUPABASE_URL}/functions/v1/conversational-rag`;
8
+ exports.STRIPE_PAYMENT_LINK_ID = '5kQ8wO1xs0rS8q15NE2sM03';
9
+ exports.STRIPE_LINKS = {
10
+ creator: `https://buy.stripe.com/${exports.STRIPE_PAYMENT_LINK_ID}`,
11
+ pro: `https://buy.stripe.com/${exports.STRIPE_PAYMENT_LINK_ID}`,
12
+ mentorPlus: `https://buy.stripe.com/${exports.STRIPE_PAYMENT_LINK_ID}`,
13
+ };
14
+ exports.BOT_SHIELD_HEADERS = {
15
+ 'User-Agent': 'Mozilla/5.0 (compatible; brandguideai-mcp/1.0)',
16
+ 'Origin': 'https://ai.brandguide.hu',
17
+ 'Accept-Language': 'en',
18
+ };
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
7
+ const client_js_1 = require("./client.js");
8
+ const server = new index_js_1.Server({ name: 'brandguideai-mcp', version: '1.0.0' }, { capabilities: { tools: {} } });
9
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
10
+ tools: [
11
+ {
12
+ name: 'search_brand_knowledge',
13
+ description: "Search brandguide/AI's brand strategy knowledge base — 1,000+ pages of methodology, design guidelines, and expert video transcripts. Works in Hungarian and English.",
14
+ inputSchema: {
15
+ type: 'object',
16
+ properties: {
17
+ query: {
18
+ type: 'string',
19
+ description: 'Your brand strategy question',
20
+ },
21
+ },
22
+ required: ['query'],
23
+ },
24
+ },
25
+ ],
26
+ }));
27
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
28
+ if (request.params.name !== 'search_brand_knowledge') {
29
+ return {
30
+ content: [{ type: 'text', text: `Unknown tool: ${request.params.name}` }],
31
+ isError: true,
32
+ };
33
+ }
34
+ const query = String(request.params.arguments?.query || '').trim();
35
+ if (!query) {
36
+ return {
37
+ content: [{ type: 'text', text: 'Query is required.' }],
38
+ isError: true,
39
+ };
40
+ }
41
+ try {
42
+ const result = await (0, client_js_1.searchBrandKnowledge)(query);
43
+ if (result.quotaExceeded) {
44
+ return {
45
+ content: [{ type: 'text', text: result.answer }],
46
+ };
47
+ }
48
+ const footer = result.remaining !== null && result.total !== null
49
+ ? `\n\n[${result.remaining}/${result.total} queries remaining]`
50
+ : '';
51
+ return {
52
+ content: [{ type: 'text', text: result.answer + footer }],
53
+ };
54
+ }
55
+ catch (err) {
56
+ const message = err instanceof Error ? err.message : 'Unknown error';
57
+ return {
58
+ content: [{ type: 'text', text: `Error: ${message}` }],
59
+ isError: true,
60
+ };
61
+ }
62
+ });
63
+ async function main() {
64
+ const transport = new stdio_js_1.StdioServerTransport();
65
+ await server.connect(transport);
66
+ }
67
+ main().catch((err) => {
68
+ console.error('Fatal:', err);
69
+ process.exit(1);
70
+ });
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "brandguideai-mcp",
3
+ "version": "1.0.0",
4
+ "description": "brandguide/AI MCP server — brand strategy knowledge base for Claude Code, Cursor, and any MCP client",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "brandguideai-mcp": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsx src/index.ts"
12
+ },
13
+ "keywords": ["mcp", "brand", "branding", "ai", "claude", "brandguide"],
14
+ "files": ["dist", "README.md"],
15
+ "license": "MIT",
16
+ "dependencies": {
17
+ "@modelcontextprotocol/sdk": "^1.22.0",
18
+ "@supabase/supabase-js": "^2.39.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^20.0.0",
22
+ "typescript": "^5.0.0",
23
+ "tsx": "^4.0.0"
24
+ },
25
+ "engines": {
26
+ "node": ">=18"
27
+ }
28
+ }