@debugg-ai/debugg-ai-mcp 1.0.44 → 1.0.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -20,7 +20,8 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
20
20
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
21
21
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
22
22
  import { config } from "./config/index.js";
23
- import { tools, getTool } from "./tools/index.js";
23
+ import { initTools, getTools, getTool } from "./tools/index.js";
24
+ import { resolveProjectContext } from "./services/projectContext.js";
24
25
  import { Logger, validateInput, createErrorResponse, toMCPError, handleConfigurationError, Telemetry, TelemetryEvents, } from "./utils/index.js";
25
26
  // Initialize logger
26
27
  const logger = new Logger({ module: 'main' });
@@ -132,6 +133,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
132
133
  * Handle list tools requests
133
134
  */
134
135
  server.setRequestHandler(ListToolsRequestSchema, async () => {
136
+ const tools = getTools();
135
137
  logger.info('Tools list requested', { toolCount: tools.length });
136
138
  return {
137
139
  tools: tools,
@@ -162,12 +164,18 @@ async function main() {
162
164
  }));
163
165
  logger.info('Telemetry enabled (PostHog)');
164
166
  }
167
+ // Resolve project context (repo → project → environments/credentials)
168
+ // This enriches tool descriptions with available credentials for the detected project.
169
+ const projectCtx = await resolveProjectContext();
170
+ initTools(projectCtx);
165
171
  // Create and connect transport
166
172
  const transport = new StdioServerTransport();
167
173
  await server.connect(transport);
174
+ const tools = getTools();
168
175
  logger.info('DebuggAI MCP Server is running and ready to accept requests', {
169
176
  transport: 'stdio',
170
- toolsAvailable: tools.map(t => t.name)
177
+ toolsAvailable: tools.map(t => t.name),
178
+ detectedProject: projectCtx?.project.name ?? null,
171
179
  });
172
180
  }
173
181
  catch (error) {
@@ -47,26 +47,30 @@ export class DebuggAIServerClient {
47
47
  this.tunnels = createTunnelsService(this.tx);
48
48
  }
49
49
  /**
50
- * Look up a project by repo name. Uses ?search= then client-side filters
51
- * on repo.name (which is "owner/repo-name" format).
52
- * Returns the first match or null.
50
+ * Look up a project by repo name.
51
+ * Accepts "owner/repo" or bare "repo" — searches with the short name
52
+ * (more likely to match project names) then ranks results by match quality.
53
53
  */
54
54
  async findProjectByRepoName(repoName) {
55
55
  if (!this.tx)
56
56
  throw new Error('Client not initialized — call init() first');
57
- const response = await this.tx.get('api/v1/projects/', { search: repoName });
57
+ // "debugg-ai/react-web-app" short = "react-web-app"
58
+ const short = repoName.includes('/') ? repoName.split('/').pop() : repoName;
59
+ const response = await this.tx.get('api/v1/projects/', { search: short });
58
60
  const projects = response?.results ?? [];
59
61
  if (projects.length === 0)
60
62
  return null;
61
- // Exact match on project name or slug first
62
- const exact = projects.find(p => p.name === repoName || p.slug === repoName);
63
+ // Exact match on full "owner/repo" or short name against project name/slug
64
+ const exact = projects.find(p => p.name === repoName || p.name === short
65
+ || p.slug === repoName || p.slug === short);
63
66
  if (exact)
64
67
  return exact;
65
- // Match on repo.name (owner/repo-name check if it ends with /repoName)
66
- const repoMatch = projects.find(p => p.repo?.name === repoName || p.repo?.name?.endsWith(`/${repoName}`));
68
+ // Match on repo.name — backend may store "owner/repo" or just "repo"
69
+ const repoMatch = projects.find(p => p.repo?.name === repoName || p.repo?.name === short
70
+ || p.repo?.name?.endsWith(`/${short}`));
67
71
  if (repoMatch)
68
72
  return repoMatch;
69
- // Fallback to first result from search
73
+ // Fallback to first search result
70
74
  return projects[0];
71
75
  }
72
76
  /**
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Project Context Service
3
+ * At startup: detect repo → resolve project → fetch environments + credentials.
4
+ * Exposes the result so tool descriptions can be enriched dynamically.
5
+ */
6
+ import { config } from '../config/index.js';
7
+ import { DebuggAIServerClient } from './index.js';
8
+ import { detectRepoName } from '../utils/gitContext.js';
9
+ import { Logger } from '../utils/logger.js';
10
+ const logger = new Logger({ module: 'projectContext' });
11
+ let cached = null;
12
+ let initialized = false;
13
+ /**
14
+ * Resolve the current project context: repo → project → environments → credentials.
15
+ * Safe to call multiple times — caches after first successful resolution.
16
+ */
17
+ export async function resolveProjectContext() {
18
+ if (initialized)
19
+ return cached;
20
+ initialized = true;
21
+ const repoName = detectRepoName();
22
+ if (!repoName) {
23
+ logger.info('No git repo detected — skipping project context');
24
+ return null;
25
+ }
26
+ try {
27
+ const client = new DebuggAIServerClient(config.api.key);
28
+ await client.init();
29
+ const project = await client.findProjectByRepoName(repoName);
30
+ if (!project) {
31
+ logger.info(`No project found for repo "${repoName}"`);
32
+ return null;
33
+ }
34
+ logger.info(`Resolved project: ${project.name} (${project.uuid})`);
35
+ // Fetch environments for this project
36
+ const envResponse = await client.tx.get(`api/v1/projects/${project.uuid}/environments/`);
37
+ const rawEnvs = envResponse?.results ?? [];
38
+ // Fetch credentials for each environment in parallel
39
+ const environments = await Promise.all(rawEnvs
40
+ .filter((e) => e.isActive)
41
+ .map(async (env) => {
42
+ let credentials = [];
43
+ try {
44
+ const credResponse = await client.tx.get(`api/v1/projects/${project.uuid}/environments/${env.uuid}/credentials/`);
45
+ credentials = (credResponse?.results ?? [])
46
+ .filter((c) => c.isActive)
47
+ .map((c) => ({
48
+ uuid: c.uuid,
49
+ label: c.label || c.username,
50
+ username: c.username,
51
+ role: c.role,
52
+ environmentName: env.name,
53
+ environmentUuid: env.uuid,
54
+ }));
55
+ }
56
+ catch {
57
+ // Some environments may not support credentials
58
+ }
59
+ return {
60
+ uuid: env.uuid,
61
+ name: env.name,
62
+ url: env.url || env.activeUrl || '',
63
+ credentials,
64
+ };
65
+ }));
66
+ cached = { repoName, project, environments };
67
+ const totalCreds = environments.reduce((n, e) => n + e.credentials.length, 0);
68
+ logger.info(`Project context ready: ${environments.length} environments, ${totalCreds} credentials`);
69
+ return cached;
70
+ }
71
+ catch (err) {
72
+ logger.warn(`Failed to resolve project context: ${err}`);
73
+ return null;
74
+ }
75
+ }
76
+ export function getProjectContext() {
77
+ return cached;
78
+ }
@@ -23,6 +23,10 @@ export const createWorkflowsService = (tx) => {
23
23
  },
24
24
  async executeWorkflow(workflowUuid, contextData, env) {
25
25
  const body = { contextData };
26
+ // Send projectId at top level too — backend may read it from either location
27
+ if (contextData.projectId) {
28
+ body.projectId = contextData.projectId;
29
+ }
26
30
  if (env && Object.keys(env).length > 0) {
27
31
  body.env = env;
28
32
  }
@@ -1,17 +1,25 @@
1
- import { testPageChangesTool, validatedTestPageChangesTool } from './testPageChanges.js';
2
- export const tools = [
3
- testPageChangesTool,
4
- ];
5
- export const validatedTools = [
6
- validatedTestPageChangesTool,
7
- ];
8
- export const toolRegistry = new Map();
9
- for (const tool of validatedTools) {
10
- toolRegistry.set(tool.name, tool);
1
+ import { buildTestPageChangesTool, buildValidatedTestPageChangesTool } from './testPageChanges.js';
2
+ let _tools = null;
3
+ let _validatedTools = null;
4
+ const toolRegistry = new Map();
5
+ /**
6
+ * Initialize tools with project context (call once after resolveProjectContext).
7
+ */
8
+ export function initTools(ctx) {
9
+ const tool = buildTestPageChangesTool(ctx);
10
+ const validated = buildValidatedTestPageChangesTool(ctx);
11
+ _tools = [tool];
12
+ _validatedTools = [validated];
13
+ toolRegistry.clear();
14
+ toolRegistry.set(validated.name, validated);
15
+ }
16
+ export function getTools() {
17
+ if (!_tools)
18
+ initTools(null);
19
+ return _tools;
11
20
  }
12
21
  export function getTool(name) {
22
+ if (!_validatedTools)
23
+ initTools(null);
13
24
  return toolRegistry.get(name);
14
25
  }
15
- export function hasToolTool(name) {
16
- return toolRegistry.has(name);
17
- }
@@ -1,62 +1,97 @@
1
1
  /**
2
2
  * Test Page Changes Tool Definition
3
- * Defines the check_app_in_browser tool with proper validation
3
+ * Defines the check_app_in_browser tool with proper validation.
4
+ * Tool description is enriched at startup with available environments/credentials.
4
5
  */
5
6
  import { TestPageChangesInputSchema } from '../types/index.js';
6
7
  import { testPageChangesHandler } from '../handlers/testPageChangesHandler.js';
8
+ const BASE_DESCRIPTION = `Give an AI agent eyes on a live website or app. The agent browses it, interacts with it, and tells you whether a given task or check passed. Works on localhost or any URL. Use for visual QA, flow validation, regression checks, or anything that needs a real browser to verify.
9
+
10
+ LOCALHOST SUPPORT: Pass any localhost URL (e.g. http://localhost:3000) and it Just Works. A secure tunnel is automatically created so the remote browser can reach your local dev server — no manual ngrok setup, no port forwarding, no config.`;
7
11
  /**
8
- * Tool definition for testing page changes with DebuggAI
12
+ * Build the dynamic tool description including available environments/credentials.
9
13
  */
10
- export const testPageChangesTool = {
11
- name: "check_app_in_browser",
12
- title: "Run E2E Browser Test",
13
- description: "Give an AI agent eyes on a live website or app. The agent browses it, interacts with it, and tells you whether a given task or check passed. Works on localhost or any URL. Use for visual QA, flow validation, regression checks, or anything that needs a real browser to verify.\n\nLOCALHOST SUPPORT: Pass any localhost URL (e.g. http://localhost:3000) and it Just Works. A secure tunnel is automatically created so the remote browser can reach your local dev server — no manual ngrok setup, no port forwarding, no config. Supports localhost, 127.0.0.1, 0.0.0.0, [::1], and private IPs (192.168.x.x, 10.x.x.x). The tunnel stays alive for 55 minutes and is reused across calls to the same port.",
14
- inputSchema: {
15
- type: "object",
16
- properties: {
17
- description: {
18
- type: "string",
19
- description: "Natural language description of what to test or evaluate (e.g., 'Does the login form validate empty fields?' or 'Navigate to the homepage and verify the hero section loads')",
20
- minLength: 1
21
- },
22
- url: {
23
- type: "string",
24
- description: "URL to navigate to. Can be any public URL (https://example.com) OR a localhost/local dev server URL. For localhost URLs (http://localhost:3000, http://127.0.0.1:8080, etc.), a secure tunnel is automatically created so the remote browser can reach your machine — just make sure your dev server is running on that port. No extra setup needed."
25
- },
26
- environmentId: {
27
- type: "string",
28
- description: "UUID of a specific environment to use for this test"
29
- },
30
- credentialId: {
31
- type: "string",
32
- description: "UUID of a specific credential to use for login"
33
- },
34
- credentialRole: {
35
- type: "string",
36
- description: "Pick a credential by role (e.g. 'admin', 'guest') from the resolved environment"
37
- },
38
- username: {
39
- type: "string",
40
- description: "Username to log in with (creates or updates a credential idempotently)"
41
- },
42
- password: {
43
- type: "string",
44
- description: "Password to log in with (used together with username)"
45
- },
46
- repoName: {
47
- type: "string",
48
- description: "GitHub repository name (e.g. 'my-org/my-repo'). Auto-detected from the current git repo — only provide this if you want to run against a different project than the one you're in."
14
+ export function buildToolDescription(ctx) {
15
+ if (!ctx)
16
+ return BASE_DESCRIPTION;
17
+ const envsWithCreds = ctx.environments.filter(e => e.credentials.length > 0);
18
+ if (envsWithCreds.length === 0) {
19
+ return `${BASE_DESCRIPTION}\n\nDETECTED PROJECT: "${ctx.project.name}" (repo: ${ctx.repoName}). No credentials configured — provide username/password if the app requires login.`;
20
+ }
21
+ const lines = [
22
+ `\n\nDETECTED PROJECT: "${ctx.project.name}" (repo: ${ctx.repoName})`,
23
+ `\nAVAILABLE ENVIRONMENTS & CREDENTIALS (pass environmentId + credentialId for authenticated testing):`,
24
+ ];
25
+ for (const env of envsWithCreds) {
26
+ lines.push(`\n Environment: "${env.name}" (${env.uuid})${env.url ? ` — ${env.url}` : ''}`);
27
+ for (const cred of env.credentials) {
28
+ const parts = [` - "${cred.label}" (${cred.uuid}) user: ${cred.username}`];
29
+ if (cred.role)
30
+ parts[0] += `, role: ${cred.role}`;
31
+ lines.push(parts[0]);
32
+ }
33
+ }
34
+ lines.push(`\nTo use: pass environmentId and credentialId from above. Or provide username/password directly.`);
35
+ return BASE_DESCRIPTION + lines.join('\n');
36
+ }
37
+ /**
38
+ * Build the full tool definition, optionally enriched with project context.
39
+ */
40
+ export function buildTestPageChangesTool(ctx) {
41
+ return {
42
+ name: "check_app_in_browser",
43
+ title: "Run E2E Browser Test",
44
+ description: buildToolDescription(ctx),
45
+ inputSchema: {
46
+ type: "object",
47
+ properties: {
48
+ description: {
49
+ type: "string",
50
+ description: "Natural language description of what to test or evaluate (e.g., 'Does the login form validate empty fields?' or 'Navigate to the homepage and verify the hero section loads')",
51
+ minLength: 1
52
+ },
53
+ url: {
54
+ type: "string",
55
+ description: "URL to navigate to. Can be any public URL (https://example.com) OR a localhost/local dev server URL. For localhost URLs, a secure tunnel is automatically created — just make sure your dev server is running on that port."
56
+ },
57
+ environmentId: {
58
+ type: "string",
59
+ description: "UUID of a specific environment to use for this test. See available environments in the tool description above."
60
+ },
61
+ credentialId: {
62
+ type: "string",
63
+ description: "UUID of a specific credential to use for login. See available credentials in the tool description above."
64
+ },
65
+ credentialRole: {
66
+ type: "string",
67
+ description: "Pick a credential by role (e.g. 'admin', 'guest') from the resolved environment"
68
+ },
69
+ username: {
70
+ type: "string",
71
+ description: "Username to log in with (alternative to credentialId — creates or updates a credential idempotently)"
72
+ },
73
+ password: {
74
+ type: "string",
75
+ description: "Password to log in with (used together with username)"
76
+ },
77
+ repoName: {
78
+ type: "string",
79
+ description: "GitHub repository name (e.g. 'my-org/my-repo'). Auto-detected from the current git repo — only provide this if you want to run against a different project than the one you're in."
80
+ },
49
81
  },
82
+ required: ["description", "url"],
83
+ additionalProperties: false
50
84
  },
51
- required: ["description", "url"],
52
- additionalProperties: false
53
- },
54
- };
85
+ };
86
+ }
55
87
  /**
56
- * Validated tool with schema and handler
88
+ * Build the validated tool with schema and handler.
57
89
  */
58
- export const validatedTestPageChangesTool = {
59
- ...testPageChangesTool,
60
- inputSchema: TestPageChangesInputSchema,
61
- handler: testPageChangesHandler,
62
- };
90
+ export function buildValidatedTestPageChangesTool(ctx) {
91
+ const tool = buildTestPageChangesTool(ctx);
92
+ return {
93
+ ...tool,
94
+ inputSchema: TestPageChangesInputSchema,
95
+ handler: testPageChangesHandler,
96
+ };
97
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugg-ai/debugg-ai-mcp",
3
- "version": "1.0.44",
3
+ "version": "1.0.46",
4
4
  "description": "Zero-Config, Fully AI-Managed End-to-End Testing for all code gen platforms.",
5
5
  "type": "module",
6
6
  "bin": {