Skip to main content
Glama
debugg-ai

Debugg AI MCP

Official
by debugg-ai

Search Environments

search_environments

Find environments by UUID or filter with keyword, project, and pagination. Returns environments with inline credentials (passwords never included). Auto-resolves project from git origin if not specified.

Instructions

Search or look up environments, with credentials expanded inline per environment.

Two modes:

  • uuid mode: {"uuid": ""} → single env with full detail + its credentials. NotFound if the uuid doesn't exist.

  • filter mode: omit uuid, optionally {"q": "", "projectUuid", "page", "pageSize"} → paginated envs, each with its credentials.

Project resolution: if projectUuid is omitted, the current git repo's origin is auto-resolved to a DebuggAI project. Returns {error:"NoProjectResolved", environments:[]} if neither is available.

Credentials are returned inline per env as {uuid, label, username, role}. Password is NEVER returned — the handler defensively strips it regardless of what the service layer provides.

Response: {project, filter, pageInfo, environments[]} — each environment includes a credentials[] array.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
uuidNoEnvironment UUID. Returns single env with credentials inline. Mutually exclusive with projectUuid/q filter params.
projectUuidNoOverride the auto-detected project. Used in filter mode.
qNoFree-text search over environment name. Mutually exclusive with uuid.
pageNoPage number (1-indexed).
pageSizeNoPage size (1..200). Default 20.

Implementation Reference

  • Main handler function that resolves projectUuid (via git detection or explicit), then operates in uuid mode (single env lookup with credentials) or filter mode (paginated search with inline credentials). Never returns passwords. Handles NotFound and NoProjectResolved errors.
    /**
     * search_environments handler (bead 5kw)
     *
     * Absorbs list_environments + get_environment + all credential search.
     * Each environment in the response has its credentials expanded inline.
     *
     * Modes:
     *   uuid mode: {uuid, projectUuid?} → {filter:{uuid}, project, pageInfo:{totalCount:1,...},
     *                                      environments:[{...env, credentials:[...]}]}
     *   filter mode: {projectUuid?, q?, page?, pageSize?} → paginated list, creds inline per env
     *
     * Invariants:
     *   - NEVER returns a password field anywhere in the response (defensive strip at handler edge)
     *   - Git-fallback for projectUuid: detectRepoName() → findProjectByRepoName(); NoProjectResolved if both fail
     *   - NotFound on unknown uuid returns isError:true
     */
    
    import {
      SearchEnvironmentsInput,
      ToolContext,
      ToolResponse,
    } from '../types/index.js';
    import { Logger } from '../utils/logger.js';
    import { handleExternalServiceError } from '../utils/errors.js';
    import { DebuggAIServerClient } from '../services/index.js';
    import { config } from '../config/index.js';
    import { detectRepoName } from '../utils/gitContext.js';
    import { toPaginationParams, makePageInfo } from '../utils/pagination.js';
    
    const logger = new Logger({ module: 'searchEnvironmentsHandler' });
    
    type SafeCredential = { uuid: string; label: string; username: string; role: string | null; environmentUuid?: string };
    
    function stripPassword(cred: any): SafeCredential {
      // Defensive: take only known-safe keys. Never spread the source.
      return {
        uuid: cred.uuid,
        label: cred.label,
        username: cred.username,
        role: cred.role ?? null,
        ...(cred.environmentUuid ? { environmentUuid: cred.environmentUuid } : {}),
      };
    }
    
    function notFound(uuid: string): ToolResponse {
      return {
        content: [{
          type: 'text',
          text: JSON.stringify({ error: 'NotFound', message: `Environment ${uuid} not found.`, uuid }, null, 2),
        }],
        isError: true,
      };
    }
    
    function noProjectResolved(pagination: { page: number; pageSize: number }, reason: string): ToolResponse {
      return {
        content: [{
          type: 'text',
          text: JSON.stringify({
            error: 'NoProjectResolved',
            message: reason,
            pageInfo: makePageInfo(pagination.page, pagination.pageSize, 0, null),
            environments: [],
          }, null, 2),
        }],
      };
    }
    
    export async function searchEnvironmentsHandler(
      input: SearchEnvironmentsInput,
      _context: ToolContext,
    ): Promise<ToolResponse> {
      const start = Date.now();
      const pagination = toPaginationParams({ page: input.page, pageSize: input.pageSize });
      logger.toolStart('search_environments', { ...input, ...pagination });
    
      try {
        const client = new DebuggAIServerClient(config.api.key);
        await client.init();
    
        // ── Resolve projectUuid ──
        // Bead gb4n: when projectUuid is provided directly (caller skips git
        // auto-resolution), `name` and `repoName` are unknown. OMIT those fields
        // rather than emitting nulls — null fields surprised callers and
        // muddied the contract. If a caller needs them, they fetch via
        // search_projects.
        let projectUuid = input.projectUuid;
        let project: { uuid: string; name?: string; repoName?: string } | null = null;
    
        if (!projectUuid) {
          const repoName = detectRepoName();
          if (!repoName) {
            return noProjectResolved(pagination,
              'No git repo detected and no projectUuid provided. Pass projectUuid (get via search_projects) or invoke from a directory with a git origin.');
          }
          const resolved = await client.findProjectByRepoName(repoName);
          if (!resolved) {
            return noProjectResolved(pagination,
              `No DebuggAI project found for repo "${repoName}". Pass projectUuid explicitly.`);
          }
          projectUuid = resolved.uuid;
          project = { uuid: resolved.uuid };
          if (resolved.name) project.name = resolved.name;
          const rn = resolved.repo?.name ?? repoName;
          if (rn) project.repoName = rn;
        } else {
          project = { uuid: projectUuid };
        }
    
        // ── uuid mode ──
        if (input.uuid) {
          try {
            const env = await client.getEnvironment(projectUuid, input.uuid);
            const creds = await client.listCredentialsForEnvironment(projectUuid, input.uuid).catch(() => []);
            const payload = {
              project,
              filter: { uuid: input.uuid },
              pageInfo: { page: 1, pageSize: 1, totalCount: 1, totalPages: 1, hasMore: false },
              environments: [{ ...env, credentials: creds.map(stripPassword) }],
            };
            logger.toolComplete('search_environments', Date.now() - start);
            return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] };
          } catch (err: any) {
            if (err?.statusCode === 404 || err?.response?.status === 404) return notFound(input.uuid);
            throw err;
          }
        }
    
        // ── Filter mode ──
        const { pageInfo, environments } = await client.listEnvironmentsPaginated(projectUuid, pagination, input.q);
    
        // Expand creds per env (sequential — bounded by page size, typically ≤20)
        const withCreds = [];
        for (const env of environments) {
          const creds = await client.listCredentialsForEnvironment(projectUuid, env.uuid).catch(() => []);
          withCreds.push({ ...env, credentials: creds.map(stripPassword) });
        }
    
        const payload = {
          project,
          filter: { q: input.q ?? null },
          pageInfo,
          environments: withCreds,
        };
        logger.toolComplete('search_environments', Date.now() - start);
        return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] };
      } catch (error) {
        logger.toolError('search_environments', error as Error, Date.now() - start);
        throw handleExternalServiceError(error, 'DebuggAI', 'search_environments');
      }
    }
  • Zod schema and type for the tool input. Enforces uuid is optional, projectUuid optional, q optional, page/pageSize optional. Uses .strict() to reject unknown properties. Refine ensures uuid and q are mutually exclusive.
    export const SearchEnvironmentsInputSchema = z.object({
      uuid: z.string().uuid().optional(),
      projectUuid: z.string().uuid().optional(),
      q: z.string().min(1).optional(),
      page: z.number().int().min(1).optional(),
      pageSize: z.number().int().min(1).optional(),
    }).strict().refine(
      (v) => !(v.uuid && v.q !== undefined),
      { message: 'Cannot combine uuid with q (they are mutually exclusive — uuid mode returns one env; q filters a list).' },
    );
    export type SearchEnvironmentsInput = z.infer<typeof SearchEnvironmentsInputSchema>;
  • Tool definition and registration. buildSearchEnvironmentsTool creates the MCP Tool schema (name, description, inputSchema). buildValidatedSearchEnvironmentsTool wraps it with Zod validation schema and the handler function.
    export function buildSearchEnvironmentsTool(): Tool {
      return {
        name: 'search_environments',
        title: 'Search Environments',
        description: DESCRIPTION,
        inputSchema: {
          type: 'object',
          properties: {
            uuid: { type: 'string', description: 'Environment UUID. Returns single env with credentials inline. Mutually exclusive with projectUuid/q filter params.' },
            projectUuid: { type: 'string', description: 'Override the auto-detected project. Used in filter mode.' },
            q: { type: 'string', description: 'Free-text search over environment name. Mutually exclusive with uuid.' },
            page: { type: 'number', description: 'Page number (1-indexed).' },
            pageSize: { type: 'number', description: 'Page size (1..200). Default 20.' },
          },
          additionalProperties: false,
        },
      };
    }
    
    export function buildValidatedSearchEnvironmentsTool(): ValidatedTool {
      const tool = buildSearchEnvironmentsTool();
      return { ...tool, inputSchema: SearchEnvironmentsInputSchema, handler: searchEnvironmentsHandler };
    }
  • tools/index.ts:24-59 (registration)
    Registration of search_environments in the central tool registry via initTools(). Both the unvalidated Tool and validated ValidatedTool are built and stored, and the tool is added to the toolRegistry map.
    export function initTools(ctx: ProjectContext | null): void {
      const tools: Tool[] = [
        buildTestPageChangesTool(ctx),
        buildTriggerCrawlTool(ctx),
        buildProbePageTool(),
        buildSearchProjectsTool(),
        buildSearchEnvironmentsTool(),
        buildCreateEnvironmentTool(),
        buildUpdateEnvironmentTool(),
        buildDeleteEnvironmentTool(),
        buildUpdateProjectTool(),
        buildDeleteProjectTool(),
        buildSearchExecutionsTool(),
        buildCreateProjectTool(),
      ];
      const validated: ValidatedTool[] = [
        buildValidatedTestPageChangesTool(ctx),
        buildValidatedTriggerCrawlTool(ctx),
        buildValidatedProbePageTool(),
        buildValidatedSearchProjectsTool(),
        buildValidatedSearchEnvironmentsTool(),
        buildValidatedCreateEnvironmentTool(),
        buildValidatedUpdateEnvironmentTool(),
        buildValidatedDeleteEnvironmentTool(),
        buildValidatedUpdateProjectTool(),
        buildValidatedDeleteProjectTool(),
        buildValidatedSearchExecutionsTool(),
        buildValidatedCreateProjectTool(),
      ];
    
      _tools = tools;
      _validatedTools = validated;
    
      toolRegistry.clear();
      for (const v of validated) toolRegistry.set(v.name, v);
    }
  • Helper function that strips credentials to only safe fields (uuid, label, username, role, environmentUuid). Never returns password.
    function stripPassword(cred: any): SafeCredential {
      // Defensive: take only known-safe keys. Never spread the source.
      return {
        uuid: cred.uuid,
        label: cred.label,
        username: cred.username,
        role: cred.role ?? null,
        ...(cred.environmentUuid ? { environmentUuid: cred.environmentUuid } : {}),
      };
    }
Behavior5/5

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

With no annotations provided, description fully discloses behavioral traits: credential expansion, password never returned due to defensive stripping, pagination behavior, and error response. No contradictions.

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

Conciseness4/5

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

Description is relatively long but well-structured into sections (modes, project resolution, credentials, response). Every sentence adds value; not overly verbose for the complexity.

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

Completeness5/5

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

Tool has 5 parameters, no output schema. Description covers all aspects: two modes, project resolution, credential handling, response shape, and error case. Complete for effective use.

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

Parameters5/5

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

Schema coverage is 100%, but description adds significant meaning: defines two modes, explains mutual exclusivity of uuid and q, projectUuid auto-resolution, and pagination defaults. Goes well beyond schema.

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

Purpose5/5

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

Description clearly states 'Search or look up environments' with credential inline. It distinguishes two modes (uuid vs filter) and specifies credential expansion per environment, differentiating it from sibling search tools like search_executions or search_projects.

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

Usage Guidelines4/5

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

Provides explicit guidance on when to use uuid mode vs filter mode, explains project resolution behavior, and error cases. Does not explicitly mention alternatives but given sibling list, usage context is clear.

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/debugg-ai/debugg-ai-mcp'

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