sombra-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 (3) hide show
  1. package/README.md +80 -0
  2. package/dist/index.js +188 -0
  3. package/package.json +40 -0
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # sombra-mcp
2
+
3
+ Stdio-to-HTTP bridge for [Sombra](https://sombra.so). Persistent reader mode for AI.
4
+
5
+ MCP clients that only support stdio-based servers (OpenClaw, Cursor, Windsurf, and others) can't connect directly to remote HTTP MCP servers. This package bridges that gap: it speaks stdio locally and proxies requests to `https://sombra.so/mcp` over Streamable HTTP.
6
+
7
+ ## Quick start
8
+
9
+ ```bash
10
+ npx sombra-mcp --token sombra_pat_YOUR_TOKEN
11
+ ```
12
+
13
+ Or set the token as an environment variable:
14
+
15
+ ```bash
16
+ SOMBRA_TOKEN=sombra_pat_YOUR_TOKEN npx sombra-mcp
17
+ ```
18
+
19
+ ## Getting a token
20
+
21
+ 1. Sign up or log in at [sombra.so](https://sombra.so/login)
22
+ 2. Go to **Settings > Access Tokens**
23
+ 3. Click **Create Token**
24
+ 4. Copy the token (starts with `sombra_pat_`)
25
+
26
+ Tokens authenticate your agent against your personal Sombra library. Each token has full read/write access to your account.
27
+
28
+ ## Configuring MCP clients
29
+
30
+ ### OpenClaw, Claude Desktop, Cursor, Windsurf
31
+
32
+ Add to your MCP config file (`openclaw.json`, `claude_desktop_config.json`, etc.):
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "sombra": {
38
+ "command": "npx",
39
+ "args": ["-y", "sombra-mcp", "--token", "sombra_pat_YOUR_TOKEN"]
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ ### Claude.ai
46
+
47
+ Add Sombra as a connector in **Settings > Connectors**. No bridge needed - and it will automatically pop up in Claude Code.
48
+
49
+
50
+ ### Claude Code
51
+
52
+ Claude Code supports remote HTTP MCP servers directly, so you don't need this bridge, or if you don't want to set it up in Claude.ai or Claude Desktop.
53
+
54
+ ```bash
55
+ claude mcp add --transport http sombra https://sombra.so/mcp
56
+ ```
57
+
58
+ ## What is Sombra?
59
+
60
+ Sombra is a research library that your AI agent can read from and write to through MCP. Save web pages as clean markdown, organise them into collections, distil the important parts into dense context, and search across everything.
61
+
62
+ 19 tools for saving, searching, organising, and distilling web content and notes. Two prompts for context distillation. Collections exposed as MCP resources.
63
+
64
+ Your research persists between sessions. Tomorrow's conversation picks up where today's left off.
65
+
66
+ [sombra.so](https://sombra.so) | [MCP setup docs](https://sombra.so/mcp) | [What is Sombra?](https://sombra.so/blog/what-is-sombra)
67
+
68
+ ## How it works
69
+
70
+ The bridge reads JSON-RPC messages from stdin, forwards them to `https://sombra.so/mcp` as HTTP POST requests with your Bearer token, and writes the responses back to stdout. It handles session management, protocol version negotiation, and streaming (SSE) responses.
71
+
72
+ No config beyond the token. No local state.
73
+
74
+ ## Requirements
75
+
76
+ Node.js 18 or later.
77
+
78
+ ## License
79
+
80
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ const VERSION = "1.0.0";
4
+ const SOMBRA_URL = "https://sombra.so/mcp";
5
+ function log(msg) {
6
+ process.stderr.write(`[sombra-mcp] ${msg}\n`);
7
+ }
8
+ function printHelp() {
9
+ process.stdout.write(`sombra-mcp v${VERSION} — Sombra MCP stdio-to-HTTP bridge
10
+
11
+ Reader mode for AI. Give your agent a persistent research library.
12
+
13
+ USAGE
14
+
15
+ npx sombra-mcp --token <token>
16
+ SOMBRA_TOKEN=<token> npx sombra-mcp
17
+
18
+ OPTIONS
19
+
20
+ --token <token> Personal Access Token (or set SOMBRA_TOKEN env var)
21
+ --help, -h Show this help message
22
+ --version, -v Show version number
23
+
24
+ GETTING A TOKEN
25
+
26
+ 1. Sign up or log in at https://sombra.so
27
+ 2. Go to Settings → Access Tokens
28
+ 3. Click "Create Token" — tokens start with sombra_pat_
29
+ 4. Copy the token immediately (it won't be shown again)
30
+
31
+ CONFIGURING MCP CLIENTS
32
+
33
+ Claude Code:
34
+
35
+ claude mcp add --transport http sombra https://sombra.so/mcp
36
+
37
+ OpenClaw, Claude Desktop, Cursor, Windsurf (stdio clients):
38
+
39
+ Add to your MCP config file:
40
+
41
+ {
42
+ "mcpServers": {
43
+ "sombra": {
44
+ "command": "npx",
45
+ "args": ["-y", "sombra-mcp", "--token", "sombra_pat_YOUR_TOKEN"]
46
+ }
47
+ }
48
+ }
49
+
50
+ ABOUT SOMBRA
51
+
52
+ Sombra gives your AI agent a persistent research library accessible
53
+ via the Model Context Protocol (MCP). Save web pages as clean markdown,
54
+ organise them into collections, distil the key insights, and search
55
+ across everything — all through 19 MCP tools.
56
+
57
+ https://sombra.so Sign up & manage your library
58
+ https://sombra.so/mcp MCP setup for all clients
59
+ `);
60
+ }
61
+ function getToken() {
62
+ const args = process.argv.slice(2);
63
+ if (args.includes("--help") || args.includes("-h")) {
64
+ printHelp();
65
+ process.exit(0);
66
+ }
67
+ if (args.includes("--version") || args.includes("-v")) {
68
+ process.stdout.write(`${VERSION}\n`);
69
+ process.exit(0);
70
+ }
71
+ const tokenIdx = args.indexOf("--token");
72
+ const token = (tokenIdx !== -1 ? args[tokenIdx + 1] : undefined) ??
73
+ process.env.SOMBRA_TOKEN;
74
+ if (!token) {
75
+ process.stderr.write([
76
+ "Error: token required.",
77
+ "",
78
+ " Use --token <token> or set SOMBRA_TOKEN env var.",
79
+ " Get a token at https://sombra.so → Settings → Access Tokens.",
80
+ "",
81
+ ' Run "npx sombra-mcp --help" for full setup instructions.',
82
+ "",
83
+ ].join("\n"));
84
+ process.exit(1);
85
+ }
86
+ return token;
87
+ }
88
+ async function readSSE(response, stdio) {
89
+ const body = response.body;
90
+ if (!body)
91
+ return;
92
+ const reader = body.getReader();
93
+ const decoder = new TextDecoder();
94
+ let buffer = "";
95
+ for (;;) {
96
+ const { done, value } = await reader.read();
97
+ if (done)
98
+ break;
99
+ buffer += decoder.decode(value, { stream: true });
100
+ const parts = buffer.split("\n\n");
101
+ buffer = parts.pop() || "";
102
+ for (const part of parts) {
103
+ let data = "";
104
+ for (const line of part.split("\n")) {
105
+ if (line.startsWith("data: "))
106
+ data += line.slice(6);
107
+ else if (line.startsWith("data:"))
108
+ data += line.slice(5);
109
+ }
110
+ if (data) {
111
+ try {
112
+ await stdio.send(JSON.parse(data));
113
+ }
114
+ catch {
115
+ /* skip malformed events */
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
121
+ async function main() {
122
+ const token = getToken();
123
+ const stdio = new StdioServerTransport();
124
+ let sessionId;
125
+ let protocolVersion;
126
+ stdio.onmessage = async (message) => {
127
+ try {
128
+ const headers = {
129
+ "Content-Type": "application/json",
130
+ Accept: "application/json, text/event-stream",
131
+ Authorization: `Bearer ${token}`,
132
+ };
133
+ if (sessionId)
134
+ headers["mcp-session-id"] = sessionId;
135
+ if (protocolVersion)
136
+ headers["mcp-protocol-version"] = protocolVersion;
137
+ const response = await fetch(SOMBRA_URL, {
138
+ method: "POST",
139
+ headers,
140
+ body: JSON.stringify(message),
141
+ });
142
+ const sid = response.headers.get("mcp-session-id");
143
+ if (sid)
144
+ sessionId = sid;
145
+ // Capture negotiated protocol version from initialize response
146
+ if ("method" in message && message.method === "initialize") {
147
+ const clone = response.clone();
148
+ try {
149
+ const body = await clone.json();
150
+ if (body?.result?.protocolVersion) {
151
+ protocolVersion = body.result.protocolVersion;
152
+ }
153
+ }
154
+ catch { /* handled below */ }
155
+ }
156
+ if (response.status === 202)
157
+ return; // notification accepted
158
+ if (!response.ok) {
159
+ const text = await response.text();
160
+ log(`server ${response.status}: ${text}`);
161
+ return;
162
+ }
163
+ const ct = response.headers.get("content-type") || "";
164
+ if (ct.includes("text/event-stream")) {
165
+ await readSSE(response, stdio);
166
+ }
167
+ else {
168
+ await stdio.send((await response.json()));
169
+ }
170
+ }
171
+ catch (err) {
172
+ log(`send error: ${err.message}`);
173
+ }
174
+ };
175
+ stdio.onclose = () => process.exit(0);
176
+ const shutdown = () => {
177
+ stdio.close().catch(() => { });
178
+ process.exit(0);
179
+ };
180
+ process.on("SIGINT", shutdown);
181
+ process.on("SIGTERM", shutdown);
182
+ await stdio.start();
183
+ log("ready");
184
+ }
185
+ main().catch((err) => {
186
+ log(`fatal: ${err.message}`);
187
+ process.exit(1);
188
+ });
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "sombra-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Sombra MCP stdio-to-HTTP bridge. Persistent reader mode for AI.",
5
+ "type": "module",
6
+ "bin": {
7
+ "sombra-mcp": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "dependencies": {
17
+ "@modelcontextprotocol/sdk": "1.27.1"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "^22.0.0",
21
+ "typescript": "^5.7.0"
22
+ },
23
+ "engines": {
24
+ "node": ">=18.0.0"
25
+ },
26
+ "keywords": [
27
+ "mcp",
28
+ "sombra",
29
+ "ai",
30
+ "model-context-protocol",
31
+ "research",
32
+ "context-engineering"
33
+ ],
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/dazld/sombra-mcp"
38
+ },
39
+ "homepage": "https://sombra.so"
40
+ }