suma-mcp-proxy 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 +112 -0
  2. package/index.js +265 -0
  3. package/package.json +38 -0
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # SUMA MCP Local Proxy v1.0
2
+
3
+ > Stable stdio bridge to stateless SUMA REST API
4
+
5
+ ## Why This Exists
6
+
7
+ MCP (Model Context Protocol) was designed for **local/stdio** connections, not resilient cloud APIs. When you connect your IDE directly to a Cloud Run MCP endpoint:
8
+
9
+ - Cloud Run scales to zero → session dies
10
+ - Deploy new version → "Session not found"
11
+ - Network hiccup → reconnection fails
12
+
13
+ **This proxy runs locally**, maintaining an infinitely stable stdio connection to your IDE. It translates MCP tool calls to stateless REST API calls that Cloud Run handles gracefully.
14
+
15
+ ```
16
+ ┌─────────────────────────────────────────────────────────┐
17
+ │ Your Laptop │
18
+ │ ┌─────────────┐ stdio ┌──────────────────┐ │
19
+ │ │ Cursor / │◄──────────────►│ suma-mcp-proxy │ │
20
+ │ │ Claude Code │ (stable) │ (this script) │ │
21
+ │ └─────────────┘ └────────┬─────────┘ │
22
+ └──────────────────────────────────────────┼─────────────┘
23
+ │ HTTPS REST
24
+
25
+ ┌─────────────────────────────────────────────────────────┐
26
+ │ Cloud Run (stateless, scales freely, deploys anytime) │
27
+ │ https://sumapro-web-xxx.run.app/api/* │
28
+ └─────────────────────────────────────────────────────────┘
29
+ ```
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ cd quad-products/squad-suma-mcp/proxy
35
+ npm install
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ### Option 1: Environment Variable
41
+
42
+ ```bash
43
+ export SUMA_API_KEY=sk_live_xxx
44
+ node index.js
45
+ ```
46
+
47
+ ### Option 2: Command Line Argument
48
+
49
+ ```bash
50
+ node index.js --key=sk_live_xxx
51
+ ```
52
+
53
+ ### Option 3: npx (after npm publish)
54
+
55
+ ```bash
56
+ npx @quad/suma-mcp --key=sk_live_xxx
57
+ ```
58
+
59
+ ## IDE Configuration
60
+
61
+ ### Claude Code (.mcp.json)
62
+
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "suma-memory": {
67
+ "command": "node",
68
+ "args": ["/path/to/quad-products/squad-suma-mcp/proxy/index.js"],
69
+ "env": {
70
+ "SUMA_API_KEY": "sk_live_xxx"
71
+ }
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ ### Cursor
78
+
79
+ Settings → MCP → Add Server:
80
+ - **Command**: `node /path/to/proxy/index.js`
81
+ - **Environment**: `SUMA_API_KEY=sk_live_xxx`
82
+
83
+ ## Available Tools
84
+
85
+ | Tool | Description |
86
+ |------|-------------|
87
+ | `suma_ping` | Health check — verify connection |
88
+ | `suma_ingest` | Add knowledge to graph |
89
+ | `suma_search` | Search with Gravity Well Algorithm |
90
+ | `suma_talk` | Bidirectional memory (search + learn) |
91
+
92
+ ## Environment Variables
93
+
94
+ | Variable | Description | Default |
95
+ |----------|-------------|---------|
96
+ | `SUMA_API_KEY` | Your SUMA API key (required) | — |
97
+ | `SUMA_API_URL` | API endpoint | `https://sumapro-web-xxx.run.app` |
98
+
99
+ ## Architecture
100
+
101
+ This is **v1.0 (Dumb Pipe)** — a simple MCP-to-REST translator.
102
+
103
+ **v1.1 (Smart Proxy)** will add:
104
+ - Local SQLite cache for zero-latency queries
105
+ - Offline mode with sync-on-reconnect
106
+ - Same architecture as SQUAD-Companion Flutter app
107
+
108
+ See `quad-docs/QUAD-ARCHITECTURE-MCP-PROXY.md` for full design.
109
+
110
+ ## License
111
+
112
+ MIT - Suman Addanke / A2 Vibe Creators LLC
package/index.js ADDED
@@ -0,0 +1,265 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SUMA MCP Local Proxy v1.0
4
+ *
5
+ * Architect Ruling (Apr 2, 2026):
6
+ * MCP is designed for local/stdio use, not resilient cloud APIs.
7
+ * This proxy runs locally, maintains stable stdio connection to IDE,
8
+ * and translates MCP tool calls to stateless REST API calls.
9
+ *
10
+ * Usage:
11
+ * SUMA_API_KEY=sk_live_xxx node index.js
12
+ *
13
+ * Or via npx (after npm publish):
14
+ * npx @quad/suma-mcp --key=sk_live_xxx
15
+ *
16
+ * (c) 2026 Suman Addanke / A2 Vibe Creators LLC
17
+ */
18
+
19
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
20
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
21
+ import {
22
+ CallToolRequestSchema,
23
+ ListToolsRequestSchema,
24
+ } from "@modelcontextprotocol/sdk/types.js";
25
+
26
+ // ── Configuration ───────────────────────────────────────────────────────────
27
+
28
+ const SUMA_API_URL = process.env.SUMA_API_URL || "https://sumapro-web-643846995630.us-central1.run.app";
29
+ const API_KEY = process.env.SUMA_API_KEY || parseArgs().key;
30
+
31
+ function parseArgs() {
32
+ const args = process.argv.slice(2);
33
+ const result = {};
34
+ for (let i = 0; i < args.length; i++) {
35
+ if (args[i] === "--key" && args[i + 1]) {
36
+ result.key = args[i + 1];
37
+ i++;
38
+ } else if (args[i].startsWith("--key=")) {
39
+ result.key = args[i].split("=")[1];
40
+ }
41
+ }
42
+ return result;
43
+ }
44
+
45
+ if (!API_KEY) {
46
+ console.error("ERROR: SUMA_API_KEY required");
47
+ console.error("Usage: SUMA_API_KEY=sk_live_xxx node index.js");
48
+ console.error(" or: node index.js --key=sk_live_xxx");
49
+ process.exit(1);
50
+ }
51
+
52
+ // ── REST API Client ─────────────────────────────────────────────────────────
53
+
54
+ async function apiCall(endpoint, method = "GET", body = null) {
55
+ const url = `${SUMA_API_URL}/api${endpoint}`;
56
+ const options = {
57
+ method,
58
+ headers: {
59
+ "Authorization": `Bearer ${API_KEY}`,
60
+ "Content-Type": "application/json",
61
+ },
62
+ };
63
+
64
+ if (body && method !== "GET") {
65
+ options.body = JSON.stringify(body);
66
+ }
67
+
68
+ try {
69
+ const response = await fetch(url, options);
70
+ const data = await response.json();
71
+
72
+ if (!response.ok) {
73
+ return { error: data.error || `HTTP ${response.status}`, detail: data.detail };
74
+ }
75
+
76
+ return data;
77
+ } catch (err) {
78
+ return { error: "Network error", detail: err.message };
79
+ }
80
+ }
81
+
82
+ // ── Tool Definitions ────────────────────────────────────────────────────────
83
+
84
+ const TOOLS = [
85
+ {
86
+ name: "suma_ping",
87
+ description: "Health check — verify connection and org. Call once at session start.",
88
+ inputSchema: {
89
+ type: "object",
90
+ properties: {},
91
+ required: [],
92
+ },
93
+ },
94
+ {
95
+ name: "suma_ingest",
96
+ description: `Add knowledge to the SUMA graph.
97
+
98
+ [SYSTEM INSTRUCTION]: Do NOT pass raw massive text. You are the cognitive processor.
99
+ Extract core facts into compressed semantic summaries before ingesting.
100
+
101
+ Args:
102
+ text: The compressed knowledge to store (required)
103
+ sphere: Category - family, work, friends, health, architecture (optional, auto-classified)
104
+ extract_relationships: Auto-extract entities (default: true)`,
105
+ inputSchema: {
106
+ type: "object",
107
+ properties: {
108
+ text: { type: "string", description: "The knowledge to store" },
109
+ sphere: { type: "string", description: "Category (auto-classified if omitted)" },
110
+ extract_relationships: { type: "boolean", description: "Extract entities (default: true)" },
111
+ },
112
+ required: ["text"],
113
+ },
114
+ },
115
+ {
116
+ name: "suma_search",
117
+ description: `Search the knowledge graph using Gravity Well Algorithm.
118
+
119
+ Phases:
120
+ 1. Entity Strike — find entity by name
121
+ 2. Neighborhood Expansion — get relationships
122
+ 3. Evidence Bridge — get related nodes
123
+ 4. GIN Fallback — full-text search
124
+
125
+ Args:
126
+ query: Natural language query (required)
127
+ limit: Max results (default: 10)
128
+ sphere: Filter by category (optional)`,
129
+ inputSchema: {
130
+ type: "object",
131
+ properties: {
132
+ query: { type: "string", description: "Natural language query" },
133
+ limit: { type: "integer", description: "Max results (default: 10)" },
134
+ sphere: { type: "string", description: "Filter by sphere" },
135
+ },
136
+ required: ["query"],
137
+ },
138
+ },
139
+ {
140
+ name: "suma_talk",
141
+ description: `Bidirectional memory — search AND learn in one call.
142
+
143
+ Searches for relevant context, optionally stores the message as new knowledge.
144
+
145
+ Args:
146
+ message: User message (required)
147
+ learn: Store message as knowledge (default: true)
148
+ sphere: Category for storage (optional)`,
149
+ inputSchema: {
150
+ type: "object",
151
+ properties: {
152
+ message: { type: "string", description: "User message" },
153
+ learn: { type: "boolean", description: "Store as knowledge (default: true)" },
154
+ sphere: { type: "string", description: "Category for storage" },
155
+ },
156
+ required: ["message"],
157
+ },
158
+ },
159
+ ];
160
+
161
+ // ── Tool Handlers ───────────────────────────────────────────────────────────
162
+
163
+ async function handleTool(name, args) {
164
+ switch (name) {
165
+ case "suma_ping":
166
+ return await apiCall("/ping", "GET");
167
+
168
+ case "suma_ingest":
169
+ return await apiCall("/ingest", "POST", {
170
+ text: args.text,
171
+ sphere: args.sphere || "default",
172
+ extract_relationships: args.extract_relationships !== false,
173
+ });
174
+
175
+ case "suma_search":
176
+ return await apiCall("/search", "POST", {
177
+ query: args.query,
178
+ limit: args.limit || 10,
179
+ sphere: args.sphere,
180
+ });
181
+
182
+ case "suma_talk":
183
+ return await apiCall("/talk", "POST", {
184
+ message: args.message,
185
+ learn: args.learn !== false,
186
+ sphere: args.sphere,
187
+ });
188
+
189
+ default:
190
+ return { error: `Unknown tool: ${name}` };
191
+ }
192
+ }
193
+
194
+ // ── MCP Server ──────────────────────────────────────────────────────────────
195
+
196
+ const server = new Server(
197
+ {
198
+ name: "suma-mcp-proxy",
199
+ version: "1.0.0",
200
+ },
201
+ {
202
+ capabilities: {
203
+ tools: {},
204
+ },
205
+ }
206
+ );
207
+
208
+ // List available tools
209
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
210
+ return { tools: TOOLS };
211
+ });
212
+
213
+ // Handle tool calls
214
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
215
+ const { name, arguments: args } = request.params;
216
+
217
+ try {
218
+ const result = await handleTool(name, args || {});
219
+
220
+ return {
221
+ content: [
222
+ {
223
+ type: "text",
224
+ text: JSON.stringify(result, null, 2),
225
+ },
226
+ ],
227
+ };
228
+ } catch (err) {
229
+ return {
230
+ content: [
231
+ {
232
+ type: "text",
233
+ text: JSON.stringify({ error: err.message }),
234
+ },
235
+ ],
236
+ isError: true,
237
+ };
238
+ }
239
+ });
240
+
241
+ // ── Start Server ────────────────────────────────────────────────────────────
242
+
243
+ async function main() {
244
+ const transport = new StdioServerTransport();
245
+ await server.connect(transport);
246
+
247
+ // Log to stderr (stdout is for MCP protocol)
248
+ console.error(`SUMA MCP Proxy v1.0 started`);
249
+ console.error(`API: ${SUMA_API_URL}`);
250
+ console.error(`Org: Validating...`);
251
+
252
+ // Validate connection on startup
253
+ const ping = await apiCall("/ping", "GET");
254
+ if (ping.error) {
255
+ console.error(`ERROR: ${ping.error} - ${ping.detail || ""}`);
256
+ } else {
257
+ console.error(`Org ID: ${ping.org_id}, Tier: ${ping.tier}`);
258
+ console.error(`Ready.`);
259
+ }
260
+ }
261
+
262
+ main().catch((err) => {
263
+ console.error("Fatal error:", err);
264
+ process.exit(1);
265
+ });
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "suma-mcp-proxy",
3
+ "version": "1.0.0",
4
+ "description": "SUMA MCP Local Proxy — stable stdio bridge to stateless REST API",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "suma-mcp": "./index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node index.js"
12
+ },
13
+ "keywords": [
14
+ "suma",
15
+ "mcp",
16
+ "memory",
17
+ "ai",
18
+ "knowledge-graph",
19
+ "claude",
20
+ "cursor",
21
+ "llm"
22
+ ],
23
+ "author": "Suman Addanke <suman@quadframe.work>",
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "@modelcontextprotocol/sdk": "^1.0.0"
27
+ },
28
+ "engines": {
29
+ "node": ">=18.0.0"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/quadframe/suma-mcp"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ }
38
+ }