/**
* Test script for MCP server
* Uses MCP SDK Client for proper protocol handling
* Following MCP best practices: https://modelcontextprotocol.io
*/
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import 'dotenv/config';
// Test data constants
const TEST_USERNAME = 'ravindra-adireddy';
const TEST_FROM = '2025-02-01';
const TEST_TO = '2025-12-31';
const TEST_REPOS = ['radireddy/AiApps'];
// Request timeout (30 seconds)
const REQUEST_TIMEOUT = 30000;
// Test registry - maps test names to test functions
type TestFunction = (client: Client) => Promise<void>;
const testRegistry: Record<string, TestFunction> = {
'listTools': async (client) => {
console.log('\nπ Test: List available tools');
console.log('-'.repeat(60));
try {
const tools = await Promise.race([
client.listTools(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT)
)
]) as any;
console.log(`β
Found ${tools.tools?.length || 0} tools:`);
tools.tools?.forEach((tool: any) => {
console.log(` - ${tool.name}: ${tool.description?.substring(0, 80)}...`);
});
} catch (error: any) {
console.error('β Error:', error.message);
}
},
'github.getAuthoredPRs': async (client) => {
console.log('\nπ Test: Get Authored PRs');
console.log('-'.repeat(60));
console.log('Request: github.getAuthoredPRs');
console.log('Params:', JSON.stringify({
username: TEST_USERNAME,
repos: TEST_REPOS,
from: TEST_FROM,
to: TEST_TO,
}, null, 2));
console.log(`\nβ οΈ Note: Testing with date range ${TEST_FROM} to ${TEST_TO}`);
console.log(` If this is in the future, you may get 0 results if no PRs exist in that range.`);
try {
const result = await Promise.race([
client.callTool({
name: 'github.getAuthoredPRs',
arguments: {
username: TEST_USERNAME,
repos: TEST_REPOS,
from: TEST_FROM,
to: TEST_TO,
},
}),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT)
)
]) as any;
console.log('\nβ
Response received:');
const content = result.content?.[0]?.text;
if (content) {
const data = JSON.parse(content);
const prCount = data.prs?.length || 0;
console.log(` Found ${prCount} PRs`);
if (prCount === 0) {
console.log(`\n β οΈ No PRs found. This could mean:`);
console.log(` - No PRs exist for user "${TEST_USERNAME}" in repos ${TEST_REPOS.join(', ')}`);
console.log(` - No PRs exist in the date range ${TEST_FROM} to ${TEST_TO}`);
console.log(` - The date range is in the future and no PRs exist yet`);
console.log(` - The repo names "${TEST_REPOS.join(', ')}" might be incorrect`);
console.log(` - Try testing with a past date range or different repo to verify functionality`);
} else {
console.log('\n First PR:');
console.log(JSON.stringify(data.prs[0], null, 4));
}
} else {
console.log(JSON.stringify(result, null, 2));
}
} catch (error: any) {
console.error('β Error:', error.message);
if (error.message.includes('authentication')) {
console.error(' π‘ Make sure GITHUB_TOKEN is set in your environment');
}
if (error.message.includes('Timeout')) {
console.error(' π‘ Request timed out. The server may be slow or unresponsive.');
}
}
},
'github.getPRReviews': async (client) => {
console.log('\nπ Test: Get PR Reviews');
console.log('-'.repeat(60));
try {
const result = await Promise.race([
client.callTool({
name: 'github.getPRReviews',
arguments: {
username: TEST_USERNAME,
repos: TEST_REPOS,
from: TEST_FROM,
to: TEST_TO,
},
}),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT)
)
]) as any;
console.log('\nβ
Response received:');
const content = result.content?.[0]?.text;
if (content) {
const data = JSON.parse(content);
console.log(` Found ${data.reviews?.length || 0} reviews`);
if (data.reviews && data.reviews.length > 0) {
console.log('\n First review:');
console.log(JSON.stringify(data.reviews[0], null, 4));
}
}
} catch (error: any) {
console.error('β Error:', error.message);
}
},
'github.getReviewComments': async (client) => {
console.log('\nπ Test: Get Review Comments');
console.log('-'.repeat(60));
try {
const result = await Promise.race([
client.callTool({
name: 'github.getReviewComments',
arguments: {
username: TEST_USERNAME,
repos: TEST_REPOS,
from: TEST_FROM,
to: TEST_TO,
},
}),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT)
)
]) as any;
console.log('\nβ
Response received:');
const content = result.content?.[0]?.text;
if (content) {
const data = JSON.parse(content);
console.log(`\n π Summary:`);
console.log(` User ID: ${data.userId}`);
console.log(` Date Range: ${data.dateRange?.from} to ${data.dateRange?.to}`);
console.log(` Total PRs Reviewed: ${data.totalPRsReviewed}`);
console.log(` Total Comments: ${data.totalComments}`);
if (data.totalPRsReviewed === 0) {
console.log(`\n β οΈ No PRs reviewed. This could mean:`);
console.log(` - No review comments exist for user "${TEST_USERNAME}"`);
console.log(` - No comments exist in the date range ${TEST_FROM} to ${TEST_TO}`);
console.log(` - The date range is in the future and no comments exist yet`);
} else {
console.log(`\n π PR Details (showing first PR):`);
if (data.prs && data.prs.length > 0) {
const firstPR = data.prs[0];
console.log(` PR #${firstPR.prNumber}: ${firstPR.prTitle}`);
console.log(` Repository: ${firstPR.prRepo}`);
console.log(` PR Created At: ${firstPR.prCreatedAt || 'N/A'}`);
console.log(` Comments in this PR: ${firstPR.totalComments}`);
if (firstPR.comments && firstPR.comments.length > 0) {
const firstComment = firstPR.comments[0].replace(/\n/g, '\\n').substring(0, 100);
console.log(` First comment: "${firstComment}${firstPR.comments[0].length > 100 ? '...' : ''}"`);
}
}
}
}
} catch (error: any) {
console.error('β Error:', error.message);
}
},
'github.getCommentImpact': async (client) => {
console.log('\nπ Test: Get Comment Impact');
console.log('-'.repeat(60));
try {
const result = await Promise.race([
client.callTool({
name: 'github.getCommentImpact',
arguments: {
username: TEST_USERNAME,
repos: TEST_REPOS,
from: TEST_FROM,
to: TEST_TO,
},
}),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT)
)
]) as any;
console.log('\nβ
Response received:');
const content = result.content?.[0]?.text;
if (content) {
const data = JSON.parse(content);
console.log(` Found ${data.impacts?.length || 0} comment impacts`);
if (data.impacts && data.impacts.length > 0) {
console.log('\n First impact:');
console.log(JSON.stringify(data.impacts[0], null, 4));
}
}
} catch (error: any) {
console.error('β Error:', error.message);
}
},
'github.getUserComments': async (client) => {
console.log('\nπ Test: Get User Comments');
console.log('-'.repeat(60));
console.log('Request: github.getUserComments');
console.log('Params:', JSON.stringify({
username: TEST_USERNAME,
repos: TEST_REPOS,
from: TEST_FROM,
to: TEST_TO,
}, null, 2));
console.log(`\nβ οΈ Note: Testing with date range ${TEST_FROM} to ${TEST_TO}`);
console.log(` If this is in the future, you may get 0 results if no comments exist in that range.`);
try {
const result = await Promise.race([
client.callTool({
name: 'github.getUserComments',
arguments: {
username: TEST_USERNAME,
repos: TEST_REPOS,
from: TEST_FROM,
to: TEST_TO,
},
}),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT)
)
]) as any;
console.log('\nβ
Response received:');
const content = result.content?.[0]?.text;
if (content) {
const data = JSON.parse(content);
const commentCount = data.comments?.length || 0;
console.log(` Found ${commentCount} comments`);
if (commentCount === 0) {
console.log(`\n β οΈ No comments found. This could mean:`);
console.log(` - No comments exist for user "${TEST_USERNAME}" in repos ${TEST_REPOS.join(', ')}`);
console.log(` - No comments exist in the date range ${TEST_FROM} to ${TEST_TO}`);
console.log(` - The date range is in the future and no comments exist yet`);
console.log(` - The repo names "${TEST_REPOS.join(', ')}" might be incorrect`);
console.log(` - Try testing with a past date range or different repo to verify functionality`);
} else {
console.log('\n First comment:');
console.log(JSON.stringify(data.comments[0], null, 4));
// Show comment type breakdown
const reviewComments = data.comments.filter((c: any) => c.commentType === 'review').length;
const issueComments = data.comments.filter((c: any) => c.commentType === 'issue').length;
console.log(`\n Comment breakdown:`);
console.log(` - Review comments: ${reviewComments}`);
console.log(` - Issue comments: ${issueComments}`);
}
} else {
console.log(JSON.stringify(result, null, 2));
}
} catch (error: any) {
console.error('β Error:', error.message);
if (error.message.includes('authentication')) {
console.error(' π‘ Make sure GITHUB_TOKEN is set in your environment');
}
if (error.message.includes('Invalid repository format')) {
console.error(' π‘ Repository format should be "owner/repo" (e.g., "UiPath/business-apps-jamjam")');
}
if (error.message.includes('Timeout')) {
console.error(' π‘ Request timed out. The server may be slow or unresponsive.');
}
}
},
'github.getUserRepoStats': async (client) => {
console.log('\nπ Test: Get User Repository Statistics');
console.log('-'.repeat(60));
console.log('Request: github.getUserRepoStats');
console.log('Params:', JSON.stringify({
username: TEST_USERNAME,
repos: TEST_REPOS,
from: TEST_FROM,
to: TEST_TO,
}, null, 2));
console.log(`\nβ οΈ Note: Testing with date range ${TEST_FROM} to ${TEST_TO}`);
console.log(` This tool aggregates statistics from multiple sources.`);
try {
const result = await Promise.race([
client.callTool({
name: 'github.getUserRepoStats',
arguments: {
username: TEST_USERNAME,
repos: TEST_REPOS,
from: TEST_FROM,
to: TEST_TO,
},
}),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), REQUEST_TIMEOUT)
)
]) as any;
console.log('\nβ
Response received:');
const content = result.content?.[0]?.text;
if (content) {
const data = JSON.parse(content);
const stats = data.stats;
if (stats) {
console.log('\n π Repository Statistics:');
console.log(` Username: ${stats.username}`);
console.log(` Repository: ${stats.repo}`);
console.log(` Time Range: ${stats.timeRange.from} to ${stats.timeRange.to}`);
console.log(`\n π Pull Requests:`);
console.log(` Total: ${stats.prs.count}`);
console.log(` Merged: ${stats.prs.merged}`);
console.log(` Open: ${stats.prs.open}`);
console.log(` Closed: ${stats.prs.closed}`);
console.log(`\n π¬ Comments:`);
console.log(` Total: ${stats.comments.total}`);
console.log(` Review: ${stats.comments.review}`);
console.log(` Issue: ${stats.comments.issue}`);
console.log(`\n π Reviews:`);
console.log(` Total: ${stats.reviews.total}`);
console.log(` Approved: ${stats.reviews.approved}`);
console.log(` Changes Requested: ${stats.reviews.changesRequested}`);
console.log(` Commented: ${stats.reviews.commented}`);
console.log(`\n π Code Changes:`);
console.log(` Files Changed: ${stats.codeChanges.filesChanged}`);
console.log(` Additions: ${stats.codeChanges.additions} lines`);
console.log(` Deletions: ${stats.codeChanges.deletions} lines`);
console.log(` Net Change: ${stats.codeChanges.netChange} lines`);
if (stats.prs.count === 0 && stats.comments.total === 0 && stats.reviews.total === 0) {
console.log(`\n β οΈ No activity found. This could mean:`);
console.log(` - No activity exists for user "${TEST_USERNAME}" in repos ${TEST_REPOS.join(', ')}`);
console.log(` - No activity exists in the date range ${TEST_FROM} to ${TEST_TO}`);
console.log(` - The date range is in the future and no activity exists yet`);
console.log(` - The repo names "${TEST_REPOS.join(', ')}" might be incorrect`);
}
} else {
console.log(JSON.stringify(data, null, 2));
}
} else {
console.log(JSON.stringify(result, null, 2));
}
} catch (error: any) {
console.error('β Error:', error.message);
if (error.message.includes('authentication')) {
console.error(' π‘ Make sure GITHUB_TOKEN is set in your environment');
}
if (error.message.includes('Invalid repository format')) {
console.error(' π‘ Repository format should be "owner/repo" (e.g., "UiPath/business-apps-jamjam")');
}
if (error.message.includes('Timeout')) {
console.error(' π‘ Request timed out. The server may be slow or unresponsive.');
}
}
},
};
function parseTestArgs(): string[] {
const args = process.argv.slice(2);
if (args.length === 0) {
// No arguments - run all tests
return Object.keys(testRegistry);
}
// Parse arguments - support comma-separated or space-separated
const testNames: string[] = [];
for (const arg of args) {
if (arg.includes(',')) {
testNames.push(...arg.split(',').map(t => t.trim()));
} else {
testNames.push(arg.trim());
}
}
return testNames;
}
function printAvailableTests() {
console.log('\nπ Available tests:');
console.log('-'.repeat(60));
Object.keys(testRegistry).forEach(testName => {
console.log(` - ${testName}`);
});
console.log('');
}
async function runTests() {
// Check for GITHUB_TOKEN
if (!process.env.GITHUB_TOKEN) {
console.error('β Error: GITHUB_TOKEN is required');
console.error(' Set it in .env file: GITHUB_TOKEN=your_token_here');
console.error(' Or as environment variable: export GITHUB_TOKEN=your_token_here');
process.exit(1);
}
const testNames = parseTestArgs();
// Validate test names
const invalidTests = testNames.filter(name => !testRegistry[name]);
if (invalidTests.length > 0) {
console.error(`\nβ Invalid test names: ${invalidTests.join(', ')}`);
printAvailableTests();
process.exit(1);
}
// Create MCP client with stdio transport
const transport = new StdioClientTransport({
command: 'node',
args: ['dist/mcp/server.js'],
env: {
...process.env,
GITHUB_TOKEN: process.env.GITHUB_TOKEN,
},
});
const client = new Client(
{
name: 'github-mcp-test-client',
version: '1.0.0',
},
{
capabilities: {},
}
);
try {
// Connect to server
await client.connect(transport);
console.log('β
MCP Client connected to server\n');
console.log('π§ͺ Testing MCP Server\n');
console.log('='.repeat(60));
// Show which tests will run
if (testNames.length === Object.keys(testRegistry).length) {
console.log(`\nπ Running all ${testNames.length} tests\n`);
} else {
console.log(`\nπ Running ${testNames.length} selected test(s): ${testNames.join(', ')}\n`);
}
// Run selected tests
for (const testName of testNames) {
await testRegistry[testName](client);
}
console.log('\n' + '='.repeat(60));
console.log(`β
${testNames.length} test(s) completed!\n`);
} catch (error: any) {
console.error('β Test failed:', error);
process.exit(1);
} finally {
await client.close();
console.log('π Client disconnected');
}
}
// Run tests
runTests().catch(console.error);