Skip to content

Fix critical duplicate method handlers bug, WebSocket compilation issues, CI pipeline errors, missing RPC methods, and benchmark performance #11

Fix critical duplicate method handlers bug, WebSocket compilation issues, CI pipeline errors, missing RPC methods, and benchmark performance

Fix critical duplicate method handlers bug, WebSocket compilation issues, CI pipeline errors, missing RPC methods, and benchmark performance #11

Workflow file for this run

name: MCP Inspector Compatibility Test
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
NODE_VERSION: '18'
INSPECTOR_VERSION: '0.16.2'
jobs:
mcp-inspector-test:
name: Test MCP Inspector Compatibility
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
# Use the recommended action for caching Rust dependencies
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
- name: Build Solana MCP Server
run: cargo build --release
- name: Install MCP Inspector and Zod
run: |
npm install -g @modelcontextprotocol/inspector@${{ env.INSPECTOR_VERSION }}
npm install zod
- name: Create Test Configuration
run: |
# Create a directory for test configuration if it doesn't exist
mkdir -p test-config
# Update the test config with the absolute path to the server binary
cat > test-config/mcp-config.json << 'EOF'
{
"mcpServers": {
"solana": {
"command": "${{ github.workspace }}/target/release/solana-mcp-server",
"args": ["stdio"],
"env": {
"SOLANA_RPC_URL": "https://api.devnet.solana.com",
"SOLANA_COMMITMENT": "confirmed",
"RUST_LOG": "info"
}
}
}
}
EOF
- name: Verify Binary and Configuration
run: |
echo "Verifying server binary..."
ls -l ${{ github.workspace }}/target/release/solana-mcp-server
echo "Verifying test configuration..."
cat test-config/mcp-config.json
# This single step replaces the multiple, fragmented test scripts
- name: Run End-to-End Test with Schema Validation
run: |
cat > test-mcp-e2e.js << 'EOF'
const { spawn } = require('child_process');
const { z } = require('zod');
// --- Zod Schemas for Validation ---
const InitializeResultSchema = z.object({
protocolVersion: z.string(),
serverInfo: z.object({ name: z.string(), version: z.string() }),
capabilities: z.object({}).passthrough(),
});
const ToolSchema = z.object({
name: z.string(),
description: z.string().optional(),
inputSchema: z.any(),
});
const ToolsListResultSchema = z.object({
tools: z.array(ToolSchema),
});
const ToolsCallResultSchema = z.any(); // The result can be anything, so we just check for presence
// --- Test Logic ---
async function main() {
console.log('πŸš€ Starting MCP server for E2E test...');
const serverProcess = spawn(
'${{ github.workspace }}/target/release/solana-mcp-server',
['stdio'],
{
env: {
...process.env,
SOLANA_RPC_URL: 'https://api.devnet.solana.com',
RUST_LOG: 'info',
},
}
);
let responseBuffer = '';
const responsePromises = new Map();
serverProcess.stdout.on('data', (data) => {
responseBuffer += data.toString();
const lines = responseBuffer.split('\n');
responseBuffer = lines.pop(); // Keep partial line in buffer
for (const line of lines) {
if (line.trim() === '') continue;
try {
const response = JSON.parse(line);
console.log('πŸ“¨ Received from server:', JSON.stringify(response, null, 2));
if (response.id && responsePromises.has(response.id)) {
responsePromises.get(response.id).resolve(response);
responsePromises.delete(response.id);
}
} catch (error) {
console.log('πŸ“ Server Log:', line);
}
}
});
serverProcess.stderr.on('data', (data) => console.error(`πŸ”΄ STDERR: ${data}`));
serverProcess.on('exit', (code) => {
if (code !== 0 && code !== null) {
console.error(`❌ Server exited prematurely with code: ${code}`);
process.exit(1);
}
});
const sendRequest = (method, params, id) => {
return new Promise((resolve, reject) => {
const request = { jsonrpc: '2.0', id, method, params };
console.log(`πŸ“€ Sending to server (id: ${id}): ${method}`);
serverProcess.stdin.write(JSON.stringify(request) + '\n');
responsePromises.set(id, { resolve, reject });
setTimeout(() => {
if (responsePromises.has(id)) {
reject(new Error(`Timeout waiting for response to request id ${id}`));
}
}, 15000); // 15-second timeout
});
};
try {
// 1. Initialize
const initResponse = await sendRequest('initialize', {
protocolVersion: '2024-11-05',
capabilities: {},
clientInfo: { name: 'e2e-test-client', version: '1.0.0' }
}, 1);
InitializeResultSchema.parse(initResponse.result);
console.log('βœ… Initialize response is valid.');
// 2. List Tools
const listResponse = await sendRequest('tools/list', {}, 2);
ToolsListResultSchema.parse(listResponse.result);
console.log('βœ… tools/list response is valid.');
// 3. Call a Tool (e.g., getHealth)
const callResponse = await sendRequest('tools/call', { name: 'getHealth', arguments: {} }, 3);
if (callResponse.error) throw new Error(`tools/call failed: ${callResponse.error.message}`);
ToolsCallResultSchema.parse(callResponse.result);
console.log('βœ… tools/call response is valid.');
console.log('\nπŸŽ‰ All tests passed!');
} catch (error) {
console.error('\n❌ Test failed:', error.message);
if (error instanceof z.ZodError) {
console.error('Schema validation details:', error.errors);
}
process.exit(1);
} finally {
serverProcess.kill();
}
}
main().catch(err => {
console.error("Critical error during test execution:", err);
process.exit(1);
});
EOF
node test-mcp-e2e.js
- name: Test MCP Inspector CLI Compatibility
run: |
echo "πŸ” Testing MCP Inspector CLI compatibility..."
# This test verifies that the inspector can connect, but we allow failure
# as it can sometimes be flaky in headless CI environments.
timeout 30s npx @modelcontextprotocol/inspector@${{ env.INSPECTOR_VERSION }} \
--config test-config/mcp-config.json \
--server solana \
--cli << 'INSPECTOREOF' || echo "⚠️ MCP Inspector CLI test failed or timed out (non-blocking)."
list tools
exit
INSPECTOREOF
- name: Generate Test Report
if: always() # This step runs even if previous steps fail
run: |
echo "## MCP Inspector Compatibility Report" > report.md
echo "" >> report.md
echo "### Test Summary" >> report.md
echo "- **Job Status**: ${{ job.status }}" >> report.md
echo "- **Inspector Version**: ${{ env.INSPECTOR_VERSION }}" >> report.md
echo "- **Timestamp**: $(date -u)" >> report.md
echo "" >> report.md
echo "### Server Configuration Used" >> report.md
echo '```json' >> report.md
cat test-config/mcp-config.json >> report.md
echo '```' >> report.md
- name: Upload Test Report
if: always()
uses: actions/upload-artifact@v4
with:
name: mcp-inspector-report
path: report.md
retention-days: 7