@intangle/mcp-server 2.1.7 → 2.1.9
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 +16 -16
- package/biome.json +37 -37
- package/dist/index.js +9 -9
- package/dist/tool-definitions.js +3 -3
- package/index.ts +328 -329
- package/package.json +51 -51
- package/tool-definitions.ts +3 -3
- package/tsconfig.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @intangle/mcp-server
|
|
2
2
|
|
|
3
|
-
Model Context Protocol server for [Intangle](https://intangle.app) - Intelligent AI
|
|
3
|
+
Model Context Protocol server for [Intangle](https://intangle.app) - Intelligent AI context that persists across conversations.
|
|
4
4
|
|
|
5
5
|
## What is Intangle?
|
|
6
6
|
|
|
7
|
-
Intangle is an AI
|
|
7
|
+
Intangle is an AI context system that gives Claude and other AI assistants persistent, searchable context across all your conversations and isolated spaces. Unlike traditional chat interfaces where context is lost between sessions, Intangle maintains your personal context across isolated spaces, with intelligent storing and retrieval.
|
|
8
8
|
|
|
9
9
|
**Important: You need an Intangle account to use this MCP server. Sign up at [intangle.app](https://intangle.app)**
|
|
10
10
|
|
|
@@ -46,29 +46,29 @@ Standard MCP (Claude desktop, Cursor): by adding to your `claude_desktop_config.
|
|
|
46
46
|
|
|
47
47
|
## Usage
|
|
48
48
|
|
|
49
|
-
Once configured, Claude will automatically have access to your Intangle
|
|
49
|
+
Once configured, Claude will automatically have access to your Intangle context. You can:
|
|
50
50
|
|
|
51
|
-
- **Store
|
|
52
|
-
- **Search
|
|
53
|
-
- **Organize with spaces**: Separate
|
|
54
|
-
- **Tag with topics**:
|
|
55
|
-
- **Access across conversations**: All
|
|
51
|
+
- **Store context**: "Remember that I prefer TypeScript over JavaScript"
|
|
52
|
+
- **Search context**: "What did we discuss about the authentication system?"
|
|
53
|
+
- **Organize with spaces**: Separate context for personal, work, and different projects
|
|
54
|
+
- **Tag with topics**: Context is automatically tagged and organized
|
|
55
|
+
- **Access across conversations**: All context persists between chat sessions
|
|
56
56
|
|
|
57
57
|
## Available Tools
|
|
58
58
|
|
|
59
59
|
The MCP server provides these tools to Claude:
|
|
60
60
|
|
|
61
|
-
- `
|
|
62
|
-
- `
|
|
63
|
-
- `
|
|
64
|
-
- `list_spaces` - View available
|
|
61
|
+
- `add_context` - Store new context with topics
|
|
62
|
+
- `search_context` - Search across all your context
|
|
63
|
+
- `get_recent_context` - Retrieve recent context
|
|
64
|
+
- `list_spaces` - View available context spaces
|
|
65
65
|
- `get_space_info` - Get details about a specific space
|
|
66
|
-
- `get_entities` - Extract entities from
|
|
67
|
-
- `
|
|
66
|
+
- `get_entities` - Extract entities from context
|
|
67
|
+
- `delete_context` - Remove specific context
|
|
68
68
|
|
|
69
69
|
## Features
|
|
70
70
|
|
|
71
|
-
- **Multi-space support**: Organize
|
|
71
|
+
- **Multi-space support**: Organize context into separate spaces (personal, work, projects)
|
|
72
72
|
- **Intelligent search**: Combines semantic search with text matching
|
|
73
73
|
- **Entity extraction**: Automatically identifies people, places, and concepts
|
|
74
74
|
- **Temporal awareness**: Tracks when events happened vs when they were recorded
|
|
@@ -76,7 +76,7 @@ The MCP server provides these tools to Claude:
|
|
|
76
76
|
|
|
77
77
|
## Troubleshooting
|
|
78
78
|
|
|
79
|
-
If Claude can't access your
|
|
79
|
+
If Claude can't access your context:
|
|
80
80
|
|
|
81
81
|
1. Verify your API key is correct
|
|
82
82
|
2. Check that the MCP server is listed in Claude's tool menu
|
package/biome.json
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": false,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": false
|
|
7
|
+
},
|
|
8
|
+
"files": {
|
|
9
|
+
"ignoreUnknown": false
|
|
10
|
+
},
|
|
11
|
+
"formatter": {
|
|
12
|
+
"enabled": true,
|
|
13
|
+
"formatWithErrors": false,
|
|
14
|
+
"indentStyle": "space",
|
|
15
|
+
"indentWidth": 2,
|
|
16
|
+
"lineEnding": "lf",
|
|
17
|
+
"lineWidth": 80,
|
|
18
|
+
"attributePosition": "auto"
|
|
19
|
+
},
|
|
20
|
+
"linter": {
|
|
21
|
+
"enabled": true,
|
|
22
|
+
"rules": {
|
|
23
|
+
"recommended": true
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"javascript": {
|
|
27
|
+
"formatter": {
|
|
28
|
+
"jsxQuoteStyle": "double",
|
|
29
|
+
"quoteProperties": "asNeeded",
|
|
30
|
+
"trailingCommas": "es5",
|
|
31
|
+
"semicolons": "asNeeded",
|
|
32
|
+
"arrowParentheses": "always",
|
|
33
|
+
"bracketSpacing": true,
|
|
34
|
+
"bracketSameLine": false,
|
|
35
|
+
"quoteStyle": "double",
|
|
36
|
+
"attributePosition": "auto"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
39
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Endure MCP Server - Exposes Endure
|
|
3
|
+
* Endure MCP Server - Exposes Endure context functionality through MCP
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { readFileSync } from "fs";
|
|
6
|
+
import { dirname, join } from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
7
8
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
9
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
10
|
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import { dirname, join } from "path";
|
|
11
|
+
import { config } from "dotenv";
|
|
12
|
+
import fetch from "node-fetch";
|
|
13
13
|
import { TOOLS } from "./tool-definitions.js";
|
|
14
14
|
// ... imports ...
|
|
15
15
|
// Debug logging
|
|
@@ -119,7 +119,7 @@ try {
|
|
|
119
119
|
return response.json();
|
|
120
120
|
}
|
|
121
121
|
const server = new Server({
|
|
122
|
-
name: "intangle-
|
|
122
|
+
name: "intangle-context",
|
|
123
123
|
version: "1.0.0",
|
|
124
124
|
vendor: "Intangle",
|
|
125
125
|
icon: "https://intangle.tools/icon.png",
|
|
@@ -131,7 +131,7 @@ try {
|
|
|
131
131
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
132
132
|
tools: TOOLS,
|
|
133
133
|
}));
|
|
134
|
-
async function
|
|
134
|
+
async function handleSearchContext(args) {
|
|
135
135
|
const { space_id, query, topics } = args;
|
|
136
136
|
// Require space_id
|
|
137
137
|
if (!space_id) {
|
|
@@ -199,7 +199,7 @@ try {
|
|
|
199
199
|
let result;
|
|
200
200
|
switch (name) {
|
|
201
201
|
case "search":
|
|
202
|
-
result = await
|
|
202
|
+
result = await handleSearchContext(args);
|
|
203
203
|
break;
|
|
204
204
|
case "fetch_items":
|
|
205
205
|
result = await handleFetch(args);
|
package/dist/tool-definitions.js
CHANGED
|
@@ -46,7 +46,7 @@ export const TOOLS = [
|
|
|
46
46
|
{
|
|
47
47
|
name: "view_topic",
|
|
48
48
|
title: "View Topic",
|
|
49
|
-
description: "View all
|
|
49
|
+
description: "View all context, tasks, and processes tagged with a specific topic. Perfect for exploring a topic area or following up on topics from the start briefing.",
|
|
50
50
|
inputSchema: {
|
|
51
51
|
type: "object",
|
|
52
52
|
properties: {
|
|
@@ -115,7 +115,7 @@ export const TOOLS = [
|
|
|
115
115
|
config_badges: {
|
|
116
116
|
type: "array",
|
|
117
117
|
items: { type: "string" },
|
|
118
|
-
description: "Array of keywords for start tool keywords in this NEW space. These keywords help steer what context gets loaded when running 'start' for this space. Examples: ['fitness', 'recipes', 'travel planning'], ['client work', 'meetings', 'proposals'], ['TypeScript', 'React', 'API design']. They act as search terms to prioritize relevant
|
|
118
|
+
description: "Array of keywords for start tool keywords in this NEW space. These keywords help steer what context gets loaded when running 'start' for this space. Examples: ['fitness', 'recipes', 'travel planning'], ['client work', 'meetings', 'proposals'], ['TypeScript', 'React', 'API design']. They act as search terms to prioritize relevant context.",
|
|
119
119
|
},
|
|
120
120
|
startup_preferences: {
|
|
121
121
|
type: "string",
|
|
@@ -153,7 +153,7 @@ export const TOOLS = [
|
|
|
153
153
|
},
|
|
154
154
|
add: {
|
|
155
155
|
type: "object",
|
|
156
|
-
description: "Add new items to
|
|
156
|
+
description: "Add new items to context with automatic intelligent classification and topic suggestion",
|
|
157
157
|
properties: {
|
|
158
158
|
items: {
|
|
159
159
|
type: "array",
|
package/index.ts
CHANGED
|
@@ -1,348 +1,347 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Endure MCP Server - Exposes Endure
|
|
4
|
+
* Endure MCP Server - Exposes Endure context functionality through MCP
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
7
|
+
import { readFileSync } from "fs"
|
|
8
|
+
import { dirname, join } from "path"
|
|
9
|
+
import { fileURLToPath } from "url"
|
|
10
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js"
|
|
11
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
11
12
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} from "@modelcontextprotocol/sdk/types.js"
|
|
17
|
-
import {
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
import { TOOLS } from "./tool-definitions.js";
|
|
13
|
+
CallToolRequestSchema,
|
|
14
|
+
ErrorCode,
|
|
15
|
+
ListToolsRequestSchema,
|
|
16
|
+
McpError,
|
|
17
|
+
} from "@modelcontextprotocol/sdk/types.js"
|
|
18
|
+
import { config } from "dotenv"
|
|
19
|
+
import fetch from "node-fetch"
|
|
20
|
+
import { TOOLS } from "./tool-definitions.js"
|
|
21
21
|
|
|
22
22
|
// ... imports ...
|
|
23
23
|
|
|
24
24
|
// Debug logging
|
|
25
|
-
import { appendFileSync } from "fs"
|
|
26
|
-
const DEBUG_LOG = "/tmp/intangle-mcp-debug.log"
|
|
25
|
+
import { appendFileSync } from "fs"
|
|
26
|
+
const DEBUG_LOG = "/tmp/intangle-mcp-debug.log"
|
|
27
27
|
function log(msg: string) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
try {
|
|
29
|
+
appendFileSync(DEBUG_LOG, `${new Date().toISOString()}: ${msg}\n`)
|
|
30
|
+
} catch (e) {}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
log("--- Server Starting ---")
|
|
33
|
+
log("--- Server Starting ---")
|
|
34
34
|
|
|
35
35
|
// Client info captured from MCP initialize handshake
|
|
36
|
-
let mcpClientName: string | undefined
|
|
37
|
-
let mcpClientVersion: string | undefined
|
|
36
|
+
let mcpClientName: string | undefined
|
|
37
|
+
let mcpClientVersion: string | undefined
|
|
38
38
|
|
|
39
39
|
try {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
40
|
+
// Load environment variables from .env and .env.local
|
|
41
|
+
config({ quiet: true })
|
|
42
|
+
config({ path: ".env.local", quiet: true })
|
|
43
|
+
|
|
44
|
+
// Base URL for API calls to Next.js app
|
|
45
|
+
const API_BASE_URL = process.env.NEXT_APP_URL || "https://intangle.app"
|
|
46
|
+
const MCP_API_KEY = process.env.MCP_API_KEY
|
|
47
|
+
|
|
48
|
+
log(`CWD: ${process.cwd()}`)
|
|
49
|
+
log(`Has API Key: ${!!MCP_API_KEY}`)
|
|
50
|
+
log(`Node Version: ${process.version}`)
|
|
51
|
+
|
|
52
|
+
if (!MCP_API_KEY) {
|
|
53
|
+
log("Error: MCP_API_KEY environment variable is required")
|
|
54
|
+
console.error("Error: MCP_API_KEY environment variable is required")
|
|
55
|
+
console.error(
|
|
56
|
+
"Please set your Intangle API key in the Claude Desktop configuration"
|
|
57
|
+
)
|
|
58
|
+
process.exit(1)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.error("Intangle MCP Server starting - connecting to", API_BASE_URL)
|
|
62
|
+
|
|
63
|
+
// Version checking - automatically read from package.json
|
|
64
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
65
|
+
let packageJson
|
|
66
|
+
try {
|
|
67
|
+
packageJson = JSON.parse(
|
|
68
|
+
readFileSync(join(__dirname, "package.json"), "utf-8")
|
|
69
|
+
)
|
|
70
|
+
} catch {
|
|
71
|
+
packageJson = JSON.parse(
|
|
72
|
+
readFileSync(join(__dirname, "../package.json"), "utf-8")
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
const CURRENT_VERSION = packageJson.version
|
|
76
|
+
let latestVersion: string | null = null
|
|
77
|
+
let versionCheckDone = false
|
|
78
|
+
|
|
79
|
+
async function checkVersion() {
|
|
80
|
+
if (versionCheckDone) return
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const response = await fetch(
|
|
84
|
+
"https://registry.npmjs.org/@intangle/mcp-server/latest"
|
|
85
|
+
)
|
|
86
|
+
const data = (await response.json()) as { version: string }
|
|
87
|
+
latestVersion = data.version
|
|
88
|
+
versionCheckDone = true
|
|
89
|
+
|
|
90
|
+
if (latestVersion && latestVersion !== CURRENT_VERSION) {
|
|
91
|
+
console.error("\n UPDATE AVAILABLE ")
|
|
92
|
+
console.error(`Current version: ${CURRENT_VERSION}`)
|
|
93
|
+
console.error(`Latest version: ${latestVersion}`)
|
|
94
|
+
console.error("Update with: npx @intangle/mcp-server@latest\n")
|
|
95
|
+
} else {
|
|
96
|
+
console.error(`✓ Running latest version (${CURRENT_VERSION})`)
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
// Silently fail version check - don't block startup
|
|
100
|
+
versionCheckDone = true
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check version on startup (non-blocking)
|
|
105
|
+
checkVersion()
|
|
106
|
+
|
|
107
|
+
// Helper to ensure client info is captured (called before API requests)
|
|
108
|
+
function ensureClientInfo() {
|
|
109
|
+
if (!mcpClientName) {
|
|
110
|
+
const clientInfo = server.getClientVersion()
|
|
111
|
+
if (clientInfo) {
|
|
112
|
+
mcpClientName = clientInfo.name
|
|
113
|
+
mcpClientVersion = clientInfo.version
|
|
114
|
+
log(
|
|
115
|
+
`Client identified (on-demand): ${mcpClientName} v${mcpClientVersion}`
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function makeApiCall(endpoint: string, data: any) {
|
|
122
|
+
// Ensure we have client info before making requests
|
|
123
|
+
ensureClientInfo()
|
|
124
|
+
|
|
125
|
+
// Build headers with client info if available
|
|
126
|
+
const headers: Record<string, string> = {
|
|
127
|
+
"Content-Type": "application/json",
|
|
128
|
+
Authorization: `Bearer ${MCP_API_KEY}`,
|
|
129
|
+
"User-Agent": mcpClientName
|
|
130
|
+
? `${mcpClientName}/${mcpClientVersion || "unknown"} (mcp-stdio)`
|
|
131
|
+
: "MCP-Client-Stdio/1.1.2 (mcp)",
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Add explicit client info headers for backend processing
|
|
135
|
+
if (mcpClientName) {
|
|
136
|
+
headers["X-MCP-Client-Name"] = mcpClientName
|
|
137
|
+
}
|
|
138
|
+
if (mcpClientVersion) {
|
|
139
|
+
headers["X-MCP-Client-Version"] = mcpClientVersion
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const response = await fetch(`${API_BASE_URL}/api/mcp/${endpoint}`, {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers,
|
|
145
|
+
body: JSON.stringify(data),
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
if (!response.ok) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`API call failed: ${response.status} ${response.statusText}`
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return response.json()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const server = new Server(
|
|
158
|
+
{
|
|
159
|
+
name: "intangle-context",
|
|
160
|
+
version: "1.0.0",
|
|
161
|
+
vendor: "Intangle",
|
|
162
|
+
icon: "https://intangle.tools/icon.png",
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
capabilities: {
|
|
166
|
+
tools: {},
|
|
167
|
+
},
|
|
168
|
+
}
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
172
|
+
tools: TOOLS,
|
|
173
|
+
}))
|
|
174
|
+
|
|
175
|
+
async function handleSearchContext(args: any) {
|
|
176
|
+
const { space_id, query, topics } = args as {
|
|
177
|
+
space_id: string
|
|
178
|
+
query: string
|
|
179
|
+
topics?: string[]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Require space_id
|
|
183
|
+
if (!space_id) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
"space_id is required. Use list_spaces to see available options."
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return makeApiCall("search-memories", {
|
|
190
|
+
space_id,
|
|
191
|
+
space_ids: [space_id], // Convert to array for backend compatibility
|
|
192
|
+
query,
|
|
193
|
+
topics,
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function handleFetch(args: any) {
|
|
198
|
+
const { id, ids } = args as { id?: string; ids?: string[] }
|
|
199
|
+
|
|
200
|
+
return makeApiCall("fetch", { id, ids })
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function handleViewSpaces() {
|
|
204
|
+
return makeApiCall("list-spaces", {})
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function handleViewTopics(args: any) {
|
|
208
|
+
const { space_id } = args as { space_id: string }
|
|
209
|
+
if (!space_id) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
"space_id is required. Use view_spaces to see available options."
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
return makeApiCall("view-topics", { space_id })
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function handleViewTopic(args: any) {
|
|
218
|
+
const { topic_id } = args as { topic_id: string }
|
|
219
|
+
if (!topic_id) {
|
|
220
|
+
throw new Error("topic_id is required. Use view_topics to get valid IDs.")
|
|
221
|
+
}
|
|
222
|
+
// Pass directly to API; ensure API expects 'topic_id' or adjust if it expects 'id'
|
|
223
|
+
// Usually view-topic endpoint takes 'topic_id' or 'id'
|
|
224
|
+
return makeApiCall("view-topic", { topic_id })
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function handleCreateSpace(args: any) {
|
|
228
|
+
return makeApiCall("create-space", args)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function handleViewSpace(args: any) {
|
|
232
|
+
const { space_id } = args as { space_id: string }
|
|
233
|
+
return makeApiCall("view-space", { space_id })
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function handleStart(args: any) {
|
|
237
|
+
const { space_id } = args as { space_id: string }
|
|
238
|
+
return makeApiCall("start", { space_id })
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function handleUpdateSpace(args: any) {
|
|
242
|
+
if (!args.space_id) {
|
|
243
|
+
throw new Error(
|
|
244
|
+
"space_id is required. Use view_spaces to see available options."
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (!args.add && !args.update && !args.delete) {
|
|
249
|
+
throw new Error(
|
|
250
|
+
"At least one operation (add, update, delete) must be provided"
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Pass through to API with new structure
|
|
255
|
+
return makeApiCall("update-memory", {
|
|
256
|
+
space_id: args.space_id,
|
|
257
|
+
add: args.add,
|
|
258
|
+
update: args.update,
|
|
259
|
+
delete: args.delete,
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
|
|
264
|
+
const { name, arguments: args } = request.params
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
let result: any
|
|
268
|
+
|
|
269
|
+
switch (name) {
|
|
270
|
+
case "search":
|
|
271
|
+
result = await handleSearchContext(args)
|
|
272
|
+
break
|
|
273
|
+
case "fetch_items":
|
|
274
|
+
result = await handleFetch(args)
|
|
275
|
+
break
|
|
276
|
+
case "view_spaces":
|
|
277
|
+
result = await handleViewSpaces()
|
|
278
|
+
break
|
|
279
|
+
case "view_topics":
|
|
280
|
+
result = await handleViewTopics(args)
|
|
281
|
+
break
|
|
282
|
+
case "view_topic":
|
|
283
|
+
result = await handleViewTopic(args)
|
|
284
|
+
break
|
|
285
|
+
case "create_space":
|
|
286
|
+
result = await handleCreateSpace(args)
|
|
287
|
+
break
|
|
288
|
+
case "view_space":
|
|
289
|
+
result = await handleViewSpace(args)
|
|
290
|
+
break
|
|
291
|
+
case "start":
|
|
292
|
+
result = await handleStart(args)
|
|
293
|
+
break
|
|
294
|
+
case "update_space":
|
|
295
|
+
result = await handleUpdateSpace(args)
|
|
296
|
+
break
|
|
297
|
+
default:
|
|
298
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Extract response field for Layer 2 compressed tools, otherwise stringify full result
|
|
302
|
+
let responseText: string
|
|
303
|
+
if (
|
|
304
|
+
result &&
|
|
305
|
+
typeof result === "object" &&
|
|
306
|
+
"response" in result &&
|
|
307
|
+
typeof result.response === "string"
|
|
308
|
+
) {
|
|
309
|
+
// Tool has Layer 2 compression - use the formatted response field
|
|
310
|
+
responseText = result.response
|
|
311
|
+
} else {
|
|
312
|
+
// Standard tool - return full JSON
|
|
313
|
+
responseText = JSON.stringify(result, null, 2)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Add version warning to response if outdated
|
|
317
|
+
if (latestVersion && latestVersion !== CURRENT_VERSION) {
|
|
318
|
+
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`
|
|
319
|
+
responseText += warning
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
content: [{ type: "text", text: responseText }],
|
|
324
|
+
}
|
|
325
|
+
} catch (error) {
|
|
326
|
+
throw new McpError(
|
|
327
|
+
ErrorCode.InternalError,
|
|
328
|
+
`Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`
|
|
329
|
+
)
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
async function main() {
|
|
334
|
+
const transport = new StdioServerTransport()
|
|
335
|
+
await server.connect(transport)
|
|
336
|
+
log("Server connected to transport")
|
|
337
|
+
// Client info is captured on-demand in ensureClientInfo() before first API call
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
main().catch((err) => {
|
|
341
|
+
log(`Error in main: ${err}`)
|
|
342
|
+
process.exit(1)
|
|
343
|
+
})
|
|
345
344
|
} catch (err) {
|
|
346
|
-
|
|
347
|
-
|
|
345
|
+
log(`Fatal startup error: ${err}`)
|
|
346
|
+
process.exit(1)
|
|
348
347
|
}
|
package/package.json
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
2
|
+
"name": "@intangle/mcp-server",
|
|
3
|
+
"version": "2.1.9",
|
|
4
|
+
"description": "Model Context Protocol server for Intangle - AI context that persists across conversations",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "node dist/index.js",
|
|
9
|
+
"dev": "tsx watch index.ts",
|
|
10
|
+
"prebuild": "cp ../web/src/app/api/mcp-remote/tool-definitions.ts ./tool-definitions.ts",
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"lint": "biome check .",
|
|
13
|
+
"lint:fix": "biome check --fix .",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"intangle-mcp": "dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"model-context-protocol",
|
|
22
|
+
"claude",
|
|
23
|
+
"claude-code",
|
|
24
|
+
"ai",
|
|
25
|
+
"context",
|
|
26
|
+
"knowledge-management",
|
|
27
|
+
"intangle",
|
|
28
|
+
"claude-desktop",
|
|
29
|
+
"anthropic"
|
|
30
|
+
],
|
|
31
|
+
"author": "Intangle",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/intangle/mcp-server.git"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://intangle.app",
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18.0.0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
43
|
+
"dotenv": "^17.2.0",
|
|
44
|
+
"node-fetch": "^3.3.2"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@biomejs/biome": "^1.9.4",
|
|
48
|
+
"@types/node": "^22.0.0",
|
|
49
|
+
"@types/node-fetch": "^2.6.12",
|
|
50
|
+
"tsx": "^4.0.0",
|
|
51
|
+
"typescript": "^5.0.0"
|
|
52
|
+
}
|
|
53
53
|
}
|
package/tool-definitions.ts
CHANGED
|
@@ -52,7 +52,7 @@ export const TOOLS = [
|
|
|
52
52
|
name: "view_topic",
|
|
53
53
|
title: "View Topic",
|
|
54
54
|
description:
|
|
55
|
-
"View all
|
|
55
|
+
"View all context, tasks, and processes tagged with a specific topic. Perfect for exploring a topic area or following up on topics from the start briefing.",
|
|
56
56
|
inputSchema: {
|
|
57
57
|
type: "object",
|
|
58
58
|
properties: {
|
|
@@ -128,7 +128,7 @@ export const TOOLS = [
|
|
|
128
128
|
type: "array",
|
|
129
129
|
items: { type: "string" },
|
|
130
130
|
description:
|
|
131
|
-
"Array of keywords for start tool keywords in this NEW space. These keywords help steer what context gets loaded when running 'start' for this space. Examples: ['fitness', 'recipes', 'travel planning'], ['client work', 'meetings', 'proposals'], ['TypeScript', 'React', 'API design']. They act as search terms to prioritize relevant
|
|
131
|
+
"Array of keywords for start tool keywords in this NEW space. These keywords help steer what context gets loaded when running 'start' for this space. Examples: ['fitness', 'recipes', 'travel planning'], ['client work', 'meetings', 'proposals'], ['TypeScript', 'React', 'API design']. They act as search terms to prioritize relevant context.",
|
|
132
132
|
},
|
|
133
133
|
startup_preferences: {
|
|
134
134
|
type: "string",
|
|
@@ -170,7 +170,7 @@ export const TOOLS = [
|
|
|
170
170
|
},
|
|
171
171
|
add: {
|
|
172
172
|
type: "object",
|
|
173
|
-
description: "Add new items to
|
|
173
|
+
description: "Add new items to context with automatic intelligent classification and topic suggestion",
|
|
174
174
|
properties: {
|
|
175
175
|
items: {
|
|
176
176
|
type: "array",
|
package/tsconfig.json
CHANGED