@speakai/mcp-server 1.12.2 → 1.13.0

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.
Files changed (3) hide show
  1. package/README.md +46 -7
  2. package/dist/index.js +1223 -179
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2390,6 +2390,27 @@ var recorder_exports = {};
2390
2390
  __export(recorder_exports, {
2391
2391
  register: () => register5
2392
2392
  });
2393
+ function optionsFromMetaType(metaType) {
2394
+ const bool = (v, fallback) => typeof v === "boolean" ? v : fallback;
2395
+ const upload = metaType?.upload ?? {};
2396
+ return {
2397
+ audio: bool(metaType?.audio, true),
2398
+ video: bool(metaType?.video, true),
2399
+ screenShare: bool(metaType?.screenShare, false),
2400
+ upload: {
2401
+ file: bool(upload.file, true),
2402
+ multiple: bool(upload.multiple, false),
2403
+ url: bool(upload.url, false)
2404
+ },
2405
+ liveTranscription: bool(metaType?.liveTranscription, false)
2406
+ };
2407
+ }
2408
+ function addRecorderOptions(recorder) {
2409
+ if (recorder && typeof recorder === "object") {
2410
+ recorder.options = optionsFromMetaType(recorder.meta?.type);
2411
+ }
2412
+ return recorder;
2413
+ }
2393
2414
  function register5(server, client) {
2394
2415
  const api = client ?? speakClient;
2395
2416
  registerSpeakTool(
@@ -2441,6 +2462,7 @@ function register5(server, client) {
2441
2462
  async (body) => {
2442
2463
  try {
2443
2464
  const result = await api.post("/v1/recorder/create", body);
2465
+ addRecorderOptions(result.data?.data?.recorderData);
2444
2466
  return {
2445
2467
  content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
2446
2468
  };
@@ -2471,6 +2493,7 @@ function register5(server, client) {
2471
2493
  async (params) => {
2472
2494
  try {
2473
2495
  const result = await api.get("/v1/recorder", { params });
2496
+ result.data?.data?.recorderList?.forEach?.(addRecorderOptions);
2474
2497
  return {
2475
2498
  content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
2476
2499
  };
@@ -2530,6 +2553,7 @@ function register5(server, client) {
2530
2553
  async ({ recorderId }) => {
2531
2554
  try {
2532
2555
  const result = await api.get(`/v1/recorder/${recorderId}`);
2556
+ addRecorderOptions(result.data?.data);
2533
2557
  return {
2534
2558
  content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
2535
2559
  };
@@ -2616,6 +2640,7 @@ function register5(server, client) {
2616
2640
  async ({ recorderId, ...body }) => {
2617
2641
  try {
2618
2642
  const result = await api.put(`/v1/recorder/settings/${recorderId}`, body);
2643
+ addRecorderOptions(result.data?.data?.recorderData);
2619
2644
  return {
2620
2645
  content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
2621
2646
  };
@@ -3641,8 +3666,16 @@ function register10(server, client) {
3641
3666
  registerSpeakTool(
3642
3667
  server,
3643
3668
  "list_automations",
3644
- "List all automation rules configured in the workspace.",
3645
- {},
3669
+ "List automation rules in the workspace, with paging and filters.",
3670
+ {
3671
+ page: import_zod11.z.number().int().min(0).optional().describe("0-based page index"),
3672
+ pageSize: import_zod11.z.number().int().min(1).max(100).optional().describe("Results per page"),
3673
+ sortBy: import_zod11.z.string().optional().describe('Sort expression, e.g. "createdAt:desc"'),
3674
+ query: import_zod11.z.string().optional().describe("Free-text search over automation names"),
3675
+ folderIds: import_zod11.z.string().optional().describe("Comma-separated folder ids to filter by"),
3676
+ isActive: import_zod11.z.boolean().optional().describe("Filter by active state"),
3677
+ runType: import_zod11.z.enum(["instant", "schedule"]).optional().describe("Filter by run type")
3678
+ },
3646
3679
  {
3647
3680
  title: "List Automations",
3648
3681
  readOnlyHint: true,
@@ -3650,9 +3683,35 @@ function register10(server, client) {
3650
3683
  idempotentHint: true,
3651
3684
  openWorldHint: false
3652
3685
  },
3686
+ async (params) => {
3687
+ try {
3688
+ const result = await api.get("/v1/automations", { params });
3689
+ return {
3690
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
3691
+ };
3692
+ } catch (err) {
3693
+ return {
3694
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
3695
+ isError: true
3696
+ };
3697
+ }
3698
+ }
3699
+ );
3700
+ registerSpeakTool(
3701
+ server,
3702
+ "list_automation_names",
3703
+ "List automations as lightweight { name, id } pairs \u2014 useful for pickers without fetching full configs.",
3704
+ {},
3705
+ {
3706
+ title: "List Automation Names",
3707
+ readOnlyHint: true,
3708
+ destructiveHint: false,
3709
+ idempotentHint: true,
3710
+ openWorldHint: false
3711
+ },
3653
3712
  async () => {
3654
3713
  try {
3655
- const result = await api.get("/v1/automations");
3714
+ const result = await api.get("/v1/automations/list");
3656
3715
  return {
3657
3716
  content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
3658
3717
  };
@@ -3667,7 +3726,7 @@ function register10(server, client) {
3667
3726
  registerSpeakTool(
3668
3727
  server,
3669
3728
  "get_automation",
3670
- "Get detailed information about a specific automation rule.",
3729
+ "Get detailed information about a specific automation rule, including its trigger and step graph.",
3671
3730
  {
3672
3731
  automationId: import_zod11.z.string().min(1).describe("Unique identifier of the automation")
3673
3732
  },
@@ -3694,21 +3753,40 @@ function register10(server, client) {
3694
3753
  );
3695
3754
  registerSpeakTool(
3696
3755
  server,
3697
- "create_automation",
3698
- "Create a new automation rule for automatic media processing workflows.",
3756
+ "get_automation_runs",
3757
+ "Get the run history (executions) for an automation, with paging and optional status filter.",
3699
3758
  {
3700
- name: import_zod11.z.string().min(1).describe("Display name for the automation"),
3701
- trigger: import_zod11.z.record(import_zod11.z.unknown()).describe(
3702
- 'Trigger object. Keys: `type` (e.g. "folders") and `folderIds` (array of folder IDs).'
3703
- ),
3704
- action: import_zod11.z.record(import_zod11.z.unknown()).describe(
3705
- 'Single action object (not an array). Keys: `type` ("magic-prompt" or "translation"). For magic-prompt, include a `magicPrompt` object ({ prompt, title?, assistantType?, fieldIds?, ... }). For translation, include a `translation` object ({ targetLanguage }).'
3706
- ),
3707
- description: import_zod11.z.string().optional().describe("Optional description"),
3708
- isActive: import_zod11.z.boolean().optional().describe("Whether the automation is active (defaults to true)"),
3709
- runType: import_zod11.z.string().optional().describe('Run type, e.g. "instant" (default) or "scheduled"'),
3710
- schedule: import_zod11.z.record(import_zod11.z.unknown()).optional().describe("Schedule object for scheduled automations: { timePeriod, repeatAt }")
3759
+ automationId: import_zod11.z.string().min(1).describe("Unique identifier of the automation"),
3760
+ page: import_zod11.z.number().int().min(0).optional().describe("0-based page index"),
3761
+ pageSize: import_zod11.z.number().int().min(1).max(100).optional().describe("Results per page"),
3762
+ status: import_zod11.z.enum(["pending", "running", "completed", "failed", "killed"]).optional().describe("Filter runs by status")
3763
+ },
3764
+ {
3765
+ title: "Get Automation Runs",
3766
+ readOnlyHint: true,
3767
+ destructiveHint: false,
3768
+ idempotentHint: true,
3769
+ openWorldHint: false
3711
3770
  },
3771
+ async ({ automationId, ...params }) => {
3772
+ try {
3773
+ const result = await api.get(`/v1/automations/${automationId}/runs`, { params });
3774
+ return {
3775
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
3776
+ };
3777
+ } catch (err) {
3778
+ return {
3779
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
3780
+ isError: true
3781
+ };
3782
+ }
3783
+ }
3784
+ );
3785
+ registerSpeakTool(
3786
+ server,
3787
+ "create_automation",
3788
+ "Create a new automation rule using the V2 graph model (trigger + ordered steps). Fetch valid step/trigger options with list_automation_triggers / list_automation_actions if unsure.",
3789
+ writeSchema,
3712
3790
  {
3713
3791
  title: "Create Automation",
3714
3792
  readOnlyHint: false,
@@ -3733,20 +3811,10 @@ function register10(server, client) {
3733
3811
  registerSpeakTool(
3734
3812
  server,
3735
3813
  "update_automation",
3736
- "Update an existing automation rule. This replaces the whole automation, so fetch the current values with get_automation first and pass them all back.",
3814
+ "Update an existing automation rule. This replaces the whole automation (name, trigger, and steps), so fetch the current values with get_automation first and pass them all back with your changes.",
3737
3815
  {
3738
3816
  automationId: import_zod11.z.string().min(1).describe("Unique identifier of the automation"),
3739
- name: import_zod11.z.string().min(1).describe("Display name for the automation"),
3740
- trigger: import_zod11.z.record(import_zod11.z.unknown()).describe(
3741
- 'Trigger object. Keys: `type` (e.g. "folders") and `folderIds` (array of folder IDs).'
3742
- ),
3743
- action: import_zod11.z.record(import_zod11.z.unknown()).describe(
3744
- 'Single action object (not an array). Keys: `type` ("magic-prompt" or "translation"). For magic-prompt, include a `magicPrompt` object ({ prompt, title?, assistantType?, fieldIds?, ... }). For translation, include a `translation` object ({ targetLanguage }).'
3745
- ),
3746
- description: import_zod11.z.string().optional().describe("Optional description"),
3747
- isActive: import_zod11.z.boolean().optional().describe("Whether the automation is active (defaults to true)"),
3748
- runType: import_zod11.z.string().optional().describe('Run type, e.g. "instant" (default) or "scheduled"'),
3749
- schedule: import_zod11.z.record(import_zod11.z.unknown()).optional().describe("Schedule object for scheduled automations: { timePeriod, repeatAt }")
3817
+ ...writeSchema
3750
3818
  },
3751
3819
  {
3752
3820
  title: "Update Automation",
@@ -3757,10 +3825,7 @@ function register10(server, client) {
3757
3825
  },
3758
3826
  async ({ automationId, ...body }) => {
3759
3827
  try {
3760
- const result = await api.put(
3761
- `/v1/automations/${automationId}`,
3762
- body
3763
- );
3828
+ const result = await api.put(`/v1/automations/${automationId}`, body);
3764
3829
  return {
3765
3830
  content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
3766
3831
  };
@@ -3788,9 +3853,204 @@ function register10(server, client) {
3788
3853
  },
3789
3854
  async ({ automationId }) => {
3790
3855
  try {
3791
- const result = await api.put(
3792
- `/v1/automations/status/${automationId}`
3793
- );
3856
+ const result = await api.put(`/v1/automations/status/${automationId}`);
3857
+ return {
3858
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
3859
+ };
3860
+ } catch (err) {
3861
+ return {
3862
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
3863
+ isError: true
3864
+ };
3865
+ }
3866
+ }
3867
+ );
3868
+ registerSpeakTool(
3869
+ server,
3870
+ "bulk_update_automation_status",
3871
+ "Activate or deactivate multiple automations at once.",
3872
+ {
3873
+ automationIds: import_zod11.z.array(import_zod11.z.string().min(1)).min(1).max(100).describe("Automation ids to update"),
3874
+ isActive: import_zod11.z.boolean().describe("true to activate, false to deactivate, for all listed automations")
3875
+ },
3876
+ {
3877
+ title: "Bulk Update Automation Status",
3878
+ readOnlyHint: false,
3879
+ destructiveHint: false,
3880
+ idempotentHint: true,
3881
+ openWorldHint: false
3882
+ },
3883
+ async (body) => {
3884
+ try {
3885
+ const result = await api.put("/v1/automations/bulk/status", body);
3886
+ return {
3887
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
3888
+ };
3889
+ } catch (err) {
3890
+ return {
3891
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
3892
+ isError: true
3893
+ };
3894
+ }
3895
+ }
3896
+ );
3897
+ registerSpeakTool(
3898
+ server,
3899
+ "bulk_assign_automation_folders",
3900
+ "Set the folder scope for multiple automations at once. Pass an empty folderIds array to remove the folder restriction (run on all folders).",
3901
+ {
3902
+ automationIds: import_zod11.z.array(import_zod11.z.string().min(1)).min(1).max(100).describe("Automation ids to update"),
3903
+ folderIds: import_zod11.z.array(import_zod11.z.string().min(1)).max(50).describe("Folder ids to scope the automations to. Empty array = all folders.")
3904
+ },
3905
+ {
3906
+ title: "Bulk Assign Automation Folders",
3907
+ readOnlyHint: false,
3908
+ destructiveHint: false,
3909
+ idempotentHint: true,
3910
+ openWorldHint: false
3911
+ },
3912
+ async (body) => {
3913
+ try {
3914
+ const result = await api.put("/v1/automations/bulk/folders", body);
3915
+ return {
3916
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
3917
+ };
3918
+ } catch (err) {
3919
+ return {
3920
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
3921
+ isError: true
3922
+ };
3923
+ }
3924
+ }
3925
+ );
3926
+ registerSpeakTool(
3927
+ server,
3928
+ "run_automations",
3929
+ "Manually run one or more automations against one or more media items now (outside the normal trigger).",
3930
+ {
3931
+ mediaIds: import_zod11.z.array(import_zod11.z.string().min(1)).min(1).describe("Media ids to run the automations against"),
3932
+ automationIds: import_zod11.z.array(import_zod11.z.string().min(1)).min(1).describe("Automation ids to run")
3933
+ },
3934
+ {
3935
+ title: "Run Automations",
3936
+ readOnlyHint: false,
3937
+ destructiveHint: false,
3938
+ idempotentHint: false,
3939
+ openWorldHint: false
3940
+ },
3941
+ async (body) => {
3942
+ try {
3943
+ const result = await api.post("/v1/automations/run", body);
3944
+ return {
3945
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
3946
+ };
3947
+ } catch (err) {
3948
+ return {
3949
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
3950
+ isError: true
3951
+ };
3952
+ }
3953
+ }
3954
+ );
3955
+ registerSpeakTool(
3956
+ server,
3957
+ "delete_automation",
3958
+ "Permanently delete an automation rule.",
3959
+ {
3960
+ automationId: import_zod11.z.string().min(1).describe("Unique identifier of the automation to delete")
3961
+ },
3962
+ {
3963
+ title: "Delete Automation",
3964
+ readOnlyHint: false,
3965
+ destructiveHint: true,
3966
+ idempotentHint: true,
3967
+ openWorldHint: false
3968
+ },
3969
+ async ({ automationId }) => {
3970
+ try {
3971
+ const result = await api.delete(`/v1/automations/${automationId}`);
3972
+ return {
3973
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
3974
+ };
3975
+ } catch (err) {
3976
+ return {
3977
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
3978
+ isError: true
3979
+ };
3980
+ }
3981
+ }
3982
+ );
3983
+ registerSpeakTool(
3984
+ server,
3985
+ "list_automation_apps",
3986
+ "List the apps available in the automation catalog (e.g. Speak native + connected integrations). Use to discover what triggers/actions exist before building an automation.",
3987
+ {},
3988
+ {
3989
+ title: "List Automation Apps",
3990
+ readOnlyHint: true,
3991
+ destructiveHint: false,
3992
+ idempotentHint: true,
3993
+ openWorldHint: false
3994
+ },
3995
+ async () => {
3996
+ try {
3997
+ const result = await api.get("/v1/automations/catalog/apps");
3998
+ return {
3999
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
4000
+ };
4001
+ } catch (err) {
4002
+ return {
4003
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
4004
+ isError: true
4005
+ };
4006
+ }
4007
+ }
4008
+ );
4009
+ registerSpeakTool(
4010
+ server,
4011
+ "list_automation_triggers",
4012
+ "List the trigger types available in the automation catalog. Optionally filter by app.",
4013
+ {
4014
+ app: import_zod11.z.string().min(1).max(100).optional().describe("Filter triggers to a specific app slug")
4015
+ },
4016
+ {
4017
+ title: "List Automation Triggers",
4018
+ readOnlyHint: true,
4019
+ destructiveHint: false,
4020
+ idempotentHint: true,
4021
+ openWorldHint: false
4022
+ },
4023
+ async (params) => {
4024
+ try {
4025
+ const result = await api.get("/v1/automations/catalog/triggers", { params });
4026
+ return {
4027
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
4028
+ };
4029
+ } catch (err) {
4030
+ return {
4031
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
4032
+ isError: true
4033
+ };
4034
+ }
4035
+ }
4036
+ );
4037
+ registerSpeakTool(
4038
+ server,
4039
+ "list_automation_actions",
4040
+ "List the action/step types available in the automation catalog. Optionally filter by app.",
4041
+ {
4042
+ app: import_zod11.z.string().min(1).max(100).optional().describe("Filter actions to a specific app slug")
4043
+ },
4044
+ {
4045
+ title: "List Automation Actions",
4046
+ readOnlyHint: true,
4047
+ destructiveHint: false,
4048
+ idempotentHint: true,
4049
+ openWorldHint: false
4050
+ },
4051
+ async (params) => {
4052
+ try {
4053
+ const result = await api.get("/v1/automations/catalog/actions", { params });
3794
4054
  return {
3795
4055
  content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
3796
4056
  };
@@ -3803,13 +4063,26 @@ function register10(server, client) {
3803
4063
  }
3804
4064
  );
3805
4065
  }
3806
- var import_zod11;
4066
+ var import_zod11, STEPS_DESCRIPTION, TRIGGER_DESCRIPTION, writeSchema;
3807
4067
  var init_automations = __esm({
3808
4068
  "src/tools/automations.ts"() {
3809
4069
  "use strict";
3810
4070
  import_zod11 = require("zod");
3811
4071
  init_helpers();
3812
4072
  init_client();
4073
+ STEPS_DESCRIPTION = 'Ordered array of graph steps (1-20). Each step is an object: { stepId: string (unique within the array), stepType: one of "magic-prompt" | "translation" | "filter" | "composio-action" | "speak-upload", dependsOn?: string[] (stepIds this step runs after) } plus ONE payload key matching stepType:\n- magic-prompt -> magicPrompt: { prompt (required unless fieldIds given), title?, assistantType? ("general"|"researcher"|"marketer"|"sales"|"recruiter"|"custom", default "general"), assistantTemplateId? (required if assistantType="custom"), fieldIds?: string[] (max 10, for field extraction) }\n- translation -> translation: { targetLanguage: BCP-47 code, e.g. "es" }\n- filter -> filter: { logic: "AND"|"OR", rules: [{ field, op: "eq"|"neq"|"contains"|"ncontains"|"startsWith"|"gt"|"lt"|"exists", value? }] }\n- composio-action -> composio: { app, action, connectedAccountId?, argsTemplate? } (Composio is currently behind a server flag and may be unavailable)\n- speak-upload -> speakUpload: { folderId, sourceMode: "url"|"file", sourceUrl? (required when sourceMode="url"), name?, fieldsMap?, language? }';
4074
+ TRIGGER_DESCRIPTION = 'Trigger object. For folder-based automations: { type: "folders", folderIds: string[] } (at least one folder is required). Note: only "folders" is currently supported for multi-step automations via the API \u2014 "tags"/"keywords" are rejected, and "composio"/"webhook" triggers (provider, app, triggerSlug, webhookId, childKey, connectedAccountId) are gated by server flags.';
4075
+ writeSchema = {
4076
+ name: import_zod11.z.string().min(1).max(150).describe("Display name for the automation"),
4077
+ trigger: import_zod11.z.record(import_zod11.z.unknown()).describe(TRIGGER_DESCRIPTION),
4078
+ steps: import_zod11.z.array(import_zod11.z.record(import_zod11.z.unknown())).min(1).max(20).describe(STEPS_DESCRIPTION),
4079
+ description: import_zod11.z.string().max(1e3).optional().describe("Optional description"),
4080
+ isActive: import_zod11.z.boolean().optional().describe("Whether the automation is active (defaults to true)"),
4081
+ runType: import_zod11.z.enum(["instant", "schedule"]).optional().describe('Run type: "instant" (default, runs on trigger) or "schedule" (cron)'),
4082
+ schedule: import_zod11.z.record(import_zod11.z.unknown()).optional().describe(
4083
+ 'Required when runType="schedule": { timePeriod: "today"|"yesterday"|"last7days"|"last14days"|"thisWeek", repeatAt: string }'
4084
+ )
4085
+ };
3813
4086
  }
3814
4087
  });
3815
4088
 
@@ -4379,36 +4652,708 @@ var init_workflows = __esm({
4379
4652
  }
4380
4653
  });
4381
4654
 
4382
- // src/tools/index.ts
4383
- var tools_exports = {};
4384
- __export(tools_exports, {
4385
- registerAllTools: () => registerAllTools
4655
+ // src/tools/users.ts
4656
+ var users_exports = {};
4657
+ __export(users_exports, {
4658
+ register: () => register15
4386
4659
  });
4387
- function registerAllTools(server, client) {
4388
- for (const mod of modules) {
4389
- mod.register(server, client);
4390
- }
4391
- }
4392
- var modules;
4393
- var init_tools = __esm({
4394
- "src/tools/index.ts"() {
4395
- "use strict";
4396
- init_media3();
4397
- init_text2();
4398
- init_exports();
4399
- init_folders();
4400
- init_recorder3();
4401
- init_embed3();
4402
- init_prompt3();
4403
- init_meeting3();
4404
- init_fields2();
4405
- init_automations();
4406
- init_webhooks();
4407
- init_analytics();
4408
- init_clips();
4409
- init_workflows();
4410
- modules = [
4411
- media_exports,
4660
+ function register15(server, client) {
4661
+ const api = client ?? speakClient;
4662
+ registerSpeakTool(
4663
+ server,
4664
+ "list_users",
4665
+ "List the users (members) in the workspace/company, with their ids, names, emails, and permissions. Use the returned _id values when assigning members to user groups.",
4666
+ {
4667
+ filterName: import_zod16.z.string().optional().describe(
4668
+ 'Search text. Plain text matches first/last name or email; prefix with "email:" or "name:" to scope, e.g. "email:jane@acme.com".'
4669
+ ),
4670
+ sortBy: import_zod16.z.string().optional().describe('Sort expression "field:asc" or "field:desc", e.g. "createdAt:desc", "email:asc"'),
4671
+ page: import_zod16.z.number().int().min(0).optional().describe("0-based page index (default 0)"),
4672
+ pageSize: import_zod16.z.number().int().min(1).max(200).optional().describe("Results per page (default 50)")
4673
+ },
4674
+ {
4675
+ title: "List Users",
4676
+ readOnlyHint: true,
4677
+ destructiveHint: false,
4678
+ idempotentHint: true,
4679
+ openWorldHint: false
4680
+ },
4681
+ async (params) => {
4682
+ try {
4683
+ const result = await api.get("/v1/admin/users", { params });
4684
+ return {
4685
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
4686
+ };
4687
+ } catch (err) {
4688
+ return {
4689
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
4690
+ isError: true
4691
+ };
4692
+ }
4693
+ }
4694
+ );
4695
+ registerSpeakTool(
4696
+ server,
4697
+ "list_user_groups",
4698
+ "List all user groups in the company. Each group includes its members (hydrated names/emails) and member ids. Use this to discover group ids and current membership before updating or deleting a group.",
4699
+ {},
4700
+ {
4701
+ title: "List User Groups",
4702
+ readOnlyHint: true,
4703
+ destructiveHint: false,
4704
+ idempotentHint: true,
4705
+ openWorldHint: false
4706
+ },
4707
+ async () => {
4708
+ try {
4709
+ const result = await api.get("/v1/admin/usergroup");
4710
+ return {
4711
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
4712
+ };
4713
+ } catch (err) {
4714
+ return {
4715
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
4716
+ isError: true
4717
+ };
4718
+ }
4719
+ }
4720
+ );
4721
+ registerSpeakTool(
4722
+ server,
4723
+ "create_user_group",
4724
+ "Create a new user group and assign members. Member ids come from list_users. Fails with a 409 if a group with the same name already exists in the company.",
4725
+ {
4726
+ description: import_zod16.z.string().min(1).describe("Group name"),
4727
+ users: import_zod16.z.array(import_zod16.z.string().min(1)).default([]).describe("User _id strings to add as members (fetch via list_users)")
4728
+ },
4729
+ {
4730
+ title: "Create User Group",
4731
+ readOnlyHint: false,
4732
+ destructiveHint: false,
4733
+ idempotentHint: false,
4734
+ openWorldHint: false
4735
+ },
4736
+ async (body) => {
4737
+ try {
4738
+ const result = await api.post("/v1/admin/usergroup", body);
4739
+ return {
4740
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
4741
+ };
4742
+ } catch (err) {
4743
+ return {
4744
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
4745
+ isError: true
4746
+ };
4747
+ }
4748
+ }
4749
+ );
4750
+ registerSpeakTool(
4751
+ server,
4752
+ "update_user_group",
4753
+ "Update a user group's name and member list. NOTE: the users array is a FULL REPLACEMENT, not a delta \u2014 any member id you omit is removed from the group. Fetch the current members with list_user_groups first and send the complete list.",
4754
+ {
4755
+ _id: import_zod16.z.string().min(1).describe("Group _id to update (from list_user_groups)"),
4756
+ description: import_zod16.z.string().min(1).describe("New group name"),
4757
+ users: import_zod16.z.array(import_zod16.z.string().min(1)).describe("Full replacement list of member _id strings (omitted users are removed)")
4758
+ },
4759
+ {
4760
+ title: "Update User Group",
4761
+ readOnlyHint: false,
4762
+ destructiveHint: false,
4763
+ idempotentHint: true,
4764
+ openWorldHint: false
4765
+ },
4766
+ async (body) => {
4767
+ try {
4768
+ const result = await api.put("/v1/admin/usergroup", body);
4769
+ return {
4770
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
4771
+ };
4772
+ } catch (err) {
4773
+ return {
4774
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
4775
+ isError: true
4776
+ };
4777
+ }
4778
+ }
4779
+ );
4780
+ registerSpeakTool(
4781
+ server,
4782
+ "delete_user_group",
4783
+ "Delete a user group. This removes the group only; it does not delete the users themselves.",
4784
+ {
4785
+ id: import_zod16.z.string().min(1).describe("Group _id to delete (from list_user_groups)")
4786
+ },
4787
+ {
4788
+ title: "Delete User Group",
4789
+ readOnlyHint: false,
4790
+ destructiveHint: true,
4791
+ idempotentHint: true,
4792
+ openWorldHint: false
4793
+ },
4794
+ async ({ id }) => {
4795
+ try {
4796
+ const result = await api.delete(`/v1/admin/usergroup/${id}`);
4797
+ return {
4798
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
4799
+ };
4800
+ } catch (err) {
4801
+ return {
4802
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
4803
+ isError: true
4804
+ };
4805
+ }
4806
+ }
4807
+ );
4808
+ }
4809
+ var import_zod16;
4810
+ var init_users = __esm({
4811
+ "src/tools/users.ts"() {
4812
+ "use strict";
4813
+ import_zod16 = require("zod");
4814
+ init_helpers();
4815
+ init_client();
4816
+ }
4817
+ });
4818
+
4819
+ // src/tools/dashboard-widgets.ts
4820
+ function buildDashboardWidgets(items) {
4821
+ let y = 0;
4822
+ let rowX = 0;
4823
+ let rowH = 0;
4824
+ return items.map((item) => {
4825
+ const meta = WIDGET_META[item.type];
4826
+ const full = meta.w >= GRID_COLS;
4827
+ let x;
4828
+ if (full) {
4829
+ if (rowX !== 0) {
4830
+ y += rowH;
4831
+ rowX = 0;
4832
+ rowH = 0;
4833
+ }
4834
+ x = 0;
4835
+ const widget2 = makeWidget(item, x, y);
4836
+ y += meta.h;
4837
+ return widget2;
4838
+ }
4839
+ if (rowX + meta.w > GRID_COLS) {
4840
+ y += rowH;
4841
+ rowX = 0;
4842
+ rowH = 0;
4843
+ }
4844
+ x = rowX;
4845
+ const widget = makeWidget(item, x, y);
4846
+ rowX += meta.w;
4847
+ rowH = Math.max(rowH, meta.h);
4848
+ if (rowX >= GRID_COLS) {
4849
+ y += rowH;
4850
+ rowX = 0;
4851
+ rowH = 0;
4852
+ }
4853
+ return widget;
4854
+ });
4855
+ }
4856
+ function makeWidget(item, x, y) {
4857
+ const meta = WIDGET_META[item.type];
4858
+ return {
4859
+ id: (0, import_crypto.randomUUID)(),
4860
+ type: item.type,
4861
+ title: item.title ?? meta.titleDefault,
4862
+ config: item.config ?? {},
4863
+ layout: { x, y, w: meta.w, h: meta.h, minW: meta.minW, minH: meta.minH }
4864
+ };
4865
+ }
4866
+ var import_crypto, GRID_COLS, WIDGET_TYPES, WIDGET_META, WIDGET_CATALOG, COMMON_WIDGET_CONFIG, DASHBOARD_EXAMPLES;
4867
+ var init_dashboard_widgets = __esm({
4868
+ "src/tools/dashboard-widgets.ts"() {
4869
+ "use strict";
4870
+ import_crypto = require("crypto");
4871
+ GRID_COLS = 12;
4872
+ WIDGET_TYPES = [
4873
+ "stat-cards",
4874
+ "sentiment",
4875
+ "media-list",
4876
+ "field-distribution",
4877
+ "upload-timeline",
4878
+ "themes",
4879
+ "team-activity",
4880
+ "kpi-trend",
4881
+ "field-metric",
4882
+ "metric-group",
4883
+ "notes",
4884
+ "people",
4885
+ "comparison",
4886
+ "sentiment-trend"
4887
+ ];
4888
+ WIDGET_META = {
4889
+ "stat-cards": { w: GRID_COLS, h: 3, minW: 4, minH: 2, titleDefault: "Usage overview" },
4890
+ sentiment: { w: 6, h: 4, minW: 3, minH: 3, titleDefault: "Sentiment" },
4891
+ "media-list": { w: GRID_COLS, h: 4, minW: 4, minH: 3, titleDefault: "Recent media" },
4892
+ "field-distribution": { w: 6, h: 4, minW: 3, minH: 3, titleDefault: "Field breakdown" },
4893
+ "upload-timeline": { w: 6, h: 4, minW: 3, minH: 3, titleDefault: "Uploads over time" },
4894
+ themes: { w: 6, h: 4, minW: 3, minH: 3, titleDefault: "Themes" },
4895
+ "team-activity": { w: 6, h: 4, minW: 3, minH: 3, titleDefault: "Team activity" },
4896
+ "kpi-trend": { w: 3, h: 2, minW: 2, minH: 2, titleDefault: "Total files" },
4897
+ "field-metric": { w: 3, h: 2, minW: 2, minH: 2, titleDefault: "Field metric" },
4898
+ "metric-group": { w: 6, h: 2, minW: 3, minH: 2, titleDefault: "Metrics" },
4899
+ notes: { w: GRID_COLS, h: 2, minW: 2, minH: 1, titleDefault: "Note" },
4900
+ people: { w: 6, h: 4, minW: 3, minH: 3, titleDefault: "People" },
4901
+ comparison: { w: 6, h: 3, minW: 3, minH: 2, titleDefault: "Comparison" },
4902
+ "sentiment-trend": { w: 6, h: 4, minW: 3, minH: 3, titleDefault: "Sentiment over time" }
4903
+ };
4904
+ WIDGET_CATALOG = [
4905
+ {
4906
+ type: "stat-cards",
4907
+ purpose: "Headline totals for the scope (full width).",
4908
+ config: 'metrics?: string[] \u2014 any of "media", "storage", "words", "speakers", "duration", "sentiment"'
4909
+ },
4910
+ { type: "sentiment", purpose: "Sentiment distribution + representative sentences.", config: "none" },
4911
+ { type: "media-list", purpose: "Most recent media in scope (full width).", config: "none" },
4912
+ {
4913
+ type: "field-distribution",
4914
+ purpose: "Value-frequency breakdown for one custom field.",
4915
+ config: 'fieldId: string (a custom field id from list_fields); measure?: "count" | "words" | "duration" | "avg:<numericFieldId>" | "sum:<numericFieldId>"'
4916
+ },
4917
+ { type: "upload-timeline", purpose: "Uploads over time per media type.", config: "none" },
4918
+ { type: "themes", purpose: "Dominant theme clusters.", config: 'chartType?: "cloud" | "bar"' },
4919
+ { type: "team-activity", purpose: "Activity by team member.", config: "none" },
4920
+ {
4921
+ type: "kpi-trend",
4922
+ purpose: "A KPI with period-over-period delta.",
4923
+ config: 'metrics?: string[] \u2014 any of "totalFiles", "totalDurationSeconds", "totalWords", "uniqueSpeakers"'
4924
+ },
4925
+ {
4926
+ type: "field-metric",
4927
+ purpose: "A single custom field's aggregate, with a period-over-period delta.",
4928
+ config: 'fieldId: string (custom field id from list_fields); aggregator: "sum" | "avg" | "min" | "max" | "count"'
4929
+ },
4930
+ {
4931
+ type: "metric-group",
4932
+ purpose: "A scorecard of several field metrics in one card.",
4933
+ config: 'metrics: Array<{ fieldId: string, op: "sum"|"avg"|"min"|"max"|"count", label?: string }> (note the per-item key is `op`, not `aggregator`)'
4934
+ },
4935
+ {
4936
+ type: "notes",
4937
+ purpose: "Free-text note/context block (full width).",
4938
+ config: "heading?: string; content?: string"
4939
+ },
4940
+ { type: "people", purpose: "Speaker/people breakdown.", config: "none" },
4941
+ {
4942
+ type: "comparison",
4943
+ purpose: "Period-over-period comparison of KPIs.",
4944
+ config: 'metrics?: string[] \u2014 any of "totalFiles", "totalDurationSeconds", "totalWords", "uniqueSpeakers"'
4945
+ },
4946
+ { type: "sentiment-trend", purpose: "Sentiment over time.", config: "none" }
4947
+ ];
4948
+ COMMON_WIDGET_CONFIG = [
4949
+ {
4950
+ key: "accentColor",
4951
+ appliesTo: "any widget",
4952
+ description: 'Hex color for the widget accent, e.g. "#6366F1". Omit for the default.'
4953
+ },
4954
+ {
4955
+ key: "scopeOverride",
4956
+ appliesTo: "any widget except notes",
4957
+ description: "Override the dashboard's scope for just this widget: { datePreset: string, folderId: string }. Omit to inherit the dashboard scope."
4958
+ }
4959
+ ];
4960
+ DASHBOARD_EXAMPLES = [
4961
+ {
4962
+ name: "Sales calls overview",
4963
+ payload: {
4964
+ title: "Customer Calls Overview",
4965
+ folderScope: ["<folderId>"],
4966
+ dateRange: { preset: "last30days" },
4967
+ filters: { filterList: [{ fieldName: "Stage", fieldOperator: "is", fieldValue: ["Closed Won"] }] },
4968
+ widgets: [
4969
+ { type: "stat-cards", config: { metrics: ["media", "words", "speakers"] } },
4970
+ { type: "sentiment", config: { accentColor: "#6366F1" } },
4971
+ { type: "field-distribution", title: "Deals by stage", config: { fieldId: "<fieldId>", measure: "count" } },
4972
+ { type: "themes", config: { chartType: "bar" } },
4973
+ { type: "media-list" }
4974
+ ]
4975
+ }
4976
+ },
4977
+ {
4978
+ name: "Field-metrics scorecard (new widgets)",
4979
+ payload: {
4980
+ title: "Deal Metrics",
4981
+ folderScope: ["<folderId>"],
4982
+ dateRange: { preset: "thisQuarter" },
4983
+ widgets: [
4984
+ { type: "kpi-trend", title: "Total calls", config: { metrics: ["totalFiles"] } },
4985
+ { type: "field-metric", title: "Avg deal size", config: { fieldId: "<dealSizeFieldId>", aggregator: "avg" } },
4986
+ {
4987
+ type: "metric-group",
4988
+ title: "Pipeline",
4989
+ config: {
4990
+ metrics: [
4991
+ { fieldId: "<dealSizeFieldId>", op: "sum", label: "Total pipeline" },
4992
+ { fieldId: "<dealSizeFieldId>", op: "max", label: "Largest deal" }
4993
+ ]
4994
+ }
4995
+ },
4996
+ // Per-widget scope override: this card ignores the dashboard scope.
4997
+ { type: "field-metric", title: "EMEA avg", config: { fieldId: "<dealSizeFieldId>", aggregator: "avg", scopeOverride: { datePreset: "last7days", folderId: "<emeaFolderId>" } } }
4998
+ ]
4999
+ }
5000
+ }
5001
+ ];
5002
+ }
5003
+ });
5004
+
5005
+ // src/tools/dashboards.ts
5006
+ var dashboards_exports = {};
5007
+ __export(dashboards_exports, {
5008
+ register: () => register16
5009
+ });
5010
+ function withBuiltWidgets(body) {
5011
+ if (Array.isArray(body.widgets)) {
5012
+ return { ...body, widgets: buildDashboardWidgets(body.widgets) };
5013
+ }
5014
+ return body;
5015
+ }
5016
+ function register16(server, client) {
5017
+ const api = client ?? speakClient;
5018
+ registerSpeakTool(
5019
+ server,
5020
+ "list_dashboards",
5021
+ "List all analytics dashboards the caller can access, including share state.",
5022
+ {},
5023
+ {
5024
+ title: "List Dashboards",
5025
+ readOnlyHint: true,
5026
+ destructiveHint: false,
5027
+ idempotentHint: true,
5028
+ openWorldHint: false
5029
+ },
5030
+ async () => {
5031
+ try {
5032
+ const result = await api.get("/v1/dashboards");
5033
+ return {
5034
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
5035
+ };
5036
+ } catch (err) {
5037
+ return {
5038
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
5039
+ isError: true
5040
+ };
5041
+ }
5042
+ }
5043
+ );
5044
+ registerSpeakTool(
5045
+ server,
5046
+ "get_dashboard",
5047
+ "Get a single dashboard's full config (widgets, filters, date range, folder scope).",
5048
+ {
5049
+ dashboardId: import_zod17.z.string().min(1).describe("Dashboard business id (the dashboardId field from list_dashboards, not the Mongo _id)")
5050
+ },
5051
+ {
5052
+ title: "Get Dashboard",
5053
+ readOnlyHint: true,
5054
+ destructiveHint: false,
5055
+ idempotentHint: true,
5056
+ openWorldHint: false
5057
+ },
5058
+ async ({ dashboardId }) => {
5059
+ try {
5060
+ const result = await api.get(`/v1/dashboards/${dashboardId}`);
5061
+ return {
5062
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
5063
+ };
5064
+ } catch (err) {
5065
+ return {
5066
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
5067
+ isError: true
5068
+ };
5069
+ }
5070
+ }
5071
+ );
5072
+ registerSpeakTool(
5073
+ server,
5074
+ "list_dashboard_widgets",
5075
+ "Discovery + how-to helper for building and customizing dashboards. Returns every widget type with what it shows and the exact `config` keys it accepts (metrics, fieldId, aggregator, chartType, accentColor, scopeOverride, \u2026), the config that applies to all widgets, two complete worked example payloads (including the field-metric and metric-group cards), and tips for managing dashboards. Call this before create_dashboard / update_dashboard.",
5076
+ {},
5077
+ {
5078
+ title: "List Dashboard Widgets",
5079
+ readOnlyHint: true,
5080
+ destructiveHint: false,
5081
+ idempotentHint: true,
5082
+ openWorldHint: false
5083
+ },
5084
+ async () => {
5085
+ const data = {
5086
+ widgets: WIDGET_CATALOG,
5087
+ commonConfig: COMMON_WIDGET_CONFIG,
5088
+ notes: [
5089
+ "Pass widgets to create_dashboard as a simple ordered list; ids and grid layout are computed for you.",
5090
+ "Field-driven widgets (field-distribution, field-metric, metric-group) need custom field ids from list_fields in their config.",
5091
+ "field-metric uses config.aggregator; metric-group items use the key `op` (both: sum|avg|min|max|count).",
5092
+ "accentColor (hex) and scopeOverride ({datePreset, folderId}) can be set on any widget's config to customize it.",
5093
+ "Most other widgets render on sensible defaults with no config."
5094
+ ],
5095
+ managing: [
5096
+ "To customize an existing dashboard, call get_dashboard, edit the widgets array, and send the full array to update_dashboard (widgets are replaced, not merged).",
5097
+ "To start from a working layout, duplicate_dashboard a good one, then update_dashboard to tweak it.",
5098
+ "share_dashboard returns a public token; chain it after the dashboard is built."
5099
+ ],
5100
+ examples: DASHBOARD_EXAMPLES
5101
+ };
5102
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
5103
+ }
5104
+ );
5105
+ registerSpeakTool(
5106
+ server,
5107
+ "create_dashboard",
5108
+ "Create an analytics dashboard. Only `title` is required. Add widgets by listing their types (the MCP assigns ids and lays them out automatically) and scope with folderScope, dateRange, and filters. Call list_dashboard_widgets first to see the available widget types and their config keys, plus a full example.",
5109
+ {
5110
+ title: import_zod17.z.string().min(1).max(200).describe("Dashboard name (the only required field)"),
5111
+ ...writeFields
5112
+ },
5113
+ {
5114
+ title: "Create Dashboard",
5115
+ readOnlyHint: false,
5116
+ destructiveHint: false,
5117
+ idempotentHint: false,
5118
+ openWorldHint: false
5119
+ },
5120
+ async (body) => {
5121
+ try {
5122
+ const result = await api.post("/v1/dashboards", withBuiltWidgets(body));
5123
+ return {
5124
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
5125
+ };
5126
+ } catch (err) {
5127
+ return {
5128
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
5129
+ isError: true
5130
+ };
5131
+ }
5132
+ }
5133
+ );
5134
+ registerSpeakTool(
5135
+ server,
5136
+ "update_dashboard",
5137
+ "Update a dashboard. Only the fields you pass are changed (partial update). NOTE: passing `widgets` replaces the entire widget set \u2014 fetch the dashboard first if you want to preserve existing widgets.",
5138
+ {
5139
+ dashboardId: import_zod17.z.string().min(1).describe("Dashboard business id"),
5140
+ title: import_zod17.z.string().min(1).max(200).optional().describe("New dashboard name"),
5141
+ ...writeFields
5142
+ },
5143
+ {
5144
+ title: "Update Dashboard",
5145
+ readOnlyHint: false,
5146
+ destructiveHint: false,
5147
+ idempotentHint: true,
5148
+ openWorldHint: false
5149
+ },
5150
+ async ({ dashboardId, ...body }) => {
5151
+ try {
5152
+ const result = await api.put(`/v1/dashboards/${dashboardId}`, withBuiltWidgets(body));
5153
+ return {
5154
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
5155
+ };
5156
+ } catch (err) {
5157
+ return {
5158
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
5159
+ isError: true
5160
+ };
5161
+ }
5162
+ }
5163
+ );
5164
+ registerSpeakTool(
5165
+ server,
5166
+ "delete_dashboard",
5167
+ "Soft-delete a dashboard. This also deactivates its public share link.",
5168
+ {
5169
+ dashboardId: import_zod17.z.string().min(1).describe("Dashboard business id to delete")
5170
+ },
5171
+ {
5172
+ title: "Delete Dashboard",
5173
+ readOnlyHint: false,
5174
+ destructiveHint: true,
5175
+ idempotentHint: true,
5176
+ openWorldHint: false
5177
+ },
5178
+ async ({ dashboardId }) => {
5179
+ try {
5180
+ const result = await api.delete(`/v1/dashboards/${dashboardId}`);
5181
+ return {
5182
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
5183
+ };
5184
+ } catch (err) {
5185
+ return {
5186
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
5187
+ isError: true
5188
+ };
5189
+ }
5190
+ }
5191
+ );
5192
+ registerSpeakTool(
5193
+ server,
5194
+ "duplicate_dashboard",
5195
+ 'Clone an existing dashboard. The copy gets fresh widget ids, a "<name> (copy)" title, and cleared sharing. Ideal for cloning a fully-configured dashboard, then tweaking it via update_dashboard.',
5196
+ {
5197
+ dashboardId: import_zod17.z.string().min(1).describe("Source dashboard business id to clone")
5198
+ },
5199
+ {
5200
+ title: "Duplicate Dashboard",
5201
+ readOnlyHint: false,
5202
+ destructiveHint: false,
5203
+ idempotentHint: false,
5204
+ openWorldHint: false
5205
+ },
5206
+ async ({ dashboardId }) => {
5207
+ try {
5208
+ const result = await api.post(`/v1/dashboards/${dashboardId}/duplicate`, {});
5209
+ return {
5210
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
5211
+ };
5212
+ } catch (err) {
5213
+ return {
5214
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
5215
+ isError: true
5216
+ };
5217
+ }
5218
+ }
5219
+ );
5220
+ registerSpeakTool(
5221
+ server,
5222
+ "share_dashboard",
5223
+ "Enable public sharing for a dashboard and return its share token + embed id. WARNING: by default the public link resolves with no passphrase, so anyone with the token can view the dashboard data until an owner sets one.",
5224
+ {
5225
+ dashboardId: import_zod17.z.string().min(1).describe("Dashboard business id to share")
5226
+ },
5227
+ {
5228
+ title: "Share Dashboard",
5229
+ readOnlyHint: false,
5230
+ destructiveHint: false,
5231
+ idempotentHint: true,
5232
+ openWorldHint: false
5233
+ },
5234
+ async ({ dashboardId }) => {
5235
+ try {
5236
+ const result = await api.put(`/v1/dashboards/${dashboardId}/share`, {});
5237
+ return {
5238
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
5239
+ };
5240
+ } catch (err) {
5241
+ return {
5242
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
5243
+ isError: true
5244
+ };
5245
+ }
5246
+ }
5247
+ );
5248
+ registerSpeakTool(
5249
+ server,
5250
+ "get_dashboard_speakers_insight",
5251
+ "Compute a speakers breakdown for a given folder scope, date range, and field filters. Standalone analytics \u2014 does not require a dashboard to exist.",
5252
+ SPEAKERS_FILTER_SCHEMA,
5253
+ {
5254
+ title: "Get Dashboard Speakers Insight",
5255
+ readOnlyHint: true,
5256
+ destructiveHint: false,
5257
+ idempotentHint: true,
5258
+ openWorldHint: false
5259
+ },
5260
+ async (body) => {
5261
+ try {
5262
+ const result = await api.post("/v1/dashboards/insights/speakers", body);
5263
+ return {
5264
+ content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
5265
+ };
5266
+ } catch (err) {
5267
+ return {
5268
+ content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
5269
+ isError: true
5270
+ };
5271
+ }
5272
+ }
5273
+ );
5274
+ }
5275
+ var import_zod17, FILTER_LIST_DESCRIPTION, widgetInputSchema, writeFields, SPEAKERS_FILTER_SCHEMA;
5276
+ var init_dashboards = __esm({
5277
+ "src/tools/dashboards.ts"() {
5278
+ "use strict";
5279
+ import_zod17 = require("zod");
5280
+ init_helpers();
5281
+ init_client();
5282
+ init_dashboard_widgets();
5283
+ FILTER_LIST_DESCRIPTION = "Field filters. filters.filterList is an array of { fieldName, fieldOperator?, fieldValue?: string[], fieldCondition? }. Other keys pass through but only filterList is enforced.";
5284
+ widgetInputSchema = import_zod17.z.object({
5285
+ type: import_zod17.z.enum(WIDGET_TYPES).describe(
5286
+ "Widget type: stat-cards | sentiment | media-list | field-distribution | upload-timeline | themes | team-activity | kpi-trend | field-metric | metric-group | notes | people | comparison | sentiment-trend"
5287
+ ),
5288
+ title: import_zod17.z.string().max(200).optional().describe("Widget title (defaults to a per-type label)"),
5289
+ config: import_zod17.z.record(import_zod17.z.unknown()).optional().describe(
5290
+ "Optional render settings. Per-type: fieldId+measure (field-distribution), fieldId+aggregator (field-metric), metrics list (stat-cards, kpi-trend, comparison, metric-group), chartType (themes), heading+content (notes). Any widget also accepts accentColor (hex) and scopeOverride ({datePreset, folderId}). Call list_dashboard_widgets for the full catalog + examples. Leave empty for sensible defaults."
5291
+ )
5292
+ });
5293
+ writeFields = {
5294
+ description: import_zod17.z.string().max(2e3).optional().describe("Dashboard description"),
5295
+ icon: import_zod17.z.string().max(200).optional().describe("Icon identifier"),
5296
+ folderScope: import_zod17.z.array(import_zod17.z.string().max(100)).max(100).optional().describe("Folder ids to scope analytics to. Empty/omitted = all accessible folders."),
5297
+ assignTo: import_zod17.z.array(import_zod17.z.string()).max(100).optional().describe('User ids, or group ids in the "<groupId> (G)" convention, to share view access with'),
5298
+ dateRange: import_zod17.z.object({
5299
+ preset: import_zod17.z.string().max(50).optional(),
5300
+ startDate: import_zod17.z.string().optional(),
5301
+ endDate: import_zod17.z.string().optional()
5302
+ }).optional().describe("Date range: { preset?, startDate?, endDate? }"),
5303
+ filters: import_zod17.z.record(import_zod17.z.unknown()).optional().describe(FILTER_LIST_DESCRIPTION),
5304
+ widgets: import_zod17.z.array(widgetInputSchema).max(50).optional().describe(
5305
+ "Widgets to place on the dashboard, in order. The MCP assigns ids and computes a tidy two-per-row grid layout matching the Speak UI. Just list the widget types you want."
5306
+ ),
5307
+ isDefault: import_zod17.z.boolean().optional().describe("Make this the company default dashboard")
5308
+ };
5309
+ SPEAKERS_FILTER_SCHEMA = {
5310
+ folderScope: import_zod17.z.array(import_zod17.z.string().max(100)).max(100).optional().describe("Folder ids to scope to"),
5311
+ startDate: import_zod17.z.string().optional().describe("ISO start date"),
5312
+ endDate: import_zod17.z.string().optional().describe("ISO end date"),
5313
+ filterList: import_zod17.z.array(
5314
+ import_zod17.z.object({
5315
+ fieldName: import_zod17.z.string().max(100),
5316
+ fieldOperator: import_zod17.z.string().max(50).optional(),
5317
+ fieldValue: import_zod17.z.array(import_zod17.z.string().max(500)).optional(),
5318
+ fieldCondition: import_zod17.z.string().max(50).optional()
5319
+ })
5320
+ ).max(20).optional().describe("Field filter rules")
5321
+ };
5322
+ }
5323
+ });
5324
+
5325
+ // src/tools/index.ts
5326
+ var tools_exports = {};
5327
+ __export(tools_exports, {
5328
+ registerAllTools: () => registerAllTools
5329
+ });
5330
+ function registerAllTools(server, client) {
5331
+ for (const mod of modules) {
5332
+ mod.register(server, client);
5333
+ }
5334
+ }
5335
+ var modules;
5336
+ var init_tools = __esm({
5337
+ "src/tools/index.ts"() {
5338
+ "use strict";
5339
+ init_media3();
5340
+ init_text2();
5341
+ init_exports();
5342
+ init_folders();
5343
+ init_recorder3();
5344
+ init_embed3();
5345
+ init_prompt3();
5346
+ init_meeting3();
5347
+ init_fields2();
5348
+ init_automations();
5349
+ init_webhooks();
5350
+ init_analytics();
5351
+ init_clips();
5352
+ init_workflows();
5353
+ init_users();
5354
+ init_dashboards();
5355
+ modules = [
5356
+ media_exports,
4412
5357
  text_exports,
4413
5358
  exports_exports,
4414
5359
  folders_exports,
@@ -4421,7 +5366,9 @@ var init_tools = __esm({
4421
5366
  webhooks_exports,
4422
5367
  analytics_exports,
4423
5368
  clips_exports,
4424
- workflows_exports
5369
+ workflows_exports,
5370
+ users_exports,
5371
+ dashboards_exports
4425
5372
  ];
4426
5373
  }
4427
5374
  });
@@ -4538,8 +5485,8 @@ function registerPrompts(server) {
4538
5485
  "analyze-meeting",
4539
5486
  "Upload a meeting recording and get a full analysis \u2014 transcript, insights, action items, and key takeaways.",
4540
5487
  {
4541
- url: import_zod16.z.string().describe("URL of the meeting recording \u2014 a direct file link or a shareable social/video link (resolved automatically)"),
4542
- name: import_zod16.z.string().optional().describe("Meeting name (optional)")
5488
+ url: import_zod18.z.string().describe("URL of the meeting recording \u2014 a direct file link or a shareable social/video link (resolved automatically)"),
5489
+ name: import_zod18.z.string().optional().describe("Meeting name (optional)")
4543
5490
  },
4544
5491
  async ({ url, name }) => ({
4545
5492
  messages: [
@@ -4571,8 +5518,8 @@ function registerPrompts(server) {
4571
5518
  "research-across-media",
4572
5519
  "Search for themes, patterns, or topics across multiple recordings or your entire media library.",
4573
5520
  {
4574
- topic: import_zod16.z.string().describe("The topic, theme, or question to research"),
4575
- folder: import_zod16.z.string().optional().describe("Folder ID to scope the research (optional)")
5521
+ topic: import_zod18.z.string().describe("The topic, theme, or question to research"),
5522
+ folder: import_zod18.z.string().optional().describe("Folder ID to scope the research (optional)")
4576
5523
  },
4577
5524
  async ({ topic, folder }) => ({
4578
5525
  messages: [
@@ -4605,8 +5552,8 @@ function registerPrompts(server) {
4605
5552
  "meeting-brief",
4606
5553
  "Prepare a brief from recent meetings \u2014 pull transcripts, extract decisions, and summarize open items.",
4607
5554
  {
4608
- days: import_zod16.z.string().optional().describe("Number of days to look back (default: 7)"),
4609
- folder: import_zod16.z.string().optional().describe("Folder ID to scope to (optional)")
5555
+ days: import_zod18.z.string().optional().describe("Number of days to look back (default: 7)"),
5556
+ folder: import_zod18.z.string().optional().describe("Folder ID to scope to (optional)")
4610
5557
  },
4611
5558
  async ({ days, folder }) => {
4612
5559
  const lookback = parseInt(days ?? "7");
@@ -4643,11 +5590,148 @@ function registerPrompts(server) {
4643
5590
  }
4644
5591
  );
4645
5592
  }
4646
- var import_zod16;
5593
+ var import_zod18;
4647
5594
  var init_prompts = __esm({
4648
5595
  "src/prompts.ts"() {
4649
5596
  "use strict";
4650
- import_zod16 = require("zod");
5597
+ import_zod18 = require("zod");
5598
+ }
5599
+ });
5600
+
5601
+ // src/tool-names.ts
5602
+ var tool_names_exports = {};
5603
+ __export(tool_names_exports, {
5604
+ SPEAK_MCP_TOOL_NAMES: () => SPEAK_MCP_TOOL_NAMES
5605
+ });
5606
+ var SPEAK_MCP_TOOL_NAMES;
5607
+ var init_tool_names = __esm({
5608
+ "src/tool-names.ts"() {
5609
+ "use strict";
5610
+ SPEAK_MCP_TOOL_NAMES = [
5611
+ // analytics
5612
+ "get_media_statistics",
5613
+ // automations
5614
+ "list_automations",
5615
+ "list_automation_names",
5616
+ "get_automation",
5617
+ "get_automation_runs",
5618
+ "create_automation",
5619
+ "update_automation",
5620
+ "toggle_automation_status",
5621
+ "bulk_update_automation_status",
5622
+ "bulk_assign_automation_folders",
5623
+ "run_automations",
5624
+ "delete_automation",
5625
+ "list_automation_apps",
5626
+ "list_automation_triggers",
5627
+ "list_automation_actions",
5628
+ // clips
5629
+ "get_clips",
5630
+ "create_clip",
5631
+ "update_clip",
5632
+ "delete_clip",
5633
+ // embed
5634
+ "create_embed",
5635
+ "update_embed",
5636
+ "check_embed",
5637
+ "get_embed_iframe_url",
5638
+ // exports
5639
+ "export_media",
5640
+ "export_multiple_media",
5641
+ // fields
5642
+ "list_fields",
5643
+ "create_field",
5644
+ "update_field",
5645
+ "update_multiple_fields",
5646
+ // folders
5647
+ "list_folders",
5648
+ "create_folder",
5649
+ "update_folder",
5650
+ "delete_folder",
5651
+ "get_folder_info",
5652
+ "clone_folder",
5653
+ "get_folder_views",
5654
+ "get_all_folder_views",
5655
+ "create_folder_view",
5656
+ "update_folder_view",
5657
+ "clone_folder_view",
5658
+ // media
5659
+ "get_signed_upload_url",
5660
+ "upload_media",
5661
+ "get_media_status",
5662
+ "get_media_insights",
5663
+ "get_transcript",
5664
+ "list_media",
5665
+ "search_media",
5666
+ "delete_media",
5667
+ "update_media_metadata",
5668
+ "toggle_media_favorite",
5669
+ "reanalyze_media",
5670
+ "get_captions",
5671
+ "list_supported_languages",
5672
+ "update_transcript_speakers",
5673
+ "bulk_update_transcript_speakers",
5674
+ "bulk_move_media",
5675
+ // meeting
5676
+ "list_meeting_events",
5677
+ "schedule_meeting_event",
5678
+ "remove_assistant_from_meeting",
5679
+ "delete_scheduled_assistant",
5680
+ "get_live_meeting_transcript",
5681
+ // prompt
5682
+ "ask_magic_prompt",
5683
+ "list_prompts",
5684
+ "get_favorite_prompts",
5685
+ "toggle_prompt_favorite",
5686
+ "get_chat_history",
5687
+ "get_chat_messages",
5688
+ "update_chat_title",
5689
+ "delete_chat_message",
5690
+ "submit_chat_feedback",
5691
+ "retry_magic_prompt",
5692
+ "export_chat_answer",
5693
+ "get_chat_statistics",
5694
+ // recorder
5695
+ "list_recorders",
5696
+ "create_recorder",
5697
+ "update_recorder_settings",
5698
+ "update_recorder_questions",
5699
+ "delete_recorder",
5700
+ "generate_recorder_url",
5701
+ "get_recorder_info",
5702
+ "get_recorder_recordings",
5703
+ "check_recorder_status",
5704
+ "clone_recorder",
5705
+ // text
5706
+ "create_text_note",
5707
+ "update_text_note",
5708
+ "get_text_insight",
5709
+ "reanalyze_text",
5710
+ // webhooks
5711
+ "list_webhooks",
5712
+ "create_webhook",
5713
+ "update_webhook",
5714
+ "delete_webhook",
5715
+ // workflows (high-level wrappers around media + upload tools)
5716
+ "upload_and_analyze",
5717
+ "upload_local_file",
5718
+ // users / team management
5719
+ "list_users",
5720
+ "list_user_groups",
5721
+ "create_user_group",
5722
+ "update_user_group",
5723
+ "delete_user_group",
5724
+ // dashboards
5725
+ "list_dashboard_widgets",
5726
+ "list_dashboards",
5727
+ "get_dashboard",
5728
+ "create_dashboard",
5729
+ "update_dashboard",
5730
+ "delete_dashboard",
5731
+ "duplicate_dashboard",
5732
+ "share_dashboard",
5733
+ "get_dashboard_speakers_insight"
5734
+ ];
4651
5735
  }
4652
5736
  });
4653
5737
 
@@ -5694,8 +6778,71 @@ function createCli() {
5694
6778
  process.exit(1);
5695
6779
  }
5696
6780
  });
6781
+ async function loadToolHandlers() {
6782
+ const client = await getClient();
6783
+ const handlers = {};
6784
+ const stub = {
6785
+ registerTool: (name, _def, cb) => {
6786
+ handlers[name] = cb;
6787
+ return {};
6788
+ }
6789
+ };
6790
+ const { registerAllTools: registerAllTools2 } = await Promise.resolve().then(() => (init_tools(), tools_exports));
6791
+ registerAllTools2(stub, client);
6792
+ return handlers;
6793
+ }
6794
+ program.command("tools").description("List every MCP tool callable via `call`").option("--json", "Output raw JSON").action(async (opts) => {
6795
+ const { SPEAK_MCP_TOOL_NAMES: SPEAK_MCP_TOOL_NAMES2 } = await Promise.resolve().then(() => (init_tool_names(), tool_names_exports));
6796
+ const names = [...SPEAK_MCP_TOOL_NAMES2].sort();
6797
+ if (opts.json) {
6798
+ printJson(names);
6799
+ } else {
6800
+ console.log(`${names.length} tools:
6801
+ `);
6802
+ for (const n of names) console.log(` ${n}`);
6803
+ }
6804
+ });
6805
+ program.command("call").description("Call any MCP tool by name with JSON arguments").argument("<tool>", "Tool name (see `speakai-mcp tools`)").argument("[json]", "Arguments as a JSON object", "{}").action(async (tool, json) => {
6806
+ requireApiKey();
6807
+ let args2;
6808
+ try {
6809
+ args2 = JSON.parse(json);
6810
+ } catch {
6811
+ printError(`Invalid JSON arguments: ${json}`);
6812
+ process.exit(1);
6813
+ return;
6814
+ }
6815
+ const handlers = await loadToolHandlers();
6816
+ const handler = handlers[tool];
6817
+ if (!handler) {
6818
+ printError(`Unknown tool "${tool}". Run "speakai-mcp tools" to list them.`);
6819
+ process.exit(1);
6820
+ return;
6821
+ }
6822
+ try {
6823
+ const result = await handler(args2);
6824
+ const text = result?.content?.find((c) => c.type === "text")?.text;
6825
+ if (result?.isError) {
6826
+ printError(text ?? "Tool call failed");
6827
+ process.exit(1);
6828
+ return;
6829
+ }
6830
+ const data = result?.structuredContent?.data ?? (text ? safeParse(text) : result);
6831
+ printJson(data);
6832
+ } catch (err) {
6833
+ printError(err.response?.data?.message ?? err.message);
6834
+ process.exit(1);
6835
+ }
6836
+ });
5697
6837
  return program;
5698
6838
  }
6839
+ function safeParse(text) {
6840
+ try {
6841
+ return JSON.parse(text);
6842
+ } catch {
6843
+ return text;
6844
+ }
6845
+ }
5699
6846
  var import_commander, import_readline;
5700
6847
  var init_cli = __esm({
5701
6848
  "src/cli/index.ts"() {
@@ -5723,110 +6870,7 @@ init_tools();
5723
6870
  init_resources();
5724
6871
  init_prompts();
5725
6872
  init_client();
5726
-
5727
- // src/tool-names.ts
5728
- var SPEAK_MCP_TOOL_NAMES = [
5729
- // analytics
5730
- "get_media_statistics",
5731
- // automations
5732
- "list_automations",
5733
- "get_automation",
5734
- "create_automation",
5735
- "update_automation",
5736
- "toggle_automation_status",
5737
- // clips
5738
- "get_clips",
5739
- "create_clip",
5740
- "update_clip",
5741
- "delete_clip",
5742
- // embed
5743
- "create_embed",
5744
- "update_embed",
5745
- "check_embed",
5746
- "get_embed_iframe_url",
5747
- // exports
5748
- "export_media",
5749
- "export_multiple_media",
5750
- // fields
5751
- "list_fields",
5752
- "create_field",
5753
- "update_field",
5754
- "update_multiple_fields",
5755
- // folders
5756
- "list_folders",
5757
- "create_folder",
5758
- "update_folder",
5759
- "delete_folder",
5760
- "get_folder_info",
5761
- "clone_folder",
5762
- "get_folder_views",
5763
- "get_all_folder_views",
5764
- "create_folder_view",
5765
- "update_folder_view",
5766
- "clone_folder_view",
5767
- // media
5768
- "get_signed_upload_url",
5769
- "upload_media",
5770
- "get_media_status",
5771
- "get_media_insights",
5772
- "get_transcript",
5773
- "list_media",
5774
- "search_media",
5775
- "delete_media",
5776
- "update_media_metadata",
5777
- "toggle_media_favorite",
5778
- "reanalyze_media",
5779
- "get_captions",
5780
- "list_supported_languages",
5781
- "update_transcript_speakers",
5782
- "bulk_update_transcript_speakers",
5783
- "bulk_move_media",
5784
- // meeting
5785
- "list_meeting_events",
5786
- "schedule_meeting_event",
5787
- "remove_assistant_from_meeting",
5788
- "delete_scheduled_assistant",
5789
- "get_live_meeting_transcript",
5790
- // prompt
5791
- "ask_magic_prompt",
5792
- "list_prompts",
5793
- "get_favorite_prompts",
5794
- "toggle_prompt_favorite",
5795
- "get_chat_history",
5796
- "get_chat_messages",
5797
- "update_chat_title",
5798
- "delete_chat_message",
5799
- "submit_chat_feedback",
5800
- "retry_magic_prompt",
5801
- "export_chat_answer",
5802
- "get_chat_statistics",
5803
- // recorder
5804
- "list_recorders",
5805
- "create_recorder",
5806
- "update_recorder_settings",
5807
- "update_recorder_questions",
5808
- "delete_recorder",
5809
- "generate_recorder_url",
5810
- "get_recorder_info",
5811
- "get_recorder_recordings",
5812
- "check_recorder_status",
5813
- "clone_recorder",
5814
- // text
5815
- "create_text_note",
5816
- "update_text_note",
5817
- "get_text_insight",
5818
- "reanalyze_text",
5819
- // webhooks
5820
- "list_webhooks",
5821
- "create_webhook",
5822
- "update_webhook",
5823
- "delete_webhook",
5824
- // workflows (high-level wrappers around media + upload tools)
5825
- "upload_and_analyze",
5826
- "upload_local_file"
5827
- ];
5828
-
5829
- // src/index.ts
6873
+ init_tool_names();
5830
6874
  var args = process.argv.slice(2);
5831
6875
  var cliCommands = [
5832
6876
  "config",