@intangle/mcp-server 2.6.7 → 2.6.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/index.ts DELETED
@@ -1,813 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Intangle MCP Server - Exposes Intangle context functionality through MCP
5
- */
6
-
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"
12
- import {
13
- CallToolRequestSchema,
14
- ErrorCode,
15
- ListResourceTemplatesRequestSchema,
16
- ListResourcesRequestSchema,
17
- ListToolsRequestSchema,
18
- McpError,
19
- ReadResourceRequestSchema,
20
- } from "@modelcontextprotocol/sdk/types.js"
21
- import { config } from "dotenv"
22
- import fetch from "node-fetch"
23
- import { TOOLS } from "./tool-definitions.js"
24
-
25
- // ... imports ...
26
-
27
- // Debug logging
28
- import { appendFileSync } from "fs"
29
- const DEBUG_LOG = "/tmp/intangle-mcp-debug.log"
30
- function log(msg: string) {
31
- try {
32
- appendFileSync(DEBUG_LOG, `${new Date().toISOString()}: ${msg}\n`)
33
- } catch (e) {}
34
- }
35
-
36
- log("--- Server Starting ---")
37
-
38
- // Client info captured from MCP initialize handshake
39
- let mcpClientName: string | undefined
40
- let mcpClientVersion: string | undefined
41
-
42
- try {
43
- // Load environment variables from .env and .env.local
44
- config({ quiet: true })
45
- config({ path: ".env.local", quiet: true })
46
-
47
- // Base URL for API calls to Next.js app
48
- const API_BASE_URL = process.env.NEXT_APP_URL || "https://renvoi.co/intangle"
49
- const MCP_API_KEY = process.env.MCP_API_KEY
50
- const VERCEL_BYPASS_TOKEN =
51
- process.env.VERCEL_BYPASS_TOKEN || process.env.VERCEL_PROTECTION_BYPASS
52
-
53
- log(`CWD: ${process.cwd()}`)
54
- log(`Has API Key: ${!!MCP_API_KEY}`)
55
- log(`Has Vercel Bypass Token: ${!!VERCEL_BYPASS_TOKEN}`)
56
- log(`Node Version: ${process.version}`)
57
-
58
- if (!MCP_API_KEY) {
59
- log("Error: MCP_API_KEY environment variable is required")
60
- console.error("Error: MCP_API_KEY environment variable is required")
61
- console.error(
62
- "Please set your Intangle API key in the Claude Desktop configuration"
63
- )
64
- process.exit(1)
65
- }
66
-
67
- console.error("Intangle MCP Server starting - connecting to", API_BASE_URL)
68
-
69
- // Version checking - automatically read from package.json
70
- const __dirname = dirname(fileURLToPath(import.meta.url))
71
- let packageJson
72
- try {
73
- packageJson = JSON.parse(
74
- readFileSync(join(__dirname, "package.json"), "utf-8")
75
- )
76
- } catch {
77
- packageJson = JSON.parse(
78
- readFileSync(join(__dirname, "../package.json"), "utf-8")
79
- )
80
- }
81
- const CURRENT_VERSION = packageJson.version
82
- let latestVersion: string | null = null
83
- let versionCheckDone = false
84
-
85
- async function checkVersion() {
86
- if (versionCheckDone) return
87
-
88
- try {
89
- const response = await fetch(
90
- "https://registry.npmjs.org/@intangle/mcp-server/latest"
91
- )
92
- const data = (await response.json()) as { version: string }
93
- latestVersion = data.version
94
- versionCheckDone = true
95
-
96
- if (latestVersion && latestVersion !== CURRENT_VERSION) {
97
- console.error("\n UPDATE AVAILABLE ")
98
- console.error(`Current version: ${CURRENT_VERSION}`)
99
- console.error(`Latest version: ${latestVersion}`)
100
- console.error("Update with: npx @intangle/mcp-server@latest\n")
101
- } else {
102
- console.error(`✓ Running latest version (${CURRENT_VERSION})`)
103
- }
104
- } catch (error) {
105
- // Silently fail version check - don't block startup
106
- versionCheckDone = true
107
- }
108
- }
109
-
110
- // Check version on startup (non-blocking)
111
- checkVersion()
112
-
113
- // Helper to ensure client info is captured (called before API requests)
114
- function ensureClientInfo() {
115
- if (!mcpClientName) {
116
- const clientInfo = server.getClientVersion()
117
- if (clientInfo) {
118
- mcpClientName = clientInfo.name
119
- mcpClientVersion = clientInfo.version
120
- log(
121
- `Client identified (on-demand): ${mcpClientName} v${mcpClientVersion}`
122
- )
123
- }
124
- }
125
- }
126
-
127
- // Default timeout for API calls (90 seconds)
128
- const API_TIMEOUT_MS = 90_000
129
-
130
- function buildApiUrl(endpoint: string) {
131
- const url = new URL(`/api/mcp/${endpoint}`, API_BASE_URL)
132
-
133
- if (VERCEL_BYPASS_TOKEN) {
134
- url.searchParams.set("x-vercel-protection-bypass", VERCEL_BYPASS_TOKEN)
135
- }
136
-
137
- return url.toString()
138
- }
139
-
140
- function buildRemoteApiUrl() {
141
- const url = new URL("/api/mcp-remote", API_BASE_URL)
142
-
143
- if (VERCEL_BYPASS_TOKEN) {
144
- url.searchParams.set("x-vercel-protection-bypass", VERCEL_BYPASS_TOKEN)
145
- }
146
-
147
- return url.toString()
148
- }
149
-
150
- async function makeApiCall(endpoint: string, data: any, timeoutMs?: number) {
151
- // Ensure we have client info before making requests
152
- ensureClientInfo()
153
-
154
- // Build headers with client info if available
155
- const headers: Record<string, string> = {
156
- "Content-Type": "application/json",
157
- Authorization: `Bearer ${MCP_API_KEY}`,
158
- "User-Agent": mcpClientName
159
- ? `${mcpClientName}/${mcpClientVersion || "unknown"} (mcp-stdio)`
160
- : "MCP-Client-Stdio/1.1.2 (mcp)",
161
- }
162
-
163
- // Add explicit client info headers for backend processing
164
- if (mcpClientName) {
165
- headers["X-MCP-Client-Name"] = mcpClientName
166
- }
167
- if (mcpClientVersion) {
168
- headers["X-MCP-Client-Version"] = mcpClientVersion
169
- }
170
- if (VERCEL_BYPASS_TOKEN) {
171
- headers["x-vercel-protection-bypass"] = VERCEL_BYPASS_TOKEN
172
- }
173
-
174
- // Apply timeout to prevent hanging requests
175
- const controller = new AbortController()
176
- const effectiveTimeout = timeoutMs || API_TIMEOUT_MS
177
- const timeoutId = setTimeout(() => controller.abort(), effectiveTimeout)
178
-
179
- try {
180
- const response = await fetch(buildApiUrl(endpoint), {
181
- method: "POST",
182
- headers,
183
- body: JSON.stringify(data),
184
- signal: controller.signal as AbortSignal,
185
- })
186
-
187
- const contentType = response.headers.get("content-type") || ""
188
- const responseBody = await response.text()
189
-
190
- if (!response.ok) {
191
- if (
192
- response.status === 401 &&
193
- contentType.includes("text/html") &&
194
- responseBody.includes("Vercel Authentication")
195
- ) {
196
- throw new Error(
197
- VERCEL_BYPASS_TOKEN
198
- ? "API call blocked by Vercel deployment protection despite bypass token. Check token validity and deployment protection settings."
199
- : "API call blocked by Vercel deployment protection. Set VERCEL_BYPASS_TOKEN in MCP server env."
200
- )
201
- }
202
-
203
- throw new Error(
204
- `API call failed: ${response.status} ${response.statusText}`
205
- )
206
- }
207
-
208
- if (responseBody.trim() === "") {
209
- return {}
210
- }
211
-
212
- if (
213
- contentType.includes("text/html") ||
214
- /^\s*<!DOCTYPE\s+html/i.test(responseBody) ||
215
- /^\s*<html\b/i.test(responseBody)
216
- ) {
217
- throw new Error(
218
- `API call to ${buildApiUrl(endpoint)} returned HTML instead of JSON. Check NEXT_APP_URL and point it at the app host, e.g. https://renvoi.co/intangle.`
219
- )
220
- }
221
-
222
- return JSON.parse(responseBody)
223
- } catch (error: unknown) {
224
- if (error instanceof Error && error.name === "AbortError") {
225
- throw new Error(
226
- `API call to ${endpoint} timed out after ${effectiveTimeout / 1000}s`
227
- )
228
- }
229
- throw error
230
- } finally {
231
- clearTimeout(timeoutId)
232
- }
233
- }
234
-
235
- async function makeRemoteRpcCall(
236
- method: string,
237
- params: Record<string, unknown>
238
- ) {
239
- ensureClientInfo()
240
-
241
- const headers: Record<string, string> = {
242
- "Content-Type": "application/json",
243
- Authorization: `Bearer ${MCP_API_KEY}`,
244
- "User-Agent": mcpClientName
245
- ? `${mcpClientName}/${mcpClientVersion || "unknown"} (mcp-stdio)`
246
- : "MCP-Client-Stdio/1.1.2 (mcp)",
247
- }
248
-
249
- if (mcpClientName) {
250
- headers["X-MCP-Client-Name"] = mcpClientName
251
- }
252
- if (mcpClientVersion) {
253
- headers["X-MCP-Client-Version"] = mcpClientVersion
254
- }
255
- if (VERCEL_BYPASS_TOKEN) {
256
- headers["x-vercel-protection-bypass"] = VERCEL_BYPASS_TOKEN
257
- }
258
-
259
- const requestId = Date.now()
260
- const rpcBody = {
261
- jsonrpc: "2.0",
262
- id: requestId,
263
- method,
264
- params,
265
- }
266
-
267
- const response = await fetch(buildRemoteApiUrl(), {
268
- method: "POST",
269
- headers,
270
- body: JSON.stringify(rpcBody),
271
- })
272
-
273
- const responseText = await response.text()
274
- let responseJson: any = {}
275
-
276
- try {
277
- responseJson = responseText ? JSON.parse(responseText) : {}
278
- } catch {
279
- throw new Error(`Failed to parse MCP remote ${method} response`)
280
- }
281
-
282
- if (!response.ok) {
283
- const message =
284
- responseJson?.error?.message ||
285
- responseJson?.message ||
286
- `${response.status} ${response.statusText}`
287
- throw new Error(`MCP remote ${method} failed: ${message}`)
288
- }
289
-
290
- if (responseJson?.error) {
291
- const code = responseJson.error.code
292
- const message = responseJson.error.message || "Unknown MCP remote error"
293
- throw new Error(`MCP remote ${method} error (${code}): ${message}`)
294
- }
295
-
296
- return responseJson?.result ?? {}
297
- }
298
-
299
- const server = new Server(
300
- {
301
- name: "intangle-context",
302
- version: "1.0.0",
303
- icons: [
304
- {
305
- src: "https://intangle.tools/icon.png",
306
- mimeType: "image/png",
307
- },
308
- ],
309
- },
310
- {
311
- capabilities: {
312
- resources: {},
313
- tools: {},
314
- },
315
- }
316
- )
317
-
318
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
319
- tools: TOOLS,
320
- }))
321
-
322
- server.setRequestHandler(ListResourcesRequestSchema, async () => {
323
- const result = await makeRemoteRpcCall("resources/list", {})
324
- return {
325
- resources: Array.isArray(result?.resources) ? result.resources : [],
326
- nextCursor:
327
- typeof result?.nextCursor === "string" ? result.nextCursor : undefined,
328
- }
329
- })
330
-
331
- server.setRequestHandler(ReadResourceRequestSchema, async (request: any) => {
332
- const uri = request?.params?.uri
333
- if (!uri || typeof uri !== "string") {
334
- throw new McpError(
335
- ErrorCode.InvalidParams,
336
- "uri is required for resources/read"
337
- )
338
- }
339
-
340
- const result = await makeRemoteRpcCall("resources/read", { uri })
341
- return {
342
- contents: Array.isArray(result?.contents) ? result.contents : [],
343
- }
344
- })
345
-
346
- server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
347
- const result = await makeRemoteRpcCall("resources/templates/list", {})
348
- return {
349
- resourceTemplates: Array.isArray(result?.resourceTemplates)
350
- ? result.resourceTemplates
351
- : [],
352
- }
353
- })
354
-
355
- async function handleSearchContext(args: any) {
356
- const { space_id, query, topics, group_id, project_id, folder_id } = args as {
357
- space_id: string
358
- query: string
359
- topics?: string[]
360
- group_id?: string
361
- project_id?: string
362
- folder_id?: string
363
- }
364
-
365
- // Require space_id
366
- if (!space_id) {
367
- throw new Error(
368
- "space_id is required. Use list_spaces to see available options."
369
- )
370
- }
371
-
372
- return makeApiCall("search", {
373
- space_id,
374
- query,
375
- group_id,
376
- project_id,
377
- folder_id,
378
- topics,
379
- })
380
- }
381
-
382
- async function handleFetch(args: any) {
383
- const { id, ids, space_id } = args as {
384
- id?: string
385
- ids?: string[]
386
- space_id?: string
387
- }
388
-
389
- return makeApiCall("fetch", { id, ids, space_id })
390
- }
391
-
392
- async function handleUsageStatus() {
393
- return makeApiCall("usage-status", {})
394
- }
395
-
396
- async function handleFetchConversationMessages(args: any) {
397
- const { space_id, conversation_id } = args as {
398
- space_id?: string
399
- conversation_id?: string
400
- }
401
-
402
- if (!space_id || !conversation_id) {
403
- throw new Error("space_id and conversation_id are required")
404
- }
405
-
406
- return makeApiCall("conversation-messages", args)
407
- }
408
-
409
- async function handleSubscribeConversation(args: any) {
410
- const { space_id, conversation_id } = args as {
411
- space_id?: string
412
- conversation_id?: string
413
- }
414
-
415
- if (!space_id || !conversation_id) {
416
- throw new Error("space_id and conversation_id are required")
417
- }
418
-
419
- return makeApiCall("conversation-subscribe", args)
420
- }
421
-
422
- async function handleConversationInbox(args: any) {
423
- const { space_id } = args as { space_id?: string }
424
-
425
- if (!space_id) {
426
- throw new Error("space_id is required")
427
- }
428
-
429
- return makeApiCall("conversation-inbox", args)
430
- }
431
-
432
- async function handleMarkConversationRead(args: any) {
433
- const { space_id, conversation_id } = args as {
434
- space_id?: string
435
- conversation_id?: string
436
- }
437
-
438
- if (!space_id || !conversation_id) {
439
- throw new Error("space_id and conversation_id are required")
440
- }
441
-
442
- return makeApiCall("conversation-read", args)
443
- }
444
-
445
- async function handleViewSpaces() {
446
- return makeApiCall("list-spaces", {})
447
- }
448
-
449
- async function handleViewGroups(args: any) {
450
- const { space_id } = args as { space_id?: string }
451
- if (!space_id) {
452
- throw new Error(
453
- "space_id is required. Use view_spaces to see available options."
454
- )
455
- }
456
-
457
- return makeApiCall("view-groups", args)
458
- }
459
-
460
- async function handleViewProjects(args: any) {
461
- const { space_id } = args as { space_id: string }
462
- if (!space_id) {
463
- throw new Error(
464
- "space_id is required. Use view_spaces to see available options."
465
- )
466
- }
467
- return makeApiCall("view-projects", args)
468
- }
469
-
470
- async function handleViewProject(args: any) {
471
- const { project_id, space_id, slug } = args as {
472
- project_id?: string
473
- space_id?: string
474
- slug?: string
475
- }
476
-
477
- if (!project_id && !slug) {
478
- throw new Error(
479
- "Either project_id or slug (with space_id) is required. Use view_projects to get valid IDs."
480
- )
481
- }
482
-
483
- if (!space_id) {
484
- throw new Error(
485
- "space_id is required. Use view_spaces to see available options."
486
- )
487
- }
488
-
489
- return makeApiCall("view-project", args)
490
- }
491
-
492
- async function handleCreateGroup(args: any) {
493
- const { space_id, name } = args as { space_id?: string; name?: string }
494
- if (!space_id || !name) {
495
- throw new Error("space_id and name are required")
496
- }
497
-
498
- return makeApiCall("create-group", args)
499
- }
500
-
501
- async function handleRenameGroup(args: any) {
502
- const { space_id, group_id, name } = args as {
503
- space_id?: string
504
- group_id?: string
505
- name?: string
506
- }
507
-
508
- if (!space_id || !group_id || !name) {
509
- throw new Error("space_id, group_id, and name are required")
510
- }
511
-
512
- return makeApiCall("rename-group", args)
513
- }
514
-
515
- async function handleDeleteGroup(args: any) {
516
- const { space_id, group_id } = args as {
517
- space_id?: string
518
- group_id?: string
519
- }
520
-
521
- if (!space_id || !group_id) {
522
- throw new Error("space_id and group_id are required")
523
- }
524
-
525
- return makeApiCall("delete-group", args)
526
- }
527
-
528
- async function handleCreateProject(args: any) {
529
- const { space_id, name, group_id } = args as {
530
- space_id?: string
531
- name?: string
532
- group_id?: string
533
- }
534
-
535
- if (!space_id || !name) {
536
- throw new Error("space_id and name are required")
537
- }
538
-
539
- return makeApiCall("create-project", args)
540
- }
541
-
542
- async function handleRenameProject(args: any) {
543
- const { space_id, project_id, name } = args as {
544
- space_id?: string
545
- project_id?: string
546
- name?: string
547
- }
548
-
549
- if (!space_id || !project_id || !name) {
550
- throw new Error("space_id, project_id, and name are required")
551
- }
552
-
553
- return makeApiCall("rename-project", args)
554
- }
555
-
556
- async function handleDeleteProject(args: any) {
557
- const { space_id, project_id } = args as {
558
- space_id?: string
559
- project_id?: string
560
- }
561
-
562
- if (!space_id || !project_id) {
563
- throw new Error("space_id and project_id are required")
564
- }
565
-
566
- return makeApiCall("delete-project", args)
567
- }
568
-
569
- async function handleAssignProjectToGroup(args: any) {
570
- const { space_id, project_id, group_id } = args as {
571
- space_id?: string
572
- project_id?: string
573
- group_id?: string | null
574
- }
575
-
576
- if (!space_id || !project_id) {
577
- throw new Error("space_id and project_id are required")
578
- }
579
-
580
- return makeApiCall("assign-project-to-group", args)
581
- }
582
-
583
- async function handleCreateSpace(args: any) {
584
- return makeApiCall("create-space", args)
585
- }
586
-
587
- async function handleViewSpace(args: any) {
588
- const { space_id } = args as { space_id: string }
589
- return makeApiCall("view-space", args)
590
- }
591
-
592
- async function handleStart(args: any) {
593
- const { space_id } = args as { space_id: string }
594
- return makeApiCall("start", args)
595
- }
596
-
597
- async function handleUpdateSpace(args: any) {
598
- if (!args.space_id) {
599
- throw new Error(
600
- "space_id is required. Use view_spaces to see available options."
601
- )
602
- }
603
-
604
- if (!args.add && !args.update && !args.delete) {
605
- throw new Error("At least one operation must be provided")
606
- }
607
-
608
- // Ensure all add items have a type field to prevent classification timeout
609
- const add = args.add
610
- if (add?.items && Array.isArray(add.items)) {
611
- for (const item of add.items) {
612
- if (!item.type) {
613
- log(
614
- `WARNING: Item "${item.title}" missing type field, defaulting to "context" to prevent classification timeout`
615
- )
616
- item.type = "context"
617
- }
618
- }
619
- }
620
-
621
- // Pass through to API with new structure
622
- return makeApiCall("update-memory", {
623
- space_id: args.space_id,
624
- project_id: args.project_id,
625
- add,
626
- update: args.update,
627
- delete: args.delete,
628
- })
629
- }
630
-
631
- async function handleUpdateFolders(args: any) {
632
- if (!args.space_id) {
633
- throw new Error(
634
- "space_id is required. Use view_spaces to see available options."
635
- )
636
- }
637
-
638
- const hasOperation =
639
- !!args.list ||
640
- (Array.isArray(args.create) && args.create.length > 0) ||
641
- (Array.isArray(args.rename) && args.rename.length > 0) ||
642
- (Array.isArray(args.move_items) && args.move_items.length > 0) ||
643
- (Array.isArray(args.delete) && args.delete.length > 0)
644
-
645
- if (!hasOperation) {
646
- throw new Error(
647
- "At least one operation is required: list, create, rename, move_items, or delete."
648
- )
649
- }
650
-
651
- return makeApiCall("update-folders", {
652
- space_id: args.space_id,
653
- list: args.list,
654
- create: args.create,
655
- rename: args.rename,
656
- move_items: args.move_items,
657
- delete: args.delete,
658
- })
659
- }
660
-
661
- async function handleMessage(args: any) {
662
- if (!args.space_id || !args.content) {
663
- throw new Error("space_id and content are required")
664
- }
665
-
666
- // Keep one-turn MCP calls safely under common external tool-call deadlines
667
- // while still allowing caller override for longer jobs.
668
- const timeoutMs =
669
- typeof args.timeout_ms === "number" && Number.isFinite(args.timeout_ms)
670
- ? Math.max(10_000, Math.min(300_000, Math.trunc(args.timeout_ms)))
671
- : 100_000
672
-
673
- // Give backend a little extra headroom beyond requested assistant timeout.
674
- const requestTimeout = timeoutMs + 10_000
675
-
676
- return makeApiCall(
677
- "message",
678
- { ...args, timeout_ms: timeoutMs },
679
- requestTimeout
680
- )
681
- }
682
-
683
- server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
684
- const { name, arguments: args } = request.params
685
-
686
- try {
687
- let result: any
688
-
689
- switch (name) {
690
- case "search":
691
- result = await handleSearchContext(args)
692
- break
693
- case "fetch_items":
694
- result = await handleFetch(args)
695
- break
696
- case "usage_status":
697
- result = await handleUsageStatus()
698
- break
699
- case "fetch_conversation_messages":
700
- result = await handleFetchConversationMessages(args)
701
- break
702
- case "subscribe_conversation":
703
- result = await handleSubscribeConversation(args)
704
- break
705
- case "list_conversation_inbox":
706
- result = await handleConversationInbox(args)
707
- break
708
- case "mark_conversation_read":
709
- result = await handleMarkConversationRead(args)
710
- break
711
- case "view_spaces":
712
- result = await handleViewSpaces()
713
- break
714
- case "view_groups":
715
- result = await handleViewGroups(args)
716
- break
717
- case "create_group":
718
- result = await handleCreateGroup(args)
719
- break
720
- case "rename_group":
721
- result = await handleRenameGroup(args)
722
- break
723
- case "delete_group":
724
- result = await handleDeleteGroup(args)
725
- break
726
- case "view_projects":
727
- result = await handleViewProjects(args)
728
- break
729
- case "view_project":
730
- result = await handleViewProject(args)
731
- break
732
- case "create_project":
733
- result = await handleCreateProject(args)
734
- break
735
- case "rename_project":
736
- result = await handleRenameProject(args)
737
- break
738
- case "delete_project":
739
- result = await handleDeleteProject(args)
740
- break
741
- case "assign_project_to_group":
742
- result = await handleAssignProjectToGroup(args)
743
- break
744
- case "create_space":
745
- result = await handleCreateSpace(args)
746
- break
747
- case "view_space":
748
- result = await handleViewSpace(args)
749
- break
750
- case "start":
751
- result = await handleStart(args)
752
- break
753
- case "update_memory":
754
- case "update_space":
755
- result = await handleUpdateSpace(args)
756
- break
757
- case "update_folders":
758
- result = await handleUpdateFolders(args)
759
- break
760
- case "message":
761
- result = await handleMessage(args)
762
- break
763
- default:
764
- throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`)
765
- }
766
-
767
- // Extract response field for Layer 2 compressed tools, otherwise stringify full result
768
- let responseText: string
769
- if (
770
- result &&
771
- typeof result === "object" &&
772
- "response" in result &&
773
- typeof result.response === "string"
774
- ) {
775
- // Tool has Layer 2 compression - use the formatted response field
776
- responseText = result.response
777
- } else {
778
- // Standard tool - return full JSON
779
- responseText = JSON.stringify(result, null, 2)
780
- }
781
-
782
- // Add version warning to response if outdated
783
- if (latestVersion && latestVersion !== CURRENT_VERSION) {
784
- 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`
785
- responseText += warning
786
- }
787
-
788
- return {
789
- content: [{ type: "text", text: responseText }],
790
- }
791
- } catch (error) {
792
- throw new McpError(
793
- ErrorCode.InternalError,
794
- `Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`
795
- )
796
- }
797
- })
798
-
799
- async function main() {
800
- const transport = new StdioServerTransport()
801
- await server.connect(transport)
802
- log("Server connected to transport")
803
- // Client info is captured on-demand in ensureClientInfo() before first API call
804
- }
805
-
806
- main().catch((err) => {
807
- log(`Error in main: ${err}`)
808
- process.exit(1)
809
- })
810
- } catch (err) {
811
- log(`Fatal startup error: ${err}`)
812
- process.exit(1)
813
- }