test(windows): add stdio fixtures

This commit is contained in:
Peter Steinberger 2025-11-18 03:21:27 +00:00
parent 51b3ae1303
commit 02d27423a8
3 changed files with 138 additions and 17 deletions

View File

@ -0,0 +1,63 @@
#!/usr/bin/env node
import fs from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const rootDir = path.resolve(process.argv[2] ?? process.cwd());
const server = new McpServer({ name: 'fs-fixture', version: '1.0.0' });
server.registerTool(
'list_files',
{
title: 'List Files',
description: 'List the files in the configured root',
inputSchema: {},
outputSchema: {
files: z.array(z.string()),
},
},
async () => {
const entries = await fs.readdir(rootDir);
return {
content: [{ type: 'text', text: entries.join('\n') }],
structuredContent: { files: entries },
};
}
);
server.registerTool(
'read_text_file',
{
title: 'Read Text File',
description: 'Read a UTF-8 file relative to the MCP root',
inputSchema: {
path: z.string().describe('Relative path inside the root directory'),
},
outputSchema: {
contents: z.string(),
},
},
async ({ path: relativePath }) => {
const targetPath = path.resolve(rootDir, relativePath);
if (!targetPath.startsWith(rootDir)) {
throw new Error('path escapes configured root');
}
const data = await fs.readFile(targetPath, 'utf8');
return {
content: [{ type: 'text', text: data }],
structuredContent: { contents: data },
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
await new Promise((resolve, reject) => {
transport.onclose = resolve;
transport.onerror = reject;
});

58
tests/fixtures/stdio-memory-server.mjs vendored Normal file
View File

@ -0,0 +1,58 @@
#!/usr/bin/env node
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const server = new McpServer({ name: 'memory-fixture', version: '1.0.0' });
const memory = new Set();
server.registerTool(
'create_entities',
{
title: 'Create Entities',
description: 'Insert the provided entity names into the in-memory store',
inputSchema: {
entities: z.array(z.string()),
},
outputSchema: {
count: z.number(),
},
},
async ({ entities }) => {
for (const entity of entities) {
if (entity.trim().length > 0) {
memory.add(entity.trim());
}
}
return {
content: [{ type: 'text', text: `Stored ${memory.size} entities` }],
structuredContent: { count: memory.size },
};
}
);
server.registerTool(
'list_entities',
{
title: 'List Entities',
description: 'Return all previously stored entities',
inputSchema: {},
outputSchema: {
entities: z.array(z.string()),
},
},
async () => {
return {
content: [{ type: 'text', text: JSON.stringify(Array.from(memory)) }],
structuredContent: { entities: Array.from(memory) },
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
await new Promise((resolve, reject) => {
transport.onclose = resolve;
transport.onerror = reject;
});

View File

@ -2,6 +2,7 @@ import { execFile } from 'node:child_process';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
@ -48,6 +49,9 @@ describe('stdio MCP servers (filesystem + memory)', () => {
let configPath: string;
let fsRoot: string;
const filesystemServerScript = fileURLToPath(new URL('./fixtures/stdio-filesystem-server.mjs', import.meta.url));
const memoryServerScript = fileURLToPath(new URL('./fixtures/stdio-memory-server.mjs', import.meta.url));
beforeAll(async () => {
await ensureDistBuilt();
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mcporter-stdio-e2e-'));
@ -62,13 +66,13 @@ describe('stdio MCP servers (filesystem + memory)', () => {
mcpServers: {
'fs-test': {
description: 'Filesystem MCP for stdio e2e tests',
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', fsRoot],
command: process.execPath,
args: [filesystemServerScript, fsRoot],
},
'memory-test': {
description: 'Knowledge graph MCP for stdio e2e tests',
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-memory'],
command: process.execPath,
args: [memoryServerScript],
},
},
},
@ -83,14 +87,12 @@ describe('stdio MCP servers (filesystem + memory)', () => {
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
});
it(
'lists filesystem tools and reads files via stdio MCP',
async () => {
const listResult = await runCli(['list', 'fs-test'], configPath);
expect(listResult.stdout).toContain('Filesystem MCP for stdio e2e tests');
const callResult = await runCli(
[
'call',
it('lists filesystem tools and reads files via stdio MCP', async () => {
const listResult = await runCli(['list', 'fs-test'], configPath);
expect(listResult.stdout).toContain('Filesystem MCP for stdio e2e tests');
const callResult = await runCli(
[
'call',
'fs-test.read_text_file',
'--output',
'json',
@ -98,11 +100,9 @@ describe('stdio MCP servers (filesystem + memory)', () => {
JSON.stringify({ path: path.join(fsRoot, 'hello.txt') }),
],
configPath
);
expect(callResult.stdout).toContain('hello from stdio mcp');
},
20000
);
);
expect(callResult.stdout).toContain('hello from stdio mcp');
}, 20000);
const memoryTest = process.platform === 'win32' ? it.skip : it;