Skip to main content
Glama
Tiberriver256

Azure DevOps MCP Server

search_work_items

Search for work items across Azure DevOps projects using text queries and filters to find specific tasks, bugs, or user stories.

Instructions

Search for work items across projects in Azure DevOps

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
searchTextYesThe text to search for in work items
organizationIdNoThe ID or name of the organization (Default: mycompany)
projectIdNoThe ID or name of the project to search in (Default: MyProject). If not provided, the default project will be used.
filtersNoOptional filters to narrow search results
topNoNumber of results to return (default: 100, max: 1000)
skipNoNumber of results to skip for pagination (default: 0)
includeFacetsNoWhether to include faceting in results (default: true)
orderByNoOptions for sorting search results

Implementation Reference

  • Core handler function that executes the search for work items using the Azure DevOps Search API, handling authentication, request preparation, and API call.
    export async function searchWorkItems(
      connection: WebApi,
      options: SearchWorkItemsOptions,
    ): Promise<WorkItemSearchResponse> {
      try {
        // Prepare the search request
        const searchRequest: WorkItemSearchRequest = {
          searchText: options.searchText,
          $skip: options.skip,
          $top: options.top,
          filters: {
            ...(options.projectId
              ? { 'System.TeamProject': [options.projectId] }
              : {}),
            ...options.filters,
          },
          includeFacets: options.includeFacets,
          $orderBy: options.orderBy,
        };
    
        // Get the authorization header from the connection
        const authHeader = await getAuthorizationHeader();
    
        // Extract organization and project from the connection URL
        const { organization, project } = extractOrgAndProject(
          connection,
          options.projectId,
        );
    
        // Make the search API request
        // If projectId is provided, include it in the URL, otherwise perform organization-wide search
        const searchUrl = options.projectId
          ? `https://almsearch.dev.azure.com/${organization}/${project}/_apis/search/workitemsearchresults?api-version=7.1`
          : `https://almsearch.dev.azure.com/${organization}/_apis/search/workitemsearchresults?api-version=7.1`;
    
        const searchResponse = await axios.post<WorkItemSearchResponse>(
          searchUrl,
          searchRequest,
          {
            headers: {
              Authorization: authHeader,
              'Content-Type': 'application/json',
            },
          },
        );
    
        return searchResponse.data;
      } catch (error) {
        // If it's already an AzureDevOpsError, rethrow it
        if (error instanceof AzureDevOpsError) {
          throw error;
        }
    
        // Handle axios errors
        if (axios.isAxiosError(error)) {
          const status = error.response?.status;
          const message = error.response?.data?.message || error.message;
    
          if (status === 404) {
            throw new AzureDevOpsResourceNotFoundError(
              `Resource not found: ${message}`,
            );
          } else if (status === 400) {
            throw new AzureDevOpsValidationError(
              `Invalid request: ${message}`,
              error.response?.data,
            );
          } else if (status === 401 || status === 403) {
            throw new AzureDevOpsPermissionError(`Permission denied: ${message}`);
          } else {
            // For other axios errors, wrap in a generic AzureDevOpsError
            throw new AzureDevOpsError(`Azure DevOps API error: ${message}`);
          }
          // This code is unreachable but TypeScript doesn't know that
        }
    
        // Otherwise, wrap it in a generic error
        throw new AzureDevOpsError(
          `Failed to search work items: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }
  • Zod schema defining the input parameters and validation for the search_work_items tool.
    export const SearchWorkItemsSchema = z.object({
      searchText: z.string().describe('The text to search for in work items'),
      organizationId: z
        .string()
        .optional()
        .describe(`The ID or name of the organization (Default: ${defaultOrg})`),
      projectId: z
        .string()
        .optional()
        .describe(
          `The ID or name of the project to search in (Default: ${defaultProject}). If not provided, the default project will be used.`,
        ),
      filters: z
        .object({
          'System.TeamProject': z
            .array(z.string())
            .optional()
            .describe('Filter by project names'),
          'System.WorkItemType': z
            .array(z.string())
            .optional()
            .describe('Filter by work item types (Bug, Task, User Story, etc.)'),
          'System.State': z
            .array(z.string())
            .optional()
            .describe('Filter by work item states (New, Active, Closed, etc.)'),
          'System.AssignedTo': z
            .array(z.string())
            .optional()
            .describe('Filter by assigned users'),
          'System.AreaPath': z
            .array(z.string())
            .optional()
            .describe('Filter by area paths'),
        })
        .optional()
        .describe('Optional filters to narrow search results'),
      top: z
        .number()
        .int()
        .min(1)
        .max(1000)
        .default(100)
        .describe('Number of results to return (default: 100, max: 1000)'),
      skip: z
        .number()
        .int()
        .min(0)
        .default(0)
        .describe('Number of results to skip for pagination (default: 0)'),
      includeFacets: z
        .boolean()
        .default(true)
        .describe('Whether to include faceting in results (default: true)'),
      orderBy: z
        .array(
          z.object({
            field: z.string().describe('Field to sort by'),
            sortOrder: z.enum(['ASC', 'DESC']).describe('Sort order (ASC/DESC)'),
          }),
        )
        .optional()
        .describe('Options for sorting search results'),
    });
  • Tool registration definition including name, description, and input schema reference.
    {
      name: 'search_work_items',
      description: 'Search for work items across projects in Azure DevOps',
      inputSchema: zodToJsonSchema(SearchWorkItemsSchema),
    },
  • Request handler dispatch case that parses arguments with schema and calls the searchWorkItems handler.
    case 'search_work_items': {
      const args = SearchWorkItemsSchema.parse(request.params.arguments);
      const result = await searchWorkItems(connection, args);
      return {
        content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
      };
    }
  • TypeScript interface defining the options for SearchWorkItems, used in the handler.
    export interface SearchWorkItemsOptions {
      /**
       * The text to search for within work items
       */
      searchText: string;
    
      /**
       * The ID or name of the project to search in
       * If not provided, search will be performed across the entire organization
       */
      projectId?: string;
    
      /**
       * Optional filters to narrow search results
       */
      filters?: {
        /**
         * Filter by project names. Useful for cross-project searches.
         */
        'System.TeamProject'?: string[];
    
        /**
         * Filter by work item types (Bug, Task, User Story, etc.)
         */
        'System.WorkItemType'?: string[];
    
        /**
         * Filter by work item states (New, Active, Closed, etc.)
         */
        'System.State'?: string[];
    
        /**
         * Filter by assigned users
         */
        'System.AssignedTo'?: string[];
    
        /**
         * Filter by area paths
         */
        'System.AreaPath'?: string[];
      };
    
      /**
       * Number of results to return
       * @default 100
       * @minimum 1
       * @maximum 1000
       */
      top?: number;
    
      /**
       * Number of results to skip for pagination
       * @default 0
       * @minimum 0
       */
      skip?: number;
    
      /**
       * Whether to include faceting in results
       * @default true
       */
      includeFacets?: boolean;
    
      /**
       * Options for sorting search results
       * If null, results are sorted by relevance
       */
      orderBy?: SortOption[];
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It mentions 'search' but doesn't describe what the search returns (e.g., fields, format), whether it's paginated (though schema hints at skip/top), authentication requirements, rate limits, or error conditions. For a complex search tool with 8 parameters, this is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that states the core purpose without redundancy. It's appropriately sized and front-loaded, with every word earning its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (8 parameters, nested objects, no output schema, and no annotations), the description is inadequate. It doesn't explain what the search returns, how results are structured, or behavioral aspects like authentication or error handling. The agent would struggle to use this tool effectively based solely on the description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds no additional parameter semantics beyond what's in the schema (e.g., search behavior, filter logic, or result structure). The baseline of 3 is appropriate when the schema does all the work.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('search for work items') and scope ('across projects in Azure DevOps'), providing a specific verb+resource combination. However, it doesn't differentiate from sibling tools like 'list_work_items' or 'search_code', which would require explicit comparison to achieve a score of 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives like 'list_work_items' or 'get_work_item'. There's no mention of prerequisites, use cases, or exclusions, leaving the agent to infer usage from the tool name alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Tiberriver256/mcp-server-azure-devops'

If you have feedback or need assistance with the MCP directory API, please join our Discord server