Skip to content

Commit e94d2db

Browse files
authored
Update mcp-inspector.yml
1 parent 2a932eb commit e94d2db

File tree

1 file changed

+81
-177
lines changed

1 file changed

+81
-177
lines changed

.github/workflows/mcp-inspector.yml

Lines changed: 81 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ on:
99

1010
env:
1111
CARGO_TERM_COLOR: always
12-
RUST_BACKTRACE: 1
12+
RUST_BACKTRACE: full
1313
NODE_VERSION: '18'
14-
INSPECTOR_VERSION: '0.16.2'
14+
# Note: The inspector package name is different in your example. Adjust as needed.
15+
INSPECTOR_PACKAGE: '@open-svm/[email protected]'
16+
MCP_HOST: '127.0.0.1'
17+
MCP_PORT: 31902
1518

1619
jobs:
1720
mcp-inspector-test:
18-
name: Test MCP Inspector Compatibility
21+
name: Test MCP Server with Inspector CLI
1922
runs-on: ubuntu-latest
2023
timeout-minutes: 20
2124

@@ -33,206 +36,107 @@ jobs:
3336
with:
3437
node-version: ${{ env.NODE_VERSION }}
3538

36-
# Use the recommended action for caching Rust dependencies
3739
- name: Cache Rust dependencies
3840
uses: Swatinem/rust-cache@v2
3941

40-
- name: Build Solana MCP Server
42+
- name: Build solana-mcp-server
4143
run: cargo build --release
4244

43-
- name: Install MCP Inspector and Zod
45+
- name: Install MCP Inspector CLI and Zod
4446
run: |
45-
npm install -g @modelcontextprotocol/inspector@${{ env.INSPECTOR_VERSION }}
47+
npm install -g ${{ env.INSPECTOR_PACKAGE }}
4648
npm install zod
4749
48-
- name: Create Test Configuration
50+
- name: Create MCP Server Config
4951
run: |
50-
# Create a directory for test configuration if it doesn't exist
5152
mkdir -p test-config
52-
53-
# Update the test config with the absolute path to the server binary
54-
cat > test-config/mcp-config.json << 'EOF'
53+
cat <<EOF > test-config/mcp-config.json
5554
{
56-
"mcpServers": {
57-
"solana": {
58-
"command": "${{ github.workspace }}/target/release/solana-mcp-server",
59-
"args": ["stdio"],
60-
"env": {
61-
"SOLANA_RPC_URL": "https://api.devnet.solana.com",
62-
"SOLANA_COMMITMENT": "confirmed",
63-
"RUST_LOG": "info"
64-
}
65-
}
66-
}
55+
"network": "devnet",
56+
"rpc_url": "https://api.devnet.solana.com",
57+
"mcp_port": ${{ env.MCP_PORT }},
58+
"mcp_host": "${{ env.MCP_HOST }}",
59+
"mcp_token": ""
6760
}
6861
EOF
6962
70-
- name: Verify Binary and Configuration
63+
- name: Start Server and Wait for it to be Ready
7164
run: |
72-
echo "Verifying server binary..."
73-
ls -l ${{ github.workspace }}/target/release/solana-mcp-server
74-
echo "Verifying test configuration..."
75-
cat test-config/mcp-config.json
65+
echo "Starting server in the background..."
66+
./target/release/solana-mcp-server --config test-config/mcp-config.json > server.log 2>&1 &
67+
68+
echo "Waiting for server to become available..."
69+
timeout=120
70+
for i in $(seq 1 $timeout); do
71+
if mcp-inspector ping --host ${{ env.MCP_HOST }} --port ${{ env.MCP_PORT }}; then
72+
echo "✅ Server is up and running!"
73+
exit 0
74+
fi
75+
echo "Still waiting for server... (${i}s)"
76+
sleep 1
77+
done
78+
79+
echo "❌ Server did not start within $timeout seconds."
80+
echo "--- Server Log ---"
81+
cat server.log
82+
exit 1
7683
77-
# This single step replaces the multiple, fragmented test scripts
78-
- name: Run End-to-End Test with Schema Validation
84+
- name: Run Inspector CLI Tests and Validate Schema
7985
run: |
80-
cat > test-mcp-e2e.js << 'EOF'
81-
const { spawn } = require('child_process');
82-
const { z } = require('zod');
86+
echo "--- Testing 'initialize' method ---"
87+
mcp-inspector call --host ${{ env.MCP_HOST }} --port ${{ env.MCP_PORT }} --method "initialize" --params '{}' --json > initialize_response.json
88+
cat initialize_response.json
89+
90+
echo "--- Testing 'tools/list' method ---"
91+
mcp-inspector call --host ${{ env.MCP_HOST }} --port ${{ env.MCP_PORT }} --method "tools/list" --params '{}' --json > tools_list_response.json
92+
cat tools_list_response.json
8393
84-
// --- Zod Schemas for Validation ---
85-
const InitializeResultSchema = z.object({
86-
protocolVersion: z.string(),
87-
serverInfo: z.object({ name: z.string(), version: z.string() }),
88-
capabilities: z.object({}).passthrough(),
89-
});
90-
91-
const ToolSchema = z.object({
92-
name: z.string(),
93-
description: z.string().optional(),
94-
inputSchema: z.any(),
95-
});
96-
97-
const ToolsListResultSchema = z.object({
98-
tools: z.array(ToolSchema),
99-
});
100-
101-
const ToolsCallResultSchema = z.any(); // The result can be anything, so we just check for presence
102-
103-
// --- Test Logic ---
104-
async function main() {
105-
console.log('🚀 Starting MCP server for E2E test...');
106-
const serverProcess = spawn(
107-
'${{ github.workspace }}/target/release/solana-mcp-server',
108-
['stdio'],
109-
{
110-
env: {
111-
...process.env,
112-
SOLANA_RPC_URL: 'https://api.devnet.solana.com',
113-
RUST_LOG: 'info',
114-
},
115-
}
116-
);
117-
118-
let responseBuffer = '';
119-
const responsePromises = new Map();
120-
121-
serverProcess.stdout.on('data', (data) => {
122-
responseBuffer += data.toString();
123-
const lines = responseBuffer.split('\n');
124-
responseBuffer = lines.pop(); // Keep partial line in buffer
125-
126-
for (const line of lines) {
127-
if (line.trim() === '') continue;
128-
try {
129-
const response = JSON.parse(line);
130-
console.log('📨 Received from server:', JSON.stringify(response, null, 2));
131-
if (response.id && responsePromises.has(response.id)) {
132-
responsePromises.get(response.id).resolve(response);
133-
responsePromises.delete(response.id);
134-
}
135-
} catch (error) {
136-
console.log('📝 Server Log:', line);
137-
}
138-
}
94+
echo "--- Validating response schemas ---"
95+
node <<EOF
96+
const { z } = require('zod');
97+
const fs = require('fs');
98+
99+
try {
100+
const initResp = JSON.parse(fs.readFileSync('initialize_response.json', 'utf8'));
101+
const toolsResp = JSON.parse(fs.readFileSync('tools_list_response.json', 'utf8'));
102+
103+
// Define schemas based on the MCP spec
104+
const initializeResultSchema = z.object({
105+
protocolVersion: z.string(),
106+
serverInfo: z.object({ name: z.string(), version: z.string() }),
107+
}).passthrough();
108+
109+
const toolSchema = z.object({
110+
name: z.string(),
111+
description: z.string().optional(),
112+
inputSchema: z.any()
139113
});
140114
141-
serverProcess.stderr.on('data', (data) => console.error(`🔴 STDERR: ${data}`));
142-
serverProcess.on('exit', (code) => {
143-
if (code !== 0 && code !== null) {
144-
console.error(`❌ Server exited prematurely with code: ${code}`);
145-
process.exit(1);
146-
}
115+
const toolsListResultSchema = z.object({
116+
tools: z.array(toolSchema),
147117
});
148118
149-
const sendRequest = (method, params, id) => {
150-
return new Promise((resolve, reject) => {
151-
const request = { jsonrpc: '2.0', id, method, params };
152-
console.log(`📤 Sending to server (id: ${id}): ${method}`);
153-
serverProcess.stdin.write(JSON.stringify(request) + '\n');
154-
responsePromises.set(id, { resolve, reject });
155-
setTimeout(() => {
156-
if (responsePromises.has(id)) {
157-
reject(new Error(`Timeout waiting for response to request id ${id}`));
158-
}
159-
}, 15000); // 15-second timeout
160-
});
161-
};
162-
163-
try {
164-
// 1. Initialize
165-
const initResponse = await sendRequest('initialize', {
166-
protocolVersion: '2024-11-05',
167-
capabilities: {},
168-
clientInfo: { name: 'e2e-test-client', version: '1.0.0' }
169-
}, 1);
170-
InitializeResultSchema.parse(initResponse.result);
171-
console.log('✅ Initialize response is valid.');
172-
173-
// 2. List Tools
174-
const listResponse = await sendRequest('tools/list', {}, 2);
175-
ToolsListResultSchema.parse(listResponse.result);
176-
console.log('✅ tools/list response is valid.');
177-
178-
// 3. Call a Tool (e.g., getHealth)
179-
const callResponse = await sendRequest('tools/call', { name: 'getHealth', arguments: {} }, 3);
180-
if (callResponse.error) throw new Error(`tools/call failed: ${callResponse.error.message}`);
181-
ToolsCallResultSchema.parse(callResponse.result);
182-
console.log('✅ tools/call response is valid.');
183-
184-
console.log('\n🎉 All tests passed!');
185-
} catch (error) {
186-
console.error('\n❌ Test failed:', error.message);
187-
if (error instanceof z.ZodError) {
188-
console.error('Schema validation details:', error.errors);
189-
}
190-
process.exit(1);
191-
} finally {
192-
serverProcess.kill();
193-
}
194-
}
119+
// Validate 'initialize'
120+
initializeResultSchema.parse(initResp.result);
121+
console.log("✅ 'initialize' response schema is valid.");
195122
196-
main().catch(err => {
197-
console.error("Critical error during test execution:", err);
198-
process.exit(1);
199-
});
200-
EOF
123+
// Validate 'tools/list'
124+
toolsListResultSchema.parse(toolsResp.result);
125+
console.log("✅ 'tools/list' response schema is valid.");
201126
202-
node test-mcp-e2e.js
127+
} catch (error) {
128+
console.error("❌ Schema validation failed:", error.message);
129+
process.exit(1);
130+
}
131+
EOF
203132
204-
- name: Test MCP Inspector CLI Compatibility
205-
run: |
206-
echo "🔍 Testing MCP Inspector CLI compatibility..."
207-
# This test verifies that the inspector can connect, but we allow failure
208-
# as it can sometimes be flaky in headless CI environments.
209-
timeout 30s npx @modelcontextprotocol/inspector@${{ env.INSPECTOR_VERSION }} \
210-
--config test-config/mcp-config.json \
211-
--server solana \
212-
--cli << 'INSPECTOREOF' || echo "⚠️ MCP Inspector CLI test failed or timed out (non-blocking)."
213-
list tools
214-
exit
215-
INSPECTOREOF
216-
217-
- name: Generate Test Report
218-
if: always() # This step runs even if previous steps fail
219-
run: |
220-
echo "## MCP Inspector Compatibility Report" > report.md
221-
echo "" >> report.md
222-
echo "### Test Summary" >> report.md
223-
echo "- **Job Status**: ${{ job.status }}" >> report.md
224-
echo "- **Inspector Version**: ${{ env.INSPECTOR_VERSION }}" >> report.md
225-
echo "- **Timestamp**: $(date -u)" >> report.md
226-
echo "" >> report.md
227-
echo "### Server Configuration Used" >> report.md
228-
echo '```json' >> report.md
229-
cat test-config/mcp-config.json >> report.md
230-
echo '```' >> report.md
231-
232-
- name: Upload Test Report
133+
- name: Upload Test Artifacts
233134
if: always()
234135
uses: actions/upload-artifact@v4
235136
with:
236-
name: mcp-inspector-report
237-
path: report.md
137+
name: mcp-test-artifacts
138+
path: |
139+
server.log
140+
initialize_response.json
141+
tools_list_response.json
238142
retention-days: 7

0 commit comments

Comments
 (0)