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