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.
- package/README.md +112 -0
- package/index.js +265 -0
- 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
|
+
}
|