@abraca/mcp 1.1.2 → 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -8731,6 +8731,7 @@ declare class AbracadabraMCPServer {
8731
8731
  private _statusClearTimer;
8732
8732
  private _typingInterval;
8733
8733
  private _lastChatChannel;
8734
+ private _signFn;
8734
8735
  constructor(config: MCPServerConfig);
8735
8736
  get agentName(): string;
8736
8737
  get agentColor(): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abraca/mcp",
3
- "version": "1.1.2",
3
+ "version": "1.3.4",
4
4
  "description": "MCP server for Abracadabra — AI agent collaboration on CRDT documents",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/server.ts CHANGED
@@ -51,6 +51,7 @@ export class AbracadabraMCPServer {
51
51
  private _statusClearTimer: ReturnType<typeof setTimeout> | null = null
52
52
  private _typingInterval: ReturnType<typeof setInterval> | null = null
53
53
  private _lastChatChannel: string | null = null
54
+ private _signFn: ((challenge: string) => Promise<string>) | null = null
54
55
 
55
56
  constructor(config: MCPServerConfig) {
56
57
  this.config = config
@@ -98,6 +99,7 @@ export class AbracadabraMCPServer {
98
99
  const keypair = await loadOrCreateKeypair(this.config.keyFile)
99
100
  this._userId = keypair.publicKeyB64
100
101
  const signFn = (challenge: string) => Promise.resolve(signChallenge(challenge, keypair.privateKey))
102
+ this._signFn = signFn
101
103
 
102
104
  // Step 2: Authenticate via challenge-response (register on first run)
103
105
  try {
@@ -179,6 +181,13 @@ export class AbracadabraMCPServer {
179
181
  return existing
180
182
  }
181
183
 
184
+ // Re-authenticate if JWT has expired (prevents WS auth failures)
185
+ if (!this.client.isTokenValid() && this._signFn && this._userId) {
186
+ console.error('[abracadabra-mcp] JWT expired, re-authenticating...')
187
+ await this.client.loginWithKey(this._userId, this._signFn)
188
+ console.error('[abracadabra-mcp] Re-authenticated successfully')
189
+ }
190
+
182
191
  const doc = new Y.Doc({ guid: docId })
183
192
  const provider = new AbracadabraProvider({
184
193
  name: docId,
@@ -256,6 +265,13 @@ export class AbracadabraMCPServer {
256
265
  throw new Error('Not connected. Call connect() first.')
257
266
  }
258
267
 
268
+ // Re-authenticate if JWT has expired (prevents child WS auth failures)
269
+ if (!this.client.isTokenValid() && this._signFn && this._userId) {
270
+ console.error('[abracadabra-mcp] JWT expired, re-authenticating...')
271
+ await this.client.loginWithKey(this._userId, this._signFn)
272
+ console.error('[abracadabra-mcp] Re-authenticated successfully')
273
+ }
274
+
259
275
  const childProvider = await activeProvider.loadChild(docId)
260
276
  await waitForSync(childProvider)
261
277
 
package/src/tools/tree.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Document tree tools — operate on the root Y.Doc's "doc-tree" Y.Map.
3
3
  */
4
- import type * as Y from 'yjs'
4
+ import * as Y from 'yjs'
5
5
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
6
6
  import { z } from 'zod'
7
7
  import type { AbracadabraMCPServer } from '../server.ts'
@@ -17,9 +17,16 @@ function normalizeRootId(id: string | null | undefined, server: AbracadabraMCPSe
17
17
  return id === server.rootDocId ? null : id
18
18
  }
19
19
 
20
+ /** Safely read a tree map value, converting Y.Map to plain object if needed. */
21
+ function toPlain(val: any): any {
22
+ return val instanceof Y.Map ? val.toJSON() : val
23
+ }
24
+
20
25
  function readEntries(treeMap: Y.Map<any>): TreeEntry[] {
21
26
  const entries: TreeEntry[] = []
22
- treeMap.forEach((value: any, id: string) => {
27
+ treeMap.forEach((raw: any, id: string) => {
28
+ const value = toPlain(raw)
29
+ if (typeof value !== 'object' || value === null) return // skip non-entry keys (e.g. ":updatedAt" timestamps)
23
30
  entries.push({
24
31
  id,
25
32
  label: value.label || 'Untitled',
@@ -257,12 +264,13 @@ export function registerTreeTools(mcp: McpServer, server: AbracadabraMCPServer)
257
264
  return { content: [{ type: 'text', text: 'Not connected' }] }
258
265
  }
259
266
 
260
- const entry = treeMap.get(id)
261
- if (!entry) {
267
+ const raw = treeMap.get(id)
268
+ if (!raw) {
262
269
  server.setActiveToolCall(null)
263
270
  return { content: [{ type: 'text', text: `Document ${id} not found` }] }
264
271
  }
265
272
 
273
+ const entry = toPlain(raw)
266
274
  treeMap.set(id, { ...entry, label, updatedAt: Date.now() })
267
275
  server.setActiveToolCall(null)
268
276
  return { content: [{ type: 'text', text: `Renamed to "${label}"` }] }
@@ -286,12 +294,13 @@ export function registerTreeTools(mcp: McpServer, server: AbracadabraMCPServer)
286
294
  return { content: [{ type: 'text', text: 'Not connected' }] }
287
295
  }
288
296
 
289
- const entry = treeMap.get(id)
290
- if (!entry) {
297
+ const raw = treeMap.get(id)
298
+ if (!raw) {
291
299
  server.setActiveToolCall(null)
292
300
  return { content: [{ type: 'text', text: `Document ${id} not found` }] }
293
301
  }
294
302
 
303
+ const entry = toPlain(raw)
295
304
  treeMap.set(id, {
296
305
  ...entry,
297
306
  parentId: normalizeRootId(newParentId, server),
@@ -326,8 +335,9 @@ export function registerTreeTools(mcp: McpServer, server: AbracadabraMCPServer)
326
335
  const now = Date.now()
327
336
  rootDoc.transact(() => {
328
337
  for (const nid of toDelete) {
329
- const entry = treeMap.get(nid)
330
- if (!entry) continue
338
+ const raw = treeMap.get(nid)
339
+ if (!raw) continue
340
+ const entry = toPlain(raw)
331
341
  trashMap.set(nid, {
332
342
  label: entry.label || 'Untitled',
333
343
  parentId: entry.parentId ?? null,
@@ -361,12 +371,13 @@ export function registerTreeTools(mcp: McpServer, server: AbracadabraMCPServer)
361
371
  return { content: [{ type: 'text', text: 'Not connected' }] }
362
372
  }
363
373
 
364
- const entry = treeMap.get(id)
365
- if (!entry) {
374
+ const raw = treeMap.get(id)
375
+ if (!raw) {
366
376
  server.setActiveToolCall(null)
367
377
  return { content: [{ type: 'text', text: `Document ${id} not found` }] }
368
378
  }
369
379
 
380
+ const entry = toPlain(raw)
370
381
  treeMap.set(id, { ...entry, type, updatedAt: Date.now() })
371
382
  server.setActiveToolCall(null)
372
383
  return { content: [{ type: 'text', text: `Changed type to "${type}"` }] }
@@ -410,9 +421,10 @@ export function registerTreeTools(mcp: McpServer, server: AbracadabraMCPServer)
410
421
  const treeMap = server.getTreeMap()
411
422
  if (!treeMap) return { content: [{ type: 'text', text: 'Not connected' }] }
412
423
 
413
- const entry = treeMap.get(id)
414
- if (!entry) return { content: [{ type: 'text', text: `Document ${id} not found` }] }
424
+ const raw = treeMap.get(id)
425
+ if (!raw) return { content: [{ type: 'text', text: `Document ${id} not found` }] }
415
426
 
427
+ const entry = toPlain(raw)
416
428
  const newId = crypto.randomUUID()
417
429
  treeMap.set(newId, {
418
430
  ...entry,