Skip to main content
Glama
Tiberriver256

Azure DevOps MCP Server

search_wiki

Search for content across wiki pages in Azure DevOps projects to find specific information quickly.

Instructions

Search for content across wiki pages in a project

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
searchTextYesThe text to search for in wikis
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)

Implementation Reference

  • Core handler function that executes the wiki search using the Azure DevOps ALM search API, handling authentication, request preparation, and error wrapping.
    export async function searchWiki(
      connection: WebApi,
      options: SearchWikiOptions,
    ): Promise<WikiSearchResponse> {
      try {
        // Prepare the search request
        const searchRequest: WikiSearchRequest = {
          searchText: options.searchText,
          $skip: options.skip,
          $top: options.top,
          filters: options.projectId
            ? {
                Project: [options.projectId],
              }
            : {},
          includeFacets: options.includeFacets,
        };
    
        // Add custom filters if provided
        if (
          options.filters &&
          options.filters.Project &&
          options.filters.Project.length > 0
        ) {
          if (!searchRequest.filters) {
            searchRequest.filters = {};
          }
    
          if (!searchRequest.filters.Project) {
            searchRequest.filters.Project = [];
          }
    
          searchRequest.filters.Project = [
            ...(searchRequest.filters.Project || []),
            ...options.filters.Project,
          ];
        }
    
        // 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/wikisearchresults?api-version=7.1`
          : `https://almsearch.dev.azure.com/${organization}/_apis/search/wikisearchresults?api-version=7.1`;
    
        const searchResponse = await axios.post<WikiSearchResponse>(
          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 return is never reached but helps TypeScript understand the control flow
          return null as never;
        }
    
        // Otherwise, wrap it in a generic error
        throw new AzureDevOpsError(
          `Failed to search wiki: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }
  • Zod schema for validating input arguments to the search_wiki tool.
    export const SearchWikiSchema = z.object({
      searchText: z.string().describe('The text to search for in wikis'),
      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({
          Project: z
            .array(z.string())
            .optional()
            .describe('Filter by project names'),
        })
        .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)'),
    });
  • ToolDefinition object registering the search_wiki tool with its name, description, and input schema.
    {
      name: 'search_wiki',
      description: 'Search for content across wiki pages in a project',
      inputSchema: zodToJsonSchema(SearchWikiSchema),
    },
  • Dispatch handler in the search feature that routes search_wiki requests, parses arguments, calls the core handler, and formats the response.
    case 'search_wiki': {
      const args = SearchWikiSchema.parse(request.params.arguments);
      const result = await searchWiki(connection, args);
      return {
        content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
      };
    }
  • Helper function to obtain the authorization header for API calls using environment-based authentication.
    async function getAuthorizationHeader(): Promise<string> {
      try {
        // For PAT authentication, we can construct the header directly
        if (
          process.env.AZURE_DEVOPS_AUTH_METHOD?.toLowerCase() === 'pat' &&
          process.env.AZURE_DEVOPS_PAT
        ) {
          // For PAT auth, we can construct the Basic auth header directly
          const token = process.env.AZURE_DEVOPS_PAT;
          const base64Token = Buffer.from(`:${token}`).toString('base64');
          return `Basic ${base64Token}`;
        }
    
        // For Azure Identity / Azure CLI auth, we need to get a token
        // using the Azure DevOps resource ID
        // Choose the appropriate credential based on auth method
        const credential =
          process.env.AZURE_DEVOPS_AUTH_METHOD?.toLowerCase() === 'azure-cli'
            ? new AzureCliCredential()
            : new DefaultAzureCredential();
    
        // Azure DevOps resource ID for token acquisition
        const AZURE_DEVOPS_RESOURCE_ID = '499b84ac-1321-427f-aa17-267ca6975798';
    
        // Get token for Azure DevOps
        const token = await credential.getToken(
          `${AZURE_DEVOPS_RESOURCE_ID}/.default`,
        );
    
        if (!token || !token.token) {
          throw new Error('Failed to acquire token for Azure DevOps');
        }
    
        return `Bearer ${token.token}`;
      } catch (error) {
        throw new AzureDevOpsValidationError(
          `Failed to get authorization header: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It states it's a search operation but doesn't mention whether this is a read-only operation, what permissions are required, how results are returned (e.g., pagination behavior beyond schema parameters), or any rate limits. The description is minimal and lacks important behavioral context for a search tool.

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 gets straight to the point with zero wasted words. It's appropriately sized for a search tool and front-loads the core functionality without unnecessary elaboration.

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?

For a search tool with 7 parameters, no annotations, and no output schema, the description is insufficient. It doesn't explain what the search returns (e.g., page titles, snippets, full content), how results are structured, or any behavioral aspects like performance characteristics or error conditions. The minimal description leaves too many gaps for effective tool selection.

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 7 parameters thoroughly. The description adds no additional parameter semantics beyond what's in the schema - it doesn't explain search behavior, result ranking, or how filters interact with projectId. This meets the baseline for high schema coverage but doesn't add value.

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 content') and resource ('across wiki pages in a project'), making the purpose immediately understandable. However, it doesn't explicitly differentiate from sibling tools like 'search_code' or 'search_work_items' beyond mentioning 'wiki pages', which is good but not fully specific about scope boundaries.

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 'get_wiki_page' or 'list_wiki_pages'. It mentions searching 'in a project' but doesn't clarify if this is the only way to find wiki content or when other wiki-related tools might be more appropriate.

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