Skip to main content
Glama

Storacha MCP Storage Server

Official
by storacha
stdio-mcp-server.test.ts6.41 kB
import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; import path from 'path'; import fs from 'fs/promises'; import { fileURLToPath } from 'url'; import { getTestEnv, TEST_FILEPATH } from './test-config.js'; // Get directory name in ESM const __dirname = path.dirname(fileURLToPath(import.meta.url)); // 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 STDIO Integration Tests', () => { let client: Client; let tempFilePath: string; let clientTransport: StdioClientTransport; // Create a temporary test file and start server beforeAll(async () => { // Create a temporary file for testing tempFilePath = path.join(__dirname, 'test-file.txt'); await fs.writeFile(tempFilePath, 'This is a test file for upload'); // Create the client that connects to the server in stdio mode clientTransport = new StdioClientTransport({ command: 'node', args: ['dist/index.js'], env: { ...getTestEnv(), MCP_TRANSPORT_MODE: 'stdio', // Ensure stdio mode }, }); client = new Client( { name: 'test-client', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // Connect to the server await client.connect(clientTransport); // Wait a moment for the connection to establish await new Promise(resolve => setTimeout(resolve, 1000)); }, 15000); // Increase timeout for server startup afterAll(async () => { // Clean up temporary file try { await fs.unlink(tempFilePath); } catch (e) { // Ignore errors if file doesn't exist } // Close the transport, which should terminate the child process await clientTransport.close(); }); it('should list available tools', async () => { const tools = await client.listTools(); // Check for tools directly in the response format expect(tools.tools.some(tool => tool.name === 'identity')).toBe(true); expect(tools.tools.some(tool => tool.name === 'upload')).toBe(true); expect(tools.tools.some(tool => tool.name === 'retrieve')).toBe(true); }); it('should get identity information', async () => { const response = await client.callTool({ name: 'identity', arguments: {}, // Send an empty object }); const responseContent = response.content as Array<{ type: string; text: string }>; expect(responseContent.length).toBeGreaterThan(0); const content = responseContent[0]; expect(content).toHaveProperty('text'); const responseData = JSON.parse(content.text); expect(responseData).toHaveProperty('id'); expect(responseData.id).toContain('did:key:'); }); it('should upload a file', async () => { // Read file content and convert to base64 string const fileBuffer = await fs.readFile(tempFilePath); const fileContent = fileBuffer.toString('base64'); // Upload file const uploadResponse = await client.callTool({ name: 'upload', arguments: { file: fileContent, // Send as base64 string name: 'test-file.txt', type: 'text/plain', }, }); // Type assertion for upload response const uploadContent = ( uploadResponse.content as Array<{ type: string; text: string; error: boolean }> )[0]; expect(uploadContent).toHaveProperty('text'); // Parse upload response const uploadResult = JSON.parse(uploadContent.text); expect(uploadResult).toHaveProperty('root'); // The root can be either a string or an object with toString method 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 (could be array or object format from dag-json) const fileEntries = Object.entries(uploadResult.files); expect(fileEntries.length).toBeGreaterThan(0); // Check the first file entry (could be in different formats depending on serialization) const firstFile = fileEntries[0]; expect(firstFile).toBeDefined(); // The root can be either a string or an object with toString method 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(); }, 30_000); // Increase the timeout for upload test it('should retrieve a file', async () => { // Call retrieve tool const retrieveResponse = await client.callTool({ name: 'retrieve', arguments: { filepath: TEST_FILEPATH, }, }); // Type assertion for retrieve response const retrieveContent = (retrieveResponse.content as Array<{ type: string; text: string }>)[0]; expect(retrieveContent).toHaveProperty('text'); // Parse retrieve response // const retrieveResult = JSON.parse(retrieveContent.text); // FIXME: Add assertions for the retrieve result }, 30_000); it('should handle invalid upload parameters', async () => { // Try uploading without required parameters try { await client.callTool({ name: 'upload', arguments: { // Missing required parameters }, }); // Should not reach here expect(true).toBe(false); } catch (error) { expect(error).toBeDefined(); } }); it('should handle invalid retrieve parameters', async () => { // Test with invalid CID format (missing filename) try { await client.callTool({ name: 'retrieve', arguments: { filepath: 'invalid-cid', // Missing the required filename }, }); // Should not reach here expect(true).toBe(false); } catch (error) { expect(error).toBeDefined(); } }); });

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