@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.
- package/.context/public/guides/ema-user-guide.md +7 -6
- package/.context/public/guides/mcp-tools-guide.md +46 -23
- package/dist/config/index.js +11 -0
- package/dist/config/workflow-patterns.js +361 -0
- package/dist/mcp/autobuilder.js +2 -2
- package/dist/mcp/domain/generation-schema.js +15 -9
- package/dist/mcp/domain/structural-rules.js +3 -3
- package/dist/mcp/domain/validation-rules.js +20 -27
- package/dist/mcp/domain/workflow-generator.js +3 -3
- package/dist/mcp/domain/workflow-graph.js +1 -1
- package/dist/mcp/guidance.js +60 -1
- package/dist/mcp/handlers/conversation/adapter.js +13 -0
- package/dist/mcp/handlers/conversation/create.js +19 -0
- package/dist/mcp/handlers/conversation/delete.js +18 -0
- package/dist/mcp/handlers/conversation/formatters.js +62 -0
- package/dist/mcp/handlers/conversation/history.js +15 -0
- package/dist/mcp/handlers/conversation/index.js +43 -0
- package/dist/mcp/handlers/conversation/list.js +40 -0
- package/dist/mcp/handlers/conversation/messages.js +13 -0
- package/dist/mcp/handlers/conversation/rename.js +16 -0
- package/dist/mcp/handlers/conversation/send.js +90 -0
- package/dist/mcp/handlers/data/index.js +169 -3
- package/dist/mcp/handlers/feedback/client-id.js +49 -0
- package/dist/mcp/handlers/feedback/coalesce.js +167 -0
- package/dist/mcp/handlers/feedback/index.js +42 -1
- package/dist/mcp/handlers/feedback/outbox.js +301 -0
- package/dist/mcp/handlers/feedback/probes.js +127 -0
- package/dist/mcp/handlers/feedback/remote-store.js +59 -0
- package/dist/mcp/handlers/feedback/store.js +13 -1
- package/dist/mcp/handlers/persona/delete.js +7 -28
- package/dist/mcp/handlers/persona/update.js +7 -26
- package/dist/mcp/handlers/persona/version.js +30 -15
- package/dist/mcp/handlers/template/adapter.js +23 -0
- package/dist/mcp/handlers/template/crud.js +174 -0
- package/dist/mcp/handlers/template/index.js +6 -7
- package/dist/mcp/handlers/workflow/adapter.js +30 -46
- package/dist/mcp/handlers/workflow/index.js +2 -2
- package/dist/mcp/handlers/workflow/validation.js +2 -2
- package/dist/mcp/knowledge-guidance-topics.js +90 -53
- package/dist/mcp/knowledge.js +7 -357
- package/dist/mcp/prompts.js +5 -5
- package/dist/mcp/resources-dynamic.js +46 -38
- package/dist/mcp/resources-validation.js +5 -5
- package/dist/mcp/server.js +38 -5
- package/dist/mcp/tools.js +340 -8
- package/dist/sdk/client-adapter.js +90 -2
- package/dist/sdk/client.js +7 -0
- package/dist/sdk/ema-client.js +242 -27
- package/dist/sdk/generated/agent-catalog.js +96 -39
- package/dist/sdk/generated/deprecated-actions.js +1 -1
- package/dist/sdk/grpc-client.js +67 -5
- package/dist/sync/central-factory.js +86 -0
- package/dist/sync/central-version-storage.js +387 -0
- package/dist/sync/dis-port.js +75 -0
- package/dist/sync/version-policy.js +29 -31
- package/dist/sync/version-storage-interface.js +11 -0
- package/dist/sync/version-storage.js +22 -22
- package/package.json +2 -1
package/dist/sdk/ema-client.js
CHANGED
|
@@ -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
|
|
628
|
-
*
|
|
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
|
-
|
|
632
|
-
|
|
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
|
/**
|