@debugg-ai/debugg-ai-mcp 1.0.45 → 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 +10 -2
- package/dist/services/projectContext.js +78 -0
- package/dist/services/workflows.js +4 -0
- package/dist/tools/index.js +21 -13
- package/dist/tools/testPageChanges.js +86 -51
- package/package.json +1 -1
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 {
|
|
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) {
|
|
@@ -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
|
}
|
package/dist/tools/index.js
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
*
|
|
12
|
+
* Build the dynamic tool description including available environments/credentials.
|
|
9
13
|
*/
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
description:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
},
|
|
54
|
-
};
|
|
85
|
+
};
|
|
86
|
+
}
|
|
55
87
|
/**
|
|
56
|
-
*
|
|
88
|
+
* Build the validated tool with schema and handler.
|
|
57
89
|
*/
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
90
|
+
export function buildValidatedTestPageChangesTool(ctx) {
|
|
91
|
+
const tool = buildTestPageChangesTool(ctx);
|
|
92
|
+
return {
|
|
93
|
+
...tool,
|
|
94
|
+
inputSchema: TestPageChangesInputSchema,
|
|
95
|
+
handler: testPageChangesHandler,
|
|
96
|
+
};
|
|
97
|
+
}
|