Skip to main content
Glama

Dolphin MCP

logging_and_concurrency.test.ts6.24 kB
import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from 'bun:test' import { startMockRest } from './mockServer.js' import { makeSearchKnowledge } from '../mcp/tools/search_knowledge.js' import { makeFetchChunk } from '../mcp/tools/fetch_chunk.js' import { makeFetchLines } from '../mcp/tools/fetch_lines.js' import { initLogger, logInfo, logWarn, logError } from '../util/logger.js' import { readFileSync, existsSync, unlinkSync } from 'fs' import { join } from 'path' let stop: () => Promise<void> const logDir = join(process.cwd(), 'logs') const logFile = join(logDir, 'mcp.log') beforeAll(async () => { await initLogger(); stop = await startMockRest(7777) // Clean up any existing log files if (existsSync(logFile)) { unlinkSync(logFile) } }) afterAll(async () => { await stop?.() }) describe('logging', () => { beforeEach(() => { // Clean up log file before each test if (existsSync(logFile)) { unlinkSync(logFile) } }) it('writes JSONL to logs/mcp.log; no stdout writes', async () => { const { handler } = makeSearchKnowledge() const res = await handler({ input: { query: 'test' } }) expect(res.isError).toBe(false) // Give logger time to write await new Promise(resolve => setTimeout(resolve, 200)) // Check that log file exists and contains JSONL expect(existsSync(logFile)).toBe(true) const logContent = readFileSync(logFile, 'utf-8') expect(logContent).toBeTruthy() // Parse first line as JSON const lines = logContent.split('\n').filter(line => line.trim()) expect(lines.length).toBeGreaterThan(0) const firstLine = lines[0] expect(() => JSON.parse(firstLine)).not.toThrow() const logEntry = JSON.parse(firstLine) expect(logEntry.event).toBeDefined() expect(logEntry.level).toBeDefined() expect(logEntry.message).toBeDefined() expect(logEntry.ts).toBeDefined() }) it('warns when MCP applies local truncation due to 50 KB cap', async () => { // This would require a test that triggers truncation // For now, we test the logging function exists const { handler } = makeSearchKnowledge() const res = await handler({ input: { query: 'test' } }) expect(res.isError).toBe(false) // The implementation should log warnings when truncation occurs }) it('logs request start and end with proper metadata', async () => { const { handler } = makeSearchKnowledge() const res = await handler({ input: { query: 'test' } }) expect(res.isError).toBe(false) // Give logger time to write await new Promise(resolve => setTimeout(resolve, 200)) if (existsSync(logFile)) { const logContent = readFileSync(logFile, 'utf-8') const lines = logContent.split('\n').filter(line => line.trim()) // Should have at least one log entry expect(lines.length).toBeGreaterThan(0) lines.forEach(line => { const entry = JSON.parse(line) expect(entry.event).toBeDefined() expect(entry.level).toBeDefined() expect(entry.message).toBeDefined() expect(entry.ts).toBeDefined() // Context and meta are optional in current implementation }) } }) }) describe('concurrency and stability', () => { it('parallel calls to multiple tools execute without race conditions', async () => { const { handler: searchHandler } = makeSearchKnowledge() const { handler: fetchChunkHandler } = makeFetchChunk() const { handler: fetchLinesHandler } = makeFetchLines() // Make multiple concurrent calls const promises = [ searchHandler({ input: { query: 'test1' } }), searchHandler({ input: { query: 'test2' } }), fetchChunkHandler({ input: { chunk_id: '1' } }), fetchLinesHandler({ input: { repo: 'repoa', path: 'src/a.ts', start: 1, end: 10 } }), searchHandler({ input: { query: 'test3' } }), fetchChunkHandler({ input: { chunk_id: '2' } }), fetchLinesHandler({ input: { repo: 'repob', path: 'src/b.py', start: 5, end: 15 } }), searchHandler({ input: { query: 'test4' } }), ] const results = await Promise.all(promises) // All should complete without errors results.forEach(result => { expect(result.isError).toBe(false) }) // Verify each result has the expected structure results.forEach((result, index) => { expect(result.content).toBeDefined() expect(Array.isArray(result.content)).toBe(true) }) }) it('8 concurrent search_knowledge calls execute without shared-state corruption', async () => { const { handler } = makeSearchKnowledge() // Create 8 concurrent search requests with different queries const queries = Array.from({ length: 8 }, (_, i) => `query-${i}`) const promises = queries.map(query => handler({ input: { query } }) ) const results = await Promise.all(promises) // All should complete without errors results.forEach(result => { expect(result.isError).toBe(false) }) // Verify each result has unique content based on query results.forEach((result, index) => { expect(result._meta).toBeDefined() expect(result._meta.top_k).toBe(5) // Default top_k }) }) }) describe('payload and schema validation', () => { it('jsonSizeBytes used to enforce cap; end result size ≤ 50 KB', async () => { const { handler } = makeSearchKnowledge() const res = await handler({ input: { query: 'test' } }) expect(res.isError).toBe(false) // Convert result to JSON and check size const resultJson = JSON.stringify(res) const sizeInBytes = new TextEncoder().encode(resultJson).length expect(sizeInBytes).toBeLessThanOrEqual(50 * 1024) }) it('all tool definitions include valid JSON Schema', async () => { const tools = [ makeSearchKnowledge(), makeFetchChunk(), makeFetchLines(), ] tools.forEach(({ definition }) => { expect(definition.inputSchema).toBeDefined() expect(definition.inputSchema.type).toBe('object') expect(definition.inputSchema.properties).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/plasticbeachllc/dolphin-mcp'

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