@intangle/mcp-server 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/biome.json +39 -0
- package/dist/index.js +292 -0
- package/index.ts +377 -0
- package/intangle-memory-1.0.0.tgz +0 -0
- package/package.json +52 -0
- package/tsconfig.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# @intangle/mcp-server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol server for [Intangle](https://intangle.ai) - AI memory that persists across conversations.
|
|
4
|
+
|
|
5
|
+
## What is Intangle?
|
|
6
|
+
|
|
7
|
+
Intangle provides persistent memory for AI assistants like Claude. Your conversations, context, and knowledge are stored and instantly accessible across all your AI interactions.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @intangle/mcp-server
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
1. **Get your API key** from [intangle.app](https://intangle.app) after signing up in "Settings" -> "Connect"
|
|
18
|
+
|
|
19
|
+
2. **Configure Claude Desktop** by adding to your `claude_desktop_config.json`:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"intangle": {
|
|
25
|
+
"command": "npx",
|
|
26
|
+
"args": ["@intangle/mcp-server"],
|
|
27
|
+
"env": {
|
|
28
|
+
"MCP_API_KEY": "your-api-key-here",
|
|
29
|
+
"NEXT_APP_URL": "https://intangle.app"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
> **Note**: The config file location varies by OS:
|
|
37
|
+
> - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
38
|
+
> - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
39
|
+
> - Linux: `~/.config/Claude/claude_desktop_config.json`
|
|
40
|
+
|
|
41
|
+
3. **Restart Claude Desktop** to load the MCP server
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
Once configured, Claude will automatically have access to your Intangle memory. You can:
|
|
46
|
+
|
|
47
|
+
- **Store memories**: "Remember that I prefer TypeScript over JavaScript"
|
|
48
|
+
- **Search memories**: "What did we discuss about the authentication system?"
|
|
49
|
+
- **Organize with topics**: Memories are automatically tagged and organized
|
|
50
|
+
- **Access across conversations**: All memories persist between chat sessions
|
|
51
|
+
|
|
52
|
+
## Available Tools
|
|
53
|
+
|
|
54
|
+
The MCP server provides these tools to Claude:
|
|
55
|
+
|
|
56
|
+
- `add_memory` - Store new memories with topics
|
|
57
|
+
- `search_memories` - Search across all your memories
|
|
58
|
+
- `get_recent_memories` - Retrieve recent memories
|
|
59
|
+
- `list_spaces` - View available memory spaces
|
|
60
|
+
- `get_space_info` - Get details about a specific space
|
|
61
|
+
- `get_entities` - Extract entities from memories
|
|
62
|
+
- `delete_memory` - Remove specific memories
|
|
63
|
+
|
|
64
|
+
## Features
|
|
65
|
+
|
|
66
|
+
- **Multi-space support**: Organize memories into separate spaces (personal, work, projects)
|
|
67
|
+
- **Intelligent search**: Combines semantic search with text matching
|
|
68
|
+
- **Entity extraction**: Automatically identifies people, places, and concepts
|
|
69
|
+
- **Temporal awareness**: Tracks when events happened vs when they were recorded
|
|
70
|
+
|
|
71
|
+
## Development
|
|
72
|
+
|
|
73
|
+
### Local Development
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Clone the repository
|
|
77
|
+
git clone https://github.com/intangle/mcp-server
|
|
78
|
+
cd mcp-server
|
|
79
|
+
|
|
80
|
+
# Install dependencies
|
|
81
|
+
npm install
|
|
82
|
+
|
|
83
|
+
# Build the TypeScript code
|
|
84
|
+
npm run build
|
|
85
|
+
|
|
86
|
+
# Run in development mode
|
|
87
|
+
npm run dev
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Environment Variables
|
|
91
|
+
|
|
92
|
+
- `MCP_API_KEY`: Your Intangle API key
|
|
93
|
+
- `NEXT_APP_URL`: The Intangle API endpoint (default: https://app.intangle.ai)
|
|
94
|
+
|
|
95
|
+
## Troubleshooting
|
|
96
|
+
|
|
97
|
+
If Claude can't access your memories:
|
|
98
|
+
|
|
99
|
+
1. Verify your API key is correct
|
|
100
|
+
2. Check that the MCP server is listed in Claude's tool menu
|
|
101
|
+
3. Ensure you have an active internet connection
|
|
102
|
+
4. Try restarting Claude Desktop
|
|
103
|
+
|
|
104
|
+
## Support
|
|
105
|
+
|
|
106
|
+
- Documentation: [intangle.ai/docs](https://intangle.ai/docs)
|
|
107
|
+
- Issues: [github.com/intangle/mcp-server/issues](https://github.com/intangle/mcp-server/issues)
|
|
108
|
+
- Email: support@intangle.ai
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
MIT
|
package/biome.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
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
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Endure MCP Server - Exposes Endure memory functionality through MCP
|
|
4
|
+
*/
|
|
5
|
+
import { config } from "dotenv";
|
|
6
|
+
import fetch from "node-fetch";
|
|
7
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
// Load environment variables from .env and .env.local
|
|
11
|
+
config({ quiet: true });
|
|
12
|
+
config({ path: ".env.local", quiet: true });
|
|
13
|
+
// Base URL for API calls to Next.js app
|
|
14
|
+
const API_BASE_URL = process.env.NEXT_APP_URL || "https://intangle.app";
|
|
15
|
+
const MCP_API_KEY = process.env.MCP_API_KEY;
|
|
16
|
+
// No space configuration needed - AI discovers spaces dynamically
|
|
17
|
+
if (!MCP_API_KEY) {
|
|
18
|
+
console.error("Error: MCP_API_KEY environment variable is required");
|
|
19
|
+
console.error("Please set your Intangle API key in the Claude Desktop configuration");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
console.log("Intangle MCP Server starting - connecting to", API_BASE_URL);
|
|
23
|
+
async function makeApiCall(endpoint, data) {
|
|
24
|
+
const response = await fetch(`${API_BASE_URL}/api/mcp/${endpoint}`, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: {
|
|
27
|
+
"Content-Type": "application/json",
|
|
28
|
+
Authorization: `Bearer ${MCP_API_KEY}`,
|
|
29
|
+
},
|
|
30
|
+
body: JSON.stringify(data),
|
|
31
|
+
});
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new Error(`API call failed: ${response.status} ${response.statusText}`);
|
|
34
|
+
}
|
|
35
|
+
return response.json();
|
|
36
|
+
}
|
|
37
|
+
const server = new Server({
|
|
38
|
+
name: "intangle-memory",
|
|
39
|
+
version: "1.0.0",
|
|
40
|
+
}, {
|
|
41
|
+
capabilities: {
|
|
42
|
+
tools: {},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
const TOOLS = [
|
|
46
|
+
{
|
|
47
|
+
name: "add_memory",
|
|
48
|
+
description: "Store information in the user's memory graph with flexible topics. REQUIRES space_id parameter.",
|
|
49
|
+
inputSchema: {
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: {
|
|
52
|
+
space_id: {
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "REQUIRED: Space to store memory in (use list_spaces to see available options)",
|
|
55
|
+
},
|
|
56
|
+
title: {
|
|
57
|
+
type: "string",
|
|
58
|
+
description: "Title/summary of the memory",
|
|
59
|
+
},
|
|
60
|
+
content: {
|
|
61
|
+
type: "string",
|
|
62
|
+
description: "Detailed content of the memory",
|
|
63
|
+
},
|
|
64
|
+
topics: {
|
|
65
|
+
type: "array",
|
|
66
|
+
items: { type: "string" },
|
|
67
|
+
description: "Flexible topics/tags for organization (e.g., ['coding', 'react'])",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
required: ["space_id", "title", "content"],
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "search_memories",
|
|
75
|
+
description: "Search through stored memories using advanced hybrid search (semantic + text + entity relationships). ALWAYS provide either space_id OR space_ids parameter - this is mandatory for all searches.",
|
|
76
|
+
inputSchema: {
|
|
77
|
+
type: "object",
|
|
78
|
+
properties: {
|
|
79
|
+
space_id: {
|
|
80
|
+
type: "string",
|
|
81
|
+
description: "REQUIRED (or use space_ids): Space to search in (use list_spaces to see available options)",
|
|
82
|
+
},
|
|
83
|
+
space_ids: {
|
|
84
|
+
type: "array",
|
|
85
|
+
items: { type: "string" },
|
|
86
|
+
description: "REQUIRED (or use space_id): Multiple spaces to search across",
|
|
87
|
+
},
|
|
88
|
+
query: {
|
|
89
|
+
type: "string",
|
|
90
|
+
description: "Search query for finding relevant memories",
|
|
91
|
+
},
|
|
92
|
+
topics: {
|
|
93
|
+
type: "array",
|
|
94
|
+
items: { type: "string" },
|
|
95
|
+
description: "Filter by specific topics",
|
|
96
|
+
},
|
|
97
|
+
max_results: {
|
|
98
|
+
type: "number",
|
|
99
|
+
description: "Maximum number of results to return",
|
|
100
|
+
default: 10,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
required: ["query"],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "get_recent_memories",
|
|
108
|
+
description: "Get the most recent memories stored in the system. REQUIRES space_id parameter.",
|
|
109
|
+
inputSchema: {
|
|
110
|
+
type: "object",
|
|
111
|
+
properties: {
|
|
112
|
+
space_id: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "REQUIRED: Space to get memories from (use list_spaces to see available options)",
|
|
115
|
+
},
|
|
116
|
+
topics: {
|
|
117
|
+
type: "array",
|
|
118
|
+
items: { type: "string" },
|
|
119
|
+
description: "Filter by specific topics",
|
|
120
|
+
},
|
|
121
|
+
limit: {
|
|
122
|
+
type: "number",
|
|
123
|
+
description: "Number of recent memories to retrieve",
|
|
124
|
+
default: 20,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
required: ["space_id"],
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "get_entities",
|
|
132
|
+
description: "Get extracted entities (people, places, concepts) from memories",
|
|
133
|
+
inputSchema: {
|
|
134
|
+
type: "object",
|
|
135
|
+
properties: {
|
|
136
|
+
memory_id: {
|
|
137
|
+
type: "string",
|
|
138
|
+
description: "Optional memory ID to get entities for specific memory",
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "delete_memory",
|
|
145
|
+
description: "Delete a specific memory item",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
type: "object",
|
|
148
|
+
properties: {
|
|
149
|
+
memory_id: {
|
|
150
|
+
type: "string",
|
|
151
|
+
description: "ID of the memory to delete",
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
required: ["memory_id"],
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: "debug_memory_structure",
|
|
159
|
+
description: "Debug tool to inspect the current memory structure in the database",
|
|
160
|
+
inputSchema: {
|
|
161
|
+
type: "object",
|
|
162
|
+
properties: {},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "list_spaces",
|
|
167
|
+
description: "Get all available spaces with their descriptions and memory counts",
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: {},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "get_space_info",
|
|
175
|
+
description: "Get detailed information about a specific space",
|
|
176
|
+
inputSchema: {
|
|
177
|
+
type: "object",
|
|
178
|
+
properties: {
|
|
179
|
+
space_id: {
|
|
180
|
+
type: "string",
|
|
181
|
+
description: "ID of space to get info for",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
required: ["space_id"],
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
];
|
|
188
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
189
|
+
tools: TOOLS,
|
|
190
|
+
}));
|
|
191
|
+
async function handleAddMemory(args) {
|
|
192
|
+
// Require space_id to be provided
|
|
193
|
+
if (!args.space_id) {
|
|
194
|
+
throw new Error("space_id is required. Use list_spaces to see available options.");
|
|
195
|
+
}
|
|
196
|
+
const memoryData = {
|
|
197
|
+
topics: [],
|
|
198
|
+
...args,
|
|
199
|
+
};
|
|
200
|
+
return makeApiCall("add-memory", memoryData);
|
|
201
|
+
}
|
|
202
|
+
async function handleSearchMemories(args) {
|
|
203
|
+
const { space_id, space_ids, query, topics, max_results = 10, } = args;
|
|
204
|
+
// Require either space_id or space_ids
|
|
205
|
+
if (!space_id && (!space_ids || space_ids.length === 0)) {
|
|
206
|
+
throw new Error("Either space_id or space_ids is required. Use list_spaces to see available options.");
|
|
207
|
+
}
|
|
208
|
+
return makeApiCall("search-memories", {
|
|
209
|
+
space_id,
|
|
210
|
+
space_ids,
|
|
211
|
+
query,
|
|
212
|
+
topics,
|
|
213
|
+
max_results,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
async function handleGetRecentMemories(args) {
|
|
217
|
+
const { space_id, topics, limit = 20, } = args;
|
|
218
|
+
// Require space_id to be provided
|
|
219
|
+
if (!space_id) {
|
|
220
|
+
throw new Error("space_id is required. Use list_spaces to see available options.");
|
|
221
|
+
}
|
|
222
|
+
return makeApiCall("get-recent-memories", {
|
|
223
|
+
space_id,
|
|
224
|
+
topics,
|
|
225
|
+
limit,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
async function handleGetEntities(args) {
|
|
229
|
+
const { memory_id } = args;
|
|
230
|
+
return makeApiCall("get-entities", { memory_id });
|
|
231
|
+
}
|
|
232
|
+
async function handleDeleteMemory(args) {
|
|
233
|
+
const { memory_id } = args;
|
|
234
|
+
return makeApiCall("delete-memory", { memory_id });
|
|
235
|
+
}
|
|
236
|
+
async function handleDebugStructure() {
|
|
237
|
+
return makeApiCall("debug-structure", {});
|
|
238
|
+
}
|
|
239
|
+
async function handleListSpaces() {
|
|
240
|
+
return makeApiCall("list-spaces", {});
|
|
241
|
+
}
|
|
242
|
+
async function handleGetSpaceInfo(args) {
|
|
243
|
+
const { space_id } = args;
|
|
244
|
+
return makeApiCall("get-space-info", { space_id });
|
|
245
|
+
}
|
|
246
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
247
|
+
const { name, arguments: args } = request.params;
|
|
248
|
+
try {
|
|
249
|
+
let result;
|
|
250
|
+
switch (name) {
|
|
251
|
+
case "add_memory":
|
|
252
|
+
result = await handleAddMemory(args);
|
|
253
|
+
break;
|
|
254
|
+
case "search_memories":
|
|
255
|
+
result = await handleSearchMemories(args);
|
|
256
|
+
break;
|
|
257
|
+
case "get_recent_memories":
|
|
258
|
+
result = await handleGetRecentMemories(args);
|
|
259
|
+
break;
|
|
260
|
+
case "get_entities":
|
|
261
|
+
result = await handleGetEntities(args);
|
|
262
|
+
break;
|
|
263
|
+
case "delete_memory":
|
|
264
|
+
result = await handleDeleteMemory(args);
|
|
265
|
+
break;
|
|
266
|
+
case "debug_memory_structure":
|
|
267
|
+
result = await handleDebugStructure();
|
|
268
|
+
break;
|
|
269
|
+
case "list_spaces":
|
|
270
|
+
result = await handleListSpaces();
|
|
271
|
+
break;
|
|
272
|
+
case "get_space_info":
|
|
273
|
+
result = await handleGetSpaceInfo(args);
|
|
274
|
+
break;
|
|
275
|
+
default:
|
|
276
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
throw new McpError(ErrorCode.InternalError, `Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
async function main() {
|
|
287
|
+
const transport = new StdioServerTransport();
|
|
288
|
+
await server.connect(transport);
|
|
289
|
+
}
|
|
290
|
+
main().catch(() => {
|
|
291
|
+
process.exit(1);
|
|
292
|
+
});
|
package/index.ts
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Endure MCP Server - Exposes Endure memory functionality through MCP
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { config } from "dotenv";
|
|
8
|
+
import fetch from "node-fetch";
|
|
9
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
10
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11
|
+
import {
|
|
12
|
+
CallToolRequestSchema,
|
|
13
|
+
ErrorCode,
|
|
14
|
+
ListToolsRequestSchema,
|
|
15
|
+
McpError,
|
|
16
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
17
|
+
|
|
18
|
+
// Load environment variables from .env and .env.local
|
|
19
|
+
config({ quiet: true });
|
|
20
|
+
config({ path: ".env.local", quiet: true });
|
|
21
|
+
|
|
22
|
+
// Base URL for API calls to Next.js app
|
|
23
|
+
const API_BASE_URL = process.env.NEXT_APP_URL || "https://intangle.app";
|
|
24
|
+
const MCP_API_KEY = process.env.MCP_API_KEY;
|
|
25
|
+
|
|
26
|
+
// No space configuration needed - AI discovers spaces dynamically
|
|
27
|
+
|
|
28
|
+
if (!MCP_API_KEY) {
|
|
29
|
+
console.error("Error: MCP_API_KEY environment variable is required");
|
|
30
|
+
console.error("Please set your Intangle API key in the Claude Desktop configuration");
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log("Intangle MCP Server starting - connecting to", API_BASE_URL);
|
|
35
|
+
|
|
36
|
+
async function makeApiCall(endpoint: string, data: any) {
|
|
37
|
+
const response = await fetch(`${API_BASE_URL}/api/mcp/${endpoint}`, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
Authorization: `Bearer ${MCP_API_KEY}`,
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify(data),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`API call failed: ${response.status} ${response.statusText}`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return response.json();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const server = new Server(
|
|
56
|
+
{
|
|
57
|
+
name: "intangle-memory",
|
|
58
|
+
version: "1.0.0",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
capabilities: {
|
|
62
|
+
tools: {},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const TOOLS = [
|
|
68
|
+
{
|
|
69
|
+
name: "add_memory",
|
|
70
|
+
description:
|
|
71
|
+
"Store information in the user's memory graph with flexible topics. REQUIRES space_id parameter.",
|
|
72
|
+
inputSchema: {
|
|
73
|
+
type: "object",
|
|
74
|
+
properties: {
|
|
75
|
+
space_id: {
|
|
76
|
+
type: "string",
|
|
77
|
+
description:
|
|
78
|
+
"REQUIRED: Space to store memory in (use list_spaces to see available options)",
|
|
79
|
+
},
|
|
80
|
+
title: {
|
|
81
|
+
type: "string",
|
|
82
|
+
description: "Title/summary of the memory",
|
|
83
|
+
},
|
|
84
|
+
content: {
|
|
85
|
+
type: "string",
|
|
86
|
+
description: "Detailed content of the memory",
|
|
87
|
+
},
|
|
88
|
+
topics: {
|
|
89
|
+
type: "array",
|
|
90
|
+
items: { type: "string" },
|
|
91
|
+
description:
|
|
92
|
+
"Flexible topics/tags for organization (e.g., ['coding', 'react'])",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
required: ["space_id", "title", "content"],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "search_memories",
|
|
100
|
+
description:
|
|
101
|
+
"Search through stored memories using advanced hybrid search (semantic + text + entity relationships). ALWAYS provide either space_id OR space_ids parameter - this is mandatory for all searches.",
|
|
102
|
+
inputSchema: {
|
|
103
|
+
type: "object",
|
|
104
|
+
properties: {
|
|
105
|
+
space_id: {
|
|
106
|
+
type: "string",
|
|
107
|
+
description:
|
|
108
|
+
"REQUIRED (or use space_ids): Space to search in (use list_spaces to see available options)",
|
|
109
|
+
},
|
|
110
|
+
space_ids: {
|
|
111
|
+
type: "array",
|
|
112
|
+
items: { type: "string" },
|
|
113
|
+
description:
|
|
114
|
+
"REQUIRED (or use space_id): Multiple spaces to search across",
|
|
115
|
+
},
|
|
116
|
+
query: {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: "Search query for finding relevant memories",
|
|
119
|
+
},
|
|
120
|
+
topics: {
|
|
121
|
+
type: "array",
|
|
122
|
+
items: { type: "string" },
|
|
123
|
+
description: "Filter by specific topics",
|
|
124
|
+
},
|
|
125
|
+
max_results: {
|
|
126
|
+
type: "number",
|
|
127
|
+
description: "Maximum number of results to return",
|
|
128
|
+
default: 10,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
required: ["query"],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "get_recent_memories",
|
|
136
|
+
description:
|
|
137
|
+
"Get the most recent memories stored in the system. REQUIRES space_id parameter.",
|
|
138
|
+
inputSchema: {
|
|
139
|
+
type: "object",
|
|
140
|
+
properties: {
|
|
141
|
+
space_id: {
|
|
142
|
+
type: "string",
|
|
143
|
+
description:
|
|
144
|
+
"REQUIRED: Space to get memories from (use list_spaces to see available options)",
|
|
145
|
+
},
|
|
146
|
+
topics: {
|
|
147
|
+
type: "array",
|
|
148
|
+
items: { type: "string" },
|
|
149
|
+
description: "Filter by specific topics",
|
|
150
|
+
},
|
|
151
|
+
limit: {
|
|
152
|
+
type: "number",
|
|
153
|
+
description: "Number of recent memories to retrieve",
|
|
154
|
+
default: 20,
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
required: ["space_id"],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: "get_entities",
|
|
162
|
+
description:
|
|
163
|
+
"Get extracted entities (people, places, concepts) from memories",
|
|
164
|
+
inputSchema: {
|
|
165
|
+
type: "object",
|
|
166
|
+
properties: {
|
|
167
|
+
memory_id: {
|
|
168
|
+
type: "string",
|
|
169
|
+
description: "Optional memory ID to get entities for specific memory",
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: "delete_memory",
|
|
176
|
+
description: "Delete a specific memory item",
|
|
177
|
+
inputSchema: {
|
|
178
|
+
type: "object",
|
|
179
|
+
properties: {
|
|
180
|
+
memory_id: {
|
|
181
|
+
type: "string",
|
|
182
|
+
description: "ID of the memory to delete",
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
required: ["memory_id"],
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: "debug_memory_structure",
|
|
190
|
+
description:
|
|
191
|
+
"Debug tool to inspect the current memory structure in the database",
|
|
192
|
+
inputSchema: {
|
|
193
|
+
type: "object",
|
|
194
|
+
properties: {},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "list_spaces",
|
|
199
|
+
description:
|
|
200
|
+
"Get all available spaces with their descriptions and memory counts",
|
|
201
|
+
inputSchema: {
|
|
202
|
+
type: "object",
|
|
203
|
+
properties: {},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: "get_space_info",
|
|
208
|
+
description: "Get detailed information about a specific space",
|
|
209
|
+
inputSchema: {
|
|
210
|
+
type: "object",
|
|
211
|
+
properties: {
|
|
212
|
+
space_id: {
|
|
213
|
+
type: "string",
|
|
214
|
+
description: "ID of space to get info for",
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
required: ["space_id"],
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
223
|
+
tools: TOOLS,
|
|
224
|
+
}));
|
|
225
|
+
|
|
226
|
+
async function handleAddMemory(args: any) {
|
|
227
|
+
// Require space_id to be provided
|
|
228
|
+
if (!args.space_id) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
"space_id is required. Use list_spaces to see available options.",
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const memoryData = {
|
|
235
|
+
topics: [],
|
|
236
|
+
...args,
|
|
237
|
+
};
|
|
238
|
+
return makeApiCall("add-memory", memoryData);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function handleSearchMemories(args: any) {
|
|
242
|
+
const {
|
|
243
|
+
space_id,
|
|
244
|
+
space_ids,
|
|
245
|
+
query,
|
|
246
|
+
topics,
|
|
247
|
+
max_results = 10,
|
|
248
|
+
} = args as {
|
|
249
|
+
space_id?: string;
|
|
250
|
+
space_ids?: string[];
|
|
251
|
+
query: string;
|
|
252
|
+
topics?: string[];
|
|
253
|
+
max_results?: number;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// Require either space_id or space_ids
|
|
257
|
+
if (!space_id && (!space_ids || space_ids.length === 0)) {
|
|
258
|
+
throw new Error(
|
|
259
|
+
"Either space_id or space_ids is required. Use list_spaces to see available options.",
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return makeApiCall("search-memories", {
|
|
264
|
+
space_id,
|
|
265
|
+
space_ids,
|
|
266
|
+
query,
|
|
267
|
+
topics,
|
|
268
|
+
max_results,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async function handleGetRecentMemories(args: any) {
|
|
273
|
+
const {
|
|
274
|
+
space_id,
|
|
275
|
+
topics,
|
|
276
|
+
limit = 20,
|
|
277
|
+
} = args as {
|
|
278
|
+
space_id?: string;
|
|
279
|
+
topics?: string[];
|
|
280
|
+
limit?: number;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// Require space_id to be provided
|
|
284
|
+
if (!space_id) {
|
|
285
|
+
throw new Error(
|
|
286
|
+
"space_id is required. Use list_spaces to see available options.",
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return makeApiCall("get-recent-memories", {
|
|
291
|
+
space_id,
|
|
292
|
+
topics,
|
|
293
|
+
limit,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function handleGetEntities(args: any) {
|
|
298
|
+
const { memory_id } = args as { memory_id?: string };
|
|
299
|
+
|
|
300
|
+
return makeApiCall("get-entities", { memory_id });
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function handleDeleteMemory(args: any) {
|
|
304
|
+
const { memory_id } = args as {
|
|
305
|
+
memory_id: string;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
return makeApiCall("delete-memory", { memory_id });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function handleDebugStructure() {
|
|
312
|
+
return makeApiCall("debug-structure", {});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async function handleListSpaces() {
|
|
316
|
+
return makeApiCall("list-spaces", {});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async function handleGetSpaceInfo(args: any) {
|
|
320
|
+
const { space_id } = args as { space_id: string };
|
|
321
|
+
return makeApiCall("get-space-info", { space_id });
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
|
|
325
|
+
const { name, arguments: args } = request.params;
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
let result: any;
|
|
329
|
+
|
|
330
|
+
switch (name) {
|
|
331
|
+
case "add_memory":
|
|
332
|
+
result = await handleAddMemory(args);
|
|
333
|
+
break;
|
|
334
|
+
case "search_memories":
|
|
335
|
+
result = await handleSearchMemories(args);
|
|
336
|
+
break;
|
|
337
|
+
case "get_recent_memories":
|
|
338
|
+
result = await handleGetRecentMemories(args);
|
|
339
|
+
break;
|
|
340
|
+
case "get_entities":
|
|
341
|
+
result = await handleGetEntities(args);
|
|
342
|
+
break;
|
|
343
|
+
case "delete_memory":
|
|
344
|
+
result = await handleDeleteMemory(args);
|
|
345
|
+
break;
|
|
346
|
+
case "debug_memory_structure":
|
|
347
|
+
result = await handleDebugStructure();
|
|
348
|
+
break;
|
|
349
|
+
case "list_spaces":
|
|
350
|
+
result = await handleListSpaces();
|
|
351
|
+
break;
|
|
352
|
+
case "get_space_info":
|
|
353
|
+
result = await handleGetSpaceInfo(args);
|
|
354
|
+
break;
|
|
355
|
+
default:
|
|
356
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
361
|
+
};
|
|
362
|
+
} catch (error) {
|
|
363
|
+
throw new McpError(
|
|
364
|
+
ErrorCode.InternalError,
|
|
365
|
+
`Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`,
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
async function main() {
|
|
371
|
+
const transport = new StdioServerTransport();
|
|
372
|
+
await server.connect(transport);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
main().catch(() => {
|
|
376
|
+
process.exit(1);
|
|
377
|
+
});
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@intangle/mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Model Context Protocol server for Intangle - AI memory 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
|
+
"build": "tsc",
|
|
11
|
+
"lint": "biome check .",
|
|
12
|
+
"lint:fix": "biome check --fix .",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"intangle-mcp": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mcp",
|
|
20
|
+
"model-context-protocol",
|
|
21
|
+
"claude",
|
|
22
|
+
"claude-code",
|
|
23
|
+
"ai",
|
|
24
|
+
"memory",
|
|
25
|
+
"knowledge-management",
|
|
26
|
+
"intangle",
|
|
27
|
+
"claude-desktop",
|
|
28
|
+
"anthropic"
|
|
29
|
+
],
|
|
30
|
+
"author": "Intangle",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/intangle/mcp-server"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://intangle.app",
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
42
|
+
"dotenv": "^17.2.0",
|
|
43
|
+
"node-fetch": "^3.3.2"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@biomejs/biome": "^1.9.4",
|
|
47
|
+
"@types/node": "^22.0.0",
|
|
48
|
+
"@types/node-fetch": "^2.6.12",
|
|
49
|
+
"tsx": "^4.0.0",
|
|
50
|
+
"typescript": "^5.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"allowSyntheticDefaultImports": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"isolatedModules": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["**/*.ts"],
|
|
17
|
+
"exclude": ["node_modules", "dist"]
|
|
18
|
+
}
|