mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-04-18 08:03:26 +02:00
Merge branch 'main' into claude/issue-2526-20250824-0240
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
@@ -23,19 +23,19 @@ import {
|
||||
} from '../lib.js';
|
||||
|
||||
// Mock fs module
|
||||
jest.mock('fs/promises');
|
||||
const mockFs = fs as jest.Mocked<typeof fs>;
|
||||
vi.mock('fs/promises');
|
||||
const mockFs = fs as any;
|
||||
|
||||
describe('Lib Functions', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
// Set up allowed directories for tests
|
||||
const allowedDirs = process.platform === 'win32' ? ['C:\\Users\\test', 'C:\\temp', 'C:\\allowed'] : ['/home/user', '/tmp', '/allowed'];
|
||||
setAllowedDirectories(allowedDirs);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
vi.restoreAllMocks();
|
||||
// Clear allowed directories after tests
|
||||
setAllowedDirectories([]);
|
||||
});
|
||||
@@ -591,8 +591,8 @@ describe('Lib Functions', () => {
|
||||
|
||||
// Mock file handle with proper typing
|
||||
const mockFileHandle = {
|
||||
read: jest.fn(),
|
||||
close: jest.fn()
|
||||
read: vi.fn(),
|
||||
close: vi.fn()
|
||||
} as any;
|
||||
|
||||
mockFileHandle.read.mockResolvedValue({ bytesRead: 0 });
|
||||
@@ -610,8 +610,8 @@ describe('Lib Functions', () => {
|
||||
mockFs.stat.mockResolvedValue({ size: 50 } as any);
|
||||
|
||||
const mockFileHandle = {
|
||||
read: jest.fn(),
|
||||
close: jest.fn()
|
||||
read: vi.fn(),
|
||||
close: vi.fn()
|
||||
} as any;
|
||||
|
||||
// Simulate reading file content in chunks
|
||||
@@ -631,8 +631,8 @@ describe('Lib Functions', () => {
|
||||
mockFs.stat.mockResolvedValue({ size: 100 } as any);
|
||||
|
||||
const mockFileHandle = {
|
||||
read: jest.fn(),
|
||||
close: jest.fn()
|
||||
read: vi.fn(),
|
||||
close: vi.fn()
|
||||
} as any;
|
||||
|
||||
mockFileHandle.read.mockResolvedValue({ bytesRead: 0 });
|
||||
@@ -650,8 +650,8 @@ describe('Lib Functions', () => {
|
||||
it('opens file for reading', async () => {
|
||||
// Mock file handle with proper typing
|
||||
const mockFileHandle = {
|
||||
read: jest.fn(),
|
||||
close: jest.fn()
|
||||
read: vi.fn(),
|
||||
close: vi.fn()
|
||||
} as any;
|
||||
|
||||
mockFileHandle.read.mockResolvedValue({ bytesRead: 0 });
|
||||
@@ -666,8 +666,8 @@ describe('Lib Functions', () => {
|
||||
|
||||
it('handles files with content and returns first lines', async () => {
|
||||
const mockFileHandle = {
|
||||
read: jest.fn(),
|
||||
close: jest.fn()
|
||||
read: vi.fn(),
|
||||
close: vi.fn()
|
||||
} as any;
|
||||
|
||||
// Simulate reading file content with newlines
|
||||
@@ -685,8 +685,8 @@ describe('Lib Functions', () => {
|
||||
|
||||
it('handles files with leftover content', async () => {
|
||||
const mockFileHandle = {
|
||||
read: jest.fn(),
|
||||
close: jest.fn()
|
||||
read: vi.fn(),
|
||||
close: vi.fn()
|
||||
} as any;
|
||||
|
||||
// Simulate reading file content without final newline
|
||||
@@ -704,8 +704,8 @@ describe('Lib Functions', () => {
|
||||
|
||||
it('handles reaching requested line count', async () => {
|
||||
const mockFileHandle = {
|
||||
read: jest.fn(),
|
||||
close: jest.fn()
|
||||
read: vi.fn(),
|
||||
close: vi.fn()
|
||||
} as any;
|
||||
|
||||
// Simulate reading exactly the requested number of lines
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
import { describe, it, expect, afterEach } from 'vitest';
|
||||
import { normalizePath, expandHome, convertToWindowsPath } from '../path-utils.js';
|
||||
|
||||
describe('Path Utilities', () => {
|
||||
@@ -10,14 +10,25 @@ describe('Path Utilities', () => {
|
||||
.toBe('/home/user/some path');
|
||||
});
|
||||
|
||||
it('converts WSL paths to Windows format', () => {
|
||||
it('never converts WSL paths (they work correctly in WSL with Node.js fs)', () => {
|
||||
// WSL paths should NEVER be converted, regardless of platform
|
||||
// They are valid Linux paths that work with Node.js fs operations inside WSL
|
||||
expect(convertToWindowsPath('/mnt/c/NS/MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
.toBe('/mnt/c/NS/MyKindleContent');
|
||||
expect(convertToWindowsPath('/mnt/d/Documents'))
|
||||
.toBe('/mnt/d/Documents');
|
||||
});
|
||||
|
||||
it('converts Unix-style Windows paths to Windows format', () => {
|
||||
expect(convertToWindowsPath('/c/NS/MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
it('converts Unix-style Windows paths only on Windows platform', () => {
|
||||
// On Windows, /c/ style paths should be converted
|
||||
if (process.platform === 'win32') {
|
||||
expect(convertToWindowsPath('/c/NS/MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
} else {
|
||||
// On Linux, leave them unchanged
|
||||
expect(convertToWindowsPath('/c/NS/MyKindleContent'))
|
||||
.toBe('/c/NS/MyKindleContent');
|
||||
}
|
||||
});
|
||||
|
||||
it('leaves Windows paths unchanged but ensures backslashes', () => {
|
||||
@@ -34,11 +45,20 @@ describe('Path Utilities', () => {
|
||||
.toBe('C:\\Program Files\\Some App');
|
||||
});
|
||||
|
||||
it('handles uppercase and lowercase drive letters', () => {
|
||||
it('handles drive letter paths based on platform', () => {
|
||||
// WSL paths should never be converted
|
||||
expect(convertToWindowsPath('/mnt/d/some/path'))
|
||||
.toBe('D:\\some\\path');
|
||||
expect(convertToWindowsPath('/d/some/path'))
|
||||
.toBe('D:\\some\\path');
|
||||
.toBe('/mnt/d/some/path');
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
// On Windows, Unix-style paths like /d/ should be converted
|
||||
expect(convertToWindowsPath('/d/some/path'))
|
||||
.toBe('D:\\some\\path');
|
||||
} else {
|
||||
// On Linux, /d/ is just a regular Unix path
|
||||
expect(convertToWindowsPath('/d/some/path'))
|
||||
.toBe('/d/some/path');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,6 +70,12 @@ describe('Path Utilities', () => {
|
||||
.toBe('/home/user/some path');
|
||||
expect(normalizePath('"/usr/local/some app/"'))
|
||||
.toBe('/usr/local/some app');
|
||||
expect(normalizePath('/usr/local//bin/app///'))
|
||||
.toBe('/usr/local/bin/app');
|
||||
expect(normalizePath('/'))
|
||||
.toBe('/');
|
||||
expect(normalizePath('///'))
|
||||
.toBe('/');
|
||||
});
|
||||
|
||||
it('removes surrounding quotes', () => {
|
||||
@@ -67,21 +93,33 @@ describe('Path Utilities', () => {
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
});
|
||||
|
||||
it('handles WSL paths', () => {
|
||||
it('always preserves WSL paths (they work correctly in WSL)', () => {
|
||||
// WSL paths should ALWAYS be preserved, regardless of platform
|
||||
// This is the fix for issue #2795
|
||||
expect(normalizePath('/mnt/c/NS/MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
.toBe('/mnt/c/NS/MyKindleContent');
|
||||
expect(normalizePath('/mnt/d/Documents'))
|
||||
.toBe('/mnt/d/Documents');
|
||||
});
|
||||
|
||||
it('handles Unix-style Windows paths', () => {
|
||||
expect(normalizePath('/c/NS/MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
// On Windows, /c/ paths should be converted
|
||||
if (process.platform === 'win32') {
|
||||
expect(normalizePath('/c/NS/MyKindleContent'))
|
||||
.toBe('C:\\NS\\MyKindleContent');
|
||||
} else if (process.platform === 'linux') {
|
||||
// On Linux, /c/ is just a regular Unix path
|
||||
expect(normalizePath('/c/NS/MyKindleContent'))
|
||||
.toBe('/c/NS/MyKindleContent');
|
||||
}
|
||||
});
|
||||
|
||||
it('handles paths with spaces and mixed slashes', () => {
|
||||
expect(normalizePath('C:/NS/My Kindle Content'))
|
||||
.toBe('C:\\NS\\My Kindle Content');
|
||||
// WSL paths should always be preserved
|
||||
expect(normalizePath('/mnt/c/NS/My Kindle Content'))
|
||||
.toBe('C:\\NS\\My Kindle Content');
|
||||
.toBe('/mnt/c/NS/My Kindle Content');
|
||||
expect(normalizePath('C:\\Program Files (x86)\\App Name'))
|
||||
.toBe('C:\\Program Files (x86)\\App Name');
|
||||
expect(normalizePath('"C:\\Program Files\\App Name"'))
|
||||
@@ -91,10 +129,19 @@ describe('Path Utilities', () => {
|
||||
});
|
||||
|
||||
it('preserves spaces in all path formats', () => {
|
||||
// WSL paths should always be preserved
|
||||
expect(normalizePath('/mnt/c/Program Files/App Name'))
|
||||
.toBe('C:\\Program Files\\App Name');
|
||||
expect(normalizePath('/c/Program Files/App Name'))
|
||||
.toBe('C:\\Program Files\\App Name');
|
||||
.toBe('/mnt/c/Program Files/App Name');
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
// On Windows, Unix-style paths like /c/ should be converted
|
||||
expect(normalizePath('/c/Program Files/App Name'))
|
||||
.toBe('C:\\Program Files\\App Name');
|
||||
} else {
|
||||
// On Linux, /c/ is just a regular Unix path
|
||||
expect(normalizePath('/c/Program Files/App Name'))
|
||||
.toBe('/c/Program Files/App Name');
|
||||
}
|
||||
expect(normalizePath('C:/Program Files/App Name'))
|
||||
.toBe('C:\\Program Files\\App Name');
|
||||
});
|
||||
@@ -105,15 +152,16 @@ describe('Path Utilities', () => {
|
||||
.toBe('C:\\NS\\Sub&Folder');
|
||||
expect(normalizePath('C:/NS/Sub&Folder'))
|
||||
.toBe('C:\\NS\\Sub&Folder');
|
||||
// WSL paths should always be preserved
|
||||
expect(normalizePath('/mnt/c/NS/Sub&Folder'))
|
||||
.toBe('C:\\NS\\Sub&Folder');
|
||||
|
||||
.toBe('/mnt/c/NS/Sub&Folder');
|
||||
|
||||
// Test tilde in path (short names in Windows)
|
||||
expect(normalizePath('C:\\NS\\MYKIND~1'))
|
||||
.toBe('C:\\NS\\MYKIND~1');
|
||||
expect(normalizePath('/Users/NEMANS~1/FOLDER~2/SUBFO~1/Public/P12PST~1'))
|
||||
.toBe('/Users/NEMANS~1/FOLDER~2/SUBFO~1/Public/P12PST~1');
|
||||
|
||||
|
||||
// Test other special characters
|
||||
expect(normalizePath('C:\\Path with #hash'))
|
||||
.toBe('C:\\Path with #hash');
|
||||
@@ -128,10 +176,19 @@ describe('Path Utilities', () => {
|
||||
it('capitalizes lowercase drive letters for Windows paths', () => {
|
||||
expect(normalizePath('c:/windows/system32'))
|
||||
.toBe('C:\\windows\\system32');
|
||||
expect(normalizePath('/mnt/d/my/folder')) // WSL path with lowercase drive
|
||||
.toBe('D:\\my\\folder');
|
||||
expect(normalizePath('/e/another/folder')) // Unix-style Windows path with lowercase drive
|
||||
.toBe('E:\\another\\folder');
|
||||
// WSL paths should always be preserved
|
||||
expect(normalizePath('/mnt/d/my/folder'))
|
||||
.toBe('/mnt/d/my/folder');
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
// On Windows, Unix-style paths should be converted and capitalized
|
||||
expect(normalizePath('/e/another/folder'))
|
||||
.toBe('E:\\another\\folder');
|
||||
} else {
|
||||
// On Linux, /e/ is just a regular Unix path
|
||||
expect(normalizePath('/e/another/folder'))
|
||||
.toBe('/e/another/folder');
|
||||
}
|
||||
});
|
||||
|
||||
it('handles UNC paths correctly', () => {
|
||||
@@ -145,11 +202,8 @@ describe('Path Utilities', () => {
|
||||
});
|
||||
|
||||
it('returns normalized non-Windows/WSL/Unix-style Windows paths as is after basic normalization', () => {
|
||||
// Relative path
|
||||
const relativePath = 'some/relative/path';
|
||||
expect(normalizePath(relativePath)).toBe(relativePath.replace(/\//g, '\\'));
|
||||
|
||||
// A path that looks somewhat absolute but isn't a drive or recognized Unix root for Windows conversion
|
||||
// These paths should be preserved as-is (not converted to Windows C:\ format or WSL format)
|
||||
const otherAbsolutePath = '\\someserver\\share\\file';
|
||||
expect(normalizePath(otherAbsolutePath)).toBe(otherAbsolutePath);
|
||||
});
|
||||
@@ -172,4 +226,146 @@ describe('Path Utilities', () => {
|
||||
expect(expandHome('C:/test')).toBe('C:/test');
|
||||
});
|
||||
});
|
||||
|
||||
describe('WSL path handling (issue #2795 fix)', () => {
|
||||
// Save original platform
|
||||
const originalPlatform = process.platform;
|
||||
|
||||
afterEach(() => {
|
||||
// Restore platform after each test
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: originalPlatform,
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should NEVER convert WSL paths - they work correctly in WSL with Node.js fs', () => {
|
||||
// The key insight: When running `wsl npx ...`, Node.js runs INSIDE WSL (process.platform === 'linux')
|
||||
// and /mnt/c/ paths work correctly with Node.js fs operations in that environment.
|
||||
// Converting them to C:\ format breaks fs operations because Windows paths don't work inside WSL.
|
||||
|
||||
// Mock Linux platform (inside WSL)
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: 'linux',
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
// WSL paths should NOT be converted, even inside WSL
|
||||
expect(normalizePath('/mnt/c/Users/username/folder'))
|
||||
.toBe('/mnt/c/Users/username/folder');
|
||||
|
||||
expect(normalizePath('/mnt/d/Documents/project'))
|
||||
.toBe('/mnt/d/Documents/project');
|
||||
});
|
||||
|
||||
it('should also preserve WSL paths when running on Windows', () => {
|
||||
// Mock Windows platform
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: 'win32',
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
// WSL paths should still be preserved (though they wouldn't be accessible from Windows Node.js)
|
||||
expect(normalizePath('/mnt/c/Users/username/folder'))
|
||||
.toBe('/mnt/c/Users/username/folder');
|
||||
|
||||
expect(normalizePath('/mnt/d/Documents/project'))
|
||||
.toBe('/mnt/d/Documents/project');
|
||||
});
|
||||
|
||||
it('should convert Unix-style Windows paths (/c/) only when running on Windows (win32)', () => {
|
||||
// Mock process.platform to be 'win32' (Windows)
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: 'win32',
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
// Unix-style Windows paths like /c/ should be converted on Windows
|
||||
expect(normalizePath('/c/Users/username/folder'))
|
||||
.toBe('C:\\Users\\username\\folder');
|
||||
|
||||
expect(normalizePath('/d/Documents/project'))
|
||||
.toBe('D:\\Documents\\project');
|
||||
});
|
||||
|
||||
it('should NOT convert Unix-style paths (/c/) when running inside WSL (linux)', () => {
|
||||
// Mock process.platform to be 'linux' (WSL/Linux)
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: 'linux',
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
// When on Linux, /c/ is just a regular Unix directory, not a drive letter
|
||||
expect(normalizePath('/c/some/path'))
|
||||
.toBe('/c/some/path');
|
||||
|
||||
expect(normalizePath('/d/another/path'))
|
||||
.toBe('/d/another/path');
|
||||
});
|
||||
|
||||
it('should preserve regular Unix paths on all platforms', () => {
|
||||
// Test on Linux
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: 'linux',
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
expect(normalizePath('/home/user/documents'))
|
||||
.toBe('/home/user/documents');
|
||||
|
||||
expect(normalizePath('/var/log/app'))
|
||||
.toBe('/var/log/app');
|
||||
|
||||
// Test on Windows (though these paths wouldn't work on Windows)
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: 'win32',
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
expect(normalizePath('/home/user/documents'))
|
||||
.toBe('/home/user/documents');
|
||||
|
||||
expect(normalizePath('/var/log/app'))
|
||||
.toBe('/var/log/app');
|
||||
});
|
||||
|
||||
it('reproduces exact scenario from issue #2795', () => {
|
||||
// Simulate running inside WSL: wsl npx @modelcontextprotocol/server-filesystem /mnt/c/Users/username/folder
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: 'linux',
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
// This is the exact path from the issue
|
||||
const inputPath = '/mnt/c/Users/username/folder';
|
||||
const result = normalizePath(inputPath);
|
||||
|
||||
// Should NOT convert to C:\Users\username\folder
|
||||
expect(result).toBe('/mnt/c/Users/username/folder');
|
||||
expect(result).not.toContain('C:');
|
||||
expect(result).not.toContain('\\');
|
||||
});
|
||||
|
||||
it('should handle relative path slash conversion based on platform', () => {
|
||||
// This test verifies platform-specific behavior naturally without mocking
|
||||
// On Windows: forward slashes converted to backslashes
|
||||
// On Linux/Unix: forward slashes preserved
|
||||
const relativePath = 'some/relative/path';
|
||||
const result = normalizePath(relativePath);
|
||||
|
||||
if (originalPlatform === 'win32') {
|
||||
expect(result).toBe('some\\relative\\path');
|
||||
} else {
|
||||
expect(result).toBe('some/relative/path');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as os from 'os';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { getValidRootDirectories } from '../roots-utils.js';
|
||||
import { mkdtempSync, rmSync, mkdirSync, writeFileSync, realpathSync } from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
|
||||
158
src/filesystem/__tests__/structured-content.test.ts
Normal file
158
src/filesystem/__tests__/structured-content.test.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
||||
import { spawn } from 'child_process';
|
||||
|
||||
/**
|
||||
* Integration tests to verify that tool handlers return structuredContent
|
||||
* that matches the declared outputSchema.
|
||||
*
|
||||
* These tests address issues #3110, #3106, #3093 where tools were returning
|
||||
* structuredContent: { content: [contentBlock] } (array) instead of
|
||||
* structuredContent: { content: string } as declared in outputSchema.
|
||||
*/
|
||||
describe('structuredContent schema compliance', () => {
|
||||
let client: Client;
|
||||
let transport: StdioClientTransport;
|
||||
let testDir: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Create a temp directory for testing
|
||||
testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'mcp-fs-test-'));
|
||||
|
||||
// Create test files
|
||||
await fs.writeFile(path.join(testDir, 'test.txt'), 'test content');
|
||||
await fs.mkdir(path.join(testDir, 'subdir'));
|
||||
await fs.writeFile(path.join(testDir, 'subdir', 'nested.txt'), 'nested content');
|
||||
|
||||
// Start the MCP server
|
||||
const serverPath = path.resolve(__dirname, '../dist/index.js');
|
||||
transport = new StdioClientTransport({
|
||||
command: 'node',
|
||||
args: [serverPath, testDir],
|
||||
});
|
||||
|
||||
client = new Client({
|
||||
name: 'test-client',
|
||||
version: '1.0.0',
|
||||
}, {
|
||||
capabilities: {}
|
||||
});
|
||||
|
||||
await client.connect(transport);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await client?.close();
|
||||
await fs.rm(testDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe('directory_tree', () => {
|
||||
it('should return structuredContent.content as a string, not an array', async () => {
|
||||
const result = await client.callTool({
|
||||
name: 'directory_tree',
|
||||
arguments: { path: testDir }
|
||||
});
|
||||
|
||||
// The result should have structuredContent
|
||||
expect(result.structuredContent).toBeDefined();
|
||||
|
||||
// structuredContent.content should be a string (matching outputSchema: { content: z.string() })
|
||||
const structuredContent = result.structuredContent as { content: unknown };
|
||||
expect(typeof structuredContent.content).toBe('string');
|
||||
|
||||
// It should NOT be an array
|
||||
expect(Array.isArray(structuredContent.content)).toBe(false);
|
||||
|
||||
// The content should be valid JSON representing the tree
|
||||
const treeData = JSON.parse(structuredContent.content as string);
|
||||
expect(Array.isArray(treeData)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('list_directory_with_sizes', () => {
|
||||
it('should return structuredContent.content as a string, not an array', async () => {
|
||||
const result = await client.callTool({
|
||||
name: 'list_directory_with_sizes',
|
||||
arguments: { path: testDir }
|
||||
});
|
||||
|
||||
// The result should have structuredContent
|
||||
expect(result.structuredContent).toBeDefined();
|
||||
|
||||
// structuredContent.content should be a string (matching outputSchema: { content: z.string() })
|
||||
const structuredContent = result.structuredContent as { content: unknown };
|
||||
expect(typeof structuredContent.content).toBe('string');
|
||||
|
||||
// It should NOT be an array
|
||||
expect(Array.isArray(structuredContent.content)).toBe(false);
|
||||
|
||||
// The content should contain directory listing info
|
||||
expect(structuredContent.content).toContain('[FILE]');
|
||||
});
|
||||
});
|
||||
|
||||
describe('move_file', () => {
|
||||
it('should return structuredContent.content as a string, not an array', async () => {
|
||||
const sourcePath = path.join(testDir, 'test.txt');
|
||||
const destPath = path.join(testDir, 'moved.txt');
|
||||
|
||||
const result = await client.callTool({
|
||||
name: 'move_file',
|
||||
arguments: {
|
||||
source: sourcePath,
|
||||
destination: destPath
|
||||
}
|
||||
});
|
||||
|
||||
// The result should have structuredContent
|
||||
expect(result.structuredContent).toBeDefined();
|
||||
|
||||
// structuredContent.content should be a string (matching outputSchema: { content: z.string() })
|
||||
const structuredContent = result.structuredContent as { content: unknown };
|
||||
expect(typeof structuredContent.content).toBe('string');
|
||||
|
||||
// It should NOT be an array
|
||||
expect(Array.isArray(structuredContent.content)).toBe(false);
|
||||
|
||||
// The content should contain success message
|
||||
expect(structuredContent.content).toContain('Successfully moved');
|
||||
});
|
||||
});
|
||||
|
||||
describe('list_directory (control - already working)', () => {
|
||||
it('should return structuredContent.content as a string', async () => {
|
||||
const result = await client.callTool({
|
||||
name: 'list_directory',
|
||||
arguments: { path: testDir }
|
||||
});
|
||||
|
||||
expect(result.structuredContent).toBeDefined();
|
||||
|
||||
const structuredContent = result.structuredContent as { content: unknown };
|
||||
expect(typeof structuredContent.content).toBe('string');
|
||||
expect(Array.isArray(structuredContent.content)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('search_files (control - already working)', () => {
|
||||
it('should return structuredContent.content as a string', async () => {
|
||||
const result = await client.callTool({
|
||||
name: 'search_files',
|
||||
arguments: {
|
||||
path: testDir,
|
||||
pattern: '*.txt'
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.structuredContent).toBeDefined();
|
||||
|
||||
const structuredContent = result.structuredContent as { content: unknown };
|
||||
expect(typeof structuredContent.content).toBe('string');
|
||||
expect(Array.isArray(structuredContent.content)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user