agenthub-multiagent-mcp 1.29.0 → 1.31.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.
- package/dist/client.d.ts +71 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +74 -0
- package/dist/client.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +357 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/tools.test.js +125 -2
- package/dist/tools/tools.test.js.map +1 -1
- package/package.json +4 -1
- package/skills/catalog.json +8 -0
- package/skills/commands/close-session.md +144 -144
- package/skills/manifest.json +40 -34
- package/skills/skills/code-brain-hook/SKILL.md +61 -0
package/dist/tools/index.js
CHANGED
|
@@ -723,6 +723,64 @@ export function registerTools() {
|
|
|
723
723
|
required: ["id"],
|
|
724
724
|
},
|
|
725
725
|
},
|
|
726
|
+
{
|
|
727
|
+
name: "calendar_schedule_meeting",
|
|
728
|
+
description: "Schedule a Google Calendar meeting with an auto-generated Google Meet link. Idempotent on dedupe_key (same key returns the same event). Requires the calendar capability to be enabled on the server.",
|
|
729
|
+
inputSchema: {
|
|
730
|
+
type: "object",
|
|
731
|
+
properties: {
|
|
732
|
+
title: { type: "string", description: "Meeting title" },
|
|
733
|
+
start: { type: "string", description: "Start time (RFC3339, e.g. 2026-06-02T10:00:00Z)" },
|
|
734
|
+
end: { type: "string", description: "End time (RFC3339); must be after start" },
|
|
735
|
+
attendee_emails: {
|
|
736
|
+
type: "array",
|
|
737
|
+
items: { type: "string" },
|
|
738
|
+
description: "Invitee email addresses",
|
|
739
|
+
},
|
|
740
|
+
description: { type: "string", description: "Optional meeting description" },
|
|
741
|
+
timezone: { type: "string", description: "Optional IANA timezone; defaults to the server default (UTC)" },
|
|
742
|
+
recurrence: { type: "string", description: "Optional RRULE, e.g. RRULE:FREQ=WEEKLY;BYDAY=MO" },
|
|
743
|
+
dedupe_key: { type: "string", description: "Idempotency key; same key returns the same event" },
|
|
744
|
+
source: {
|
|
745
|
+
type: "object",
|
|
746
|
+
description: "Optional linkage to a blocker/task; a task source resolves the assignee as an attendee",
|
|
747
|
+
properties: {
|
|
748
|
+
kind: { type: "string", enum: ["blocker", "task", "manual"] },
|
|
749
|
+
id: { type: "string" },
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
required: ["title", "start", "end", "dedupe_key"],
|
|
754
|
+
},
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
name: "email_send",
|
|
758
|
+
description: "Send an email via Gmail to an allowlisted recipient (external partners not in Slack). Recipient must pass the allowlist; per-org daily cap applies. Supports templates (onboarding_invite, task_assignment, digest) or raw body_text/body_html.",
|
|
759
|
+
inputSchema: {
|
|
760
|
+
type: "object",
|
|
761
|
+
properties: {
|
|
762
|
+
to: {
|
|
763
|
+
type: "array",
|
|
764
|
+
items: { type: "string" },
|
|
765
|
+
description: "Recipient email addresses (each must pass the allowlist)",
|
|
766
|
+
},
|
|
767
|
+
subject: { type: "string", description: "Email subject (required for ad-hoc sends)" },
|
|
768
|
+
body_text: { type: "string", description: "Plain-text body (required for ad-hoc sends)" },
|
|
769
|
+
body_html: { type: "string", description: "Optional HTML body" },
|
|
770
|
+
template: {
|
|
771
|
+
type: "string",
|
|
772
|
+
enum: ["onboarding_invite", "task_assignment", "digest"],
|
|
773
|
+
description: "Optional template; when set, body_* are derived from template_vars",
|
|
774
|
+
},
|
|
775
|
+
template_vars: {
|
|
776
|
+
type: "object",
|
|
777
|
+
description: "Variables for the chosen template (e.g. {name, login_url})",
|
|
778
|
+
},
|
|
779
|
+
dedupe_key: { type: "string", description: "Optional idempotency key; a repeat is a no-op" },
|
|
780
|
+
},
|
|
781
|
+
required: ["to"],
|
|
782
|
+
},
|
|
783
|
+
},
|
|
726
784
|
{
|
|
727
785
|
name: "list_channels",
|
|
728
786
|
description: "List all available channels",
|
|
@@ -731,6 +789,156 @@ export function registerTools() {
|
|
|
731
789
|
properties: {},
|
|
732
790
|
},
|
|
733
791
|
},
|
|
792
|
+
{
|
|
793
|
+
name: "list_projects",
|
|
794
|
+
description: "List the projects in your org (id, key, name). Use this to discover your project_id (UUID) — required by project-scoped tools like create_epic, feature_graph_sync_openspec, and roadmap_get. The server scopes results to your org automatically.",
|
|
795
|
+
inputSchema: {
|
|
796
|
+
type: "object",
|
|
797
|
+
properties: {
|
|
798
|
+
include_archived: {
|
|
799
|
+
type: "boolean",
|
|
800
|
+
description: "Include archived projects (default false)",
|
|
801
|
+
},
|
|
802
|
+
},
|
|
803
|
+
},
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
name: "api_search",
|
|
807
|
+
description: "Search the API knowledge graph — endpoints (REST/MCP/GraphQL/gRPC) extracted from indexed repos across the org. Use this BEFORE building a new endpoint to check if it already exists or to match a sibling project's contract. Org-wide read.",
|
|
808
|
+
inputSchema: {
|
|
809
|
+
type: "object",
|
|
810
|
+
properties: {
|
|
811
|
+
query: { type: "string", description: "Free-text match over endpoint identity + description" },
|
|
812
|
+
project_id: { type: "string", description: "Filter to one project (optional)" },
|
|
813
|
+
surface: { type: "string", description: "Filter by surface: rest | mcp | graphql | grpc (optional)" },
|
|
814
|
+
},
|
|
815
|
+
},
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
name: "api_get_endpoint",
|
|
819
|
+
description: "Get one API endpoint's full contract (handler ref, schemas, auth, description) plus its edges, by identity (e.g. \"POST /api/x\").",
|
|
820
|
+
inputSchema: {
|
|
821
|
+
type: "object",
|
|
822
|
+
properties: {
|
|
823
|
+
identity: { type: "string", description: "Endpoint identity, e.g. \"POST /api/x\" (REST) or a tool name (MCP)" },
|
|
824
|
+
project_id: { type: "string", description: "Project to look in (optional; org-wide if omitted)" },
|
|
825
|
+
},
|
|
826
|
+
required: ["identity"],
|
|
827
|
+
},
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
name: "api_endpoint_impact",
|
|
831
|
+
description: "Show an endpoint's blast radius before you change it: the consumers, models, and database tables it touches (plus linked openspec changes / feature-graph nodes).",
|
|
832
|
+
inputSchema: {
|
|
833
|
+
type: "object",
|
|
834
|
+
properties: {
|
|
835
|
+
identity: { type: "string", description: "Endpoint identity, e.g. \"POST /api/x\"" },
|
|
836
|
+
project_id: { type: "string", description: "Project to look in (optional)" },
|
|
837
|
+
},
|
|
838
|
+
required: ["identity"],
|
|
839
|
+
},
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
name: "api_graph",
|
|
843
|
+
description: "Return a project's API endpoint subgraph (endpoints with their edges) — for an overview of what a project exposes.",
|
|
844
|
+
inputSchema: {
|
|
845
|
+
type: "object",
|
|
846
|
+
properties: {
|
|
847
|
+
project_id: { type: "string", description: "Project to render (optional; org-wide if omitted)" },
|
|
848
|
+
surface: { type: "string", description: "Filter by surface: rest | mcp | graphql | grpc (optional)" },
|
|
849
|
+
},
|
|
850
|
+
},
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
name: "action_item_create",
|
|
854
|
+
description: "Create a lightweight action item — a tracked to-do assigned to a person (e.g. a task captured from chat). Distinct from the openspec-linked ticket hierarchy; promote to a ticket task when it becomes real dev work. Requires the deployment to have action items enabled.",
|
|
855
|
+
inputSchema: {
|
|
856
|
+
type: "object",
|
|
857
|
+
properties: {
|
|
858
|
+
title: { type: "string", description: "Short description of the action item" },
|
|
859
|
+
assignee_user_id: {
|
|
860
|
+
type: "string",
|
|
861
|
+
description: "AgentHub user id to assign to (optional; created unassigned if omitted)",
|
|
862
|
+
},
|
|
863
|
+
details: { type: "string", description: "Optional longer details" },
|
|
864
|
+
due_at: { type: "string", description: "Optional due timestamp (RFC3339)" },
|
|
865
|
+
source_kind: {
|
|
866
|
+
type: "string",
|
|
867
|
+
description: "Origin: manual | slack_mention | slack_command | slack_shortcut | slack_reaction | slack_autoparse (default manual)",
|
|
868
|
+
},
|
|
869
|
+
source_ref: { type: "string", description: "Optional provenance link (e.g. a Slack permalink)" },
|
|
870
|
+
},
|
|
871
|
+
required: ["title"],
|
|
872
|
+
},
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
name: "action_item_list",
|
|
876
|
+
description: "List action items in your org, optionally filtered by assignee user id and/or status (open | snoozed | done | cancelled).",
|
|
877
|
+
inputSchema: {
|
|
878
|
+
type: "object",
|
|
879
|
+
properties: {
|
|
880
|
+
assignee: { type: "string", description: "Filter by assignee AgentHub user id" },
|
|
881
|
+
status: {
|
|
882
|
+
type: "string",
|
|
883
|
+
description: "Filter by status: open | snoozed | done | cancelled",
|
|
884
|
+
},
|
|
885
|
+
},
|
|
886
|
+
},
|
|
887
|
+
},
|
|
888
|
+
{
|
|
889
|
+
name: "action_item_complete",
|
|
890
|
+
description: "Mark an action item done.",
|
|
891
|
+
inputSchema: {
|
|
892
|
+
type: "object",
|
|
893
|
+
properties: {
|
|
894
|
+
id: { type: "string", description: "Action item id" },
|
|
895
|
+
},
|
|
896
|
+
required: ["id"],
|
|
897
|
+
},
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
name: "action_item_snooze",
|
|
901
|
+
description: "Snooze an action item until a given time (RFC3339).",
|
|
902
|
+
inputSchema: {
|
|
903
|
+
type: "object",
|
|
904
|
+
properties: {
|
|
905
|
+
id: { type: "string", description: "Action item id" },
|
|
906
|
+
until: { type: "string", description: "RFC3339 timestamp to snooze until" },
|
|
907
|
+
},
|
|
908
|
+
required: ["id", "until"],
|
|
909
|
+
},
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
name: "action_item_promote",
|
|
913
|
+
description: "Promote an action item into a tracked ticket task under a story (when an ad-hoc item turns out to be real dev work). Links the two; completing the ticket later marks the action item done.",
|
|
914
|
+
inputSchema: {
|
|
915
|
+
type: "object",
|
|
916
|
+
properties: {
|
|
917
|
+
id: { type: "string", description: "Action item id to promote" },
|
|
918
|
+
story_id: { type: "string", description: "Target story id the new task will live under" },
|
|
919
|
+
},
|
|
920
|
+
required: ["id", "story_id"],
|
|
921
|
+
},
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
name: "notify_prefs_set",
|
|
925
|
+
description: "Set your action-item digest + reminder preferences (the authenticated user's). Controls which channel the daily digest is delivered on and whether overdue reminders are sent.",
|
|
926
|
+
inputSchema: {
|
|
927
|
+
type: "object",
|
|
928
|
+
properties: {
|
|
929
|
+
digest_channel: {
|
|
930
|
+
type: "string",
|
|
931
|
+
description: "Where to deliver the digest: auto (Slack DM if linked, else email) | slack | email | both",
|
|
932
|
+
},
|
|
933
|
+
digest_hour_local: {
|
|
934
|
+
type: "number",
|
|
935
|
+
description: "Hour of day (0-23, local to tz) to send the daily digest",
|
|
936
|
+
},
|
|
937
|
+
tz: { type: "string", description: "IANA timezone name (e.g. Asia/Kolkata); default UTC" },
|
|
938
|
+
reminders_enabled: { type: "boolean", description: "Whether to send overdue reminders" },
|
|
939
|
+
},
|
|
940
|
+
},
|
|
941
|
+
},
|
|
734
942
|
{
|
|
735
943
|
name: "join_channel",
|
|
736
944
|
description: "Subscribe to a channel to receive its messages",
|
|
@@ -1678,7 +1886,29 @@ export function registerTools() {
|
|
|
1678
1886
|
description: "Get the TOON-encoded org brain context package for this agent. Includes own session history, blockers, org decisions, and alerts.",
|
|
1679
1887
|
inputSchema: {
|
|
1680
1888
|
type: "object",
|
|
1681
|
-
properties: {
|
|
1889
|
+
properties: {
|
|
1890
|
+
include_code: {
|
|
1891
|
+
type: "boolean",
|
|
1892
|
+
description: "add-code-brain: when true, append a code_index section with top org-wide code/doc matches relevant to recent sessions. Default false — behaviour is byte-identical to pre-change when omitted.",
|
|
1893
|
+
},
|
|
1894
|
+
},
|
|
1895
|
+
},
|
|
1896
|
+
},
|
|
1897
|
+
{
|
|
1898
|
+
name: "brain_code_recall",
|
|
1899
|
+
description: "add-code-brain: search the org-wide indexed code + docs (Markdown / RST / OpenAPI / HTML / Jupyter / PDF + tree-sitter symbols). Use when org_search returns nothing for a code or architecture question, or when you need to discover files/symbols that no agent has written down. Read-only.",
|
|
1900
|
+
inputSchema: {
|
|
1901
|
+
type: "object",
|
|
1902
|
+
properties: {
|
|
1903
|
+
query: { type: "string", description: "Free-text query — symbol name, doc title fragment, or natural-language phrase" },
|
|
1904
|
+
k: { type: "number", description: "Max hits per kind (default 5, max 50)" },
|
|
1905
|
+
kinds: {
|
|
1906
|
+
type: "array",
|
|
1907
|
+
items: { type: "string", enum: ["docs", "symbols"] },
|
|
1908
|
+
description: "Restrict to docs and/or symbols. Default both.",
|
|
1909
|
+
},
|
|
1910
|
+
},
|
|
1911
|
+
required: ["query"],
|
|
1682
1912
|
},
|
|
1683
1913
|
},
|
|
1684
1914
|
{
|
|
@@ -2826,10 +3056,107 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
2826
3056
|
const result = await client.getAgent(args.id);
|
|
2827
3057
|
return wrapWithPendingItems(client, agentId, result, context);
|
|
2828
3058
|
}
|
|
3059
|
+
case "calendar_schedule_meeting": {
|
|
3060
|
+
const result = await client.scheduleMeeting({
|
|
3061
|
+
title: args.title,
|
|
3062
|
+
start: args.start,
|
|
3063
|
+
end: args.end,
|
|
3064
|
+
attendee_emails: args.attendee_emails,
|
|
3065
|
+
description: args.description,
|
|
3066
|
+
timezone: args.timezone,
|
|
3067
|
+
recurrence: args.recurrence,
|
|
3068
|
+
dedupe_key: args.dedupe_key,
|
|
3069
|
+
source: args.source,
|
|
3070
|
+
});
|
|
3071
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3072
|
+
}
|
|
3073
|
+
case "email_send": {
|
|
3074
|
+
const result = await client.sendEmail({
|
|
3075
|
+
to: args.to,
|
|
3076
|
+
subject: args.subject,
|
|
3077
|
+
body_text: args.body_text,
|
|
3078
|
+
body_html: args.body_html,
|
|
3079
|
+
template: args.template,
|
|
3080
|
+
template_vars: args.template_vars,
|
|
3081
|
+
dedupe_key: args.dedupe_key,
|
|
3082
|
+
});
|
|
3083
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3084
|
+
}
|
|
2829
3085
|
case "list_channels": {
|
|
2830
3086
|
const result = await client.listChannels();
|
|
2831
3087
|
return wrapWithPendingItems(client, agentId, result, context);
|
|
2832
3088
|
}
|
|
3089
|
+
case "list_projects": {
|
|
3090
|
+
// Org-scoped discovery — no agent registration required. Lets an agent
|
|
3091
|
+
// find its project_id (UUID) for project-scoped tools.
|
|
3092
|
+
const result = await client.listProjects(args.include_archived);
|
|
3093
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3094
|
+
}
|
|
3095
|
+
// add-api-knowledge-graph §3 — org-wide read of the API graph.
|
|
3096
|
+
case "api_search": {
|
|
3097
|
+
const result = await client.apiSearch({
|
|
3098
|
+
query: args.query,
|
|
3099
|
+
project_id: args.project_id,
|
|
3100
|
+
surface: args.surface,
|
|
3101
|
+
});
|
|
3102
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3103
|
+
}
|
|
3104
|
+
case "api_get_endpoint": {
|
|
3105
|
+
const result = await client.apiGetEndpoint(args.identity, args.project_id);
|
|
3106
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3107
|
+
}
|
|
3108
|
+
case "api_endpoint_impact": {
|
|
3109
|
+
const result = await client.apiEndpointImpact(args.identity, args.project_id);
|
|
3110
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3111
|
+
}
|
|
3112
|
+
case "api_graph": {
|
|
3113
|
+
const result = await client.apiGraph({
|
|
3114
|
+
project_id: args.project_id,
|
|
3115
|
+
surface: args.surface,
|
|
3116
|
+
});
|
|
3117
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3118
|
+
}
|
|
3119
|
+
// add-action-items Phase 1 — org-scoped (auth via the agent's connect
|
|
3120
|
+
// token); no agentId guard, mirroring list_projects.
|
|
3121
|
+
case "action_item_create": {
|
|
3122
|
+
const result = await client.createActionItem({
|
|
3123
|
+
title: args.title,
|
|
3124
|
+
assignee_user_id: args.assignee_user_id,
|
|
3125
|
+
details: args.details,
|
|
3126
|
+
due_at: args.due_at,
|
|
3127
|
+
source_kind: args.source_kind,
|
|
3128
|
+
source_ref: args.source_ref,
|
|
3129
|
+
});
|
|
3130
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3131
|
+
}
|
|
3132
|
+
case "action_item_list": {
|
|
3133
|
+
const result = await client.listActionItems({
|
|
3134
|
+
assignee: args.assignee,
|
|
3135
|
+
status: args.status,
|
|
3136
|
+
});
|
|
3137
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3138
|
+
}
|
|
3139
|
+
case "action_item_complete": {
|
|
3140
|
+
const result = await client.completeActionItem(args.id);
|
|
3141
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3142
|
+
}
|
|
3143
|
+
case "action_item_snooze": {
|
|
3144
|
+
const result = await client.snoozeActionItem(args.id, args.until);
|
|
3145
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3146
|
+
}
|
|
3147
|
+
case "action_item_promote": {
|
|
3148
|
+
const result = await client.promoteActionItem(args.id, args.story_id);
|
|
3149
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3150
|
+
}
|
|
3151
|
+
case "notify_prefs_set": {
|
|
3152
|
+
const result = await client.setNotifyPrefs({
|
|
3153
|
+
digest_channel: args.digest_channel,
|
|
3154
|
+
digest_hour_local: args.digest_hour_local,
|
|
3155
|
+
tz: args.tz,
|
|
3156
|
+
reminders_enabled: args.reminders_enabled,
|
|
3157
|
+
});
|
|
3158
|
+
return wrapWithPendingItems(client, agentId, result, context);
|
|
3159
|
+
}
|
|
2833
3160
|
case "join_channel": {
|
|
2834
3161
|
if (!agentId)
|
|
2835
3162
|
throw new Error("Not registered. Call agent_register first.");
|
|
@@ -3710,7 +4037,35 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
3710
4037
|
case "brain_get_context": {
|
|
3711
4038
|
if (!agentId)
|
|
3712
4039
|
throw new Error("Not registered. Call agent_register first.");
|
|
3713
|
-
|
|
4040
|
+
const includeCode = Boolean(args.include_code);
|
|
4041
|
+
const qs = includeCode ? "?include_code=true" : "";
|
|
4042
|
+
// Use raw GET so the include_code flag round-trips without a typed
|
|
4043
|
+
// client helper. Server-side decides whether to append code_index.
|
|
4044
|
+
return client.get(`/brain/context/${encodeURIComponent(agentId)}${qs}`);
|
|
4045
|
+
}
|
|
4046
|
+
case "brain_code_recall": {
|
|
4047
|
+
const query = args.query || "";
|
|
4048
|
+
if (!query)
|
|
4049
|
+
throw new Error("query is required");
|
|
4050
|
+
const k = args.k || 5;
|
|
4051
|
+
const kindsArr = args.kinds || ["docs", "symbols"];
|
|
4052
|
+
const params = new URLSearchParams();
|
|
4053
|
+
params.set("q", query);
|
|
4054
|
+
params.set("k", String(k));
|
|
4055
|
+
params.set("kinds", kindsArr.join(","));
|
|
4056
|
+
try {
|
|
4057
|
+
return await client.get(`/brain/code/search?${params.toString()}`);
|
|
4058
|
+
}
|
|
4059
|
+
catch (e) {
|
|
4060
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4061
|
+
if (msg.includes("code_brain_disabled")) {
|
|
4062
|
+
return {
|
|
4063
|
+
error: "code_brain_disabled",
|
|
4064
|
+
message: "The code brain is disabled on this server. It defaults to enabled — ask the operator to unset AGENTHUB_CODE_BRAIN_ENABLED (or set it back to true) and restart.",
|
|
4065
|
+
};
|
|
4066
|
+
}
|
|
4067
|
+
throw e;
|
|
4068
|
+
}
|
|
3714
4069
|
}
|
|
3715
4070
|
case "brain_list_sessions": {
|
|
3716
4071
|
const targetAgent = args.agent_id || agentId;
|