Search Environments
search_environmentsSearch environments by UUID or keyword, with inline credentials (excluding passwords). Auto-resolves project from git repo for filtered searches.
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
| Name | Required | Description | Default |
|---|---|---|---|
| uuid | No | Environment UUID. Returns single env with credentials inline. Mutually exclusive with projectUuid/q filter params. | |
| projectUuid | No | Override the auto-detected project. Used in filter mode. | |
| q | No | Free-text search over environment name. Mutually exclusive with uuid. | |
| page | No | Page number (1-indexed). | |
| pageSize | No | Page size (1..200). Default 20. |
Implementation Reference
- Main handler function for search_environments. Supports uuid mode (single env lookup) and filter mode (paginated search with q keyword). Resolves projectUuid via git repo detection if not provided; expands credentials inline; strips password defensively.
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'); } } - types/index.ts:64-73 (schema)Zod schema for SearchEnvironmentsInput: optional uuid (uuid mode), projectUuid, q, page, pageSize. Enforces mutual exclusivity of uuid and q via refine.
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).' }, ); - types/index.ts:74-74 (schema)TypeScript type SearchEnvironmentsInput inferred from Zod schema.
export type SearchEnvironmentsInput = z.infer<typeof SearchEnvironmentsInputSchema>; - tools/searchEnvironments.ts:17-34 (registration)buildSearchEnvironmentsTool() returns the Tool definition with name 'search_environments', description, and inputSchema.
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, }, }; } - tools/searchEnvironments.ts:36-39 (registration)buildValidatedSearchEnvironmentsTool() wraps the tool with Zod-validated inputSchema and the handler function.
export function buildValidatedSearchEnvironmentsTool(): ValidatedTool { const tool = buildSearchEnvironmentsTool(); return { ...tool, inputSchema: SearchEnvironmentsInputSchema, handler: searchEnvironmentsHandler }; } - tools/index.ts:34-96 (registration)initTools() registers search_environments via both buildSearchEnvironmentsTool() (plain) and buildValidatedSearchEnvironmentsTool() (validated). getTool() returns from toolRegistry.
export function initTools(ctx: ProjectContext | null): void { const tools: Tool[] = [ buildTestPageChangesTool(ctx), buildTriggerCrawlTool(ctx), buildProbePageTool(), buildSearchProjectsTool(), buildSearchEnvironmentsTool(), buildCreateEnvironmentTool(), buildUpdateEnvironmentTool(), buildDeleteEnvironmentTool(), buildUpdateProjectTool(), buildDeleteProjectTool(), buildSearchExecutionsTool(), buildCreateProjectTool(), buildCreateTestSuiteTool(), buildSearchTestSuitesTool(), buildDeleteTestSuiteTool(), buildCreateTestCaseTool(), buildUpdateTestCaseTool(), buildDeleteTestCaseTool(), buildRunTestSuiteTool(), buildGetTestSuiteResultsTool(), ]; const validated: ValidatedTool[] = [ buildValidatedTestPageChangesTool(ctx), buildValidatedTriggerCrawlTool(ctx), buildValidatedProbePageTool(), buildValidatedSearchProjectsTool(), buildValidatedSearchEnvironmentsTool(), buildValidatedCreateEnvironmentTool(), buildValidatedUpdateEnvironmentTool(), buildValidatedDeleteEnvironmentTool(), buildValidatedUpdateProjectTool(), buildValidatedDeleteProjectTool(), buildValidatedSearchExecutionsTool(), buildValidatedCreateProjectTool(), buildValidatedCreateTestSuiteTool(), buildValidatedSearchTestSuitesTool(), buildValidatedDeleteTestSuiteTool(), buildValidatedCreateTestCaseTool(), buildValidatedUpdateTestCaseTool(), buildValidatedDeleteTestCaseTool(), buildValidatedRunTestSuiteTool(), buildValidatedGetTestSuiteResultsTool(), ]; _tools = tools; _validatedTools = validated; toolRegistry.clear(); for (const v of validated) toolRegistry.set(v.name, v); } export function getTools(): Tool[] { if (!_tools) initTools(null); return _tools!; } export function getTool(name: string): ValidatedTool | undefined { if (!_validatedTools) initTools(null); return toolRegistry.get(name); }