still-harbor-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.
Files changed (2) hide show
  1. package/index.js +90 -0
  2. package/package.json +14 -0
package/index.js ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Still Harbor MCP — stdio server for Claude Desktop.
4
+ *
5
+ * Claude Desktop spawns this script and communicates via
6
+ * newline-delimited JSON-RPC over stdin/stdout.
7
+ * This script is a thin HTTP client — all logic lives on the server.
8
+ *
9
+ * Config in claude_desktop_config.json:
10
+ * {
11
+ * "mcpServers": {
12
+ * "still_harbor": {
13
+ * "command": "npx",
14
+ * "args": ["-y", "still-harbor-mcp"],
15
+ * "env": {
16
+ * "STILL_HARBOR_URL": "https://stillharbor.app/mcp/",
17
+ * "STILL_HARBOR_TOKEN": "your_api_token"
18
+ * }
19
+ * }
20
+ * }
21
+ * }
22
+ */
23
+
24
+ const https = require('https');
25
+ const http = require('http');
26
+ const readline = require('readline');
27
+
28
+ const MCP_URL = process.env.STILL_HARBOR_URL || 'http://localhost/mcp/';
29
+ const TOKEN = process.env.STILL_HARBOR_TOKEN || '';
30
+
31
+ let sessionId = null;
32
+
33
+ function post(body) {
34
+ return new Promise((resolve) => {
35
+ const payload = JSON.stringify(body);
36
+ const url = new URL(MCP_URL);
37
+ const lib = url.protocol === 'https:' ? https : http;
38
+
39
+ const headers = {
40
+ 'Content-Type': 'application/json',
41
+ 'Accept': 'application/json',
42
+ 'Content-Length': Buffer.byteLength(payload),
43
+ 'Authorization': `Bearer ${TOKEN}`,
44
+ };
45
+ if (sessionId) headers['Mcp-Session-Id'] = sessionId;
46
+
47
+ const req = lib.request(
48
+ { hostname: url.hostname, port: url.port || (url.protocol === 'https:' ? 443 : 80),
49
+ path: url.pathname, method: 'POST', headers },
50
+ (res) => {
51
+ if (res.headers['mcp-session-id']) sessionId = res.headers['mcp-session-id'];
52
+
53
+ let raw = '';
54
+ res.on('data', (chunk) => raw += chunk);
55
+ res.on('end', () => {
56
+ if (res.statusCode === 202 || !raw.trim()) return resolve(null);
57
+ try { resolve(JSON.parse(raw)); }
58
+ catch { resolve({ jsonrpc: '2.0', id: body.id, error: { code: -32603, message: `Bad JSON from server: ${raw}` } }); }
59
+ });
60
+ }
61
+ );
62
+
63
+ req.on('error', (err) => resolve({
64
+ jsonrpc: '2.0', id: body.id,
65
+ error: { code: -32603, message: err.message }
66
+ }));
67
+
68
+ req.write(payload);
69
+ req.end();
70
+ });
71
+ }
72
+
73
+ const rl = readline.createInterface({ input: process.stdin, terminal: false });
74
+
75
+ rl.on('line', async (line) => {
76
+ line = line.trim();
77
+ if (!line) return;
78
+
79
+ let body;
80
+ try { body = JSON.parse(line); }
81
+ catch (e) {
82
+ process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32700, message: e.message } }) + '\n');
83
+ return;
84
+ }
85
+
86
+ const response = await post(body);
87
+ if (response !== null) {
88
+ process.stdout.write(JSON.stringify(response) + '\n');
89
+ }
90
+ });
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "still-harbor-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Still Harbor MCP server for Claude Desktop",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "still-harbor-mcp": "./index.js"
8
+ },
9
+ "files": [
10
+ "index.js"
11
+ ],
12
+ "keywords": ["mcp", "still-harbor", "claude"],
13
+ "license": "MIT"
14
+ }