Skip to main content
Glama

Storacha MCP Storage Server

Official
by storacha
rest-mcp-server.test.ts11.2 kB
import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { getTestEnv, TEST_FILEPATH } from './test-config.js'; import fetch from 'node-fetch'; import { spawn, ChildProcess } from 'child_process'; // Skip tests if running in CI environment // These tests require specific environment configuration and external services // that might not be available in CI environments const isCI = process.env.CI === 'true'; (isCI ? describe.skip : describe)('MCP Server REST Integration Tests', () => { let testFileContent: string; let testFileBase64: string = ''; console.log('testFileBase64', testFileBase64); let serverProcess: ChildProcess; let baseUrl: string; const PORT = 3001; // Specific port for REST tests // Helper function to wait for a specified time const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); // Helper function to send POST requests to the server const post = async (url: string, body: any) => { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jsonrpc: '2.0', id: body.id, method: body.method, params: body.params, }), }); return response; }; // Helper function to check if server is ready using POST const isServerReady = async (url: string) => { try { // Use a JSON-RPC initialize to check if the server is alive const requestBody = { id: 'test', method: 'initialize', params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'test', version: '1.0.0', }, }, }; const response = await post(url, requestBody); let responseText; try { responseText = await response.text(); console.log(`Raw response: ${responseText}`); } catch (err) { console.log(`Error reading response text: ${err}`); } const data = responseText ? JSON.parse(responseText) : null; console.log('Server response:', JSON.stringify(data)); return data?.result && data.result.serverInfo; } catch (error: any) { console.log(`Server check failed: ${error.message}`); if (error.cause) { console.log(`Cause: ${error.cause}`); } return false; } }; // Create a test file in memory and start server beforeAll(async () => { console.log('Starting REST integration test setup'); // Create an in-memory test file testFileContent = 'This is a test file for upload'; testFileBase64 = Buffer.from(testFileContent).toString('base64'); // Set up environment variables for the server const env = { ...process.env, ...getTestEnv(), NODE_ENV: 'development', MCP_TRANSPORT_MODE: 'rest', // Use REST mode MCP_SERVER_PORT: PORT.toString(), }; console.log( 'Starting server with env:', JSON.stringify({ NODE_ENV: env.NODE_ENV, MCP_TRANSPORT_MODE: env.MCP_TRANSPORT_MODE, MCP_SERVER_PORT: env.MCP_SERVER_PORT, PRIVATE_KEY: env.PRIVATE_KEY ? 'set' : 'not set', DELEGATION: env.DELEGATION ? 'set' : 'not set', }) ); // Start the server as a separate process serverProcess = spawn('node', ['dist/index.js'], { env, stdio: 'pipe', }); // Add listeners for server process output serverProcess.stdout?.on('data', data => { console.log(`Server stdout: ${data}`); }); serverProcess.stderr?.on('data', data => { console.log(`Server stderr: ${data}`); }); serverProcess.on('error', error => { console.error(`Server process error: ${error.message}`); }); // Wait for the server to start with retries baseUrl = `http://localhost:${PORT}/rest`; const maxRetries = 5; let retries = 0; let ready = false; console.log(`Waiting for server to start at ${baseUrl}...`); await wait(2000); // Then check with retries while (!ready && retries < maxRetries) { console.log(`Checking if server is ready (attempt ${retries + 1}/${maxRetries})...`); ready = await isServerReady(baseUrl); if (!ready) { console.log(`Server not ready yet, waiting...`); await wait(1000); retries++; } } if (!ready) { throw new Error(`Server failed to start after ${maxRetries} attempts`); } console.log('REST Server is ready for tests'); }, 60000); afterAll(async () => { console.log('Cleaning up after tests'); if (serverProcess) { console.log('Terminating server process'); serverProcess.kill(); } }); it('should list available tools', async () => { console.log('Testing: list available tools'); const response = await post(baseUrl, { id: 'test', method: 'tools/list', params: {}, }); expect(response.status).toBe(200); // Get the response body const responseText = await response.text(); console.log('Tools response text:', responseText); const data = JSON.parse(responseText); // Verify the response has the expected structure expect(data).toHaveProperty('result'); expect(data.result).toHaveProperty('tools'); expect(Array.isArray(data.result.tools)).toBe(true); // Check for the expected tools const tools = data.result.tools || []; expect(tools.some((tool: { name: string }) => tool.name === 'identity')).toBe(true); expect(tools.some((tool: { name: string }) => tool.name === 'upload')).toBe(true); expect(tools.some((tool: { name: string }) => tool.name === 'retrieve')).toBe(true); }); it('should get identity information', async () => { console.log('Testing: get identity information'); const response = await post(baseUrl, { id: 'test', method: 'tools/call', params: { name: 'identity', arguments: { random_string: 'random', // Dummy parameter }, }, }); expect(response.status).toBe(200); // Get the response body const responseText = await response.text(); console.log('Identity response text:', responseText); const data = JSON.parse(responseText); // Verify the response has the expected structure expect(data).toHaveProperty('result'); expect(data.result).toHaveProperty('content'); expect(Array.isArray(data.result.content)).toBe(true); expect(data.result.content.length).toBeGreaterThan(0); // Get the tool result content const content = data.result.content[0]; expect(content).toHaveProperty('type'); expect(content).toHaveProperty('text'); // Parse the text content and verify it's the identity info const identityInfo = JSON.parse(content.text); expect(identityInfo).toHaveProperty('id'); expect(identityInfo.id).toContain('did:key:'); }); it('should upload a file', async () => { console.log('Testing: upload a file'); const response = await post(baseUrl, { id: 'test', method: 'tools/call', params: { name: 'upload', arguments: { file: testFileBase64, // Send as base64 string name: 'test-file.txt', }, }, }); expect(response.status).toBe(200); // Get the response body const responseText = await response.text(); console.log('Upload response text:', responseText); const data = JSON.parse(responseText); // Verify the response has the expected structure expect(data).toHaveProperty('result'); expect(data.result).toHaveProperty('content'); expect(Array.isArray(data.result.content)).toBe(true); expect(data.result.content.length).toBeGreaterThan(0); // Get the content const content = data.result.content[0]; expect(content).toHaveProperty('type'); expect(content).toHaveProperty('text'); // Parse the text content and verify it's the upload result const uploadResult = JSON.parse(content.text); expect(uploadResult).toHaveProperty('root'); expect(uploadResult.root).toBeDefined(); expect(uploadResult).toHaveProperty('url'); expect(uploadResult).toHaveProperty('files'); // Test that files is an object with at least one entry expect(typeof uploadResult.files).toBe('object'); expect(uploadResult.files).not.toBeNull(); // Get the first file entry const fileEntries = Object.entries(uploadResult.files); expect(fileEntries.length).toBeGreaterThan(0); expect(fileEntries[0]).toBeDefined(); }, 30000); // Increase the timeout for upload test it('should retrieve a file', async () => { console.log('Testing: retrieve a file'); const response = await post(baseUrl, { id: 'test', method: 'tools/call', params: { name: 'retrieve', arguments: { filepath: TEST_FILEPATH, }, }, }); expect(response.status).toBe(200); // Get the response body const responseText = await response.text(); console.log('Retrieve response text:', responseText); const data = JSON.parse(responseText); // Verify the response has the expected structure expect(data).toHaveProperty('result'); expect(data.result).toHaveProperty('content'); expect(Array.isArray(data.result.content)).toBe(true); expect(data.result.content.length).toBeGreaterThan(0); // Get the content const content = data.result.content[0]; expect(content).toHaveProperty('type'); expect(content).toHaveProperty('text'); // Just verify we have content expect(content.text).toBeTruthy(); }, 30000); it('should handle invalid upload parameters', async () => { console.log('Testing: invalid upload parameters'); const response = await post(baseUrl, { id: 'test', method: 'tools/call', params: { name: 'upload', arguments: { // Missing required parameters }, }, }); // For invalid parameters, we expect an error response with error details const responseText = await response.text(); console.log('Invalid upload response text:', responseText); const data = JSON.parse(responseText); expect(data).toHaveProperty('error'); }); it('should handle invalid retrieve parameters', async () => { console.log('Testing: invalid retrieve parameters'); const response = await post(baseUrl, { id: 'test', method: 'tools/call', params: { name: 'retrieve', arguments: { filepath: 'invalid-cid', // Missing the required filename structure }, }, }); // For invalid parameters, we expect a response with an error content const responseText = await response.text(); console.log('Invalid retrieve response text:', responseText); const data = JSON.parse(responseText); // Retrieve tool returns error as part of content expect(data).toHaveProperty('result'); expect(data.result).toHaveProperty('content'); expect(data.result.content[0]).toHaveProperty('error', true); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/storacha/mcp-storage-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server