@speakai/mcp-server 1.12.3 → 1.13.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/README.md +46 -7
- package/dist/index.js +1367 -348
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3666,8 +3666,16 @@ function register10(server, client) {
|
|
|
3666
3666
|
registerSpeakTool(
|
|
3667
3667
|
server,
|
|
3668
3668
|
"list_automations",
|
|
3669
|
-
"List
|
|
3670
|
-
{
|
|
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
|
+
},
|
|
3671
3679
|
{
|
|
3672
3680
|
title: "List Automations",
|
|
3673
3681
|
readOnlyHint: true,
|
|
@@ -3675,9 +3683,35 @@ function register10(server, client) {
|
|
|
3675
3683
|
idempotentHint: true,
|
|
3676
3684
|
openWorldHint: false
|
|
3677
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
|
+
},
|
|
3678
3712
|
async () => {
|
|
3679
3713
|
try {
|
|
3680
|
-
const result = await api.get("/v1/automations");
|
|
3714
|
+
const result = await api.get("/v1/automations/list");
|
|
3681
3715
|
return {
|
|
3682
3716
|
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
|
|
3683
3717
|
};
|
|
@@ -3692,7 +3726,7 @@ function register10(server, client) {
|
|
|
3692
3726
|
registerSpeakTool(
|
|
3693
3727
|
server,
|
|
3694
3728
|
"get_automation",
|
|
3695
|
-
"Get detailed information about a specific automation rule.",
|
|
3729
|
+
"Get detailed information about a specific automation rule, including its trigger and step graph.",
|
|
3696
3730
|
{
|
|
3697
3731
|
automationId: import_zod11.z.string().min(1).describe("Unique identifier of the automation")
|
|
3698
3732
|
},
|
|
@@ -3719,21 +3753,40 @@ function register10(server, client) {
|
|
|
3719
3753
|
);
|
|
3720
3754
|
registerSpeakTool(
|
|
3721
3755
|
server,
|
|
3722
|
-
"
|
|
3723
|
-
"
|
|
3756
|
+
"get_automation_runs",
|
|
3757
|
+
"Get the run history (executions) for an automation, with paging and optional status filter.",
|
|
3724
3758
|
{
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
)
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
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
|
|
3736
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,
|
|
3737
3790
|
{
|
|
3738
3791
|
title: "Create Automation",
|
|
3739
3792
|
readOnlyHint: false,
|
|
@@ -3758,20 +3811,10 @@ function register10(server, client) {
|
|
|
3758
3811
|
registerSpeakTool(
|
|
3759
3812
|
server,
|
|
3760
3813
|
"update_automation",
|
|
3761
|
-
"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.",
|
|
3762
3815
|
{
|
|
3763
3816
|
automationId: import_zod11.z.string().min(1).describe("Unique identifier of the automation"),
|
|
3764
|
-
|
|
3765
|
-
trigger: import_zod11.z.record(import_zod11.z.unknown()).describe(
|
|
3766
|
-
'Trigger object. Keys: `type` (e.g. "folders") and `folderIds` (array of folder IDs).'
|
|
3767
|
-
),
|
|
3768
|
-
action: import_zod11.z.record(import_zod11.z.unknown()).describe(
|
|
3769
|
-
'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 }).'
|
|
3770
|
-
),
|
|
3771
|
-
description: import_zod11.z.string().optional().describe("Optional description"),
|
|
3772
|
-
isActive: import_zod11.z.boolean().optional().describe("Whether the automation is active (defaults to true)"),
|
|
3773
|
-
runType: import_zod11.z.string().optional().describe('Run type, e.g. "instant" (default) or "scheduled"'),
|
|
3774
|
-
schedule: import_zod11.z.record(import_zod11.z.unknown()).optional().describe("Schedule object for scheduled automations: { timePeriod, repeatAt }")
|
|
3817
|
+
...writeSchema
|
|
3775
3818
|
},
|
|
3776
3819
|
{
|
|
3777
3820
|
title: "Update Automation",
|
|
@@ -3782,10 +3825,7 @@ function register10(server, client) {
|
|
|
3782
3825
|
},
|
|
3783
3826
|
async ({ automationId, ...body }) => {
|
|
3784
3827
|
try {
|
|
3785
|
-
const result = await api.put(
|
|
3786
|
-
`/v1/automations/${automationId}`,
|
|
3787
|
-
body
|
|
3788
|
-
);
|
|
3828
|
+
const result = await api.put(`/v1/automations/${automationId}`, body);
|
|
3789
3829
|
return {
|
|
3790
3830
|
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
|
|
3791
3831
|
};
|
|
@@ -3813,9 +3853,204 @@ function register10(server, client) {
|
|
|
3813
3853
|
},
|
|
3814
3854
|
async ({ automationId }) => {
|
|
3815
3855
|
try {
|
|
3816
|
-
const result = await api.put(
|
|
3817
|
-
|
|
3818
|
-
|
|
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 });
|
|
3819
4054
|
return {
|
|
3820
4055
|
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
|
|
3821
4056
|
};
|
|
@@ -3828,13 +4063,26 @@ function register10(server, client) {
|
|
|
3828
4063
|
}
|
|
3829
4064
|
);
|
|
3830
4065
|
}
|
|
3831
|
-
var import_zod11;
|
|
4066
|
+
var import_zod11, STEPS_DESCRIPTION, TRIGGER_DESCRIPTION, writeSchema;
|
|
3832
4067
|
var init_automations = __esm({
|
|
3833
4068
|
"src/tools/automations.ts"() {
|
|
3834
4069
|
"use strict";
|
|
3835
4070
|
import_zod11 = require("zod");
|
|
3836
4071
|
init_helpers();
|
|
3837
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
|
+
};
|
|
3838
4086
|
}
|
|
3839
4087
|
});
|
|
3840
4088
|
|
|
@@ -4109,10 +4357,799 @@ function register13(server, client) {
|
|
|
4109
4357
|
idempotentHint: true,
|
|
4110
4358
|
openWorldHint: false
|
|
4111
4359
|
},
|
|
4112
|
-
async ({ clipId, ...params }) => {
|
|
4360
|
+
async ({ clipId, ...params }) => {
|
|
4361
|
+
try {
|
|
4362
|
+
const url = clipId ? `/v1/clips/${clipId}` : "/v1/clips";
|
|
4363
|
+
const result = await api.get(url, { params });
|
|
4364
|
+
return {
|
|
4365
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
|
|
4366
|
+
};
|
|
4367
|
+
} catch (err) {
|
|
4368
|
+
return {
|
|
4369
|
+
content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
|
|
4370
|
+
isError: true
|
|
4371
|
+
};
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
);
|
|
4375
|
+
registerSpeakTool(
|
|
4376
|
+
server,
|
|
4377
|
+
"update_clip",
|
|
4378
|
+
"Update a clip's title, description, or tags.",
|
|
4379
|
+
{
|
|
4380
|
+
clipId: import_zod14.z.string().min(1).describe("ID of the clip to update"),
|
|
4381
|
+
title: import_zod14.z.string().optional().describe("New title"),
|
|
4382
|
+
description: import_zod14.z.string().optional().describe("New description"),
|
|
4383
|
+
tags: import_zod14.z.array(import_zod14.z.string()).optional().describe("New tags")
|
|
4384
|
+
},
|
|
4385
|
+
{
|
|
4386
|
+
title: "Update Clip",
|
|
4387
|
+
readOnlyHint: false,
|
|
4388
|
+
destructiveHint: false,
|
|
4389
|
+
idempotentHint: true,
|
|
4390
|
+
openWorldHint: false
|
|
4391
|
+
},
|
|
4392
|
+
async ({ clipId, ...body }) => {
|
|
4393
|
+
try {
|
|
4394
|
+
const result = await api.put(`/v1/clips/${clipId}`, body);
|
|
4395
|
+
return {
|
|
4396
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
|
|
4397
|
+
};
|
|
4398
|
+
} catch (err) {
|
|
4399
|
+
return {
|
|
4400
|
+
content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
|
|
4401
|
+
isError: true
|
|
4402
|
+
};
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
);
|
|
4406
|
+
registerSpeakTool(
|
|
4407
|
+
server,
|
|
4408
|
+
"delete_clip",
|
|
4409
|
+
"Permanently delete a clip and its associated media file.",
|
|
4410
|
+
{
|
|
4411
|
+
clipId: import_zod14.z.string().min(1).describe("ID of the clip to delete")
|
|
4412
|
+
},
|
|
4413
|
+
{
|
|
4414
|
+
title: "Delete Clip",
|
|
4415
|
+
readOnlyHint: false,
|
|
4416
|
+
destructiveHint: true,
|
|
4417
|
+
idempotentHint: true,
|
|
4418
|
+
openWorldHint: false
|
|
4419
|
+
},
|
|
4420
|
+
async ({ clipId }) => {
|
|
4421
|
+
try {
|
|
4422
|
+
const result = await api.delete(`/v1/clips/${clipId}`);
|
|
4423
|
+
return {
|
|
4424
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
|
|
4425
|
+
};
|
|
4426
|
+
} catch (err) {
|
|
4427
|
+
return {
|
|
4428
|
+
content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
|
|
4429
|
+
isError: true
|
|
4430
|
+
};
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
);
|
|
4434
|
+
}
|
|
4435
|
+
var import_zod14;
|
|
4436
|
+
var init_clips = __esm({
|
|
4437
|
+
"src/tools/clips.ts"() {
|
|
4438
|
+
"use strict";
|
|
4439
|
+
import_zod14 = require("zod");
|
|
4440
|
+
init_helpers();
|
|
4441
|
+
init_client();
|
|
4442
|
+
init_dist();
|
|
4443
|
+
}
|
|
4444
|
+
});
|
|
4445
|
+
|
|
4446
|
+
// src/media-utils.ts
|
|
4447
|
+
function isVideoFile(filePath) {
|
|
4448
|
+
return VIDEO_EXTENSIONS.includes(path.extname(filePath).toLowerCase());
|
|
4449
|
+
}
|
|
4450
|
+
function getMimeType(filePath) {
|
|
4451
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
4452
|
+
const isVideo = isVideoFile(filePath);
|
|
4453
|
+
if (ext === ".mp4") return isVideo ? "video/mp4" : "audio/mp4";
|
|
4454
|
+
if (ext === ".webm") return isVideo ? "video/webm" : "audio/webm";
|
|
4455
|
+
return MIME_TYPES[ext] ?? (isVideo ? "video/mp4" : "audio/mpeg");
|
|
4456
|
+
}
|
|
4457
|
+
function detectMediaType(filePath) {
|
|
4458
|
+
return isVideoFile(filePath) ? "video" : "audio";
|
|
4459
|
+
}
|
|
4460
|
+
var path, VIDEO_EXTENSIONS, MIME_TYPES;
|
|
4461
|
+
var init_media_utils = __esm({
|
|
4462
|
+
"src/media-utils.ts"() {
|
|
4463
|
+
"use strict";
|
|
4464
|
+
path = __toESM(require("path"));
|
|
4465
|
+
VIDEO_EXTENSIONS = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".wmv"];
|
|
4466
|
+
MIME_TYPES = {
|
|
4467
|
+
".mp3": "audio/mpeg",
|
|
4468
|
+
".m4a": "audio/mp4",
|
|
4469
|
+
".wav": "audio/wav",
|
|
4470
|
+
".ogg": "audio/ogg",
|
|
4471
|
+
".flac": "audio/flac",
|
|
4472
|
+
".mov": "video/quicktime",
|
|
4473
|
+
".avi": "video/x-msvideo",
|
|
4474
|
+
".mkv": "video/x-matroska",
|
|
4475
|
+
".wmv": "video/x-ms-wmv"
|
|
4476
|
+
};
|
|
4477
|
+
}
|
|
4478
|
+
});
|
|
4479
|
+
|
|
4480
|
+
// src/tools/workflows.ts
|
|
4481
|
+
var workflows_exports = {};
|
|
4482
|
+
__export(workflows_exports, {
|
|
4483
|
+
register: () => register14
|
|
4484
|
+
});
|
|
4485
|
+
function register14(server, client) {
|
|
4486
|
+
const api = client ?? speakClient;
|
|
4487
|
+
registerSpeakTool(
|
|
4488
|
+
server,
|
|
4489
|
+
"upload_and_analyze",
|
|
4490
|
+
"Upload and transcribe media from a URL \u2014 a direct/public file URL, OR a shareable social/video link (YouTube, Instagram, TikTok, X, Facebook, Reddit, SoundCloud, and similar), which Speak resolves to the underlying media automatically. Returns media_id immediately; after this returns, poll get_media_status until state is 'processed' (typically 1-3 min for under 60min audio), then call get_media_insights for AI summaries. This async pattern is required for remote MCP transports \u2014 long blocking calls die at proxy idle timeouts. (Vimeo links are not yet supported.)",
|
|
4491
|
+
{
|
|
4492
|
+
url: import_zod15.z.string().describe("Direct/public media file URL, or a shareable social/video page link (e.g. an Instagram reel, TikTok, YouTube, or X post URL) \u2014 page links are resolved to the underlying media server-side. Pass the URL the user gave you as-is."),
|
|
4493
|
+
name: import_zod15.z.string().optional().describe("Display name for the media (defaults to filename from URL)"),
|
|
4494
|
+
mediaType: import_zod15.z.enum([MediaType.AUDIO, MediaType.VIDEO]).optional().describe("Media type (default: audio)"),
|
|
4495
|
+
sourceLanguage: import_zod15.z.string().optional().describe("BCP-47 language code (e.g., 'en-US', 'he-IL')"),
|
|
4496
|
+
folderId: import_zod15.z.string().optional().describe("Folder ID to place the media in"),
|
|
4497
|
+
tags: import_zod15.z.string().optional().describe("Comma-separated tags")
|
|
4498
|
+
},
|
|
4499
|
+
{
|
|
4500
|
+
title: "Upload and Analyze Media",
|
|
4501
|
+
readOnlyHint: false,
|
|
4502
|
+
destructiveHint: false,
|
|
4503
|
+
idempotentHint: false,
|
|
4504
|
+
openWorldHint: false
|
|
4505
|
+
},
|
|
4506
|
+
async (params) => {
|
|
4507
|
+
try {
|
|
4508
|
+
const uploadBody = {
|
|
4509
|
+
name: params.name ?? params.url.split("/").pop()?.split("?")[0] ?? "Upload",
|
|
4510
|
+
url: params.url,
|
|
4511
|
+
mediaType: params.mediaType ?? "audio"
|
|
4512
|
+
};
|
|
4513
|
+
if (params.sourceLanguage) uploadBody.sourceLanguage = params.sourceLanguage;
|
|
4514
|
+
if (params.folderId) uploadBody.folderId = params.folderId;
|
|
4515
|
+
if (params.tags) uploadBody.tags = params.tags;
|
|
4516
|
+
const uploadRes = await api.post("/v1/media/upload", uploadBody);
|
|
4517
|
+
const mediaId = uploadRes.data?.data?.mediaId;
|
|
4518
|
+
const state = uploadRes.data?.data?.state ?? "pending";
|
|
4519
|
+
if (!mediaId) {
|
|
4520
|
+
return {
|
|
4521
|
+
content: [{ type: "text", text: `Error: Upload succeeded but no mediaId returned.
|
|
4522
|
+
${JSON.stringify(uploadRes.data, null, 2)}` }],
|
|
4523
|
+
isError: true
|
|
4524
|
+
};
|
|
4525
|
+
}
|
|
4526
|
+
const result = {
|
|
4527
|
+
mediaId,
|
|
4528
|
+
state,
|
|
4529
|
+
message: "Upload accepted. Processing has started in the background.",
|
|
4530
|
+
nextSteps: [
|
|
4531
|
+
`1. Poll get_media_status with mediaId="${mediaId}" every 10-30 seconds.`,
|
|
4532
|
+
`2. When state is "processed" (typically 1-3 min for audio under 60 min), call get_media_insights for the AI summary and get_transcript for the full transcript.`,
|
|
4533
|
+
`3. If state becomes "failed", processing did not complete \u2014 surface the error to the user.`
|
|
4534
|
+
]
|
|
4535
|
+
};
|
|
4536
|
+
return {
|
|
4537
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
4538
|
+
};
|
|
4539
|
+
} catch (err) {
|
|
4540
|
+
return {
|
|
4541
|
+
content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
|
|
4542
|
+
isError: true
|
|
4543
|
+
};
|
|
4544
|
+
}
|
|
4545
|
+
}
|
|
4546
|
+
);
|
|
4547
|
+
registerSpeakTool(
|
|
4548
|
+
server,
|
|
4549
|
+
"upload_local_file",
|
|
4550
|
+
[
|
|
4551
|
+
"Upload a local file to Speak AI for transcription and analysis.",
|
|
4552
|
+
"Reads the file from disk, gets a pre-signed S3 URL, uploads the file, then creates the media entry.",
|
|
4553
|
+
"Works with any audio or video file on the local filesystem.",
|
|
4554
|
+
"After upload, use get_media_status to poll for completion, then get_transcript and get_media_insights."
|
|
4555
|
+
].join(" "),
|
|
4556
|
+
{
|
|
4557
|
+
filePath: import_zod15.z.string().describe("Absolute path to the local audio or video file"),
|
|
4558
|
+
name: import_zod15.z.string().optional().describe("Display name (defaults to filename)"),
|
|
4559
|
+
mediaType: import_zod15.z.enum([MediaType.AUDIO, MediaType.VIDEO]).optional().describe("Media type (auto-detected from extension if omitted)"),
|
|
4560
|
+
sourceLanguage: import_zod15.z.string().optional().describe("BCP-47 language code (e.g., 'en-US')"),
|
|
4561
|
+
folderId: import_zod15.z.string().optional().describe("Folder ID to place the media in"),
|
|
4562
|
+
tags: import_zod15.z.string().optional().describe("Comma-separated tags")
|
|
4563
|
+
},
|
|
4564
|
+
{
|
|
4565
|
+
title: "Upload Local File",
|
|
4566
|
+
readOnlyHint: false,
|
|
4567
|
+
destructiveHint: false,
|
|
4568
|
+
idempotentHint: false,
|
|
4569
|
+
openWorldHint: false
|
|
4570
|
+
},
|
|
4571
|
+
async (params) => {
|
|
4572
|
+
try {
|
|
4573
|
+
const filePath = params.filePath;
|
|
4574
|
+
if (!fs.existsSync(filePath)) {
|
|
4575
|
+
return {
|
|
4576
|
+
content: [{ type: "text", text: `Error: File not found: ${filePath}` }],
|
|
4577
|
+
isError: true
|
|
4578
|
+
};
|
|
4579
|
+
}
|
|
4580
|
+
const filename = path2.basename(filePath);
|
|
4581
|
+
const isVideo = isVideoFile(filePath);
|
|
4582
|
+
const mediaType = params.mediaType ?? detectMediaType(filePath);
|
|
4583
|
+
const mimeType = getMimeType(filePath);
|
|
4584
|
+
const signedRes = await api.get("/v1/media/upload/signedurl", {
|
|
4585
|
+
params: { isVideo, filename, mimeType }
|
|
4586
|
+
});
|
|
4587
|
+
const signedData = signedRes.data?.data;
|
|
4588
|
+
const uploadUrl = signedData?.preSignedUrl ?? signedData?.signedUrl ?? signedData?.url;
|
|
4589
|
+
if (!uploadUrl) {
|
|
4590
|
+
return {
|
|
4591
|
+
content: [{ type: "text", text: `Error: Could not get signed upload URL.
|
|
4592
|
+
${JSON.stringify(signedRes.data, null, 2)}` }],
|
|
4593
|
+
isError: true
|
|
4594
|
+
};
|
|
4595
|
+
}
|
|
4596
|
+
const fileBuffer = fs.readFileSync(filePath);
|
|
4597
|
+
const axios2 = (await import("axios")).default;
|
|
4598
|
+
await axios2.put(uploadUrl, fileBuffer, {
|
|
4599
|
+
headers: {
|
|
4600
|
+
"Content-Type": mimeType
|
|
4601
|
+
},
|
|
4602
|
+
maxBodyLength: Infinity,
|
|
4603
|
+
maxContentLength: Infinity
|
|
4604
|
+
});
|
|
4605
|
+
const createBody = {
|
|
4606
|
+
name: params.name ?? filename,
|
|
4607
|
+
url: uploadUrl.split("?")[0],
|
|
4608
|
+
// S3 URL without query params; server re-signs via CloudFront
|
|
4609
|
+
mediaType
|
|
4610
|
+
};
|
|
4611
|
+
if (params.sourceLanguage) createBody.sourceLanguage = params.sourceLanguage;
|
|
4612
|
+
if (params.folderId) createBody.folderId = params.folderId;
|
|
4613
|
+
if (params.tags) createBody.tags = params.tags;
|
|
4614
|
+
const createRes = await api.post("/v1/media/upload", createBody);
|
|
4615
|
+
const data = createRes.data?.data;
|
|
4616
|
+
return {
|
|
4617
|
+
content: [
|
|
4618
|
+
{
|
|
4619
|
+
type: "text",
|
|
4620
|
+
text: JSON.stringify(
|
|
4621
|
+
{
|
|
4622
|
+
mediaId: data?.mediaId,
|
|
4623
|
+
state: data?.state,
|
|
4624
|
+
message: `File uploaded successfully. Use get_media_status to poll until state is 'processed', then use get_transcript and get_media_insights.`
|
|
4625
|
+
},
|
|
4626
|
+
null,
|
|
4627
|
+
2
|
|
4628
|
+
)
|
|
4629
|
+
}
|
|
4630
|
+
]
|
|
4631
|
+
};
|
|
4632
|
+
} catch (err) {
|
|
4633
|
+
return {
|
|
4634
|
+
content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
|
|
4635
|
+
isError: true
|
|
4636
|
+
};
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
);
|
|
4640
|
+
}
|
|
4641
|
+
var import_zod15, fs, path2;
|
|
4642
|
+
var init_workflows = __esm({
|
|
4643
|
+
"src/tools/workflows.ts"() {
|
|
4644
|
+
"use strict";
|
|
4645
|
+
import_zod15 = require("zod");
|
|
4646
|
+
init_helpers();
|
|
4647
|
+
init_client();
|
|
4648
|
+
init_dist();
|
|
4649
|
+
fs = __toESM(require("fs"));
|
|
4650
|
+
path2 = __toESM(require("path"));
|
|
4651
|
+
init_media_utils();
|
|
4652
|
+
}
|
|
4653
|
+
});
|
|
4654
|
+
|
|
4655
|
+
// src/tools/users.ts
|
|
4656
|
+
var users_exports = {};
|
|
4657
|
+
__export(users_exports, {
|
|
4658
|
+
register: () => register15
|
|
4659
|
+
});
|
|
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 }) => {
|
|
4113
5151
|
try {
|
|
4114
|
-
const
|
|
4115
|
-
const result = await api.get(url, { params });
|
|
5152
|
+
const result = await api.put(`/v1/dashboards/${dashboardId}`, withBuiltWidgets(body));
|
|
4116
5153
|
return {
|
|
4117
5154
|
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
|
|
4118
5155
|
};
|
|
@@ -4126,24 +5163,21 @@ function register13(server, client) {
|
|
|
4126
5163
|
);
|
|
4127
5164
|
registerSpeakTool(
|
|
4128
5165
|
server,
|
|
4129
|
-
"
|
|
4130
|
-
"
|
|
5166
|
+
"delete_dashboard",
|
|
5167
|
+
"Soft-delete a dashboard. This also deactivates its public share link.",
|
|
4131
5168
|
{
|
|
4132
|
-
|
|
4133
|
-
title: import_zod14.z.string().optional().describe("New title"),
|
|
4134
|
-
description: import_zod14.z.string().optional().describe("New description"),
|
|
4135
|
-
tags: import_zod14.z.array(import_zod14.z.string()).optional().describe("New tags")
|
|
5169
|
+
dashboardId: import_zod17.z.string().min(1).describe("Dashboard business id to delete")
|
|
4136
5170
|
},
|
|
4137
5171
|
{
|
|
4138
|
-
title: "
|
|
5172
|
+
title: "Delete Dashboard",
|
|
4139
5173
|
readOnlyHint: false,
|
|
4140
|
-
destructiveHint:
|
|
5174
|
+
destructiveHint: true,
|
|
4141
5175
|
idempotentHint: true,
|
|
4142
5176
|
openWorldHint: false
|
|
4143
5177
|
},
|
|
4144
|
-
async ({
|
|
5178
|
+
async ({ dashboardId }) => {
|
|
4145
5179
|
try {
|
|
4146
|
-
const result = await api.
|
|
5180
|
+
const result = await api.delete(`/v1/dashboards/${dashboardId}`);
|
|
4147
5181
|
return {
|
|
4148
5182
|
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
|
|
4149
5183
|
};
|
|
@@ -4157,21 +5191,21 @@ function register13(server, client) {
|
|
|
4157
5191
|
);
|
|
4158
5192
|
registerSpeakTool(
|
|
4159
5193
|
server,
|
|
4160
|
-
"
|
|
4161
|
-
|
|
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.',
|
|
4162
5196
|
{
|
|
4163
|
-
|
|
5197
|
+
dashboardId: import_zod17.z.string().min(1).describe("Source dashboard business id to clone")
|
|
4164
5198
|
},
|
|
4165
5199
|
{
|
|
4166
|
-
title: "
|
|
5200
|
+
title: "Duplicate Dashboard",
|
|
4167
5201
|
readOnlyHint: false,
|
|
4168
|
-
destructiveHint:
|
|
4169
|
-
idempotentHint:
|
|
5202
|
+
destructiveHint: false,
|
|
5203
|
+
idempotentHint: false,
|
|
4170
5204
|
openWorldHint: false
|
|
4171
5205
|
},
|
|
4172
|
-
async ({
|
|
5206
|
+
async ({ dashboardId }) => {
|
|
4173
5207
|
try {
|
|
4174
|
-
const result = await api.
|
|
5208
|
+
const result = await api.post(`/v1/dashboards/${dashboardId}/duplicate`, {});
|
|
4175
5209
|
return {
|
|
4176
5210
|
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
|
|
4177
5211
|
};
|
|
@@ -4183,110 +5217,25 @@ function register13(server, client) {
|
|
|
4183
5217
|
}
|
|
4184
5218
|
}
|
|
4185
5219
|
);
|
|
4186
|
-
}
|
|
4187
|
-
var import_zod14;
|
|
4188
|
-
var init_clips = __esm({
|
|
4189
|
-
"src/tools/clips.ts"() {
|
|
4190
|
-
"use strict";
|
|
4191
|
-
import_zod14 = require("zod");
|
|
4192
|
-
init_helpers();
|
|
4193
|
-
init_client();
|
|
4194
|
-
init_dist();
|
|
4195
|
-
}
|
|
4196
|
-
});
|
|
4197
|
-
|
|
4198
|
-
// src/media-utils.ts
|
|
4199
|
-
function isVideoFile(filePath) {
|
|
4200
|
-
return VIDEO_EXTENSIONS.includes(path.extname(filePath).toLowerCase());
|
|
4201
|
-
}
|
|
4202
|
-
function getMimeType(filePath) {
|
|
4203
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
4204
|
-
const isVideo = isVideoFile(filePath);
|
|
4205
|
-
if (ext === ".mp4") return isVideo ? "video/mp4" : "audio/mp4";
|
|
4206
|
-
if (ext === ".webm") return isVideo ? "video/webm" : "audio/webm";
|
|
4207
|
-
return MIME_TYPES[ext] ?? (isVideo ? "video/mp4" : "audio/mpeg");
|
|
4208
|
-
}
|
|
4209
|
-
function detectMediaType(filePath) {
|
|
4210
|
-
return isVideoFile(filePath) ? "video" : "audio";
|
|
4211
|
-
}
|
|
4212
|
-
var path, VIDEO_EXTENSIONS, MIME_TYPES;
|
|
4213
|
-
var init_media_utils = __esm({
|
|
4214
|
-
"src/media-utils.ts"() {
|
|
4215
|
-
"use strict";
|
|
4216
|
-
path = __toESM(require("path"));
|
|
4217
|
-
VIDEO_EXTENSIONS = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".wmv"];
|
|
4218
|
-
MIME_TYPES = {
|
|
4219
|
-
".mp3": "audio/mpeg",
|
|
4220
|
-
".m4a": "audio/mp4",
|
|
4221
|
-
".wav": "audio/wav",
|
|
4222
|
-
".ogg": "audio/ogg",
|
|
4223
|
-
".flac": "audio/flac",
|
|
4224
|
-
".mov": "video/quicktime",
|
|
4225
|
-
".avi": "video/x-msvideo",
|
|
4226
|
-
".mkv": "video/x-matroska",
|
|
4227
|
-
".wmv": "video/x-ms-wmv"
|
|
4228
|
-
};
|
|
4229
|
-
}
|
|
4230
|
-
});
|
|
4231
|
-
|
|
4232
|
-
// src/tools/workflows.ts
|
|
4233
|
-
var workflows_exports = {};
|
|
4234
|
-
__export(workflows_exports, {
|
|
4235
|
-
register: () => register14
|
|
4236
|
-
});
|
|
4237
|
-
function register14(server, client) {
|
|
4238
|
-
const api = client ?? speakClient;
|
|
4239
5220
|
registerSpeakTool(
|
|
4240
5221
|
server,
|
|
4241
|
-
"
|
|
4242
|
-
"
|
|
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.",
|
|
4243
5224
|
{
|
|
4244
|
-
|
|
4245
|
-
name: import_zod15.z.string().optional().describe("Display name for the media (defaults to filename from URL)"),
|
|
4246
|
-
mediaType: import_zod15.z.enum([MediaType.AUDIO, MediaType.VIDEO]).optional().describe("Media type (default: audio)"),
|
|
4247
|
-
sourceLanguage: import_zod15.z.string().optional().describe("BCP-47 language code (e.g., 'en-US', 'he-IL')"),
|
|
4248
|
-
folderId: import_zod15.z.string().optional().describe("Folder ID to place the media in"),
|
|
4249
|
-
tags: import_zod15.z.string().optional().describe("Comma-separated tags")
|
|
5225
|
+
dashboardId: import_zod17.z.string().min(1).describe("Dashboard business id to share")
|
|
4250
5226
|
},
|
|
4251
5227
|
{
|
|
4252
|
-
title: "
|
|
5228
|
+
title: "Share Dashboard",
|
|
4253
5229
|
readOnlyHint: false,
|
|
4254
5230
|
destructiveHint: false,
|
|
4255
|
-
idempotentHint:
|
|
5231
|
+
idempotentHint: true,
|
|
4256
5232
|
openWorldHint: false
|
|
4257
5233
|
},
|
|
4258
|
-
async (
|
|
5234
|
+
async ({ dashboardId }) => {
|
|
4259
5235
|
try {
|
|
4260
|
-
const
|
|
4261
|
-
name: params.name ?? params.url.split("/").pop()?.split("?")[0] ?? "Upload",
|
|
4262
|
-
url: params.url,
|
|
4263
|
-
mediaType: params.mediaType ?? "audio"
|
|
4264
|
-
};
|
|
4265
|
-
if (params.sourceLanguage) uploadBody.sourceLanguage = params.sourceLanguage;
|
|
4266
|
-
if (params.folderId) uploadBody.folderId = params.folderId;
|
|
4267
|
-
if (params.tags) uploadBody.tags = params.tags;
|
|
4268
|
-
const uploadRes = await api.post("/v1/media/upload", uploadBody);
|
|
4269
|
-
const mediaId = uploadRes.data?.data?.mediaId;
|
|
4270
|
-
const state = uploadRes.data?.data?.state ?? "pending";
|
|
4271
|
-
if (!mediaId) {
|
|
4272
|
-
return {
|
|
4273
|
-
content: [{ type: "text", text: `Error: Upload succeeded but no mediaId returned.
|
|
4274
|
-
${JSON.stringify(uploadRes.data, null, 2)}` }],
|
|
4275
|
-
isError: true
|
|
4276
|
-
};
|
|
4277
|
-
}
|
|
4278
|
-
const result = {
|
|
4279
|
-
mediaId,
|
|
4280
|
-
state,
|
|
4281
|
-
message: "Upload accepted. Processing has started in the background.",
|
|
4282
|
-
nextSteps: [
|
|
4283
|
-
`1. Poll get_media_status with mediaId="${mediaId}" every 10-30 seconds.`,
|
|
4284
|
-
`2. When state is "processed" (typically 1-3 min for audio under 60 min), call get_media_insights for the AI summary and get_transcript for the full transcript.`,
|
|
4285
|
-
`3. If state becomes "failed", processing did not complete \u2014 surface the error to the user.`
|
|
4286
|
-
]
|
|
4287
|
-
};
|
|
5236
|
+
const result = await api.put(`/v1/dashboards/${dashboardId}/share`, {});
|
|
4288
5237
|
return {
|
|
4289
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
5238
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
|
|
4290
5239
|
};
|
|
4291
5240
|
} catch (err) {
|
|
4292
5241
|
return {
|
|
@@ -4298,88 +5247,21 @@ ${JSON.stringify(uploadRes.data, null, 2)}` }],
|
|
|
4298
5247
|
);
|
|
4299
5248
|
registerSpeakTool(
|
|
4300
5249
|
server,
|
|
4301
|
-
"
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
"Reads the file from disk, gets a pre-signed S3 URL, uploads the file, then creates the media entry.",
|
|
4305
|
-
"Works with any audio or video file on the local filesystem.",
|
|
4306
|
-
"After upload, use get_media_status to poll for completion, then get_transcript and get_media_insights."
|
|
4307
|
-
].join(" "),
|
|
4308
|
-
{
|
|
4309
|
-
filePath: import_zod15.z.string().describe("Absolute path to the local audio or video file"),
|
|
4310
|
-
name: import_zod15.z.string().optional().describe("Display name (defaults to filename)"),
|
|
4311
|
-
mediaType: import_zod15.z.enum([MediaType.AUDIO, MediaType.VIDEO]).optional().describe("Media type (auto-detected from extension if omitted)"),
|
|
4312
|
-
sourceLanguage: import_zod15.z.string().optional().describe("BCP-47 language code (e.g., 'en-US')"),
|
|
4313
|
-
folderId: import_zod15.z.string().optional().describe("Folder ID to place the media in"),
|
|
4314
|
-
tags: import_zod15.z.string().optional().describe("Comma-separated tags")
|
|
4315
|
-
},
|
|
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,
|
|
4316
5253
|
{
|
|
4317
|
-
title: "
|
|
4318
|
-
readOnlyHint:
|
|
5254
|
+
title: "Get Dashboard Speakers Insight",
|
|
5255
|
+
readOnlyHint: true,
|
|
4319
5256
|
destructiveHint: false,
|
|
4320
|
-
idempotentHint:
|
|
5257
|
+
idempotentHint: true,
|
|
4321
5258
|
openWorldHint: false
|
|
4322
5259
|
},
|
|
4323
|
-
async (
|
|
5260
|
+
async (body) => {
|
|
4324
5261
|
try {
|
|
4325
|
-
const
|
|
4326
|
-
if (!fs.existsSync(filePath)) {
|
|
4327
|
-
return {
|
|
4328
|
-
content: [{ type: "text", text: `Error: File not found: ${filePath}` }],
|
|
4329
|
-
isError: true
|
|
4330
|
-
};
|
|
4331
|
-
}
|
|
4332
|
-
const filename = path2.basename(filePath);
|
|
4333
|
-
const isVideo = isVideoFile(filePath);
|
|
4334
|
-
const mediaType = params.mediaType ?? detectMediaType(filePath);
|
|
4335
|
-
const mimeType = getMimeType(filePath);
|
|
4336
|
-
const signedRes = await api.get("/v1/media/upload/signedurl", {
|
|
4337
|
-
params: { isVideo, filename, mimeType }
|
|
4338
|
-
});
|
|
4339
|
-
const signedData = signedRes.data?.data;
|
|
4340
|
-
const uploadUrl = signedData?.preSignedUrl ?? signedData?.signedUrl ?? signedData?.url;
|
|
4341
|
-
if (!uploadUrl) {
|
|
4342
|
-
return {
|
|
4343
|
-
content: [{ type: "text", text: `Error: Could not get signed upload URL.
|
|
4344
|
-
${JSON.stringify(signedRes.data, null, 2)}` }],
|
|
4345
|
-
isError: true
|
|
4346
|
-
};
|
|
4347
|
-
}
|
|
4348
|
-
const fileBuffer = fs.readFileSync(filePath);
|
|
4349
|
-
const axios2 = (await import("axios")).default;
|
|
4350
|
-
await axios2.put(uploadUrl, fileBuffer, {
|
|
4351
|
-
headers: {
|
|
4352
|
-
"Content-Type": mimeType
|
|
4353
|
-
},
|
|
4354
|
-
maxBodyLength: Infinity,
|
|
4355
|
-
maxContentLength: Infinity
|
|
4356
|
-
});
|
|
4357
|
-
const createBody = {
|
|
4358
|
-
name: params.name ?? filename,
|
|
4359
|
-
url: uploadUrl.split("?")[0],
|
|
4360
|
-
// S3 URL without query params; server re-signs via CloudFront
|
|
4361
|
-
mediaType
|
|
4362
|
-
};
|
|
4363
|
-
if (params.sourceLanguage) createBody.sourceLanguage = params.sourceLanguage;
|
|
4364
|
-
if (params.folderId) createBody.folderId = params.folderId;
|
|
4365
|
-
if (params.tags) createBody.tags = params.tags;
|
|
4366
|
-
const createRes = await api.post("/v1/media/upload", createBody);
|
|
4367
|
-
const data = createRes.data?.data;
|
|
5262
|
+
const result = await api.post("/v1/dashboards/insights/speakers", body);
|
|
4368
5263
|
return {
|
|
4369
|
-
content: [
|
|
4370
|
-
{
|
|
4371
|
-
type: "text",
|
|
4372
|
-
text: JSON.stringify(
|
|
4373
|
-
{
|
|
4374
|
-
mediaId: data?.mediaId,
|
|
4375
|
-
state: data?.state,
|
|
4376
|
-
message: `File uploaded successfully. Use get_media_status to poll until state is 'processed', then use get_transcript and get_media_insights.`
|
|
4377
|
-
},
|
|
4378
|
-
null,
|
|
4379
|
-
2
|
|
4380
|
-
)
|
|
4381
|
-
}
|
|
4382
|
-
]
|
|
5264
|
+
content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }]
|
|
4383
5265
|
};
|
|
4384
5266
|
} catch (err) {
|
|
4385
5267
|
return {
|
|
@@ -4390,17 +5272,53 @@ ${JSON.stringify(signedRes.data, null, 2)}` }],
|
|
|
4390
5272
|
}
|
|
4391
5273
|
);
|
|
4392
5274
|
}
|
|
4393
|
-
var
|
|
4394
|
-
var
|
|
4395
|
-
"src/tools/
|
|
5275
|
+
var import_zod17, FILTER_LIST_DESCRIPTION, widgetInputSchema, writeFields, SPEAKERS_FILTER_SCHEMA;
|
|
5276
|
+
var init_dashboards = __esm({
|
|
5277
|
+
"src/tools/dashboards.ts"() {
|
|
4396
5278
|
"use strict";
|
|
4397
|
-
|
|
5279
|
+
import_zod17 = require("zod");
|
|
4398
5280
|
init_helpers();
|
|
4399
5281
|
init_client();
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
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
|
+
};
|
|
4404
5322
|
}
|
|
4405
5323
|
});
|
|
4406
5324
|
|
|
@@ -4432,6 +5350,8 @@ var init_tools = __esm({
|
|
|
4432
5350
|
init_analytics();
|
|
4433
5351
|
init_clips();
|
|
4434
5352
|
init_workflows();
|
|
5353
|
+
init_users();
|
|
5354
|
+
init_dashboards();
|
|
4435
5355
|
modules = [
|
|
4436
5356
|
media_exports,
|
|
4437
5357
|
text_exports,
|
|
@@ -4446,7 +5366,9 @@ var init_tools = __esm({
|
|
|
4446
5366
|
webhooks_exports,
|
|
4447
5367
|
analytics_exports,
|
|
4448
5368
|
clips_exports,
|
|
4449
|
-
workflows_exports
|
|
5369
|
+
workflows_exports,
|
|
5370
|
+
users_exports,
|
|
5371
|
+
dashboards_exports
|
|
4450
5372
|
];
|
|
4451
5373
|
}
|
|
4452
5374
|
});
|
|
@@ -4563,8 +5485,8 @@ function registerPrompts(server) {
|
|
|
4563
5485
|
"analyze-meeting",
|
|
4564
5486
|
"Upload a meeting recording and get a full analysis \u2014 transcript, insights, action items, and key takeaways.",
|
|
4565
5487
|
{
|
|
4566
|
-
url:
|
|
4567
|
-
name:
|
|
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)")
|
|
4568
5490
|
},
|
|
4569
5491
|
async ({ url, name }) => ({
|
|
4570
5492
|
messages: [
|
|
@@ -4596,8 +5518,8 @@ function registerPrompts(server) {
|
|
|
4596
5518
|
"research-across-media",
|
|
4597
5519
|
"Search for themes, patterns, or topics across multiple recordings or your entire media library.",
|
|
4598
5520
|
{
|
|
4599
|
-
topic:
|
|
4600
|
-
folder:
|
|
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)")
|
|
4601
5523
|
},
|
|
4602
5524
|
async ({ topic, folder }) => ({
|
|
4603
5525
|
messages: [
|
|
@@ -4630,8 +5552,8 @@ function registerPrompts(server) {
|
|
|
4630
5552
|
"meeting-brief",
|
|
4631
5553
|
"Prepare a brief from recent meetings \u2014 pull transcripts, extract decisions, and summarize open items.",
|
|
4632
5554
|
{
|
|
4633
|
-
days:
|
|
4634
|
-
folder:
|
|
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)")
|
|
4635
5557
|
},
|
|
4636
5558
|
async ({ days, folder }) => {
|
|
4637
5559
|
const lookback = parseInt(days ?? "7");
|
|
@@ -4668,11 +5590,148 @@ function registerPrompts(server) {
|
|
|
4668
5590
|
}
|
|
4669
5591
|
);
|
|
4670
5592
|
}
|
|
4671
|
-
var
|
|
5593
|
+
var import_zod18;
|
|
4672
5594
|
var init_prompts = __esm({
|
|
4673
5595
|
"src/prompts.ts"() {
|
|
4674
5596
|
"use strict";
|
|
4675
|
-
|
|
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
|
+
];
|
|
4676
5735
|
}
|
|
4677
5736
|
});
|
|
4678
5737
|
|
|
@@ -5719,8 +6778,71 @@ function createCli() {
|
|
|
5719
6778
|
process.exit(1);
|
|
5720
6779
|
}
|
|
5721
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
|
+
});
|
|
5722
6837
|
return program;
|
|
5723
6838
|
}
|
|
6839
|
+
function safeParse(text) {
|
|
6840
|
+
try {
|
|
6841
|
+
return JSON.parse(text);
|
|
6842
|
+
} catch {
|
|
6843
|
+
return text;
|
|
6844
|
+
}
|
|
6845
|
+
}
|
|
5724
6846
|
var import_commander, import_readline;
|
|
5725
6847
|
var init_cli = __esm({
|
|
5726
6848
|
"src/cli/index.ts"() {
|
|
@@ -5748,110 +6870,7 @@ init_tools();
|
|
|
5748
6870
|
init_resources();
|
|
5749
6871
|
init_prompts();
|
|
5750
6872
|
init_client();
|
|
5751
|
-
|
|
5752
|
-
// src/tool-names.ts
|
|
5753
|
-
var SPEAK_MCP_TOOL_NAMES = [
|
|
5754
|
-
// analytics
|
|
5755
|
-
"get_media_statistics",
|
|
5756
|
-
// automations
|
|
5757
|
-
"list_automations",
|
|
5758
|
-
"get_automation",
|
|
5759
|
-
"create_automation",
|
|
5760
|
-
"update_automation",
|
|
5761
|
-
"toggle_automation_status",
|
|
5762
|
-
// clips
|
|
5763
|
-
"get_clips",
|
|
5764
|
-
"create_clip",
|
|
5765
|
-
"update_clip",
|
|
5766
|
-
"delete_clip",
|
|
5767
|
-
// embed
|
|
5768
|
-
"create_embed",
|
|
5769
|
-
"update_embed",
|
|
5770
|
-
"check_embed",
|
|
5771
|
-
"get_embed_iframe_url",
|
|
5772
|
-
// exports
|
|
5773
|
-
"export_media",
|
|
5774
|
-
"export_multiple_media",
|
|
5775
|
-
// fields
|
|
5776
|
-
"list_fields",
|
|
5777
|
-
"create_field",
|
|
5778
|
-
"update_field",
|
|
5779
|
-
"update_multiple_fields",
|
|
5780
|
-
// folders
|
|
5781
|
-
"list_folders",
|
|
5782
|
-
"create_folder",
|
|
5783
|
-
"update_folder",
|
|
5784
|
-
"delete_folder",
|
|
5785
|
-
"get_folder_info",
|
|
5786
|
-
"clone_folder",
|
|
5787
|
-
"get_folder_views",
|
|
5788
|
-
"get_all_folder_views",
|
|
5789
|
-
"create_folder_view",
|
|
5790
|
-
"update_folder_view",
|
|
5791
|
-
"clone_folder_view",
|
|
5792
|
-
// media
|
|
5793
|
-
"get_signed_upload_url",
|
|
5794
|
-
"upload_media",
|
|
5795
|
-
"get_media_status",
|
|
5796
|
-
"get_media_insights",
|
|
5797
|
-
"get_transcript",
|
|
5798
|
-
"list_media",
|
|
5799
|
-
"search_media",
|
|
5800
|
-
"delete_media",
|
|
5801
|
-
"update_media_metadata",
|
|
5802
|
-
"toggle_media_favorite",
|
|
5803
|
-
"reanalyze_media",
|
|
5804
|
-
"get_captions",
|
|
5805
|
-
"list_supported_languages",
|
|
5806
|
-
"update_transcript_speakers",
|
|
5807
|
-
"bulk_update_transcript_speakers",
|
|
5808
|
-
"bulk_move_media",
|
|
5809
|
-
// meeting
|
|
5810
|
-
"list_meeting_events",
|
|
5811
|
-
"schedule_meeting_event",
|
|
5812
|
-
"remove_assistant_from_meeting",
|
|
5813
|
-
"delete_scheduled_assistant",
|
|
5814
|
-
"get_live_meeting_transcript",
|
|
5815
|
-
// prompt
|
|
5816
|
-
"ask_magic_prompt",
|
|
5817
|
-
"list_prompts",
|
|
5818
|
-
"get_favorite_prompts",
|
|
5819
|
-
"toggle_prompt_favorite",
|
|
5820
|
-
"get_chat_history",
|
|
5821
|
-
"get_chat_messages",
|
|
5822
|
-
"update_chat_title",
|
|
5823
|
-
"delete_chat_message",
|
|
5824
|
-
"submit_chat_feedback",
|
|
5825
|
-
"retry_magic_prompt",
|
|
5826
|
-
"export_chat_answer",
|
|
5827
|
-
"get_chat_statistics",
|
|
5828
|
-
// recorder
|
|
5829
|
-
"list_recorders",
|
|
5830
|
-
"create_recorder",
|
|
5831
|
-
"update_recorder_settings",
|
|
5832
|
-
"update_recorder_questions",
|
|
5833
|
-
"delete_recorder",
|
|
5834
|
-
"generate_recorder_url",
|
|
5835
|
-
"get_recorder_info",
|
|
5836
|
-
"get_recorder_recordings",
|
|
5837
|
-
"check_recorder_status",
|
|
5838
|
-
"clone_recorder",
|
|
5839
|
-
// text
|
|
5840
|
-
"create_text_note",
|
|
5841
|
-
"update_text_note",
|
|
5842
|
-
"get_text_insight",
|
|
5843
|
-
"reanalyze_text",
|
|
5844
|
-
// webhooks
|
|
5845
|
-
"list_webhooks",
|
|
5846
|
-
"create_webhook",
|
|
5847
|
-
"update_webhook",
|
|
5848
|
-
"delete_webhook",
|
|
5849
|
-
// workflows (high-level wrappers around media + upload tools)
|
|
5850
|
-
"upload_and_analyze",
|
|
5851
|
-
"upload_local_file"
|
|
5852
|
-
];
|
|
5853
|
-
|
|
5854
|
-
// src/index.ts
|
|
6873
|
+
init_tool_names();
|
|
5855
6874
|
var args = process.argv.slice(2);
|
|
5856
6875
|
var cliCommands = [
|
|
5857
6876
|
"config",
|