Merge pull request #38 from openSVM/copilot/fix-37 #30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: MCP Inspector Compatibility Test | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| pull_request: | |
| branches: [ main, develop ] | |
| workflow_dispatch: | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: full | |
| NODE_VERSION: '18' | |
| # Use the official MCP Inspector package | |
| INSPECTOR_PACKAGE: '@modelcontextprotocol/[email protected]' | |
| jobs: | |
| mcp-inspector-test: | |
| name: Test MCP Server with Inspector CLI | |
| 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 }} | |
| - 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 ${{ env.INSPECTOR_PACKAGE }} | |
| npm install zod | |
| - name: Create MCP Server Config | |
| run: | | |
| mkdir -p test-config | |
| cat <<EOF > test-config/mcp-config.json | |
| { | |
| "mcpServers": { | |
| "solana": { | |
| "command": "./target/release/solana-mcp-server", | |
| "args": ["stdio"], | |
| "env": { | |
| "SOLANA_RPC_URL": "https://api.devnet.solana.com", | |
| "SOLANA_COMMITMENT": "confirmed", | |
| "RUST_LOG": "info" | |
| } | |
| } | |
| } | |
| } | |
| EOF | |
| - name: Test MCP Protocol Communication | |
| run: | | |
| echo "Testing MCP protocol communication with Solana server..." | |
| # Test the server directly using Node.js to verify MCP protocol | |
| node <<'EOF' | |
| const { spawn } = require('child_process'); | |
| const readline = require('readline'); | |
| // Start the server | |
| const server = spawn('./target/release/solana-mcp-server', ['stdio'], { | |
| env: { | |
| ...process.env, | |
| SOLANA_RPC_URL: 'https://api.devnet.solana.com', | |
| SOLANA_COMMITMENT: 'confirmed', | |
| RUST_LOG: 'info' | |
| } | |
| }); | |
| let testResults = { | |
| protocolNotification: false, | |
| initialize: false, | |
| toolsList: false | |
| }; | |
| let serverReady = false; | |
| // Handle server output | |
| server.stdout.on('data', (data) => { | |
| const lines = data.toString().split('\n'); | |
| for (const line of lines) { | |
| if (!line.trim()) continue; | |
| try { | |
| const jsonResponse = JSON.parse(line); | |
| // Handle protocol notification (server ready) | |
| if (jsonResponse.method === 'protocol' && jsonResponse.params && jsonResponse.params.version) { | |
| console.log('✅ Protocol notification received, server version:', jsonResponse.params.version); | |
| testResults.protocolNotification = true; | |
| serverReady = true; | |
| // Server is ready, send initialize request | |
| console.log('Sending initialize request...'); | |
| const initializeRequest = { | |
| jsonrpc: "2.0", | |
| id: 1, | |
| method: "initialize", | |
| params: { | |
| protocolVersion: "2025-06-18", | |
| capabilities: {}, | |
| clientInfo: { | |
| name: "test-client", | |
| version: "1.0.0" | |
| } | |
| } | |
| }; | |
| server.stdin.write(JSON.stringify(initializeRequest) + '\n'); | |
| return; | |
| } | |
| // Check for initialize response | |
| if (jsonResponse.id === 1) { | |
| if (jsonResponse.result && jsonResponse.result.protocolVersion) { | |
| console.log('✅ Initialize response received with protocol version:', jsonResponse.result.protocolVersion); | |
| testResults.initialize = true; | |
| // Wait a bit then send tools/list request | |
| setTimeout(() => { | |
| const toolsListRequest = { | |
| jsonrpc: "2.0", | |
| id: 2, | |
| method: "tools/list", | |
| params: {} | |
| }; | |
| console.log('Sending tools/list request...'); | |
| server.stdin.write(JSON.stringify(toolsListRequest) + '\n'); | |
| }, 100); | |
| } else if (jsonResponse.error) { | |
| console.log('❌ Initialize failed with error:', jsonResponse.error); | |
| server.kill(); | |
| process.exit(1); | |
| } | |
| } | |
| // Check for tools/list response | |
| if (jsonResponse.id === 2) { | |
| if (jsonResponse.result && jsonResponse.result.tools) { | |
| console.log('✅ Tools list response received with', jsonResponse.result.tools.length, 'tools'); | |
| testResults.toolsList = true; | |
| // Check if we have the expected Solana RPC methods | |
| const toolNames = jsonResponse.result.tools.map(tool => tool.name); | |
| const expectedMethods = ['getAccountInfo', 'getBalance', 'sendTransaction']; | |
| const hasExpectedMethods = expectedMethods.every(method => toolNames.includes(method)); | |
| console.log('Available tools:', toolNames.slice(0, 10).join(', '), '... (total:', toolNames.length, ')'); | |
| if (hasExpectedMethods) { | |
| console.log('✅ Expected Solana RPC methods found in tools list'); | |
| console.log('✅ All MCP protocol tests passed'); | |
| server.kill(); | |
| process.exit(0); | |
| } else { | |
| console.log('❌ Some expected Solana RPC methods missing from tools list'); | |
| console.log('Missing methods:', expectedMethods.filter(method => !toolNames.includes(method))); | |
| server.kill(); | |
| process.exit(1); | |
| } | |
| } else if (jsonResponse.error) { | |
| console.log('❌ Tools list failed with error:', jsonResponse.error); | |
| server.kill(); | |
| process.exit(1); | |
| } | |
| } | |
| } catch (e) { | |
| // This line is not JSON, probably a log message - ignore | |
| console.log('Non-JSON output:', line.substring(0, 100)); | |
| } | |
| } | |
| }); | |
| server.stderr.on('data', (data) => { | |
| // Server logs go to stderr - only show if there are actual errors | |
| const output = data.toString(); | |
| if (output.includes('"level":"ERROR"') || output.includes('error') || output.includes('Error')) { | |
| console.error('Server error:', output); | |
| } | |
| }); | |
| server.on('close', (code) => { | |
| console.log('Server process closed with code:', code); | |
| if (!testResults.initialize || !testResults.toolsList) { | |
| console.log('❌ MCP protocol test failed - missing required responses'); | |
| console.log('Test results:', testResults); | |
| process.exit(1); | |
| } | |
| }); | |
| server.on('error', (err) => { | |
| console.error('❌ Failed to start server:', err); | |
| process.exit(1); | |
| }); | |
| // Wait for server to be ready, then the protocol notification will trigger the initialize request | |
| console.log('Waiting for server to start and send protocol notification...'); | |
| // Timeout after 30 seconds (reduced from 60) | |
| setTimeout(() => { | |
| console.log('❌ Test timeout after 30 seconds'); | |
| console.log('Test results so far:', testResults); | |
| console.log('Server ready status:', serverReady); | |
| server.kill(); | |
| process.exit(1); | |
| }, 30000); | |
| EOF | |
| - name: Upload Test Artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: mcp-test-artifacts | |
| path: | | |
| test-config/mcp-config.json | |
| retention-days: 7 |