@khirby/plugin-pokelo 1.0.0 → 1.0.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khirby/plugin-pokelo",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Khirby — Pokelo RAG knowledge base context for AI Compose",
5
5
  "main": "src/index.ts",
6
6
  "keywords": [
@@ -21,4 +21,4 @@
21
21
  "access": "public",
22
22
  "registry": "https://registry.npmjs.org"
23
23
  }
24
- }
24
+ }
@@ -9,6 +9,7 @@ const SNIPPET_MAX_CHARS = 800;
9
9
  type McpToolResult = {
10
10
  result?: {
11
11
  content?: Array<{ type?: string; text?: string }>;
12
+ isError?: boolean;
12
13
  };
13
14
  error?: { message?: string };
14
15
  };
@@ -177,6 +178,11 @@ export class PokeloContextService implements PokeloContextServiceLike {
177
178
  if (envelope.error) {
178
179
  throw new Error(envelope.error.message ?? 'Pokelo MCP tool error');
179
180
  }
181
+ // Pokelo maps tool failures to CallToolResult.isError (HTTP 200), not JSON-RPC error.
182
+ if (envelope.result?.isError) {
183
+ const msg = envelope.result.content?.[0]?.text?.trim() || 'Pokelo MCP tool error';
184
+ throw new Error(msg);
185
+ }
180
186
 
181
187
  return envelope.result?.content?.[0]?.text ?? '';
182
188
  }
@@ -223,12 +229,19 @@ export function parseProjectList(text: string): Array<{ id: string; name: string
223
229
  if (!text.trim()) return [];
224
230
  try {
225
231
  const parsed = JSON.parse(text) as {
226
- items?: Array<{ id?: string; name?: string }>;
232
+ items?: Array<{ id?: string; projectId?: string; name?: string }>;
227
233
  };
228
234
  if (Array.isArray(parsed.items)) {
229
235
  return parsed.items
230
- .filter((p): p is { id: string; name: string } => !!p.id && !!p.name)
231
- .map((p) => ({ id: p.id, name: p.name }));
236
+ .map((p) => {
237
+ const id =
238
+ (typeof p.projectId === 'string' && p.projectId.trim()) ||
239
+ (typeof p.id === 'string' && p.id.trim()) ||
240
+ '';
241
+ const name = typeof p.name === 'string' ? p.name.trim() : '';
242
+ return { id, name };
243
+ })
244
+ .filter((p) => p.id && p.name);
232
245
  }
233
246
  } catch {
234
247
  // ignore
@@ -50,12 +50,28 @@ describe('parseSseJsonRpc / parseSearchMatches / parseProjectList', () => {
50
50
  expect(matches).toEqual(['Snippet A', 'Snippet B']);
51
51
  });
52
52
 
53
- it('parses project list JSON', () => {
53
+ it('parses project list JSON with current MCP projectId', () => {
54
+ const projects = parseProjectList(
55
+ JSON.stringify({
56
+ items: [{ projectId: 'p1', name: 'Bearly CRM', slug: 'bearly-crm-p1' }, { name: 'x' }],
57
+ }),
58
+ );
59
+ expect(projects).toEqual([{ id: 'p1', name: 'Bearly CRM' }]);
60
+ });
61
+
62
+ it('parses legacy project list JSON with id', () => {
54
63
  const projects = parseProjectList(
55
64
  JSON.stringify({ items: [{ id: 'p1', name: 'Bearly CRM' }, { id: 'x' }] }),
56
65
  );
57
66
  expect(projects).toEqual([{ id: 'p1', name: 'Bearly CRM' }]);
58
67
  });
68
+
69
+ it('prefers projectId over id when both are present', () => {
70
+ const projects = parseProjectList(
71
+ JSON.stringify({ items: [{ id: 'legacy', projectId: 'current', name: 'CRM' }] }),
72
+ );
73
+ expect(projects).toEqual([{ id: 'current', name: 'CRM' }]);
74
+ });
59
75
  });
60
76
 
61
77
  function makeSelectChain(returnValue: unknown[]) {
@@ -197,7 +213,7 @@ describe('PokeloContextService.fetchContext', () => {
197
213
  content: [
198
214
  {
199
215
  type: 'text',
200
- text: JSON.stringify({ items: [{ id: 'proj-uuid', name: 'Bearly CRM' }] }),
216
+ text: JSON.stringify({ items: [{ projectId: 'proj-uuid', name: 'Bearly CRM' }] }),
201
217
  },
202
218
  ],
203
219
  },
@@ -264,8 +280,8 @@ describe('PokeloContextService.fetchContext', () => {
264
280
  type: 'text',
265
281
  text: JSON.stringify({
266
282
  items: [
267
- { id: 'a', name: 'CRM' },
268
- { id: 'b', name: 'Finsly' },
283
+ { projectId: 'a', name: 'CRM' },
284
+ { projectId: 'b', name: 'Finsly' },
269
285
  ],
270
286
  }),
271
287
  },
@@ -319,4 +335,33 @@ describe('PokeloContextService.fetchContext', () => {
319
335
  const ctx = new PokeloContextService(settings);
320
336
  expect(await ctx.fetchContext('pricing')).toBe('');
321
337
  });
338
+
339
+ it('returns empty string on MCP tool isError instead of using the error as a snippet', async () => {
340
+ const settings = new PokeloSettingsService(
341
+ makeMockDb({
342
+ encryptedToken: encrypt('mcp_tok'),
343
+ projectIds: ['proj-uuid'],
344
+ projectId: 'proj-uuid',
345
+ baseUrl: 'https://rag.bearly.pro/v1',
346
+ }) as any,
347
+ makeMockRegistry(true) as any,
348
+ );
349
+
350
+ (global.fetch as jest.Mock).mockResolvedValue({
351
+ ok: true,
352
+ headers: { get: () => 'application/json' },
353
+ text: async () =>
354
+ JSON.stringify({
355
+ jsonrpc: '2.0',
356
+ id: 1,
357
+ result: {
358
+ isError: true,
359
+ content: [{ type: 'text', text: 'Project not found' }],
360
+ },
361
+ }),
362
+ });
363
+
364
+ const ctx = new PokeloContextService(settings);
365
+ expect(await ctx.fetchContext('pricing')).toBe('');
366
+ });
322
367
  });
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Khirby Labs
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.