@intangle/mcp-server 2.1.1 → 2.1.3
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/dist/index.js +188 -166
- package/index.ts +227 -203
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11,189 +11,211 @@ import { readFileSync } from "fs";
|
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
12
|
import { dirname, join } from "path";
|
|
13
13
|
import { TOOLS } from "./tool-definitions.js";
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
console.error("Please set your Intangle API key in the Claude Desktop configuration");
|
|
24
|
-
process.exit(1);
|
|
14
|
+
// ... imports ...
|
|
15
|
+
// Debug logging
|
|
16
|
+
import { appendFileSync } from "fs";
|
|
17
|
+
const DEBUG_LOG = "/tmp/intangle-mcp-debug.log";
|
|
18
|
+
function log(msg) {
|
|
19
|
+
try {
|
|
20
|
+
appendFileSync(DEBUG_LOG, `${new Date().toISOString()}: ${msg}\n`);
|
|
21
|
+
}
|
|
22
|
+
catch (e) { }
|
|
25
23
|
}
|
|
26
|
-
|
|
27
|
-
// Version checking - automatically read from package.json
|
|
28
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
29
|
-
let packageJson;
|
|
24
|
+
log("--- Server Starting ---");
|
|
30
25
|
try {
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
|
|
26
|
+
// Load environment variables from .env and .env.local
|
|
27
|
+
config({ quiet: true });
|
|
28
|
+
config({ path: ".env.local", quiet: true });
|
|
29
|
+
// Base URL for API calls to Next.js app
|
|
30
|
+
const API_BASE_URL = process.env.NEXT_APP_URL || "https://intangle.app";
|
|
31
|
+
const MCP_API_KEY = process.env.MCP_API_KEY;
|
|
32
|
+
log(`CWD: ${process.cwd()}`);
|
|
33
|
+
log(`Has API Key: ${!!MCP_API_KEY}`);
|
|
34
|
+
log(`Node Version: ${process.version}`);
|
|
35
|
+
if (!MCP_API_KEY) {
|
|
36
|
+
log("Error: MCP_API_KEY environment variable is required");
|
|
37
|
+
console.error("Error: MCP_API_KEY environment variable is required");
|
|
38
|
+
console.error("Please set your Intangle API key in the Claude Desktop configuration");
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
console.error("Intangle MCP Server starting - connecting to", API_BASE_URL);
|
|
42
|
+
// Version checking - automatically read from package.json
|
|
43
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
44
|
+
let packageJson;
|
|
42
45
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
packageJson = JSON.parse(readFileSync(join(__dirname, "package.json"), "utf-8"));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
|
50
|
+
}
|
|
51
|
+
const CURRENT_VERSION = packageJson.version;
|
|
52
|
+
let latestVersion = null;
|
|
53
|
+
let versionCheckDone = false;
|
|
54
|
+
async function checkVersion() {
|
|
55
|
+
if (versionCheckDone)
|
|
56
|
+
return;
|
|
57
|
+
try {
|
|
58
|
+
const response = await fetch("https://registry.npmjs.org/@intangle/mcp-server/latest");
|
|
59
|
+
const data = (await response.json());
|
|
60
|
+
latestVersion = data.version;
|
|
61
|
+
versionCheckDone = true;
|
|
62
|
+
if (latestVersion && latestVersion !== CURRENT_VERSION) {
|
|
63
|
+
console.error("\n UPDATE AVAILABLE ");
|
|
64
|
+
console.error(`Current version: ${CURRENT_VERSION}`);
|
|
65
|
+
console.error(`Latest version: ${latestVersion}`);
|
|
66
|
+
console.error("Update with: npx @intangle/mcp-server@latest\n");
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.error(`✓ Running latest version (${CURRENT_VERSION})`);
|
|
70
|
+
}
|
|
52
71
|
}
|
|
53
|
-
|
|
54
|
-
|
|
72
|
+
catch (error) {
|
|
73
|
+
// Silently fail version check - don't block startup
|
|
74
|
+
versionCheckDone = true;
|
|
55
75
|
}
|
|
56
76
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
77
|
+
// Check version on startup (non-blocking)
|
|
78
|
+
checkVersion();
|
|
79
|
+
async function makeApiCall(endpoint, data) {
|
|
80
|
+
const response = await fetch(`${API_BASE_URL}/api/mcp/${endpoint}`, {
|
|
81
|
+
method: "POST",
|
|
82
|
+
headers: {
|
|
83
|
+
"Content-Type": "application/json",
|
|
84
|
+
Authorization: `Bearer ${MCP_API_KEY}`,
|
|
85
|
+
"User-Agent": "MCP-Client-Stdio/1.1.2 (mcp)",
|
|
86
|
+
},
|
|
87
|
+
body: JSON.stringify(data),
|
|
88
|
+
});
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
throw new Error(`API call failed: ${response.status} ${response.statusText}`);
|
|
91
|
+
}
|
|
92
|
+
return response.json();
|
|
60
93
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
Authorization: `Bearer ${MCP_API_KEY}`,
|
|
70
|
-
"User-Agent": "MCP-Client-Stdio/1.1.2 (mcp)",
|
|
94
|
+
const server = new Server({
|
|
95
|
+
name: "intangle-memory",
|
|
96
|
+
version: "1.0.0",
|
|
97
|
+
vendor: "Intangle",
|
|
98
|
+
icon: "https://intangle.tools/icon.png",
|
|
99
|
+
}, {
|
|
100
|
+
capabilities: {
|
|
101
|
+
tools: {},
|
|
71
102
|
},
|
|
72
|
-
body: JSON.stringify(data),
|
|
73
103
|
});
|
|
74
|
-
|
|
75
|
-
|
|
104
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
105
|
+
tools: TOOLS,
|
|
106
|
+
}));
|
|
107
|
+
async function handleSearchMemories(args) {
|
|
108
|
+
const { space_id, query, topics } = args;
|
|
109
|
+
// Require space_id
|
|
110
|
+
if (!space_id) {
|
|
111
|
+
throw new Error("space_id is required. Use list_spaces to see available options.");
|
|
112
|
+
}
|
|
113
|
+
return makeApiCall("search-memories", {
|
|
114
|
+
space_id,
|
|
115
|
+
space_ids: [space_id], // Convert to array for backend compatibility
|
|
116
|
+
query,
|
|
117
|
+
topics,
|
|
118
|
+
});
|
|
76
119
|
}
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
name: "intangle-memory",
|
|
81
|
-
version: "1.0.0",
|
|
82
|
-
vendor: "Intangle",
|
|
83
|
-
icon: "https://intangle.tools/icon.png",
|
|
84
|
-
}, {
|
|
85
|
-
capabilities: {
|
|
86
|
-
tools: {},
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
90
|
-
tools: TOOLS,
|
|
91
|
-
}));
|
|
92
|
-
async function handleSearchMemories(args) {
|
|
93
|
-
const { space_id, query, topics } = args;
|
|
94
|
-
// Require space_id
|
|
95
|
-
if (!space_id) {
|
|
96
|
-
throw new Error("space_id is required. Use list_spaces to see available options.");
|
|
120
|
+
async function handleFetch(args) {
|
|
121
|
+
const { id, ids } = args;
|
|
122
|
+
return makeApiCall("fetch", { id, ids });
|
|
97
123
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
space_ids: [space_id], // Convert to array for backend compatibility
|
|
101
|
-
query,
|
|
102
|
-
topics,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
async function handleFetch(args) {
|
|
106
|
-
const { id, ids } = args;
|
|
107
|
-
return makeApiCall("fetch", { id, ids });
|
|
108
|
-
}
|
|
109
|
-
async function handleViewSpaces() {
|
|
110
|
-
return makeApiCall("list-spaces", {});
|
|
111
|
-
}
|
|
112
|
-
async function handleCreateSpace(args) {
|
|
113
|
-
return makeApiCall("create-space", args);
|
|
114
|
-
}
|
|
115
|
-
async function handleViewSpace(args) {
|
|
116
|
-
const { space_id } = args;
|
|
117
|
-
return makeApiCall("view-space", { space_id });
|
|
118
|
-
}
|
|
119
|
-
async function handleStart(args) {
|
|
120
|
-
const { space_id } = args;
|
|
121
|
-
return makeApiCall("start", { space_id });
|
|
122
|
-
}
|
|
123
|
-
async function handleUpdateSpace(args) {
|
|
124
|
-
if (!args.space_id) {
|
|
125
|
-
throw new Error("space_id is required. Use view_spaces to see available options.");
|
|
124
|
+
async function handleViewSpaces() {
|
|
125
|
+
return makeApiCall("list-spaces", {});
|
|
126
126
|
}
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
async function handleCreateSpace(args) {
|
|
128
|
+
return makeApiCall("create-space", args);
|
|
129
129
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
space_id
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
let result;
|
|
142
|
-
switch (name) {
|
|
143
|
-
case "search":
|
|
144
|
-
result = await handleSearchMemories(args);
|
|
145
|
-
break;
|
|
146
|
-
case "fetch":
|
|
147
|
-
result = await handleFetch(args);
|
|
148
|
-
break;
|
|
149
|
-
case "view_spaces":
|
|
150
|
-
result = await handleViewSpaces();
|
|
151
|
-
break;
|
|
152
|
-
case "create_space":
|
|
153
|
-
result = await handleCreateSpace(args);
|
|
154
|
-
break;
|
|
155
|
-
case "view_space":
|
|
156
|
-
result = await handleViewSpace(args);
|
|
157
|
-
break;
|
|
158
|
-
case "start":
|
|
159
|
-
result = await handleStart(args);
|
|
160
|
-
break;
|
|
161
|
-
case "update_space":
|
|
162
|
-
result = await handleUpdateSpace(args);
|
|
163
|
-
break;
|
|
164
|
-
default:
|
|
165
|
-
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
130
|
+
async function handleViewSpace(args) {
|
|
131
|
+
const { space_id } = args;
|
|
132
|
+
return makeApiCall("view-space", { space_id });
|
|
133
|
+
}
|
|
134
|
+
async function handleStart(args) {
|
|
135
|
+
const { space_id } = args;
|
|
136
|
+
return makeApiCall("start", { space_id });
|
|
137
|
+
}
|
|
138
|
+
async function handleUpdateSpace(args) {
|
|
139
|
+
if (!args.space_id) {
|
|
140
|
+
throw new Error("space_id is required. Use view_spaces to see available options.");
|
|
166
141
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (result &&
|
|
170
|
-
typeof result === "object" &&
|
|
171
|
-
"response" in result &&
|
|
172
|
-
typeof result.response === "string") {
|
|
173
|
-
// Tool has Layer 2 compression - use the formatted response field
|
|
174
|
-
responseText = result.response;
|
|
142
|
+
if (!args.add && !args.update && !args.delete) {
|
|
143
|
+
throw new Error("At least one operation (add, update, delete) must be provided");
|
|
175
144
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
145
|
+
// Pass through to API with new structure
|
|
146
|
+
return makeApiCall("update-memory", {
|
|
147
|
+
space_id: args.space_id,
|
|
148
|
+
add: args.add,
|
|
149
|
+
update: args.update,
|
|
150
|
+
delete: args.delete,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
154
|
+
const { name, arguments: args } = request.params;
|
|
155
|
+
try {
|
|
156
|
+
let result;
|
|
157
|
+
switch (name) {
|
|
158
|
+
case "search":
|
|
159
|
+
result = await handleSearchMemories(args);
|
|
160
|
+
break;
|
|
161
|
+
case "fetch":
|
|
162
|
+
result = await handleFetch(args);
|
|
163
|
+
break;
|
|
164
|
+
case "view_spaces":
|
|
165
|
+
result = await handleViewSpaces();
|
|
166
|
+
break;
|
|
167
|
+
case "create_space":
|
|
168
|
+
result = await handleCreateSpace(args);
|
|
169
|
+
break;
|
|
170
|
+
case "view_space":
|
|
171
|
+
result = await handleViewSpace(args);
|
|
172
|
+
break;
|
|
173
|
+
case "start":
|
|
174
|
+
result = await handleStart(args);
|
|
175
|
+
break;
|
|
176
|
+
case "update_space":
|
|
177
|
+
result = await handleUpdateSpace(args);
|
|
178
|
+
break;
|
|
179
|
+
default:
|
|
180
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
181
|
+
}
|
|
182
|
+
// Extract response field for Layer 2 compressed tools, otherwise stringify full result
|
|
183
|
+
let responseText;
|
|
184
|
+
if (result &&
|
|
185
|
+
typeof result === "object" &&
|
|
186
|
+
"response" in result &&
|
|
187
|
+
typeof result.response === "string") {
|
|
188
|
+
// Tool has Layer 2 compression - use the formatted response field
|
|
189
|
+
responseText = result.response;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
// Standard tool - return full JSON
|
|
193
|
+
responseText = JSON.stringify(result, null, 2);
|
|
194
|
+
}
|
|
195
|
+
// Add version warning to response if outdated
|
|
196
|
+
if (latestVersion && latestVersion !== CURRENT_VERSION) {
|
|
197
|
+
const warning = `\n\n UPDATE AVAILABLE: MCP Server v${latestVersion} is available (you're on v${CURRENT_VERSION}). Update with: npx @intangle/mcp-server@latest`;
|
|
198
|
+
responseText += warning;
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
content: [{ type: "text", text: responseText }],
|
|
202
|
+
};
|
|
179
203
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const warning = `\n\n UPDATE AVAILABLE: MCP Server v${latestVersion} is available (you're on v${CURRENT_VERSION}). Update with: npx @intangle/mcp-server@latest`;
|
|
183
|
-
responseText += warning;
|
|
204
|
+
catch (error) {
|
|
205
|
+
throw new McpError(ErrorCode.InternalError, `Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
184
206
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
throw new McpError(ErrorCode.InternalError, `Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
207
|
+
});
|
|
208
|
+
async function main() {
|
|
209
|
+
const transport = new StdioServerTransport();
|
|
210
|
+
await server.connect(transport);
|
|
211
|
+
log("Server connected to transport");
|
|
191
212
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
213
|
+
main().catch((err) => {
|
|
214
|
+
log(`Error in main: ${err}`);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
});
|
|
196
217
|
}
|
|
197
|
-
|
|
218
|
+
catch (err) {
|
|
219
|
+
log(`Fatal startup error: ${err}`);
|
|
198
220
|
process.exit(1);
|
|
199
|
-
}
|
|
221
|
+
}
|
package/index.ts
CHANGED
|
@@ -19,245 +19,269 @@ import { fileURLToPath } from "url";
|
|
|
19
19
|
import { dirname, join } from "path";
|
|
20
20
|
import { TOOLS } from "./tool-definitions.js";
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
config({ quiet: true });
|
|
24
|
-
config({ path: ".env.local", quiet: true });
|
|
22
|
+
// ... imports ...
|
|
25
23
|
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
console.error("Error: MCP_API_KEY environment variable is required");
|
|
34
|
-
console.error(
|
|
35
|
-
"Please set your Intangle API key in the Claude Desktop configuration",
|
|
36
|
-
);
|
|
37
|
-
process.exit(1);
|
|
24
|
+
// Debug logging
|
|
25
|
+
import { appendFileSync } from "fs";
|
|
26
|
+
const DEBUG_LOG = "/tmp/intangle-mcp-debug.log";
|
|
27
|
+
function log(msg: string) {
|
|
28
|
+
try {
|
|
29
|
+
appendFileSync(DEBUG_LOG, `${new Date().toISOString()}: ${msg}\n`);
|
|
30
|
+
} catch (e) {}
|
|
38
31
|
}
|
|
39
32
|
|
|
40
|
-
|
|
33
|
+
log("--- Server Starting ---");
|
|
41
34
|
|
|
42
|
-
// Version checking - automatically read from package.json
|
|
43
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
44
|
-
let packageJson;
|
|
45
35
|
try {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
36
|
+
// Load environment variables from .env and .env.local
|
|
37
|
+
config({ quiet: true });
|
|
38
|
+
config({ path: ".env.local", quiet: true });
|
|
39
|
+
|
|
40
|
+
// Base URL for API calls to Next.js app
|
|
41
|
+
const API_BASE_URL = process.env.NEXT_APP_URL || "https://intangle.app";
|
|
42
|
+
const MCP_API_KEY = process.env.MCP_API_KEY;
|
|
43
|
+
|
|
44
|
+
log(`CWD: ${process.cwd()}`);
|
|
45
|
+
log(`Has API Key: ${!!MCP_API_KEY}`);
|
|
46
|
+
log(`Node Version: ${process.version}`);
|
|
47
|
+
|
|
48
|
+
if (!MCP_API_KEY) {
|
|
49
|
+
log("Error: MCP_API_KEY environment variable is required");
|
|
50
|
+
console.error("Error: MCP_API_KEY environment variable is required");
|
|
51
|
+
console.error(
|
|
52
|
+
"Please set your Intangle API key in the Claude Desktop configuration",
|
|
53
|
+
);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
if (versionCheckDone) return;
|
|
57
|
+
console.error("Intangle MCP Server starting - connecting to", API_BASE_URL);
|
|
60
58
|
|
|
59
|
+
// Version checking - automatically read from package.json
|
|
60
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
61
|
+
let packageJson;
|
|
61
62
|
try {
|
|
62
|
-
|
|
63
|
-
"
|
|
63
|
+
packageJson = JSON.parse(
|
|
64
|
+
readFileSync(join(__dirname, "package.json"), "utf-8"),
|
|
64
65
|
);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
66
|
+
} catch {
|
|
67
|
+
packageJson = JSON.parse(
|
|
68
|
+
readFileSync(join(__dirname, "../package.json"), "utf-8"),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const CURRENT_VERSION = packageJson.version;
|
|
72
|
+
let latestVersion: string | null = null;
|
|
73
|
+
let versionCheckDone = false;
|
|
74
|
+
|
|
75
|
+
async function checkVersion() {
|
|
76
|
+
if (versionCheckDone) return;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const response = await fetch(
|
|
80
|
+
"https://registry.npmjs.org/@intangle/mcp-server/latest",
|
|
81
|
+
);
|
|
82
|
+
const data = (await response.json()) as { version: string };
|
|
83
|
+
latestVersion = data.version;
|
|
84
|
+
versionCheckDone = true;
|
|
85
|
+
|
|
86
|
+
if (latestVersion && latestVersion !== CURRENT_VERSION) {
|
|
87
|
+
console.error("\n UPDATE AVAILABLE ");
|
|
88
|
+
console.error(`Current version: ${CURRENT_VERSION}`);
|
|
89
|
+
console.error(`Latest version: ${latestVersion}`);
|
|
90
|
+
console.error("Update with: npx @intangle/mcp-server@latest\n");
|
|
91
|
+
} else {
|
|
92
|
+
console.error(`✓ Running latest version (${CURRENT_VERSION})`);
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
// Silently fail version check - don't block startup
|
|
96
|
+
versionCheckDone = true;
|
|
76
97
|
}
|
|
77
|
-
} catch (error) {
|
|
78
|
-
// Silently fail version check - don't block startup
|
|
79
|
-
versionCheckDone = true;
|
|
80
98
|
}
|
|
81
|
-
}
|
|
82
99
|
|
|
83
|
-
// Check version on startup (non-blocking)
|
|
84
|
-
checkVersion();
|
|
85
|
-
|
|
86
|
-
async function makeApiCall(endpoint: string, data: any) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
100
|
+
// Check version on startup (non-blocking)
|
|
101
|
+
checkVersion();
|
|
102
|
+
|
|
103
|
+
async function makeApiCall(endpoint: string, data: any) {
|
|
104
|
+
const response = await fetch(`${API_BASE_URL}/api/mcp/${endpoint}`, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: {
|
|
107
|
+
"Content-Type": "application/json",
|
|
108
|
+
Authorization: `Bearer ${MCP_API_KEY}`,
|
|
109
|
+
"User-Agent": "MCP-Client-Stdio/1.1.2 (mcp)",
|
|
110
|
+
},
|
|
111
|
+
body: JSON.stringify(data),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`API call failed: ${response.status} ${response.statusText}`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
96
119
|
|
|
97
|
-
|
|
98
|
-
throw new Error(
|
|
99
|
-
`API call failed: ${response.status} ${response.statusText}`,
|
|
100
|
-
);
|
|
120
|
+
return response.json();
|
|
101
121
|
}
|
|
102
122
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
version: "1.0.0",
|
|
110
|
-
vendor: "Intangle",
|
|
111
|
-
icon: "https://intangle.tools/icon.png",
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
capabilities: {
|
|
115
|
-
tools: {},
|
|
123
|
+
const server = new Server(
|
|
124
|
+
{
|
|
125
|
+
name: "intangle-memory",
|
|
126
|
+
version: "1.0.0",
|
|
127
|
+
vendor: "Intangle",
|
|
128
|
+
icon: "https://intangle.tools/icon.png",
|
|
116
129
|
},
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
async function handleSearchMemories(args: any) {
|
|
125
|
-
const { space_id, query, topics } = args as {
|
|
126
|
-
space_id: string;
|
|
127
|
-
query: string;
|
|
128
|
-
topics?: string[];
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
// Require space_id
|
|
132
|
-
if (!space_id) {
|
|
133
|
-
throw new Error(
|
|
134
|
-
"space_id is required. Use list_spaces to see available options.",
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return makeApiCall("search-memories", {
|
|
139
|
-
space_id,
|
|
140
|
-
space_ids: [space_id], // Convert to array for backend compatibility
|
|
141
|
-
query,
|
|
142
|
-
topics,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
130
|
+
{
|
|
131
|
+
capabilities: {
|
|
132
|
+
tools: {},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
);
|
|
145
136
|
|
|
146
|
-
async
|
|
147
|
-
|
|
137
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
138
|
+
tools: TOOLS,
|
|
139
|
+
}));
|
|
148
140
|
|
|
149
|
-
|
|
150
|
-
}
|
|
141
|
+
async function handleSearchMemories(args: any) {
|
|
142
|
+
const { space_id, query, topics } = args as {
|
|
143
|
+
space_id: string;
|
|
144
|
+
query: string;
|
|
145
|
+
topics?: string[];
|
|
146
|
+
};
|
|
151
147
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
148
|
+
// Require space_id
|
|
149
|
+
if (!space_id) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"space_id is required. Use list_spaces to see available options.",
|
|
152
|
+
);
|
|
153
|
+
}
|
|
155
154
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
return makeApiCall("search-memories", {
|
|
156
|
+
space_id,
|
|
157
|
+
space_ids: [space_id], // Convert to array for backend compatibility
|
|
158
|
+
query,
|
|
159
|
+
topics,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
159
162
|
|
|
160
|
-
async function
|
|
161
|
-
|
|
162
|
-
return makeApiCall("view-space", { space_id });
|
|
163
|
-
}
|
|
163
|
+
async function handleFetch(args: any) {
|
|
164
|
+
const { id, ids } = args as { id?: string; ids?: string[] };
|
|
164
165
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return makeApiCall("start", { space_id });
|
|
168
|
-
}
|
|
166
|
+
return makeApiCall("fetch", { id, ids });
|
|
167
|
+
}
|
|
169
168
|
|
|
170
|
-
async function
|
|
171
|
-
|
|
172
|
-
throw new Error(
|
|
173
|
-
"space_id is required. Use view_spaces to see available options.",
|
|
174
|
-
);
|
|
169
|
+
async function handleViewSpaces() {
|
|
170
|
+
return makeApiCall("list-spaces", {});
|
|
175
171
|
}
|
|
176
172
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
"At least one operation (add, update, delete) must be provided",
|
|
180
|
-
);
|
|
173
|
+
async function handleCreateSpace(args: any) {
|
|
174
|
+
return makeApiCall("create-space", args);
|
|
181
175
|
}
|
|
182
176
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
space_id
|
|
186
|
-
|
|
187
|
-
update: args.update,
|
|
188
|
-
delete: args.delete,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
177
|
+
async function handleViewSpace(args: any) {
|
|
178
|
+
const { space_id } = args as { space_id: string };
|
|
179
|
+
return makeApiCall("view-space", { space_id });
|
|
180
|
+
}
|
|
191
181
|
|
|
192
|
-
|
|
193
|
-
|
|
182
|
+
async function handleStart(args: any) {
|
|
183
|
+
const { space_id } = args as { space_id: string };
|
|
184
|
+
return makeApiCall("start", { space_id });
|
|
185
|
+
}
|
|
194
186
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
result = await handleSearchMemories(args);
|
|
201
|
-
break;
|
|
202
|
-
case "fetch":
|
|
203
|
-
result = await handleFetch(args);
|
|
204
|
-
break;
|
|
205
|
-
case "view_spaces":
|
|
206
|
-
result = await handleViewSpaces();
|
|
207
|
-
break;
|
|
208
|
-
case "create_space":
|
|
209
|
-
result = await handleCreateSpace(args);
|
|
210
|
-
break;
|
|
211
|
-
case "view_space":
|
|
212
|
-
result = await handleViewSpace(args);
|
|
213
|
-
break;
|
|
214
|
-
case "start":
|
|
215
|
-
result = await handleStart(args);
|
|
216
|
-
break;
|
|
217
|
-
case "update_space":
|
|
218
|
-
result = await handleUpdateSpace(args);
|
|
219
|
-
break;
|
|
220
|
-
default:
|
|
221
|
-
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
187
|
+
async function handleUpdateSpace(args: any) {
|
|
188
|
+
if (!args.space_id) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
"space_id is required. Use view_spaces to see available options.",
|
|
191
|
+
);
|
|
222
192
|
}
|
|
223
193
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
typeof result === "object" &&
|
|
229
|
-
"response" in result &&
|
|
230
|
-
typeof result.response === "string"
|
|
231
|
-
) {
|
|
232
|
-
// Tool has Layer 2 compression - use the formatted response field
|
|
233
|
-
responseText = result.response;
|
|
234
|
-
} else {
|
|
235
|
-
// Standard tool - return full JSON
|
|
236
|
-
responseText = JSON.stringify(result, null, 2);
|
|
194
|
+
if (!args.add && !args.update && !args.delete) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
"At least one operation (add, update, delete) must be provided",
|
|
197
|
+
);
|
|
237
198
|
}
|
|
238
199
|
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
200
|
+
// Pass through to API with new structure
|
|
201
|
+
return makeApiCall("update-memory", {
|
|
202
|
+
space_id: args.space_id,
|
|
203
|
+
add: args.add,
|
|
204
|
+
update: args.update,
|
|
205
|
+
delete: args.delete,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
|
|
210
|
+
const { name, arguments: args } = request.params;
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
let result: any;
|
|
214
|
+
|
|
215
|
+
switch (name) {
|
|
216
|
+
case "search":
|
|
217
|
+
result = await handleSearchMemories(args);
|
|
218
|
+
break;
|
|
219
|
+
case "fetch":
|
|
220
|
+
result = await handleFetch(args);
|
|
221
|
+
break;
|
|
222
|
+
case "view_spaces":
|
|
223
|
+
result = await handleViewSpaces();
|
|
224
|
+
break;
|
|
225
|
+
case "create_space":
|
|
226
|
+
result = await handleCreateSpace(args);
|
|
227
|
+
break;
|
|
228
|
+
case "view_space":
|
|
229
|
+
result = await handleViewSpace(args);
|
|
230
|
+
break;
|
|
231
|
+
case "start":
|
|
232
|
+
result = await handleStart(args);
|
|
233
|
+
break;
|
|
234
|
+
case "update_space":
|
|
235
|
+
result = await handleUpdateSpace(args);
|
|
236
|
+
break;
|
|
237
|
+
default:
|
|
238
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Extract response field for Layer 2 compressed tools, otherwise stringify full result
|
|
242
|
+
let responseText: string;
|
|
243
|
+
if (
|
|
244
|
+
result &&
|
|
245
|
+
typeof result === "object" &&
|
|
246
|
+
"response" in result &&
|
|
247
|
+
typeof result.response === "string"
|
|
248
|
+
) {
|
|
249
|
+
// Tool has Layer 2 compression - use the formatted response field
|
|
250
|
+
responseText = result.response;
|
|
251
|
+
} else {
|
|
252
|
+
// Standard tool - return full JSON
|
|
253
|
+
responseText = JSON.stringify(result, null, 2);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Add version warning to response if outdated
|
|
257
|
+
if (latestVersion && latestVersion !== CURRENT_VERSION) {
|
|
258
|
+
const warning = `\n\n UPDATE AVAILABLE: MCP Server v${latestVersion} is available (you're on v${CURRENT_VERSION}). Update with: npx @intangle/mcp-server@latest`;
|
|
259
|
+
responseText += warning;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
content: [{ type: "text", text: responseText }],
|
|
264
|
+
};
|
|
265
|
+
} catch (error) {
|
|
266
|
+
throw new McpError(
|
|
267
|
+
ErrorCode.InternalError,
|
|
268
|
+
`Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`,
|
|
269
|
+
);
|
|
243
270
|
}
|
|
271
|
+
});
|
|
244
272
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
throw new McpError(
|
|
250
|
-
ErrorCode.InternalError,
|
|
251
|
-
`Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`,
|
|
252
|
-
);
|
|
273
|
+
async function main() {
|
|
274
|
+
const transport = new StdioServerTransport();
|
|
275
|
+
await server.connect(transport);
|
|
276
|
+
log("Server connected to transport");
|
|
253
277
|
}
|
|
254
|
-
});
|
|
255
278
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
279
|
+
main().catch((err) => {
|
|
280
|
+
log(`Error in main: ${err}`);
|
|
281
|
+
process.exit(1);
|
|
282
|
+
});
|
|
260
283
|
|
|
261
|
-
|
|
284
|
+
} catch (err) {
|
|
285
|
+
log(`Fatal startup error: ${err}`);
|
|
262
286
|
process.exit(1);
|
|
263
|
-
}
|
|
287
|
+
}
|