diff --git a/components/settings/ApiKeysPanel.tsx b/components/settings/ApiKeysPanel.tsx index 3052f1a7..76736b0c 100644 --- a/components/settings/ApiKeysPanel.tsx +++ b/components/settings/ApiKeysPanel.tsx @@ -204,11 +204,20 @@ export function ApiKeysPanel() {

Claude Desktop

- Inställningar → Connectors → Add custom connector. Klistra in URL:en nedan. - Du loggas in automatiskt via OAuth. + Lägg till i claude_desktop_config.json (Inställningar → Developer):

-
-{mcpUrl}
+            
+{`{
+  "mcpServers": {
+    "gnubok": {
+      "command": "npx",
+      "args": ["gnubok-mcp"],
+      "env": {
+        "GNUBOK_API_KEY": "gnubok_sk_..."
+      }
+    }
+  }
+}`}
             
diff --git a/packages/gnubok-mcp/index.mjs b/packages/gnubok-mcp/index.mjs new file mode 100755 index 00000000..088432eb --- /dev/null +++ b/packages/gnubok-mcp/index.mjs @@ -0,0 +1,124 @@ +#!/usr/bin/env node +/** + * gnubok-mcp — Connect Claude Desktop to your gnubok bookkeeping account. + * + * Usage in claude_desktop_config.json: + * { + * "mcpServers": { + * "gnubok": { + * "command": "npx", + * "args": ["gnubok-mcp"], + * "env": { + * "GNUBOK_API_KEY": "gnubok_sk_..." + * } + * } + * } + * } + * + * Get your API key at: https://app.gnubok.se/settings?tab=api + */ + +const API_KEY = process.env.GNUBOK_API_KEY +const MCP_URL = process.env.GNUBOK_URL || 'https://app.gnubok.se/api/extensions/ext/mcp-server/mcp' + +if (!API_KEY) { + process.stderr.write( + 'Error: GNUBOK_API_KEY is required.\n' + + 'Get your API key at: https://app.gnubok.se/settings?tab=api\n' + + '\n' + + 'Add it to your Claude Desktop config:\n' + + '{\n' + + ' "mcpServers": {\n' + + ' "gnubok": {\n' + + ' "command": "npx",\n' + + ' "args": ["gnubok-mcp"],\n' + + ' "env": {\n' + + ' "GNUBOK_API_KEY": "gnubok_sk_..."\n' + + ' }\n' + + ' }\n' + + ' }\n' + + '}\n' + ) + process.exit(1) +} + +let buffer = '' + +process.stdin.setEncoding('utf8') +process.stdin.on('data', (chunk) => { + buffer += chunk + + let newlineIdx + while ((newlineIdx = buffer.indexOf('\n')) !== -1) { + const line = buffer.slice(0, newlineIdx).trim() + buffer = buffer.slice(newlineIdx + 1) + + if (!line) continue + + handleMessage(line).catch((err) => { + process.stderr.write(`gnubok-mcp error: ${err.message}\n`) + }) + } +}) + +process.stdin.on('end', () => { + process.exit(0) +}) + +async function handleMessage(line) { + let parsed + try { + parsed = JSON.parse(line) + } catch { + process.stderr.write(`gnubok-mcp: invalid JSON\n`) + return + } + + const isNotification = parsed.id === undefined || parsed.id === null + + try { + const res = await fetch(MCP_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${API_KEY}`, + }, + body: line, + }) + + if (res.status === 202 || res.status === 204) { + return + } + + const text = await res.text() + + // Guard against non-JSON error responses (CDN HTML pages, proxy errors) + if (!res.ok && !isNotification) { + let message = `HTTP ${res.status}` + try { + const json = JSON.parse(text) + if (json.error) message = typeof json.error === 'string' ? json.error : JSON.stringify(json.error) + } catch { /* body wasn't JSON — use generic message */ } + const errorResponse = JSON.stringify({ + jsonrpc: '2.0', + id: parsed.id, + error: { code: -32000, message }, + }) + process.stdout.write(errorResponse + '\n') + return + } + + if (text) { + process.stdout.write(text + '\n') + } + } catch (err) { + if (!isNotification) { + const errorResponse = JSON.stringify({ + jsonrpc: '2.0', + id: parsed.id, + error: { code: -32000, message: `Connection error: ${err.message}` }, + }) + process.stdout.write(errorResponse + '\n') + } + } +} diff --git a/packages/gnubok-mcp/package.json b/packages/gnubok-mcp/package.json new file mode 100644 index 00000000..d57dcd7a --- /dev/null +++ b/packages/gnubok-mcp/package.json @@ -0,0 +1,27 @@ +{ + "name": "gnubok-mcp", + "version": "1.0.1", + "description": "Connect Claude Desktop to your gnubok bookkeeping account", + "bin": { + "gnubok-mcp": "./index.mjs" + }, + "type": "module", + "license": "MIT", + "keywords": [ + "mcp", + "gnubok", + "bookkeeping", + "claude", + "accounting" + ], + "repository": { + "type": "git", + "url": "https://github.com/erp-mafia/gnubok" + }, + "engines": { + "node": ">=18" + }, + "files": [ + "index.mjs" + ] +}