frappe-mcp-server
Version:
Enhanced Model Context Protocol server for Frappe Framework with comprehensive API instructions and helper tools
212 lines (177 loc) • 6.26 kB
JavaScript
/**
* Test script for Streamable HTTP server
* Tests both regular JSON responses and SSE streaming
*/
import axios from 'axios';
import { EventSource } from 'eventsource';
const SERVER_URL = 'http://localhost:51953'; // 0xCAF1
// ANSI color codes
const colors = {
reset: '\x1b[0m',
bright: '\x1b[1m',
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36m'
};
function log(message, color = colors.reset) {
console.log(`${color}${message}${colors.reset}`);
}
async function testJsonResponse() {
log('\n=== Testing JSON Response Mode ===', colors.bright + colors.blue);
try {
// Test initialize
log('\nTesting initialize...', colors.cyan);
const initResponse = await axios.post(SERVER_URL, {
jsonrpc: "2.0",
id: 1,
method: "initialize",
params: {}
});
log('✓ Initialize successful', colors.green);
log(`Session ID: ${initResponse.data.result.sessionId}`, colors.yellow);
const sessionId = initResponse.data.result.sessionId;
// Test tools/list
log('\nTesting tools/list...', colors.cyan);
const toolsResponse = await axios.post(SERVER_URL, {
jsonrpc: "2.0",
id: 2,
method: "tools/list",
params: {}
}, {
headers: { 'x-session-id': sessionId }
});
log(`✓ Found ${toolsResponse.data.result.tools.length} tools`, colors.green);
// Test tools/call
log('\nTesting tools/call (ping)...', colors.cyan);
const pingResponse = await axios.post(SERVER_URL, {
jsonrpc: "2.0",
id: 3,
method: "tools/call",
params: {
name: "ping",
arguments: {}
}
}, {
headers: { 'x-session-id': sessionId }
});
log(`✓ Ping response: ${pingResponse.data.result.content[0].text}`, colors.green);
} catch (error) {
log(`✗ Error: ${error.message}`, colors.red);
if (error.response) {
log(`Response: ${JSON.stringify(error.response.data, null, 2)}`, colors.red);
}
}
}
async function testSSEStreaming() {
log('\n=== Testing SSE Streaming Mode ===', colors.bright + colors.blue);
return new Promise(async (resolve) => {
try {
// First get a session via regular JSON
log('\nGetting session...', colors.cyan);
const initResponse = await axios.post(SERVER_URL, {
jsonrpc: "2.0",
id: 1,
method: "initialize",
params: {}
});
const sessionId = initResponse.data.result.sessionId;
log(`Session ID: ${sessionId}`, colors.yellow);
// Now test SSE streaming
log('\nTesting SSE stream for tools/call...', colors.cyan);
// Use fetch for SSE request
const response = await fetch(SERVER_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'text/event-stream',
'x-session-id': sessionId
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 4,
method: "tools/call",
params: {
name: "list_documents",
arguments: {
doctype: "DocType",
limit: 5
}
}
})
});
if (response.headers.get('content-type')?.includes('text/event-stream')) {
log('✓ Received SSE stream', colors.green);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = JSON.parse(line.slice(6));
log(`Received SSE message: ${JSON.stringify(data).substring(0, 100)}...`, colors.cyan);
} else if (line.startsWith(':heartbeat')) {
log('♥ Heartbeat received', colors.yellow);
}
}
}
} else {
log('✗ Did not receive SSE stream', colors.red);
}
resolve();
} catch (error) {
log(`✗ Error: ${error.message}`, colors.red);
resolve();
}
});
}
async function testHealthAndInfo() {
log('\n=== Testing Health and Info Endpoints ===', colors.bright + colors.blue);
try {
// Test health
log('\nTesting /health...', colors.cyan);
const healthResponse = await axios.get(`${SERVER_URL}/health`);
log(`✓ Server health: ${healthResponse.data.status}`, colors.green);
log(` Transport: ${healthResponse.data.transport}`, colors.yellow);
log(` Active sessions: ${healthResponse.data.sessions}`, colors.yellow);
// Test info
log('\nTesting /info...', colors.cyan);
const infoResponse = await axios.get(`${SERVER_URL}/info`);
log(`✓ Server info:`, colors.green);
log(` Name: ${infoResponse.data.name}`, colors.yellow);
log(` Version: ${infoResponse.data.version}`, colors.yellow);
log(` Protocol: ${infoResponse.data.protocol}`, colors.yellow);
log(` Streaming: ${infoResponse.data.capabilities.streaming}`, colors.yellow);
log(` Stateful: ${infoResponse.data.capabilities.stateful}`, colors.yellow);
} catch (error) {
log(`✗ Error: ${error.message}`, colors.red);
}
}
async function runTests() {
log('\n🚀 Frappe MCP Streamable HTTP Server Test Suite', colors.bright + colors.cyan);
log('=' .repeat(50), colors.cyan);
// Check if server is running
try {
await axios.get(`${SERVER_URL}/health`);
} catch (error) {
log('\n✗ Server is not running!', colors.red);
log('Please start the server with: npm run start-streamable', colors.yellow);
process.exit(1);
}
// Run tests
await testHealthAndInfo();
await testJsonResponse();
await testSSEStreaming();
log('\n✅ All tests completed!', colors.bright + colors.green);
}
// Run tests
runTests().catch(error => {
log(`\n✗ Unexpected error: ${error.message}`, colors.red);
process.exit(1);
});
// Note: If EventSource is not available, install it with: npm install eventsource