@ema.co/mcp-toolkit 2026.2.27 → 2026.2.28

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.

Potentially problematic release.


This version of @ema.co/mcp-toolkit might be problematic. Click here for more details.

Files changed (58) hide show
  1. package/.context/public/guides/ema-user-guide.md +7 -6
  2. package/.context/public/guides/mcp-tools-guide.md +46 -23
  3. package/dist/config/index.js +11 -0
  4. package/dist/config/workflow-patterns.js +361 -0
  5. package/dist/mcp/autobuilder.js +2 -2
  6. package/dist/mcp/domain/generation-schema.js +15 -9
  7. package/dist/mcp/domain/structural-rules.js +3 -3
  8. package/dist/mcp/domain/validation-rules.js +20 -27
  9. package/dist/mcp/domain/workflow-generator.js +3 -3
  10. package/dist/mcp/domain/workflow-graph.js +1 -1
  11. package/dist/mcp/guidance.js +60 -1
  12. package/dist/mcp/handlers/conversation/adapter.js +13 -0
  13. package/dist/mcp/handlers/conversation/create.js +19 -0
  14. package/dist/mcp/handlers/conversation/delete.js +18 -0
  15. package/dist/mcp/handlers/conversation/formatters.js +62 -0
  16. package/dist/mcp/handlers/conversation/history.js +15 -0
  17. package/dist/mcp/handlers/conversation/index.js +43 -0
  18. package/dist/mcp/handlers/conversation/list.js +40 -0
  19. package/dist/mcp/handlers/conversation/messages.js +13 -0
  20. package/dist/mcp/handlers/conversation/rename.js +16 -0
  21. package/dist/mcp/handlers/conversation/send.js +90 -0
  22. package/dist/mcp/handlers/data/index.js +169 -3
  23. package/dist/mcp/handlers/feedback/client-id.js +49 -0
  24. package/dist/mcp/handlers/feedback/coalesce.js +167 -0
  25. package/dist/mcp/handlers/feedback/index.js +42 -1
  26. package/dist/mcp/handlers/feedback/outbox.js +301 -0
  27. package/dist/mcp/handlers/feedback/probes.js +127 -0
  28. package/dist/mcp/handlers/feedback/remote-store.js +59 -0
  29. package/dist/mcp/handlers/feedback/store.js +13 -1
  30. package/dist/mcp/handlers/persona/delete.js +7 -28
  31. package/dist/mcp/handlers/persona/update.js +7 -26
  32. package/dist/mcp/handlers/persona/version.js +30 -15
  33. package/dist/mcp/handlers/template/adapter.js +23 -0
  34. package/dist/mcp/handlers/template/crud.js +174 -0
  35. package/dist/mcp/handlers/template/index.js +6 -7
  36. package/dist/mcp/handlers/workflow/adapter.js +30 -46
  37. package/dist/mcp/handlers/workflow/index.js +2 -2
  38. package/dist/mcp/handlers/workflow/validation.js +2 -2
  39. package/dist/mcp/knowledge-guidance-topics.js +90 -53
  40. package/dist/mcp/knowledge.js +7 -357
  41. package/dist/mcp/prompts.js +5 -5
  42. package/dist/mcp/resources-dynamic.js +46 -38
  43. package/dist/mcp/resources-validation.js +5 -5
  44. package/dist/mcp/server.js +38 -5
  45. package/dist/mcp/tools.js +340 -8
  46. package/dist/sdk/client-adapter.js +90 -2
  47. package/dist/sdk/client.js +7 -0
  48. package/dist/sdk/ema-client.js +242 -27
  49. package/dist/sdk/generated/agent-catalog.js +96 -39
  50. package/dist/sdk/generated/deprecated-actions.js +1 -1
  51. package/dist/sdk/grpc-client.js +67 -5
  52. package/dist/sync/central-factory.js +86 -0
  53. package/dist/sync/central-version-storage.js +387 -0
  54. package/dist/sync/dis-port.js +75 -0
  55. package/dist/sync/version-policy.js +29 -31
  56. package/dist/sync/version-storage-interface.js +11 -0
  57. package/dist/sync/version-storage.js +22 -22
  58. package/package.json +2 -1
@@ -304,6 +304,56 @@ export class EmaClientV2 {
304
304
  async getPersonaTemplates() {
305
305
  return this.listPersonaTemplates();
306
306
  }
307
+ /**
308
+ * Get a single persona template by ID
309
+ *
310
+ * Returns null for non-existent or invalid template IDs.
311
+ */
312
+ async getPersonaTemplateById(templateId) {
313
+ try {
314
+ const result = await api.getPersonaTemplateById({
315
+ client: this.restClient,
316
+ path: { template_id: templateId },
317
+ });
318
+ return result.data ?? null;
319
+ }
320
+ catch (e) {
321
+ if (e instanceof EmaApiError && (e.statusCode === 404 || e.statusCode === 422)) {
322
+ return null;
323
+ }
324
+ throw e;
325
+ }
326
+ }
327
+ /**
328
+ * Create a new persona template
329
+ */
330
+ async createPersonaTemplate(data) {
331
+ const result = await api.createPersonaTemplate({
332
+ client: this.restClient,
333
+ body: data,
334
+ });
335
+ return result.data;
336
+ }
337
+ /**
338
+ * Update an existing persona template
339
+ */
340
+ async updatePersonaTemplate(data) {
341
+ const result = await api.updatePersonaTemplate({
342
+ client: this.restClient,
343
+ body: data,
344
+ });
345
+ return result.data;
346
+ }
347
+ /**
348
+ * Delete a persona template by ID
349
+ */
350
+ async deletePersonaTemplate(templateId) {
351
+ const result = await api.deletePersonaTemplate({
352
+ client: this.restClient,
353
+ path: { template_id: templateId },
354
+ });
355
+ return result.data;
356
+ }
307
357
  // ─────────────────────────────────────────────────────────────────────────────
308
358
  // Actions - gRPC API
309
359
  // ─────────────────────────────────────────────────────────────────────────────
@@ -356,6 +406,50 @@ export class EmaClientV2 {
356
406
  async getDataSourceAggregates(personaId, widgetName) {
357
407
  return this.grpcClient.getContentNodeAggregates(personaId, widgetName);
358
408
  }
409
+ /**
410
+ * Download a file's content by its UploadedFile.Id.
411
+ * Uses GetSignedUrl gRPC → HTTP fetch on the signed URL.
412
+ *
413
+ * @param fileId - The UploadedFile.Id (from ContentNodeResponse.id)
414
+ * @returns Raw file content as Buffer
415
+ * @throws If file is deleted or signed URL fetch fails
416
+ */
417
+ async downloadFile(fileId, personaId) {
418
+ const signedUrlResponse = await this.grpcClient.getSignedUrl(fileId, personaId);
419
+ if (signedUrlResponse.isDeleted) {
420
+ throw new EmaApiError({
421
+ statusCode: 410,
422
+ body: `File ${fileId} has been deleted`,
423
+ message: `downloadFile: file is deleted (${this.env.name})`,
424
+ });
425
+ }
426
+ if (!signedUrlResponse.signedUrl) {
427
+ throw new EmaApiError({
428
+ statusCode: 404,
429
+ body: `No signed URL returned for file ${fileId}`,
430
+ message: `downloadFile: no signed URL (${this.env.name})`,
431
+ });
432
+ }
433
+ const controller = new AbortController();
434
+ const timeoutId = setTimeout(() => controller.abort(), 30_000);
435
+ try {
436
+ const resp = await fetch(signedUrlResponse.signedUrl, {
437
+ signal: controller.signal,
438
+ });
439
+ if (!resp.ok) {
440
+ throw new EmaApiError({
441
+ statusCode: resp.status,
442
+ body: await resp.text(),
443
+ message: `downloadFile: fetch failed (${this.env.name})`,
444
+ });
445
+ }
446
+ const arrayBuffer = await resp.arrayBuffer();
447
+ return Buffer.from(arrayBuffer);
448
+ }
449
+ finally {
450
+ clearTimeout(timeoutId);
451
+ }
452
+ }
359
453
  /**
360
454
  * Replicate data between personas
361
455
  */
@@ -442,6 +536,52 @@ export class EmaClientV2 {
442
536
  });
443
537
  return resp;
444
538
  }
539
+ /**
540
+ * Continue a paused (HITL) workflow for a dashboard row.
541
+ * Builds the ContinuationMessage proto JSON from convenience params.
542
+ *
543
+ * @param rowId - The dashboard row ID
544
+ * @param continuation - One of text, buttons, or form
545
+ */
546
+ async continueDashboardRow(rowId, continuation) {
547
+ // Raw pass-through: used for form HITL where the full formMessage must be echoed back
548
+ if (continuation.raw) {
549
+ await this.grpcClient.continueWorkflowForRow(rowId, continuation.raw);
550
+ return;
551
+ }
552
+ // Build ContinuationMessage JSON matching the proto oneof structure
553
+ let continuationMessage;
554
+ if (continuation.text) {
555
+ continuationMessage = {
556
+ textMessage: { contents: [continuation.text] },
557
+ };
558
+ }
559
+ else if (continuation.buttons) {
560
+ // Accept both chat-style {selected_button:{label:...}} and flat {label:...}
561
+ const btn = continuation.buttons;
562
+ const selectedButton = btn.selected_button ?? btn.selectedButton ?? btn;
563
+ continuationMessage = {
564
+ buttonsMessage: {
565
+ selectedButton,
566
+ },
567
+ };
568
+ }
569
+ else if (continuation.form) {
570
+ // Accept chat-style {fields:[...], is_cancelled:bool} and pass through
571
+ const frm = continuation.form;
572
+ continuationMessage = {
573
+ formMessage: {
574
+ fields: frm.fields,
575
+ isCancelled: frm.is_cancelled ?? frm.isCancelled ?? false,
576
+ ...(frm.content !== undefined && { content: frm.content }),
577
+ },
578
+ };
579
+ }
580
+ else {
581
+ throw new Error("continueDashboardRow requires one of: text, buttons, form, or raw");
582
+ }
583
+ await this.grpcClient.continueWorkflowForRow(rowId, continuationMessage);
584
+ }
445
585
  // ─────────────────────────────────────────────────────────────────────────────
446
586
  // Document Generation - REST API (OpenAPI generated)
447
587
  // ─────────────────────────────────────────────────────────────────────────────
@@ -624,34 +764,16 @@ export class EmaClientV2 {
624
764
  }
625
765
  }
626
766
  /**
627
- * Delete a data source file from an AI Employee's knowledge base.
628
- * API endpoint: DELETE /api/v2/upload/files/{file_id}
767
+ * Delete data source file(s) from an AI Employee's knowledge base.
768
+ * Uses gRPC DeleteFilesForGroup — the ONLY deletion method DIS exposes.
769
+ *
770
+ * @param personaId - The persona owning the files
771
+ * @param fileId - Single file ID to delete
772
+ * @param opts - Optional widget name scope
629
773
  */
630
- async deleteDataSource(personaId, fileId) {
631
- const controller = new AbortController();
632
- const timeoutId = setTimeout(() => controller.abort(), 30_000);
633
- try {
634
- const resp = await fetch(`${this.env.baseUrl.replace(/\/$/, "")}/api/v2/upload/files/${fileId}?persona_id=${personaId}`, {
635
- method: "DELETE",
636
- headers: {
637
- Authorization: `Bearer ${this.env.bearerToken}`,
638
- "X-Persona-Id": personaId,
639
- },
640
- signal: controller.signal,
641
- });
642
- if (!resp.ok && resp.status !== 404) {
643
- const errorBody = await resp.text();
644
- throw new EmaApiError({
645
- statusCode: resp.status,
646
- body: errorBody,
647
- message: `deleteDataSource failed (${this.env.name})`,
648
- });
649
- }
650
- return { success: true, fileId };
651
- }
652
- finally {
653
- clearTimeout(timeoutId);
654
- }
774
+ async deleteDataSource(personaId, fileId, opts) {
775
+ await this.grpcClient.deleteFilesForGroup(personaId, [fileId], opts?.widgetName);
776
+ return { success: true, fileId };
655
777
  }
656
778
  /**
657
779
  * Delete a persona
@@ -705,6 +827,99 @@ export class EmaClientV2 {
705
827
  return { status: "timeout" };
706
828
  }
707
829
  // ─────────────────────────────────────────────────────────────────────────────
830
+ // Chat / Conversation API
831
+ // ─────────────────────────────────────────────────────────────────────────────
832
+ /**
833
+ * Create a new chat conversation for a persona.
834
+ * Uses the legacy /api/chat/ route (no tenant_id needed -- JWT identifies tenant).
835
+ */
836
+ async createConversation(personaId, opts) {
837
+ const result = await this.fetchWithAuth("POST", "/api/chat/", {
838
+ persona_id: personaId,
839
+ ...(opts?.userContext && { user_context: opts.userContext }),
840
+ ...(opts?.caller && { caller: opts.caller }),
841
+ });
842
+ return {
843
+ conversationId: result.conversation_id,
844
+ message: result.message,
845
+ };
846
+ }
847
+ /**
848
+ * Send a message in a chat conversation.
849
+ * The message should be a ChatbotMessage proto dict.
850
+ */
851
+ async sendMessage(conversationId, message, opts) {
852
+ const result = await this.fetchWithAuth("POST", `/api/chat/${conversationId}`, {
853
+ message,
854
+ ...(opts?.userContext && { user_context: opts.userContext }),
855
+ ...(opts?.originalMessageId && { original_message_id: opts.originalMessageId }),
856
+ });
857
+ return {
858
+ conversationId: result.conversation_id,
859
+ message: result.message,
860
+ snippets: result.snippets ?? [],
861
+ };
862
+ }
863
+ /**
864
+ * Get conversation message history.
865
+ */
866
+ async getConversationHistory(conversationId) {
867
+ const result = await this.fetchWithAuth("GET", `/api/chat/${conversationId}`);
868
+ return { messages: result.messages ?? [] };
869
+ }
870
+ /**
871
+ * List all conversations for the current user.
872
+ */
873
+ async listConversations() {
874
+ const result = await api.listConversations({ client: this.restClient });
875
+ return (result.data ?? []);
876
+ }
877
+ /**
878
+ * List conversations for a specific persona (paginated).
879
+ */
880
+ async listConversationsForPersona(personaId, opts) {
881
+ const result = await api.listConversationsForPersona({
882
+ client: this.restClient,
883
+ path: { persona_id: personaId },
884
+ query: {
885
+ num_conversations: opts?.limit,
886
+ pagination_token: opts?.paginationToken,
887
+ },
888
+ });
889
+ return (result.data ?? {});
890
+ }
891
+ /**
892
+ * Get all messages in a conversation.
893
+ */
894
+ async getConversationMessages(conversationId) {
895
+ const result = await api.getConversationMessages({
896
+ client: this.restClient,
897
+ path: { conversation_id: conversationId },
898
+ });
899
+ return (result.data ?? []);
900
+ }
901
+ /**
902
+ * Delete a conversation by ID.
903
+ */
904
+ async deleteConversation(conversationId) {
905
+ const result = await api.deleteConversation({
906
+ client: this.restClient,
907
+ path: { conversation_id: conversationId },
908
+ });
909
+ return (result.data ?? {});
910
+ }
911
+ /**
912
+ * Update the display name for a conversation.
913
+ */
914
+ async updateConversationDisplayName(conversationId, displayName) {
915
+ const result = await api.updateConversationDisplayName({
916
+ client: this.restClient,
917
+ path: { conversation_id: conversationId },
918
+ query: { display_name: displayName },
919
+ });
920
+ return (result.data ?? {});
921
+ }
922
+ // ─────────────────────────────────────────────────────────────────────────────
708
923
  // Private Helpers
709
924
  // ─────────────────────────────────────────────────────────────────────────────
710
925
  /**