@intangle/mcp-server 2.1.6 → 2.1.8

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 CHANGED
@@ -1,10 +1,10 @@
1
1
  # @intangle/mcp-server
2
2
 
3
- Model Context Protocol server for [Intangle](https://intangle.app) - Intelligent AI memory that persists across conversations.
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 memory system that gives Claude and other AI assistants persistent, searchable memory across all your conversations and isolated spaces. Unlike traditional chat interfaces where context is lost between sessions, Intangle maintains your personal memory across isolated spaces, with intelligent storing and retriveal.
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 memory. You can:
49
+ Once configured, Claude will automatically have access to your Intangle context. You can:
50
50
 
51
- - **Store memories**: "Remember that I prefer TypeScript over JavaScript"
52
- - **Search memories**: "What did we discuss about the authentication system?"
53
- - **Organize with spaces**: Separate memories for personal, work, and different projects
54
- - **Tag with topics**: Memories are automatically tagged and organized
55
- - **Access across conversations**: All memories persist between chat sessions
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
- - `add_memory` - Store new memories with topics
62
- - `search_memories` - Search across all your memories
63
- - `get_recent_memories` - Retrieve recent memories
64
- - `list_spaces` - View available memory spaces
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 memories
67
- - `delete_memory` - Remove specific memories
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 memories into separate spaces (personal, work, projects)
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 memories:
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
- "$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
- }
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 memory functionality through MCP
3
+ * Endure MCP Server - Exposes Endure context functionality through MCP
4
4
  */
5
- import { config } from "dotenv";
6
- import fetch from "node-fetch";
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 { readFileSync } from "fs";
11
- import { fileURLToPath } from "url";
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
@@ -22,6 +22,9 @@ function log(msg) {
22
22
  catch (e) { }
23
23
  }
24
24
  log("--- Server Starting ---");
25
+ // Client info captured from MCP initialize handshake
26
+ let mcpClientName;
27
+ let mcpClientVersion;
25
28
  try {
26
29
  // Load environment variables from .env and .env.local
27
30
  config({ quiet: true });
@@ -76,14 +79,38 @@ try {
76
79
  }
77
80
  // Check version on startup (non-blocking)
78
81
  checkVersion();
82
+ // Helper to ensure client info is captured (called before API requests)
83
+ function ensureClientInfo() {
84
+ if (!mcpClientName) {
85
+ const clientInfo = server.getClientVersion();
86
+ if (clientInfo) {
87
+ mcpClientName = clientInfo.name;
88
+ mcpClientVersion = clientInfo.version;
89
+ log(`Client identified (on-demand): ${mcpClientName} v${mcpClientVersion}`);
90
+ }
91
+ }
92
+ }
79
93
  async function makeApiCall(endpoint, data) {
94
+ // Ensure we have client info before making requests
95
+ ensureClientInfo();
96
+ // Build headers with client info if available
97
+ const headers = {
98
+ "Content-Type": "application/json",
99
+ Authorization: `Bearer ${MCP_API_KEY}`,
100
+ "User-Agent": mcpClientName
101
+ ? `${mcpClientName}/${mcpClientVersion || "unknown"} (mcp-stdio)`
102
+ : "MCP-Client-Stdio/1.1.2 (mcp)",
103
+ };
104
+ // Add explicit client info headers for backend processing
105
+ if (mcpClientName) {
106
+ headers["X-MCP-Client-Name"] = mcpClientName;
107
+ }
108
+ if (mcpClientVersion) {
109
+ headers["X-MCP-Client-Version"] = mcpClientVersion;
110
+ }
80
111
  const response = await fetch(`${API_BASE_URL}/api/mcp/${endpoint}`, {
81
112
  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
- },
113
+ headers,
87
114
  body: JSON.stringify(data),
88
115
  });
89
116
  if (!response.ok) {
@@ -92,7 +119,7 @@ try {
92
119
  return response.json();
93
120
  }
94
121
  const server = new Server({
95
- name: "intangle-memory",
122
+ name: "intangle-context",
96
123
  version: "1.0.0",
97
124
  vendor: "Intangle",
98
125
  icon: "https://intangle.tools/icon.png",
@@ -104,13 +131,13 @@ try {
104
131
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
105
132
  tools: TOOLS,
106
133
  }));
107
- async function handleSearchMemories(args) {
134
+ async function handleSearchContext(args) {
108
135
  const { space_id, query, topics } = args;
109
136
  // Require space_id
110
137
  if (!space_id) {
111
138
  throw new Error("space_id is required. Use list_spaces to see available options.");
112
139
  }
113
- return makeApiCall("search-memories", {
140
+ return makeApiCall("search-context", {
114
141
  space_id,
115
142
  space_ids: [space_id], // Convert to array for backend compatibility
116
143
  query,
@@ -172,7 +199,7 @@ try {
172
199
  let result;
173
200
  switch (name) {
174
201
  case "search":
175
- result = await handleSearchMemories(args);
202
+ result = await handleSearchContext(args);
176
203
  break;
177
204
  case "fetch_items":
178
205
  result = await handleFetch(args);
@@ -231,6 +258,7 @@ try {
231
258
  const transport = new StdioServerTransport();
232
259
  await server.connect(transport);
233
260
  log("Server connected to transport");
261
+ // Client info is captured on-demand in ensureClientInfo() before first API call
234
262
  }
235
263
  main().catch((err) => {
236
264
  log(`Error in main: ${err}`);
@@ -3,7 +3,7 @@ export const TOOLS = [
3
3
  {
4
4
  name: "search",
5
5
  title: "Search Space",
6
- description: "Search for information within a space. System automatically extracts quantity from natural language ('show 3 tasks' → 3 results, 'the last one' → 1 result) and intelligently formats results (1-3 items → summaries, 4+ items → IDs only). Use fetch tool to get full content for specific IDs when needed.",
6
+ description: "Search for context, tasks, and processes within a space. System automatically extracts quantity from natural language ('show 3 tasks' → 3 results, 'the last one' → 1 result) and intelligently formats results (1-3 items → summaries, 4+ items → IDs only). Use fetch tool to get full content for specific IDs when needed.",
7
7
  inputSchema: {
8
8
  type: "object",
9
9
  properties: {
@@ -33,12 +33,12 @@ export const TOOLS = [
33
33
  properties: {
34
34
  id: {
35
35
  type: "string",
36
- description: "Single ID to fetch (context or task ID like 'mem_123' or 'task_456')",
36
+ description: "Single ID to fetch (context or task ID like 'mem_123' or 'task_456' or 'proc_789')",
37
37
  },
38
38
  ids: {
39
39
  type: "array",
40
40
  items: { type: "string" },
41
- description: "Array of IDs to fetch (mix of context and task IDs). Use this to fetch multiple items in one call.",
41
+ description: "Array of IDs to fetch (mix of context, task, and process IDs). Use this to fetch multiple items in one call.",
42
42
  },
43
43
  },
44
44
  },
@@ -46,7 +46,7 @@ export const TOOLS = [
46
46
  {
47
47
  name: "view_topic",
48
48
  title: "View Topic",
49
- description: "View all memories and tasks tagged with a specific topic. Perfect for exploring a topic area or following up on topics from the start briefing.",
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 memories.",
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 memory with automatic intelligent classification and topic suggestion",
156
+ description: "Add new items to context with automatic intelligent classification and topic suggestion",
157
157
  properties: {
158
158
  items: {
159
159
  type: "array",
@@ -163,25 +163,25 @@ export const TOOLS = [
163
163
  title: { type: "string", description: "Item title" },
164
164
  content: {
165
165
  type: "string",
166
- description: "Item content - system automatically classifies as task (actionable) or context (knowledge) and suggests relevant topics",
166
+ description: "Item content - system automatically classifies as task (actionable), context (knowledge), or process (workflow/procedure) and suggests relevant topics",
167
167
  },
168
168
  },
169
169
  required: ["title", "content"],
170
170
  },
171
- description: "Array of items to add. System intelligently: (1) classifies as task or context, (2) suggests 1-3 relevant topics based on content and existing topics. Examples: 'Need to fix auth bug' → task with topics like 'authentication', 'bug-fix'. 'Learned React batches updates' → context with topics like 'react', 'learning'.",
171
+ description: "Array of items to add. System intelligently: (1) classifies as task, context, or process, (2) suggests 1-3 relevant topics based on content and existing topics. Examples: 'Need to fix auth bug' → task with topics like 'authentication', 'bug-fix'. 'Learned React batches updates' → context with topics like 'react', 'learning'.",
172
172
  },
173
173
  },
174
174
  },
175
175
  update: {
176
176
  type: "object",
177
- description: "Update existing items (context or tasks) - system detects type from ID prefix",
177
+ description: "Update existing items (context, tasks, or processes) - system detects type from ID prefix",
178
178
  properties: {
179
179
  items: {
180
180
  type: "array",
181
181
  items: {
182
182
  type: "object",
183
183
  properties: {
184
- id: { type: "string", description: "Item ID to update (mem_* for context, task_* for tasks)" },
184
+ id: { type: "string", description: "Item ID to update (mem_* for context, task_* for tasks, proc_* for processes)" },
185
185
  title: {
186
186
  type: "string",
187
187
  description: "New title (optional)",
@@ -213,7 +213,7 @@ export const TOOLS = [
213
213
  },
214
214
  required: ["id"],
215
215
  },
216
- description: "Array of items to update. Type automatically detected from ID prefix (mem_* = context, task_* = task). For context items, status/priority are ignored.",
216
+ description: "Array of items to update. Type automatically detected from ID prefix (mem_* = context, task_* = task, proc_* = process). For context/process items, status/priority are ignored.",
217
217
  },
218
218
  },
219
219
  },
@@ -224,7 +224,7 @@ export const TOOLS = [
224
224
  item_ids: {
225
225
  type: "array",
226
226
  items: { type: "string" },
227
- description: "Array of item IDs to delete (mem_* for context, task_* for tasks). Type automatically detected from ID prefix.",
227
+ description: "Array of item IDs to delete (mem_* for context, task_* for tasks, proc_* for processes). Type automatically detected from ID prefix.",
228
228
  },
229
229
  },
230
230
  },
package/index.ts CHANGED
@@ -1,315 +1,347 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Endure MCP Server - Exposes Endure memory functionality through MCP
4
+ * Endure MCP Server - Exposes Endure context functionality through MCP
5
5
  */
6
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";
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
- CallToolRequestSchema,
13
- ErrorCode,
14
- ListToolsRequestSchema,
15
- McpError,
16
- } from "@modelcontextprotocol/sdk/types.js";
17
- import { readFileSync } from "fs";
18
- import { fileURLToPath } from "url";
19
- import { dirname, join } from "path";
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
- try {
29
- appendFileSync(DEBUG_LOG, `${new Date().toISOString()}: ${msg}\n`);
30
- } catch (e) { }
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
- try {
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
- }
56
-
57
- console.error("Intangle MCP Server starting - connecting to", API_BASE_URL);
58
-
59
- // Version checking - automatically read from package.json
60
- const __dirname = dirname(fileURLToPath(import.meta.url));
61
- let packageJson;
62
- try {
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"),
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;
97
- }
98
- }
99
-
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
- }
119
-
120
- return response.json();
121
- }
122
-
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",
129
- },
130
- {
131
- capabilities: {
132
- tools: {},
133
- },
134
- },
135
- );
136
-
137
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
138
- tools: TOOLS,
139
- }));
140
-
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
- };
147
-
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
- }
154
-
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
- }
162
-
163
- async function handleFetch(args: any) {
164
- const { id, ids } = args as { id?: string; ids?: string[] };
165
-
166
- return makeApiCall("fetch", { id, ids });
167
- }
168
-
169
- async function handleViewSpaces() {
170
- return makeApiCall("list-spaces", {});
171
- }
172
-
173
- async function handleViewTopics(args: any) {
174
- const { space_id } = args as { space_id: string };
175
- if (!space_id) {
176
- throw new Error(
177
- "space_id is required. Use view_spaces to see available options.",
178
- );
179
- }
180
- return makeApiCall("view-topics", { space_id });
181
- }
182
-
183
- async function handleViewTopic(args: any) {
184
- const { topic_id } = args as { topic_id: string };
185
- if (!topic_id) {
186
- throw new Error(
187
- "topic_id is required. Use view_topics to get valid IDs.",
188
- );
189
- }
190
- // Pass directly to API; ensure API expects 'topic_id' or adjust if it expects 'id'
191
- // Usually view-topic endpoint takes 'topic_id' or 'id'
192
- return makeApiCall("view-topic", { topic_id });
193
- }
194
-
195
- async function handleCreateSpace(args: any) {
196
- return makeApiCall("create-space", args);
197
- }
198
-
199
- async function handleViewSpace(args: any) {
200
- const { space_id } = args as { space_id: string };
201
- return makeApiCall("view-space", { space_id });
202
- }
203
-
204
- async function handleStart(args: any) {
205
- const { space_id } = args as { space_id: string };
206
- return makeApiCall("start", { space_id });
207
- }
208
-
209
- async function handleUpdateSpace(args: any) {
210
- if (!args.space_id) {
211
- throw new Error(
212
- "space_id is required. Use view_spaces to see available options.",
213
- );
214
- }
215
-
216
- if (!args.add && !args.update && !args.delete) {
217
- throw new Error(
218
- "At least one operation (add, update, delete) must be provided",
219
- );
220
- }
221
-
222
- // Pass through to API with new structure
223
- return makeApiCall("update-memory", {
224
- space_id: args.space_id,
225
- add: args.add,
226
- update: args.update,
227
- delete: args.delete,
228
- });
229
- }
230
-
231
- server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
232
- const { name, arguments: args } = request.params;
233
-
234
- try {
235
- let result: any;
236
-
237
- switch (name) {
238
- case "search":
239
- result = await handleSearchMemories(args);
240
- break;
241
- case "fetch_items":
242
- result = await handleFetch(args);
243
- break;
244
- case "view_spaces":
245
- result = await handleViewSpaces();
246
- break;
247
- case "view_topics":
248
- result = await handleViewTopics(args);
249
- break;
250
- case "view_topic":
251
- result = await handleViewTopic(args);
252
- break;
253
- case "create_space":
254
- result = await handleCreateSpace(args);
255
- break;
256
- case "view_space":
257
- result = await handleViewSpace(args);
258
- break;
259
- case "start":
260
- result = await handleStart(args);
261
- break;
262
- case "update_space":
263
- result = await handleUpdateSpace(args);
264
- break;
265
- default:
266
- throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
267
- }
268
-
269
- // Extract response field for Layer 2 compressed tools, otherwise stringify full result
270
- let responseText: string;
271
- if (
272
- result &&
273
- typeof result === "object" &&
274
- "response" in result &&
275
- typeof result.response === "string"
276
- ) {
277
- // Tool has Layer 2 compression - use the formatted response field
278
- responseText = result.response;
279
- } else {
280
- // Standard tool - return full JSON
281
- responseText = JSON.stringify(result, null, 2);
282
- }
283
-
284
- // Add version warning to response if outdated
285
- if (latestVersion && latestVersion !== CURRENT_VERSION) {
286
- 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`;
287
- responseText += warning;
288
- }
289
-
290
- return {
291
- content: [{ type: "text", text: responseText }],
292
- };
293
- } catch (error) {
294
- throw new McpError(
295
- ErrorCode.InternalError,
296
- `Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`,
297
- );
298
- }
299
- });
300
-
301
- async function main() {
302
- const transport = new StdioServerTransport();
303
- await server.connect(transport);
304
- log("Server connected to transport");
305
- }
306
-
307
- main().catch((err) => {
308
- log(`Error in main: ${err}`);
309
- process.exit(1);
310
- });
35
+ // Client info captured from MCP initialize handshake
36
+ let mcpClientName: string | undefined
37
+ let mcpClientVersion: string | undefined
311
38
 
39
+ try {
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-context", {
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
+ })
312
344
  } catch (err) {
313
- log(`Fatal startup error: ${err}`);
314
- process.exit(1);
345
+ log(`Fatal startup error: ${err}`)
346
+ process.exit(1)
315
347
  }
package/package.json CHANGED
@@ -1,53 +1,53 @@
1
1
  {
2
- "name": "@intangle/mcp-server",
3
- "version": "2.1.6",
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
- "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
- "memory",
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
- }
2
+ "name": "@intangle/mcp-server",
3
+ "version": "2.1.8",
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
  }
@@ -4,7 +4,7 @@ export const TOOLS = [
4
4
  name: "search",
5
5
  title: "Search Space",
6
6
  description:
7
- "Search for information within a space. System automatically extracts quantity from natural language ('show 3 tasks' → 3 results, 'the last one' → 1 result) and intelligently formats results (1-3 items → summaries, 4+ items → IDs only). Use fetch tool to get full content for specific IDs when needed.",
7
+ "Search for context, tasks, and processes within a space. System automatically extracts quantity from natural language ('show 3 tasks' → 3 results, 'the last one' → 1 result) and intelligently formats results (1-3 items → summaries, 4+ items → IDs only). Use fetch tool to get full content for specific IDs when needed.",
8
8
  inputSchema: {
9
9
  type: "object",
10
10
  properties: {
@@ -37,13 +37,13 @@ export const TOOLS = [
37
37
  id: {
38
38
  type: "string",
39
39
  description:
40
- "Single ID to fetch (context or task ID like 'mem_123' or 'task_456')",
40
+ "Single ID to fetch (context or task ID like 'mem_123' or 'task_456' or 'proc_789')",
41
41
  },
42
42
  ids: {
43
43
  type: "array",
44
44
  items: { type: "string" },
45
45
  description:
46
- "Array of IDs to fetch (mix of context and task IDs). Use this to fetch multiple items in one call.",
46
+ "Array of IDs to fetch (mix of context, task, and process IDs). Use this to fetch multiple items in one call.",
47
47
  },
48
48
  },
49
49
  },
@@ -52,7 +52,7 @@ export const TOOLS = [
52
52
  name: "view_topic",
53
53
  title: "View Topic",
54
54
  description:
55
- "View all memories and tasks tagged with a specific topic. Perfect for exploring a topic area or following up on topics from the start briefing.",
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 memories.",
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 memory with automatic intelligent classification and topic suggestion",
173
+ description: "Add new items to context with automatic intelligent classification and topic suggestion",
174
174
  properties: {
175
175
  items: {
176
176
  type: "array",
@@ -180,25 +180,25 @@ export const TOOLS = [
180
180
  title: { type: "string", description: "Item title" },
181
181
  content: {
182
182
  type: "string",
183
- description: "Item content - system automatically classifies as task (actionable) or context (knowledge) and suggests relevant topics",
183
+ description: "Item content - system automatically classifies as task (actionable), context (knowledge), or process (workflow/procedure) and suggests relevant topics",
184
184
  },
185
185
  },
186
186
  required: ["title", "content"],
187
187
  },
188
- description: "Array of items to add. System intelligently: (1) classifies as task or context, (2) suggests 1-3 relevant topics based on content and existing topics. Examples: 'Need to fix auth bug' → task with topics like 'authentication', 'bug-fix'. 'Learned React batches updates' → context with topics like 'react', 'learning'.",
188
+ description: "Array of items to add. System intelligently: (1) classifies as task, context, or process, (2) suggests 1-3 relevant topics based on content and existing topics. Examples: 'Need to fix auth bug' → task with topics like 'authentication', 'bug-fix'. 'Learned React batches updates' → context with topics like 'react', 'learning'.",
189
189
  },
190
190
  },
191
191
  },
192
192
  update: {
193
193
  type: "object",
194
- description: "Update existing items (context or tasks) - system detects type from ID prefix",
194
+ description: "Update existing items (context, tasks, or processes) - system detects type from ID prefix",
195
195
  properties: {
196
196
  items: {
197
197
  type: "array",
198
198
  items: {
199
199
  type: "object",
200
200
  properties: {
201
- id: { type: "string", description: "Item ID to update (mem_* for context, task_* for tasks)" },
201
+ id: { type: "string", description: "Item ID to update (mem_* for context, task_* for tasks, proc_* for processes)" },
202
202
  title: {
203
203
  type: "string",
204
204
  description: "New title (optional)",
@@ -230,7 +230,7 @@ export const TOOLS = [
230
230
  },
231
231
  required: ["id"],
232
232
  },
233
- description: "Array of items to update. Type automatically detected from ID prefix (mem_* = context, task_* = task). For context items, status/priority are ignored.",
233
+ description: "Array of items to update. Type automatically detected from ID prefix (mem_* = context, task_* = task, proc_* = process). For context/process items, status/priority are ignored.",
234
234
  },
235
235
  },
236
236
  },
@@ -241,7 +241,7 @@ export const TOOLS = [
241
241
  item_ids: {
242
242
  type: "array",
243
243
  items: { type: "string" },
244
- description: "Array of item IDs to delete (mem_* for context, task_* for tasks). Type automatically detected from ID prefix.",
244
+ description: "Array of item IDs to delete (mem_* for context, task_* for tasks, proc_* for processes). Type automatically detected from ID prefix.",
245
245
  },
246
246
  },
247
247
  },
package/tsconfig.json CHANGED
@@ -15,4 +15,4 @@
15
15
  },
16
16
  "include": ["**/*.ts"],
17
17
  "exclude": ["node_modules", "dist"]
18
- }
18
+ }