feat(hex): expand API coverage, fix ID trimming, add cursor pagination (#5372)

* feat(hex): expand API coverage, fix ID trimming, add cursor pagination

- Add hex_update_collection, hex_create_group, hex_update_group,
  hex_delete_group, hex_deactivate_user tools + block wiring
- Add missing run_project fields (viewId, notifications) and
  get_project_runs runTriggerFilter
- Add after/before cursor pagination to list_projects, list_groups,
  list_data_connections, list_collections
- Add .trim() on all interpolated ID path params to guard against
  copy-paste whitespace

* fix(hex): guard JSON.parse on user-supplied array/object params

Wrap JSON.parse calls for memberUserIds, addUserIds, removeUserIds,
inputParams, and notifications in try/catch so malformed input throws
a clear error instead of an opaque parse exception.

* fix(hex): forward empty collection description on update

update_collection's params() mapping only copied collectionDescription
when truthy, so clearing it to an empty string in the UI never reached
the API. Untouched fields resolve to null (not undefined), so use a
loose null check to distinguish "cleared" from "never touched" without
sending description: null on every unrelated update.

* fix(hex): close remaining API coverage gaps found via raw OpenAPI spec

Pulled Hex's actual OpenAPI spec (static.hex.site/openapi.json) as
ground truth for a final verification pass:

- list_users was missing after/before cursor pagination and the
  userIds filter, which the spec confirms it supports (an earlier
  doc-summary pass had incorrectly flagged this as unverifiable)
- list_users response also exposes lastLoginDate per user, not
  previously surfaced
- list_projects was missing includeComponents, includeTrashed,
  creatorEmail, ownerEmail, collectionId, categories, sortBy, and
  sortDirection filters that the spec confirms are real query params

* fix(hex): trim remaining user-suppliable IDs for consistency

Trim collectionId (list_projects filter), viewId (run_project body),
and group member UUIDs (create_group/update_group) to match the
.trim() convention already applied to path-segment IDs elsewhere.

* fix(hex): validate parsed JSON is actually an array before iterating

categories, memberUserIds, addUserIds, and removeUserIds could parse
as valid JSON that isn't an array (e.g. an object), which would throw
an opaque "not iterable"/"map is not a function" error deeper in the
call. Validate Array.isArray after parsing and fail with a clear
message instead.

* fix(hex): validate array element types, send explicit ALL trigger filter

- notifications now gets the same Array.isArray guard already applied
  to categories/memberUserIds/addUserIds/removeUserIds
- member/category array elements are now validated as strings before
  .trim()/append, instead of crashing on non-string entries
- runTriggerFilter "All" option now sends the documented ALL enum
  value explicitly instead of relying on omission
This commit is contained in:
Waleed
2026-07-02 11:14:14 -07:00
committed by GitHub
parent 997740790d
commit 11c7a36f11
24 changed files with 1101 additions and 26 deletions
+324 -12
View File
@@ -8,7 +8,7 @@ export const HexBlock: BlockConfig<HexResponse> = {
name: 'Hex',
description: 'Run and manage Hex projects',
longDescription:
'Integrate Hex into your workflow. Run projects, check run status, manage collections and groups, list users, and view data connections. Requires a Hex API token.',
'Integrate Hex into your workflow. Run projects, check run status, manage collections and groups (including membership and deactivating users), list users, and view data connections. Requires a Hex API token.',
docsLink: 'https://docs.sim.ai/integrations/hex',
category: 'tools',
integrationType: IntegrationType.Analytics,
@@ -36,8 +36,13 @@ export const HexBlock: BlockConfig<HexResponse> = {
{ label: 'List Collections', id: 'list_collections' },
{ label: 'Get Collection', id: 'get_collection' },
{ label: 'Create Collection', id: 'create_collection' },
{ label: 'Update Collection', id: 'update_collection' },
{ label: 'List Data Connections', id: 'list_data_connections' },
{ label: 'Get Data Connection', id: 'get_data_connection' },
{ label: 'Create Group', id: 'create_group' },
{ label: 'Update Group', id: 'update_group' },
{ label: 'Delete Group', id: 'delete_group' },
{ label: 'Deactivate User', id: 'deactivate_user' },
],
value: () => 'run_project',
},
@@ -107,6 +112,39 @@ Example:
generationType: 'json-object',
},
},
{
id: 'viewId',
title: 'Saved View ID',
type: 'short-input',
placeholder: 'Enter a SavedView UUID (optional)',
condition: { field: 'operation', value: 'run_project' },
mode: 'advanced',
},
{
id: 'notifications',
title: 'Notifications',
type: 'code',
placeholder: '[{"type": "FAILURE", "slackChannelIds": ["C0123456789"]}]',
condition: { field: 'operation', value: 'run_project' },
mode: 'advanced',
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at creating Hex run notification configs.
Generate ONLY the raw JSON array based on the user's request.
The output MUST be a single, valid JSON array, starting with [ and ending with ].
Current value: {context}
Do not include any explanations, markdown formatting, or other text outside the JSON array.
Each item's "type" must be one of ALL, SUCCESS, or FAILURE. Optional fields: includeSuccessScreenshot (boolean), slackChannelIds, userIds, groupIds (arrays of strings).
Example:
[{"type": "FAILURE", "slackChannelIds": ["C0123456789"], "includeSuccessScreenshot": false}]`,
placeholder: 'Describe who should be notified and when...',
generationType: 'json-object',
},
},
{
id: 'projectStatus',
title: 'Status',
@@ -130,28 +168,119 @@ Example:
value: () => '',
condition: { field: 'operation', value: 'get_project_runs' },
},
{
id: 'runTriggerFilter',
title: 'Trigger Filter',
type: 'dropdown',
options: [
{ label: 'All', id: 'ALL' },
{ label: 'API', id: 'API' },
{ label: 'Scheduled', id: 'SCHEDULED' },
{ label: 'App Refresh', id: 'APP_REFRESH' },
],
value: () => 'ALL',
condition: { field: 'operation', value: 'get_project_runs' },
mode: 'advanced',
},
{
id: 'groupIdInput',
title: 'Group ID',
type: 'short-input',
placeholder: 'Enter group UUID',
condition: { field: 'operation', value: 'get_group' },
required: { field: 'operation', value: 'get_group' },
condition: { field: 'operation', value: ['get_group', 'update_group', 'delete_group'] },
required: { field: 'operation', value: ['get_group', 'update_group', 'delete_group'] },
},
{
id: 'groupName',
title: 'Group Name',
type: 'short-input',
placeholder: 'Enter group name',
condition: { field: 'operation', value: ['create_group', 'update_group'] },
required: { field: 'operation', value: 'create_group' },
},
{
id: 'groupMemberUserIds',
title: 'Initial Member User IDs',
type: 'code',
placeholder: '["uuid1", "uuid2"]',
condition: { field: 'operation', value: 'create_group' },
mode: 'advanced',
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at creating JSON arrays of user UUIDs.
Generate ONLY the raw JSON array of user ID strings based on the user's request.
The output MUST be a single, valid JSON array of strings, starting with [ and ending with ].
Current value: {context}
Do not include any explanations, markdown formatting, or other text outside the JSON array.
Example:
["a1b2c3d4-0000-0000-0000-000000000000", "e5f6a7b8-0000-0000-0000-000000000000"]`,
placeholder: 'Describe which users to add...',
generationType: 'json-object',
},
},
{
id: 'groupAddUserIds',
title: 'Add Member User IDs',
type: 'code',
placeholder: '["uuid1", "uuid2"]',
condition: { field: 'operation', value: 'update_group' },
mode: 'advanced',
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at creating JSON arrays of user UUIDs.
Generate ONLY the raw JSON array of user ID strings to add based on the user's request.
The output MUST be a single, valid JSON array of strings, starting with [ and ending with ].
Current value: {context}
Do not include any explanations, markdown formatting, or other text outside the JSON array.`,
placeholder: 'Describe which users to add...',
generationType: 'json-object',
},
},
{
id: 'groupRemoveUserIds',
title: 'Remove Member User IDs',
type: 'code',
placeholder: '["uuid1", "uuid2"]',
condition: { field: 'operation', value: 'update_group' },
mode: 'advanced',
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at creating JSON arrays of user UUIDs.
Generate ONLY the raw JSON array of user ID strings to remove based on the user's request.
The output MUST be a single, valid JSON array of strings, starting with [ and ending with ].
Current value: {context}
Do not include any explanations, markdown formatting, or other text outside the JSON array.`,
placeholder: 'Describe which users to remove...',
generationType: 'json-object',
},
},
{
id: 'collectionId',
title: 'Collection ID',
type: 'short-input',
placeholder: 'Enter collection UUID',
condition: { field: 'operation', value: 'get_collection' },
required: { field: 'operation', value: 'get_collection' },
condition: {
field: 'operation',
value: ['get_collection', 'update_collection', 'list_projects'],
},
required: { field: 'operation', value: ['get_collection', 'update_collection'] },
},
{
id: 'collectionName',
title: 'Collection Name',
type: 'short-input',
placeholder: 'Enter collection name',
condition: { field: 'operation', value: 'create_collection' },
condition: { field: 'operation', value: ['create_collection', 'update_collection'] },
required: { field: 'operation', value: 'create_collection' },
},
{
@@ -159,7 +288,7 @@ Example:
title: 'Description',
type: 'long-input',
placeholder: 'Optional description for the collection',
condition: { field: 'operation', value: 'create_collection' },
condition: { field: 'operation', value: ['create_collection', 'update_collection'] },
},
{
id: 'dataConnectionId',
@@ -169,6 +298,14 @@ Example:
condition: { field: 'operation', value: 'get_data_connection' },
required: { field: 'operation', value: 'get_data_connection' },
},
{
id: 'userId',
title: 'User ID',
type: 'short-input',
placeholder: 'Enter user UUID',
condition: { field: 'operation', value: 'deactivate_user' },
required: { field: 'operation', value: 'deactivate_user' },
},
{
id: 'apiKey',
title: 'API Key',
@@ -240,6 +377,20 @@ Example:
condition: { field: 'operation', value: 'list_projects' },
mode: 'advanced',
},
{
id: 'includeComponents',
title: 'Include Components',
type: 'switch',
condition: { field: 'operation', value: 'list_projects' },
mode: 'advanced',
},
{
id: 'includeTrashed',
title: 'Include Trashed',
type: 'switch',
condition: { field: 'operation', value: 'list_projects' },
mode: 'advanced',
},
{
id: 'statusFilter',
title: 'Status Filter',
@@ -253,6 +404,57 @@ Example:
condition: { field: 'operation', value: 'list_projects' },
mode: 'advanced',
},
{
id: 'creatorEmail',
title: 'Creator Email',
type: 'short-input',
placeholder: 'Filter by creator email (optional)',
condition: { field: 'operation', value: 'list_projects' },
mode: 'advanced',
},
{
id: 'ownerEmail',
title: 'Owner Email',
type: 'short-input',
placeholder: 'Filter by owner email (optional)',
condition: { field: 'operation', value: 'list_projects' },
mode: 'advanced',
},
{
id: 'categories',
title: 'Categories',
type: 'code',
placeholder: '["Marketing", "Finance"]',
condition: { field: 'operation', value: 'list_projects' },
mode: 'advanced',
},
{
id: 'sortBy',
title: 'Sort By',
type: 'dropdown',
options: [
{ label: 'Default', id: '' },
{ label: 'Created At', id: 'CREATED_AT' },
{ label: 'Last Edited At', id: 'LAST_EDITED_AT' },
{ label: 'Last Published At', id: 'LAST_PUBLISHED_AT' },
],
value: () => '',
condition: { field: 'operation', value: 'list_projects' },
mode: 'advanced',
},
{
id: 'sortDirection',
title: 'Sort Direction',
type: 'dropdown',
options: [
{ label: 'Default', id: '' },
{ label: 'Ascending', id: 'ASC' },
{ label: 'Descending', id: 'DESC' },
],
value: () => '',
condition: { field: 'operation', value: 'list_projects' },
mode: 'advanced',
},
{
id: 'groupId',
title: 'Filter by Group',
@@ -261,12 +463,57 @@ Example:
condition: { field: 'operation', value: 'list_users' },
mode: 'advanced',
},
{
id: 'userIds',
title: 'Filter by User IDs',
type: 'short-input',
placeholder: 'Comma-separated user UUIDs (optional)',
condition: { field: 'operation', value: 'list_users' },
mode: 'advanced',
},
{
id: 'after',
title: 'After Cursor',
type: 'short-input',
placeholder: 'Cursor for the next page',
condition: {
field: 'operation',
value: [
'list_projects',
'list_groups',
'list_collections',
'list_data_connections',
'list_users',
],
},
mode: 'advanced',
},
{
id: 'before',
title: 'Before Cursor',
type: 'short-input',
placeholder: 'Cursor for the previous page',
condition: {
field: 'operation',
value: [
'list_projects',
'list_groups',
'list_collections',
'list_data_connections',
'list_users',
],
},
mode: 'advanced',
},
],
tools: {
access: [
'hex_cancel_run',
'hex_create_collection',
'hex_create_group',
'hex_deactivate_user',
'hex_delete_group',
'hex_get_collection',
'hex_get_data_connection',
'hex_get_group',
@@ -280,6 +527,8 @@ Example:
'hex_list_projects',
'hex_list_users',
'hex_run_project',
'hex_update_collection',
'hex_update_group',
'hex_update_project',
],
config: {
@@ -313,10 +562,20 @@ Example:
return 'hex_get_collection'
case 'create_collection':
return 'hex_create_collection'
case 'update_collection':
return 'hex_update_collection'
case 'list_data_connections':
return 'hex_list_data_connections'
case 'get_data_connection':
return 'hex_get_data_connection'
case 'create_group':
return 'hex_create_group'
case 'update_group':
return 'hex_update_group'
case 'delete_group':
return 'hex_delete_group'
case 'deactivate_user':
return 'hex_deactivate_user'
default:
return 'hex_run_project'
}
@@ -330,11 +589,26 @@ Example:
if (op === 'update_project' && params.projectStatus) result.status = params.projectStatus
if (op === 'get_project_runs' && params.runStatusFilter)
result.statusFilter = params.runStatusFilter
if (op === 'get_group' && params.groupIdInput) result.groupId = params.groupIdInput
if (
(op === 'get_group' || op === 'update_group' || op === 'delete_group') &&
params.groupIdInput
)
result.groupId = params.groupIdInput
if (op === 'list_users' && params.groupId) result.groupId = params.groupId
if (op === 'create_collection' && params.collectionName) result.name = params.collectionName
if ((op === 'create_collection' || op === 'update_collection') && params.collectionName)
result.name = params.collectionName
if (op === 'create_collection' && params.collectionDescription)
result.description = params.collectionDescription
if (op === 'update_collection' && params.collectionDescription != null)
result.description = params.collectionDescription
if ((op === 'create_group' || op === 'update_group') && params.groupName)
result.name = params.groupName
if (op === 'create_group' && params.groupMemberUserIds)
result.memberUserIds = params.groupMemberUserIds
if (op === 'update_group' && params.groupAddUserIds)
result.addUserIds = params.groupAddUserIds
if (op === 'update_group' && params.groupRemoveUserIds)
result.removeUserIds = params.groupRemoveUserIds
return result
},
@@ -360,21 +634,42 @@ Example:
type: 'boolean',
description: 'Use cached SQL results instead of re-running queries',
},
viewId: { type: 'string', description: 'SavedView UUID to use for the project run' },
notifications: {
type: 'json',
description: 'Notification details to deliver once the run completes',
},
projectStatus: {
type: 'string',
description: 'New project status name (custom workspace status label)',
},
limit: { type: 'number', description: 'Max number of results to return' },
offset: { type: 'number', description: 'Offset for paginated results' },
after: { type: 'string', description: 'Cursor to fetch results after' },
before: { type: 'string', description: 'Cursor to fetch results before' },
includeArchived: { type: 'boolean', description: 'Include archived projects' },
includeComponents: { type: 'boolean', description: 'Include components in results' },
includeTrashed: { type: 'boolean', description: 'Include trashed projects in results' },
statusFilter: { type: 'string', description: 'Filter projects by status' },
creatorEmail: { type: 'string', description: 'Filter projects by creator email' },
ownerEmail: { type: 'string', description: 'Filter projects by owner email' },
categories: { type: 'json', description: 'Filter projects by category names' },
sortBy: { type: 'string', description: 'Sort field for list results' },
sortDirection: { type: 'string', description: 'Sort direction for list results' },
runStatusFilter: { type: 'string', description: 'Filter runs by status' },
runTriggerFilter: { type: 'string', description: 'Filter runs by trigger source' },
groupId: { type: 'string', description: 'Filter users by group UUID' },
groupIdInput: { type: 'string', description: 'Group UUID for get group' },
userIds: { type: 'string', description: 'Comma-separated user UUIDs to filter by' },
groupIdInput: { type: 'string', description: 'Group UUID for get/update/delete group' },
groupName: { type: 'string', description: 'Group name' },
groupMemberUserIds: { type: 'json', description: 'Initial member user UUIDs for new group' },
groupAddUserIds: { type: 'json', description: 'User UUIDs to add to the group' },
groupRemoveUserIds: { type: 'json', description: 'User UUIDs to remove from the group' },
collectionId: { type: 'string', description: 'Collection UUID' },
collectionName: { type: 'string', description: 'Collection name' },
collectionDescription: { type: 'string', description: 'Collection description' },
dataConnectionId: { type: 'string', description: 'Data connection UUID' },
userId: { type: 'string', description: 'User UUID' },
},
outputs: {
@@ -415,7 +710,10 @@ Example:
description:
'List of runs with runId, status, runUrl, startTime, endTime, elapsedTime, projectVersion',
},
users: { type: 'json', description: 'List of users with id, name, email, role' },
users: {
type: 'json',
description: 'List of users with id, name, email, role, lastLoginDate',
},
groups: { type: 'json', description: 'List of groups with id, name, createdAt' },
collections: {
type: 'json',
@@ -437,8 +735,15 @@ Example:
creator: { type: 'json', description: 'Creator details ({ email, id })' },
owner: { type: 'json', description: 'Owner details ({ email })' },
total: { type: 'number', description: 'Total results returned' },
// Cancel output
// Cancel / delete / deactivate output
success: { type: 'boolean', description: 'Whether the operation succeeded' },
groupId: { type: 'string', description: 'Group UUID' },
userId: { type: 'string', description: 'User UUID' },
// Pagination
nextPage: { type: 'string', description: 'Cursor for the next page of runs' },
previousPage: { type: 'string', description: 'Cursor for the previous page of runs' },
after: { type: 'string', description: 'Cursor for the next page of results' },
before: { type: 'string', description: 'Cursor for the previous page of results' },
// Data connection flags
connectViaSsh: { type: 'boolean', description: 'SSH tunneling enabled' },
includeMagic: { type: 'boolean', description: 'Magic AI features enabled' },
@@ -546,5 +851,12 @@ export const HexBlockMeta = {
content:
'# Inventory Projects\n\nMap what projects and data sources exist in the workspace.\n\n## Steps\n1. List projects and capture IDs, names, and owners.\n2. List collections and get details to see how projects are grouped.\n3. List data connections to map which sources power the projects.\n4. Cross-reference projects to their collections and data connections.\n\n## Output\nReturn an inventory of projects grouped by collection, each annotated with its data connections. Useful for governance and cleanup.',
},
{
name: 'onboard-offboard-teammate',
description:
'Add a new teammate to the right Hex groups on hire, or deactivate and remove them on departure.',
content:
'# Onboard/Offboard Teammate\n\nManage workspace access as people join or leave the team.\n\n## Steps\n1. List users to resolve the target user by name or email, and list groups to resolve the relevant group by name.\n2. For onboarding: add the user to the appropriate group(s) via group update.\n3. For offboarding: remove the user from their groups via group update, then deactivate the user account.\n4. Confirm the change by getting the group or listing users filtered by group.\n\n## Output\nReturn the user and group IDs affected and the action taken (added, removed, deactivated). Flag if the user or group could not be resolved.',
},
],
} as const satisfies BlockMeta
+1 -1
View File
@@ -30,7 +30,7 @@ export const cancelRunTool: ToolConfig<HexCancelRunParams, HexCancelRunResponse>
request: {
url: (params) =>
`https://app.hex.tech/api/v1/projects/${params.projectId}/runs/${params.runId}`,
`https://app.hex.tech/api/v1/projects/${params.projectId.trim()}/runs/${params.runId.trim()}`,
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
+78
View File
@@ -0,0 +1,78 @@
import type { HexCreateGroupParams, HexCreateGroupResponse } from '@/tools/hex/types'
import type { ToolConfig } from '@/tools/types'
export const createGroupTool: ToolConfig<HexCreateGroupParams, HexCreateGroupResponse> = {
id: 'hex_create_group',
name: 'Hex Create Group',
description: 'Create a new group in the Hex workspace, optionally with initial members.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Hex API token (Personal or Workspace)',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name for the new group',
},
memberUserIds: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'JSON array of user UUIDs to add as initial group members (e.g., ["uuid1", "uuid2"])',
},
},
request: {
url: 'https://app.hex.tech/api/v1/groups',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = { name: params.name }
if (params.memberUserIds) {
let userIds: unknown
try {
userIds =
typeof params.memberUserIds === 'string'
? JSON.parse(params.memberUserIds)
: params.memberUserIds
} catch {
throw new Error('memberUserIds must be a valid JSON array of user UUID strings')
}
if (!Array.isArray(userIds) || !userIds.every((id) => typeof id === 'string')) {
throw new Error('memberUserIds must be a valid JSON array of user UUID strings')
}
body.members = { users: userIds.map((id: string) => ({ id: id.trim() })) }
}
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id ?? null,
name: data.name ?? null,
createdAt: data.createdAt ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Newly created group UUID' },
name: { type: 'string', description: 'Group name' },
createdAt: { type: 'string', description: 'Creation timestamp' },
},
}
+60
View File
@@ -0,0 +1,60 @@
import type { HexDeactivateUserParams, HexDeactivateUserResponse } from '@/tools/hex/types'
import type { ToolConfig } from '@/tools/types'
export const deactivateUserTool: ToolConfig<HexDeactivateUserParams, HexDeactivateUserResponse> = {
id: 'hex_deactivate_user',
name: 'Hex Deactivate User',
description: 'Deactivate a user in the Hex workspace.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Hex API token (Personal or Workspace)',
},
userId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The UUID of the user to deactivate',
},
},
request: {
url: (params) => `https://app.hex.tech/api/v1/users/${params.userId.trim()}/deactivate`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params) => {
if (response.status === 204 || response.ok) {
return {
success: true,
output: {
success: true,
userId: params?.userId ?? '',
},
}
}
const data = await response.json().catch(() => ({}))
return {
success: false,
output: {
success: false,
userId: params?.userId ?? '',
},
error: (data as Record<string, string>).message ?? 'Failed to deactivate user',
}
},
outputs: {
success: { type: 'boolean', description: 'Whether the user was successfully deactivated' },
userId: { type: 'string', description: 'User UUID that was deactivated' },
},
}
+60
View File
@@ -0,0 +1,60 @@
import type { HexDeleteGroupParams, HexDeleteGroupResponse } from '@/tools/hex/types'
import type { ToolConfig } from '@/tools/types'
export const deleteGroupTool: ToolConfig<HexDeleteGroupParams, HexDeleteGroupResponse> = {
id: 'hex_delete_group',
name: 'Hex Delete Group',
description: 'Delete a group from the Hex workspace.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Hex API token (Personal or Workspace)',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The UUID of the group to delete',
},
},
request: {
url: (params) => `https://app.hex.tech/api/v1/groups/${params.groupId.trim()}`,
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response, params) => {
if (response.status === 204 || response.ok) {
return {
success: true,
output: {
success: true,
groupId: params?.groupId ?? '',
},
}
}
const data = await response.json().catch(() => ({}))
return {
success: false,
output: {
success: false,
groupId: params?.groupId ?? '',
},
error: (data as Record<string, string>).message ?? 'Failed to delete group',
}
},
outputs: {
success: { type: 'boolean', description: 'Whether the group was successfully deleted' },
groupId: { type: 'string', description: 'Group UUID that was deleted' },
},
}
+1 -1
View File
@@ -23,7 +23,7 @@ export const getCollectionTool: ToolConfig<HexGetCollectionParams, HexGetCollect
},
request: {
url: (params) => `https://app.hex.tech/api/v1/collections/${params.collectionId}`,
url: (params) => `https://app.hex.tech/api/v1/collections/${params.collectionId.trim()}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
+2 -1
View File
@@ -27,7 +27,8 @@ export const getDataConnectionTool: ToolConfig<
},
request: {
url: (params) => `https://app.hex.tech/api/v1/data-connections/${params.dataConnectionId}`,
url: (params) =>
`https://app.hex.tech/api/v1/data-connections/${params.dataConnectionId.trim()}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
+1 -1
View File
@@ -23,7 +23,7 @@ export const getGroupTool: ToolConfig<HexGetGroupParams, HexGetGroupResponse> =
},
request: {
url: (params) => `https://app.hex.tech/api/v1/groups/${params.groupId}`,
url: (params) => `https://app.hex.tech/api/v1/groups/${params.groupId.trim()}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
+1 -1
View File
@@ -24,7 +24,7 @@ export const getProjectTool: ToolConfig<HexGetProjectParams, HexGetProjectRespon
},
request: {
url: (params) => `https://app.hex.tech/api/v1/projects/${params.projectId}`,
url: (params) => `https://app.hex.tech/api/v1/projects/${params.projectId.trim()}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
+16 -1
View File
@@ -40,6 +40,12 @@ export const getProjectRunsTool: ToolConfig<HexGetProjectRunsParams, HexGetProje
description:
'Filter by run status: PENDING, RUNNING, ERRORED, COMPLETED, KILLED, UNABLE_TO_ALLOCATE_KERNEL',
},
runTriggerFilter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by how the run was triggered: ALL, API, SCHEDULED, or APP_REFRESH',
},
},
request: {
@@ -48,8 +54,9 @@ export const getProjectRunsTool: ToolConfig<HexGetProjectRunsParams, HexGetProje
if (params.limit) searchParams.set('limit', String(params.limit))
if (params.offset) searchParams.set('offset', String(params.offset))
if (params.statusFilter) searchParams.set('statusFilter', params.statusFilter)
if (params.runTriggerFilter) searchParams.set('runTriggerFilter', params.runTriggerFilter)
const qs = searchParams.toString()
return `https://app.hex.tech/api/v1/projects/${params.projectId}/runs${qs ? `?${qs}` : ''}`
return `https://app.hex.tech/api/v1/projects/${params.projectId.trim()}/runs${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
@@ -78,6 +85,8 @@ export const getProjectRunsTool: ToolConfig<HexGetProjectRunsParams, HexGetProje
})),
total: runs.length,
traceId: data.traceId ?? null,
nextPage: data.nextPage ?? null,
previousPage: data.previousPage ?? null,
},
}
},
@@ -111,5 +120,11 @@ export const getProjectRunsTool: ToolConfig<HexGetProjectRunsParams, HexGetProje
},
total: { type: 'number', description: 'Total number of runs returned' },
traceId: { type: 'string', description: 'Top-level trace ID', optional: true },
nextPage: { type: 'string', description: 'Cursor for the next page of runs', optional: true },
previousPage: {
type: 'string',
description: 'Cursor for the previous page of runs',
optional: true,
},
},
}
+1 -1
View File
@@ -37,7 +37,7 @@ export const getQueriedTablesTool: ToolConfig<
const searchParams = new URLSearchParams()
if (params.limit) searchParams.set('limit', String(params.limit))
const qs = searchParams.toString()
return `https://app.hex.tech/api/v1/projects/${params.projectId}/queriedTables${qs ? `?${qs}` : ''}`
return `https://app.hex.tech/api/v1/projects/${params.projectId.trim()}/queriedTables${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
+1 -1
View File
@@ -31,7 +31,7 @@ export const getRunStatusTool: ToolConfig<HexGetRunStatusParams, HexGetRunStatus
request: {
url: (params) =>
`https://app.hex.tech/api/v1/projects/${params.projectId}/runs/${params.runId}`,
`https://app.hex.tech/api/v1/projects/${params.projectId.trim()}/runs/${params.runId.trim()}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
+10
View File
@@ -1,5 +1,8 @@
import { cancelRunTool } from '@/tools/hex/cancel_run'
import { createCollectionTool } from '@/tools/hex/create_collection'
import { createGroupTool } from '@/tools/hex/create_group'
import { deactivateUserTool } from '@/tools/hex/deactivate_user'
import { deleteGroupTool } from '@/tools/hex/delete_group'
import { getCollectionTool } from '@/tools/hex/get_collection'
import { getDataConnectionTool } from '@/tools/hex/get_data_connection'
import { getGroupTool } from '@/tools/hex/get_group'
@@ -13,10 +16,15 @@ import { listGroupsTool } from '@/tools/hex/list_groups'
import { listProjectsTool } from '@/tools/hex/list_projects'
import { listUsersTool } from '@/tools/hex/list_users'
import { runProjectTool } from '@/tools/hex/run_project'
import { updateCollectionTool } from '@/tools/hex/update_collection'
import { updateGroupTool } from '@/tools/hex/update_group'
import { updateProjectTool } from '@/tools/hex/update_project'
export const hexCancelRunTool = cancelRunTool
export const hexCreateCollectionTool = createCollectionTool
export const hexCreateGroupTool = createGroupTool
export const hexDeactivateUserTool = deactivateUserTool
export const hexDeleteGroupTool = deleteGroupTool
export const hexGetCollectionTool = getCollectionTool
export const hexGetDataConnectionTool = getDataConnectionTool
export const hexGetGroupTool = getGroupTool
@@ -30,4 +38,6 @@ export const hexListGroupsTool = listGroupsTool
export const hexListProjectsTool = listProjectsTool
export const hexListUsersTool = listUsersTool
export const hexRunProjectTool = runProjectTool
export const hexUpdateCollectionTool = updateCollectionTool
export const hexUpdateGroupTool = updateGroupTool
export const hexUpdateProjectTool = updateProjectTool
+22
View File
@@ -27,6 +27,18 @@ export const listCollectionsTool: ToolConfig<HexListCollectionsParams, HexListCo
visibility: 'user-only',
description: 'Sort by field: NAME',
},
after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Cursor to fetch the page of results after this value',
},
before: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Cursor to fetch the page of results before this value',
},
},
request: {
@@ -34,6 +46,8 @@ export const listCollectionsTool: ToolConfig<HexListCollectionsParams, HexListCo
const searchParams = new URLSearchParams()
if (params.limit) searchParams.set('limit', String(params.limit))
if (params.sortBy) searchParams.set('sortBy', params.sortBy)
if (params.after) searchParams.set('after', params.after)
if (params.before) searchParams.set('before', params.before)
const qs = searchParams.toString()
return `https://app.hex.tech/api/v1/collections${qs ? `?${qs}` : ''}`
},
@@ -63,6 +77,8 @@ export const listCollectionsTool: ToolConfig<HexListCollectionsParams, HexListCo
: null,
})),
total: collections.length,
after: data.pagination?.after ?? null,
before: data.pagination?.before ?? null,
},
}
},
@@ -90,5 +106,11 @@ export const listCollectionsTool: ToolConfig<HexListCollectionsParams, HexListCo
},
},
total: { type: 'number', description: 'Total number of collections returned' },
after: { type: 'string', description: 'Cursor for the next page of results', optional: true },
before: {
type: 'string',
description: 'Cursor for the previous page of results',
optional: true,
},
},
}
@@ -39,6 +39,18 @@ export const listDataConnectionsTool: ToolConfig<
visibility: 'user-only',
description: 'Sort direction: ASC or DESC',
},
after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Cursor to fetch the page of results after this value',
},
before: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Cursor to fetch the page of results before this value',
},
},
request: {
@@ -47,6 +59,8 @@ export const listDataConnectionsTool: ToolConfig<
if (params.limit) searchParams.set('limit', String(params.limit))
if (params.sortBy) searchParams.set('sortBy', params.sortBy)
if (params.sortDirection) searchParams.set('sortDirection', params.sortDirection)
if (params.after) searchParams.set('after', params.after)
if (params.before) searchParams.set('before', params.before)
const qs = searchParams.toString()
return `https://app.hex.tech/api/v1/data-connections${qs ? `?${qs}` : ''}`
},
@@ -74,6 +88,8 @@ export const listDataConnectionsTool: ToolConfig<
allowWritebackCells: (c.allowWritebackCells as boolean) ?? null,
})),
total: connections.length,
after: data.pagination?.after ?? null,
before: data.pagination?.before ?? null,
},
}
},
@@ -112,5 +128,11 @@ export const listDataConnectionsTool: ToolConfig<
},
},
total: { type: 'number', description: 'Total number of connections returned' },
after: { type: 'string', description: 'Cursor for the next page of results', optional: true },
before: {
type: 'string',
description: 'Cursor for the previous page of results',
optional: true,
},
},
}
+22
View File
@@ -32,6 +32,18 @@ export const listGroupsTool: ToolConfig<HexListGroupsParams, HexListGroupsRespon
visibility: 'user-only',
description: 'Sort direction: ASC or DESC',
},
after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Cursor to fetch the page of results after this value',
},
before: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Cursor to fetch the page of results before this value',
},
},
request: {
@@ -40,6 +52,8 @@ export const listGroupsTool: ToolConfig<HexListGroupsParams, HexListGroupsRespon
if (params.limit) searchParams.set('limit', String(params.limit))
if (params.sortBy) searchParams.set('sortBy', params.sortBy)
if (params.sortDirection) searchParams.set('sortDirection', params.sortDirection)
if (params.after) searchParams.set('after', params.after)
if (params.before) searchParams.set('before', params.before)
const qs = searchParams.toString()
return `https://app.hex.tech/api/v1/groups${qs ? `?${qs}` : ''}`
},
@@ -63,6 +77,8 @@ export const listGroupsTool: ToolConfig<HexListGroupsParams, HexListGroupsRespon
createdAt: (g.createdAt as string) ?? null,
})),
total: groups.length,
after: data.pagination?.after ?? null,
before: data.pagination?.before ?? null,
},
}
},
@@ -81,5 +97,11 @@ export const listGroupsTool: ToolConfig<HexListGroupsParams, HexListGroupsRespon
},
},
total: { type: 'number', description: 'Total number of groups returned' },
after: { type: 'string', description: 'Cursor for the next page of results', optional: true },
before: {
type: 'string',
description: 'Cursor for the previous page of results',
optional: true,
},
},
}
+94
View File
@@ -32,6 +32,66 @@ export const listProjectsTool: ToolConfig<HexListProjectsParams, HexListProjects
visibility: 'user-or-llm',
description: 'Filter by status: PUBLISHED, DRAFT, or ALL',
},
includeComponents: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include components in results',
},
includeTrashed: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include trashed projects in results',
},
creatorEmail: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by creator email',
},
ownerEmail: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by owner email',
},
collectionId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by collection UUID',
},
categories: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'JSON array of category names to filter by (e.g., ["Marketing", "Finance"])',
},
sortBy: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Sort by field: CREATED_AT, LAST_EDITED_AT, or LAST_PUBLISHED_AT',
},
sortDirection: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Sort direction: ASC or DESC',
},
after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Cursor to fetch the page of results after this value',
},
before: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Cursor to fetch the page of results before this value',
},
},
request: {
@@ -39,7 +99,33 @@ export const listProjectsTool: ToolConfig<HexListProjectsParams, HexListProjects
const searchParams = new URLSearchParams()
if (params.limit) searchParams.set('limit', String(params.limit))
if (params.includeArchived) searchParams.set('includeArchived', 'true')
if (params.includeComponents) searchParams.set('includeComponents', 'true')
if (params.includeTrashed) searchParams.set('includeTrashed', 'true')
if (params.statusFilter) searchParams.append('statuses[]', params.statusFilter)
if (params.creatorEmail) searchParams.set('creatorEmail', params.creatorEmail)
if (params.ownerEmail) searchParams.set('ownerEmail', params.ownerEmail)
if (params.collectionId) searchParams.set('collectionId', params.collectionId.trim())
if (params.categories) {
let categories: unknown
try {
categories =
typeof params.categories === 'string'
? JSON.parse(params.categories)
: params.categories
} catch {
throw new Error('categories must be a valid JSON array of category name strings')
}
if (!Array.isArray(categories) || !categories.every((c) => typeof c === 'string')) {
throw new Error('categories must be a valid JSON array of category name strings')
}
for (const category of categories) {
searchParams.append('categories[]', category)
}
}
if (params.sortBy) searchParams.set('sortBy', params.sortBy)
if (params.sortDirection) searchParams.set('sortDirection', params.sortDirection)
if (params.after) searchParams.set('after', params.after)
if (params.before) searchParams.set('before', params.before)
const qs = searchParams.toString()
return `https://app.hex.tech/api/v1/projects${qs ? `?${qs}` : ''}`
},
@@ -80,6 +166,8 @@ export const listProjectsTool: ToolConfig<HexListProjectsParams, HexListProjects
trashedAt: (p.trashedAt as string) ?? null,
})),
total: projects.length,
after: data.pagination?.after ?? null,
before: data.pagination?.before ?? null,
},
}
},
@@ -134,5 +222,11 @@ export const listProjectsTool: ToolConfig<HexListProjectsParams, HexListProjects
},
},
total: { type: 'number', description: 'Total number of projects returned' },
after: { type: 'string', description: 'Cursor for the next page of results', optional: true },
before: {
type: 'string',
description: 'Cursor for the previous page of results',
optional: true,
},
},
}
+35
View File
@@ -38,6 +38,24 @@ export const listUsersTool: ToolConfig<HexListUsersParams, HexListUsersResponse>
visibility: 'user-or-llm',
description: 'Filter users by group UUID',
},
userIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of user UUIDs to filter by',
},
after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Cursor to fetch the page of results after this value',
},
before: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Cursor to fetch the page of results before this value',
},
},
request: {
@@ -47,6 +65,9 @@ export const listUsersTool: ToolConfig<HexListUsersParams, HexListUsersResponse>
if (params.sortBy) searchParams.set('sortBy', params.sortBy)
if (params.sortDirection) searchParams.set('sortDirection', params.sortDirection)
if (params.groupId) searchParams.set('groupId', params.groupId)
if (params.userIds) searchParams.set('userIds', params.userIds)
if (params.after) searchParams.set('after', params.after)
if (params.before) searchParams.set('before', params.before)
const qs = searchParams.toString()
return `https://app.hex.tech/api/v1/users${qs ? `?${qs}` : ''}`
},
@@ -69,8 +90,11 @@ export const listUsersTool: ToolConfig<HexListUsersParams, HexListUsersResponse>
name: (u.name as string) ?? null,
email: (u.email as string) ?? null,
role: (u.role as string) ?? null,
lastLoginDate: (u.lastLoginDate as string) ?? null,
})),
total: users.length,
after: data.pagination?.after ?? null,
before: data.pagination?.before ?? null,
},
}
},
@@ -90,9 +114,20 @@ export const listUsersTool: ToolConfig<HexListUsersParams, HexListUsersResponse>
description:
'User role (ADMIN, MANAGER, EDITOR, EXPLORER, MEMBER, GUEST, EMBEDDED_USER, ANONYMOUS)',
},
lastLoginDate: {
type: 'string',
description: 'Last login timestamp',
optional: true,
},
},
},
},
total: { type: 'number', description: 'Total number of users returned' },
after: { type: 'string', description: 'Cursor for the next page of results', optional: true },
before: {
type: 'string',
description: 'Cursor for the previous page of results',
optional: true,
},
},
}
+38 -5
View File
@@ -52,10 +52,23 @@ export const runProjectTool: ToolConfig<HexRunProjectParams, HexRunProjectRespon
visibility: 'user-only',
description: 'If true, use cached SQL results instead of re-running queries',
},
viewId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional SavedView ID to use for the project run',
},
notifications: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'JSON array of notification details to deliver once the run completes (e.g., [{"type": "FAILURE", "slackChannelIds": ["C0123456789"], "userIds": [], "groupIds": [], "includeSuccessScreenshot": false}]). type is ALL, SUCCESS, or FAILURE.',
},
},
request: {
url: (params) => `https://app.hex.tech/api/v1/projects/${params.projectId}/runs`,
url: (params) => `https://app.hex.tech/api/v1/projects/${params.projectId.trim()}/runs`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
@@ -65,10 +78,14 @@ export const runProjectTool: ToolConfig<HexRunProjectParams, HexRunProjectRespon
const body: Record<string, unknown> = {}
if (params.inputParams) {
body.inputParams =
typeof params.inputParams === 'string'
? JSON.parse(params.inputParams)
: params.inputParams
try {
body.inputParams =
typeof params.inputParams === 'string'
? JSON.parse(params.inputParams)
: params.inputParams
} catch {
throw new Error('inputParams must be valid JSON')
}
}
if (params.dryRun !== undefined) body.dryRun = params.dryRun
if (params.updateCache !== undefined) body.updateCache = params.updateCache
@@ -76,6 +93,22 @@ export const runProjectTool: ToolConfig<HexRunProjectParams, HexRunProjectRespon
body.updatePublishedResults = params.updatePublishedResults
if (params.useCachedSqlResults !== undefined)
body.useCachedSqlResults = params.useCachedSqlResults
if (params.viewId) body.viewId = params.viewId.trim()
if (params.notifications) {
let notifications: unknown
try {
notifications =
typeof params.notifications === 'string'
? JSON.parse(params.notifications)
: params.notifications
} catch {
throw new Error('notifications must be a valid JSON array')
}
if (!Array.isArray(notifications)) {
throw new Error('notifications must be a valid JSON array')
}
body.notifications = notifications
}
return body
},
+110
View File
@@ -104,7 +104,17 @@ export interface HexListProjectsParams {
apiKey: string
limit?: number
includeArchived?: boolean
includeComponents?: boolean
includeTrashed?: boolean
statusFilter?: string
creatorEmail?: string
ownerEmail?: string
collectionId?: string
categories?: string
sortBy?: string
sortDirection?: string
after?: string
before?: string
}
export interface HexListProjectsResponse extends ToolResponse {
@@ -125,6 +135,8 @@ export interface HexListProjectsResponse extends ToolResponse {
trashedAt: string | null
}>
total: number
after: string | null
before: string | null
}
}
@@ -159,6 +171,8 @@ export interface HexRunProjectParams {
updateCache?: boolean
updatePublishedResults?: boolean
useCachedSqlResults?: boolean
viewId?: string
notifications?: string
}
export interface HexRunProjectResponse extends ToolResponse {
@@ -212,6 +226,7 @@ export interface HexGetProjectRunsParams {
limit?: number
offset?: number
statusFilter?: string
runTriggerFilter?: string
}
export interface HexGetProjectRunsResponse extends ToolResponse {
@@ -229,6 +244,8 @@ export interface HexGetProjectRunsResponse extends ToolResponse {
}>
total: number
traceId: string | null
nextPage: string | null
previousPage: string | null
}
}
@@ -262,6 +279,9 @@ export interface HexListUsersParams {
sortBy?: string
sortDirection?: string
groupId?: string
userIds?: string
after?: string
before?: string
}
export interface HexListUsersResponse extends ToolResponse {
@@ -271,8 +291,11 @@ export interface HexListUsersResponse extends ToolResponse {
name: string
email: string
role: string
lastLoginDate: string | null
}>
total: number
after: string | null
before: string | null
}
}
@@ -280,6 +303,8 @@ export interface HexListCollectionsParams {
apiKey: string
limit?: number
sortBy?: string
after?: string
before?: string
}
export interface HexListCollectionsResponse extends ToolResponse {
@@ -291,6 +316,8 @@ export interface HexListCollectionsResponse extends ToolResponse {
creator: { email: string; id: string } | null
}>
total: number
after: string | null
before: string | null
}
}
@@ -299,6 +326,8 @@ export interface HexListDataConnectionsParams {
limit?: number
sortBy?: string
sortDirection?: string
after?: string
before?: string
}
export interface HexListDataConnectionsResponse extends ToolResponse {
@@ -313,6 +342,8 @@ export interface HexListDataConnectionsResponse extends ToolResponse {
allowWritebackCells: boolean | null
}>
total: number
after: string | null
before: string | null
}
}
@@ -338,6 +369,8 @@ export interface HexListGroupsParams {
limit?: number
sortBy?: string
sortDirection?: string
after?: string
before?: string
}
export interface HexListGroupsResponse extends ToolResponse {
@@ -348,6 +381,8 @@ export interface HexListGroupsResponse extends ToolResponse {
createdAt: string | null
}>
total: number
after: string | null
before: string | null
}
}
@@ -410,6 +445,76 @@ export interface HexCreateCollectionResponse extends ToolResponse {
}
}
export interface HexUpdateCollectionParams {
apiKey: string
collectionId: string
name?: string
description?: string
}
export interface HexUpdateCollectionResponse extends ToolResponse {
output: {
id: string
name: string
description: string | null
creator: { email: string; id: string } | null
}
}
export interface HexCreateGroupParams {
apiKey: string
name: string
memberUserIds?: string
}
export interface HexCreateGroupResponse extends ToolResponse {
output: {
id: string
name: string
createdAt: string | null
}
}
export interface HexUpdateGroupParams {
apiKey: string
groupId: string
name?: string
addUserIds?: string
removeUserIds?: string
}
export interface HexUpdateGroupResponse extends ToolResponse {
output: {
id: string
name: string
createdAt: string | null
}
}
export interface HexDeleteGroupParams {
apiKey: string
groupId: string
}
export interface HexDeleteGroupResponse extends ToolResponse {
output: {
success: boolean
groupId: string
}
}
export interface HexDeactivateUserParams {
apiKey: string
userId: string
}
export interface HexDeactivateUserResponse extends ToolResponse {
output: {
success: boolean
userId: string
}
}
export type HexResponse =
| HexListProjectsResponse
| HexGetProjectResponse
@@ -427,3 +532,8 @@ export type HexResponse =
| HexGetDataConnectionResponse
| HexGetCollectionResponse
| HexCreateCollectionResponse
| HexUpdateCollectionResponse
| HexCreateGroupResponse
| HexUpdateGroupResponse
| HexDeleteGroupResponse
| HexDeactivateUserResponse
+85
View File
@@ -0,0 +1,85 @@
import type { HexUpdateCollectionParams, HexUpdateCollectionResponse } from '@/tools/hex/types'
import type { ToolConfig } from '@/tools/types'
export const updateCollectionTool: ToolConfig<
HexUpdateCollectionParams,
HexUpdateCollectionResponse
> = {
id: 'hex_update_collection',
name: 'Hex Update Collection',
description: 'Update the name or description of an existing Hex collection.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Hex API token (Personal or Workspace)',
},
collectionId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The UUID of the collection to update',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New name for the collection',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New description for the collection',
},
},
request: {
url: (params) => `https://app.hex.tech/api/v1/collections/${params.collectionId.trim()}`,
method: 'PATCH',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.name) body.name = params.name
if (params.description !== undefined) body.description = params.description
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id ?? null,
name: data.name ?? null,
description: data.description ?? null,
creator: data.creator
? { email: data.creator.email ?? null, id: data.creator.id ?? null }
: null,
},
}
},
outputs: {
id: { type: 'string', description: 'Collection UUID' },
name: { type: 'string', description: 'Collection name' },
description: { type: 'string', description: 'Collection description', optional: true },
creator: {
type: 'object',
description: 'Collection creator',
optional: true,
properties: {
email: { type: 'string', description: 'Creator email' },
id: { type: 'string', description: 'Creator UUID' },
},
},
},
}
+106
View File
@@ -0,0 +1,106 @@
import type { HexUpdateGroupParams, HexUpdateGroupResponse } from '@/tools/hex/types'
import type { ToolConfig } from '@/tools/types'
export const updateGroupTool: ToolConfig<HexUpdateGroupParams, HexUpdateGroupResponse> = {
id: 'hex_update_group',
name: 'Hex Update Group',
description: 'Rename a Hex group or add/remove members from it.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Hex API token (Personal or Workspace)',
},
groupId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The UUID of the group to update',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New name for the group',
},
addUserIds: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'JSON array of user UUIDs to add to the group (e.g., ["uuid1", "uuid2"])',
},
removeUserIds: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'JSON array of user UUIDs to remove from the group (e.g., ["uuid1", "uuid2"])',
},
},
request: {
url: (params) => `https://app.hex.tech/api/v1/groups/${params.groupId.trim()}`,
method: 'PATCH',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.name) body.name = params.name
const parseIds = (value: unknown): string[] => {
let parsed: unknown
try {
parsed = typeof value === 'string' ? JSON.parse(value) : value
} catch {
throw new Error(
'addUserIds/removeUserIds must be a valid JSON array of user UUID strings'
)
}
if (!Array.isArray(parsed) || !parsed.every((id) => typeof id === 'string')) {
throw new Error(
'addUserIds/removeUserIds must be a valid JSON array of user UUID strings'
)
}
return parsed
}
if (params.addUserIds || params.removeUserIds) {
const members: Record<string, unknown> = {}
if (params.addUserIds) {
members.add = { users: parseIds(params.addUserIds).map((id) => ({ id: id.trim() })) }
}
if (params.removeUserIds) {
members.remove = {
users: parseIds(params.removeUserIds).map((id) => ({ id: id.trim() })),
}
}
body.members = members
}
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id ?? null,
name: data.name ?? null,
createdAt: data.createdAt ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Group UUID' },
name: { type: 'string', description: 'Group name' },
createdAt: { type: 'string', description: 'Creation timestamp' },
},
}
+1 -1
View File
@@ -30,7 +30,7 @@ export const updateProjectTool: ToolConfig<HexUpdateProjectParams, HexUpdateProj
},
request: {
url: (params) => `https://app.hex.tech/api/v1/projects/${params.projectId}`,
url: (params) => `https://app.hex.tech/api/v1/projects/${params.projectId.trim()}`,
method: 'PATCH',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
+10
View File
@@ -1556,6 +1556,9 @@ import { guardrailsValidateTool } from '@/tools/guardrails'
import {
hexCancelRunTool,
hexCreateCollectionTool,
hexCreateGroupTool,
hexDeactivateUserTool,
hexDeleteGroupTool,
hexGetCollectionTool,
hexGetDataConnectionTool,
hexGetGroupTool,
@@ -1569,6 +1572,8 @@ import {
hexListProjectsTool,
hexListUsersTool,
hexRunProjectTool,
hexUpdateCollectionTool,
hexUpdateGroupTool,
hexUpdateProjectTool,
} from '@/tools/hex'
import { httpRequestTool, webhookRequestTool } from '@/tools/http'
@@ -4638,6 +4643,9 @@ export const tools: Record<string, ToolConfig> = {
guardrails_validate: guardrailsValidateTool,
hex_cancel_run: hexCancelRunTool,
hex_create_collection: hexCreateCollectionTool,
hex_create_group: hexCreateGroupTool,
hex_deactivate_user: hexDeactivateUserTool,
hex_delete_group: hexDeleteGroupTool,
hex_get_collection: hexGetCollectionTool,
hex_get_data_connection: hexGetDataConnectionTool,
hex_get_group: hexGetGroupTool,
@@ -4651,6 +4659,8 @@ export const tools: Record<string, ToolConfig> = {
hex_list_projects: hexListProjectsTool,
hex_list_users: hexListUsersTool,
hex_run_project: hexRunProjectTool,
hex_update_collection: hexUpdateCollectionTool,
hex_update_group: hexUpdateGroupTool,
hex_update_project: hexUpdateProjectTool,
instantly_activate_campaign: instantlyActivateCampaignTool,
instantly_create_campaign: instantlyCreateCampaignTool,