Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 3.8k
feat(jira): add search_users tool for user lookup by email#3451
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
a78027a5cb73aee6a7d8701ac0e72c570f25ba401c1bbeee4File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| import type { JiraSearchUsersParams, JiraSearchUsersResponse } from '@/tools/jira/types' | ||
| import { TIMESTAMP_OUTPUT, USER_OUTPUT_PROPERTIES } from '@/tools/jira/types' | ||
| import { getJiraCloudId, transformUser } from '@/tools/jira/utils' | ||
| import type { ToolConfig } from '@/tools/types' | ||
| export const jiraSearchUsersTool: ToolConfig<JiraSearchUsersParams, JiraSearchUsersResponse> = { | ||
| id: 'jira_search_users', | ||
| name: 'Jira Search Users', | ||
| description: | ||
| 'Search for Jira users by email address or display name. Returns matching users with their accountId, displayName, and emailAddress.', | ||
| version: '1.0.0', | ||
| oauth: { | ||
| required: true, | ||
| provider: 'jira', | ||
| }, | ||
| params: { | ||
| accessToken: { | ||
| type: 'string', | ||
| required: true, | ||
| visibility: 'hidden', | ||
| description: 'OAuth access token for Jira', | ||
| }, | ||
| domain: { | ||
| type: 'string', | ||
| required: true, | ||
| visibility: 'user-only', | ||
| description: 'Your Jira domain (e.g., yourcompany.atlassian.net)', | ||
| }, | ||
| query: { | ||
| type: 'string', | ||
| required: true, | ||
| visibility: 'user-or-llm', | ||
| description: | ||
| 'A query string to search for users. Can be an email address, display name, or partial match.', | ||
| }, | ||
| maxResults: { | ||
| type: 'number', | ||
| required: false, | ||
| visibility: 'user-or-llm', | ||
| description: 'Maximum number of users to return (default: 50, max: 1000)', | ||
| }, | ||
| startAt: { | ||
| type: 'number', | ||
| required: false, | ||
| visibility: 'user-or-llm', | ||
| description: 'The index of the first user to return (for pagination, default: 0)', | ||
| }, | ||
| cloudId: { | ||
| type: 'string', | ||
| required: false, | ||
| visibility: 'hidden', | ||
| description: | ||
| 'Jira Cloud ID for the instance. If not provided, it will be fetched using the domain.', | ||
| }, | ||
waleedlatif1 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| }, | ||
| request: { | ||
| url: (params: JiraSearchUsersParams) => { | ||
| if (params.cloudId) { | ||
| const queryParams = new URLSearchParams() | ||
| queryParams.append('query', params.query) | ||
| if (params.maxResults !== undefined) | ||
| queryParams.append('maxResults', String(params.maxResults)) | ||
| if (params.startAt !== undefined) queryParams.append('startAt', String(params.startAt)) | ||
| return `https://api.atlassian.com/ex/jira/${params.cloudId}/rest/api/3/user/search?${queryParams.toString()}` | ||
| } | ||
| return 'https://api.atlassian.com/oauth/token/accessible-resources' | ||
| }, | ||
| method: 'GET', | ||
| headers: (params: JiraSearchUsersParams) => ({ | ||
| Accept: 'application/json', | ||
| Authorization: `Bearer ${params.accessToken}`, | ||
| }), | ||
| }, | ||
| transformResponse: async (response: Response, params?: JiraSearchUsersParams) => { | ||
| const fetchUsers = async (cloudId: string) => { | ||
| const queryParams = new URLSearchParams() | ||
| queryParams.append('query', params!.query) | ||
| if (params!.maxResults !== undefined) | ||
| queryParams.append('maxResults', String(params!.maxResults)) | ||
| if (params!.startAt !== undefined) queryParams.append('startAt', String(params!.startAt)) | ||
| const usersUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/user/search?${queryParams.toString()}` | ||
| const usersResponse = await fetch(usersUrl, { | ||
| method: 'GET', | ||
| headers: { | ||
| Accept: 'application/json', | ||
| Authorization: `Bearer ${params!.accessToken}`, | ||
| }, | ||
| }) | ||
| if (!usersResponse.ok) { | ||
| let message = `Failed to search Jira users (${usersResponse.status})` | ||
| try { | ||
| const err = await usersResponse.json() | ||
| message = err?.errorMessages?.join(', ') || err?.message || message | ||
| } catch (_e) {} | ||
| throw new Error(message) | ||
| } | ||
| return usersResponse.json() | ||
| } | ||
| let data: any | ||
| if (!params?.cloudId) { | ||
| const cloudId = await getJiraCloudId(params!.domain, params!.accessToken) | ||
| data = await fetchUsers(cloudId) | ||
waleedlatif1 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } else { | ||
| if (!response.ok) { | ||
| let message = `Failed to search Jira users (${response.status})` | ||
| try { | ||
| const err = await response.json() | ||
| message = err?.errorMessages?.join(', ') || err?.message || message | ||
| } catch (_e) {} | ||
| throw new Error(message) | ||
| } | ||
| data = await response.json() | ||
| } | ||
| const users = Array.isArray(data) ? data.filter(Boolean) : [] | ||
| return { | ||
| success: true, | ||
| output: { | ||
| ts: new Date().toISOString(), | ||
| users: users.map((user: any) => ({ | ||
| ...(transformUser(user) ?? { accountId: '', displayName: '' }), | ||
| self: user.self ?? null, | ||
| })), | ||
waleedlatif1 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. waleedlatif1 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. waleedlatif1 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| total: users.length, | ||
| startAt: params?.startAt ?? 0, | ||
| maxResults: params?.maxResults ?? 50, | ||
| }, | ||
| } | ||
| }, | ||
| outputs: { | ||
| ts: TIMESTAMP_OUTPUT, | ||
| users: { | ||
| type: 'array', | ||
| description: 'Array of matching Jira users', | ||
| items: { | ||
| type: 'object', | ||
| properties: { | ||
| ...USER_OUTPUT_PROPERTIES, | ||
| self: { | ||
| type: 'string', | ||
| description: 'REST API URL for this user', | ||
| optional: true, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| total: { | ||
| type: 'number', | ||
| description: 'Number of users returned in this page (may be less than total matches)', | ||
| }, | ||
| startAt: { type: 'number', description: 'Pagination start index' }, | ||
| maxResults: { type: 'number', description: 'Maximum results per page' }, | ||
| }, | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.