-
-
Notifications
You must be signed in to change notification settings - Fork 736
Draft: Basic MCP implementation #4982
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 3.x
Are you sure you want to change the base?
Conversation
DavertMik
commented
Apr 29, 2025
- list tests & suites
- list all available actions
- tool to open and close browser
- ability to control browser via actions
- tool to collect HTML / page info
- tool for screenshot
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces documentation for integrating CodeceptJS with Cursor using the Model Context Protocol (MCP), outlining how to start the MCP server and utilize its available tools.
- Added a dedicated markdown file (docs/MCP.md) detailing MCP usage with Cursor
- Provided step-by-step instructions for starting the MCP server and connecting via Cursor
- Outlined available tools and included troubleshooting guidance for common issues
Files not reviewed (1)
- package.json: Language not supported
|
||
The server provides these tools: | ||
|
||
- `list-tests`: List all availble tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo detected in the word 'availble'; please update it to 'available'.
- `list-tests`: List all availble tests | |
- `list-tests`: List all available tests |
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think if we do like this to keep the server.tool clean and reusable?
@@ -0,0 +1,185 @@ | |||
#!/usr/bin/env node |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#!/usr/bin/env node
const { McpServer, ResourceTemplate } = require('@modelcontextprotocol/sdk/server/mcp.js');
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
// Import specific CodeceptJS modules directly
const Codecept = require('../lib/codecept');
const container = require('../lib/container');
const { getParamsToString } = require('../lib/parser');
const { methodsOfObject } = require('../lib/utils');
const output = require('../lib/output');
const { getConfig, getTestRoot } = require('../lib/command/utils');
// Determine the project path
const projectPath = process.argv.slice(2).find(arg => !arg.includes('mcp.js')) || process.cwd();
/**
- Initializes CodeceptJS and loads tests.
- @param {string} customPath - The project path.
- @returns {object} - The initialized CodeceptJS instance and its configuration.
*/
async function initializeCodeceptJS(customPath) {
output.print = () => {}; // Disable default output
const testsPath = getTestRoot(customPath);
const config = getConfig(customPath);
const codecept = new Codecept(config, {});
await codecept.init(testsPath);
codecept.loadTests();
return { codecept, config };
}
/**
- Extracts test information from the loaded CodeceptJS instance.
- @param {object} codecept - The initialized CodeceptJS instance.
- @returns {Array} - An array of test objects.
*/
function extractTestInfo(codecept) {
const mocha = container.mocha();
mocha.files = codecept.testFiles;
mocha.loadFiles();const tests = [];
for (const suite of mocha.suite.suites) {
for (const test of suite.tests) {
tests.push({
title: test.title,
fullTitle: test.fullTitle(),
body: test.body ? test.body.toString() : '',
file: suite.file,
suiteName: suite.title,
meta: test.meta,
tags: test.tags,
});
}
}
return tests;
}/**
- Formats test information into a readable text block.
- @param {Array} tests - An array of test objects.
- @returns {object} - An MCP content object containing the formatted text.
*/
function formatTestsAsText(tests) {
const formattedText = tests
.map(test => [
Test: ${test.fullTitle}
,
File: ${test.file}
,
Suite: ${test.suiteName}
,
test.tags && test.tags.length ?Tags: ${test.tags.join(', ')}
: '',
'',
'Body:',
test.body,
'---',
]
.filter(Boolean)
.join('\n'))
.join('\n\n'); - Extracts suite information from the loaded CodeceptJS instance.
- @param {object} codecept - The initialized CodeceptJS instance.
- @returns {Array} - An array of suite objects.
*/
function extractSuiteInfo(codecept) {
const mocha = container.mocha();
mocha.files = codecept.testFiles;
mocha.loadFiles();const suites = [];
for (const suite of mocha.suite.suites) {
suites.push({
title: suite.title,
file: suite.file,
testCount: suite.tests.length,
tests: suite.tests.map(test => ({
title: test.title,
fullTitle: test.fullTitle(),
})),
});
}
return suites;
}/**
- Formats suite information into a readable text block.
- @param {Array} suites - An array of suite objects.
- @returns {object} - An MCP content object containing the formatted text.
*/
function formatSuitesAsText(suites) {
const formattedText = suites
.map(suite => {
const testList = suite.tests.map(test =>- ${test.title}
).join('\n');
return [Suite: ${suite.title}
,File: ${suite.file}
,Tests (${suite.testCount}):
, testList, '---'].join('\n');
})
.join('\n\n'); - Extracts action information from CodeceptJS helpers and support objects.
- @returns {Array} - An array of action objects.
*/
function extractActionInfo() {
const helpers = container.helpers();
const supportI = container.support('I');
const actions = [];// Get actions from helpers
for (const name in helpers) {
const helper = helpers[name];
methodsOfObject(helper).forEach(action => {
const params = getParamsToString(helper[action]);
actions.push({
name: action,
source: name,
params,
type: 'helper',
});
});
}// Get actions from I
for (const name in supportI) {
if (actions.some(a => a.name === name)) {
continue;
}
const actor = supportI[name];
const params = getParamsToString(actor);
actions.push({
name,
source: 'I',
params,
type: 'support',
});
}
return actions;
}/**
- Formats action information into a readable text block.
- @param {Array} actions - An array of action objects.
- @returns {object} - An MCP content object containing the formatted text.
*/
function formatActionsAsText(actions) {
const helperActions = actions
.filter(a => a.type === 'helper')
.sort((a, b) => (a.source === b.source ? a.name.localeCompare(b.name) : a.source.localeCompare(b.source))); - Starts the MCP Server.
*/
async function startServer() {
const { codecept } = await initializeCodeceptJS(projectPath);
const supportActions = actions.filter(a => a.type === 'support').sort((a, b) => a.name.localeCompare(b.name));
const formattedText = [
'# Helper Actions',
...helperActions.map(a =>${a.source}.${a.name}(${a.params})
),
'',
'# Support Actions',
...supportActions.map(a =>I.${a.name}(${a.params})
),
].join('\n');return { content: [{ type: 'text', text: formattedText }] };
}/**
const server = new McpServer({
name: 'CodeceptJS',
version: '1.0.0',
url: 'https://codecept.io',
description: 'CodeceptJS Model Context Protocol Server',
});server.tool('list-tests', {}, async () => {
const tests = extractTestInfo(codecept);
return formatTestsAsText(tests);
});server.tool('list-suites', {}, async () => {
const suites = extractSuiteInfo(codecept);
return formatSuitesAsText(suites);
});server.tool('list-actions', {}, async () => {
const actions = extractActionInfo();
return formatActionsAsText(actions);
});const transport = new StdioServerTransport();
await server.connect(transport);
}// Start the server
startServer().catch(err => {
console.error('Error starting MCP server:', err);
process.exit(1);
});
return { content: [{ type: 'text', text: formattedText }] };
}/**
return { content: [{ type: 'text', text: formattedText }] };
}/**
// Setup MCP server | ||
const server = new McpServer({ | ||
name: 'CodeceptJS', | ||
version: '1.0.0', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we get the current version of codeceptjs?