feat: initial public release (MAESTRO)
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import { describe, it, expect, afterEach } from 'vitest';
|
||||
import { TOOL_DEFS, executeTool, setMcpToolLookup } from './docs.js';
|
||||
|
||||
describe('docs tool (ReadToolDoc)', () => {
|
||||
it('exports ReadToolDoc definition', () => {
|
||||
expect(TOOL_DEFS).toHaveProperty('ReadToolDoc');
|
||||
});
|
||||
|
||||
it('reads existing tool doc', async () => {
|
||||
const result = await executeTool('ReadToolDoc', { name: 'BrowseWeb' }, {
|
||||
workspacePath: '/tmp/test',
|
||||
editAllowed: false,
|
||||
});
|
||||
expect(result).not.toBeNull();
|
||||
expect(result!.isError).toBe(false);
|
||||
expect(result!.output).toContain('BrowseWeb');
|
||||
});
|
||||
|
||||
it('returns error and lists available docs for unknown tool', async () => {
|
||||
const result = await executeTool('ReadToolDoc', { name: 'NonExistent' }, {
|
||||
workspacePath: '/tmp/test',
|
||||
editAllowed: false,
|
||||
});
|
||||
expect(result!.isError).toBe(true);
|
||||
expect(result!.output).toContain('利用可能なドキュメント');
|
||||
expect(result!.output).toContain('browseweb');
|
||||
});
|
||||
|
||||
it('rejects path traversal attempts', async () => {
|
||||
const result = await executeTool('ReadToolDoc', { name: '../../etc/passwd' }, {
|
||||
workspacePath: '/tmp/test',
|
||||
editAllowed: false,
|
||||
});
|
||||
expect(result!.isError).toBe(true);
|
||||
expect(result!.output).toContain('不正なツール名');
|
||||
});
|
||||
|
||||
it('rejects empty name', async () => {
|
||||
const result = await executeTool('ReadToolDoc', {}, {
|
||||
workspacePath: '/tmp/test',
|
||||
editAllowed: false,
|
||||
});
|
||||
expect(result!.isError).toBe(true);
|
||||
expect(result!.output).toContain('name パラメータが必要');
|
||||
});
|
||||
|
||||
it('returns null for unknown tool name', async () => {
|
||||
const result = await executeTool('SomeOtherTool', {}, {
|
||||
workspacePath: '/tmp/test',
|
||||
editAllowed: false,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ReadToolDoc MCP fallback', () => {
|
||||
afterEach(() => {
|
||||
setMcpToolLookup(null);
|
||||
});
|
||||
|
||||
it('returns description + schema when lookup hits', async () => {
|
||||
setMcpToolLookup((serverId, toolName) => {
|
||||
if (serverId === 'canva' && toolName === 'create_design') {
|
||||
return {
|
||||
description: 'Create a Canva design',
|
||||
input_schema: JSON.stringify({ type: 'object', properties: { title: { type: 'string' } } }),
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
const result = await executeTool('ReadToolDoc', { name: 'mcp__canva__create_design' }, {} as never);
|
||||
expect(result?.isError).toBe(false);
|
||||
expect(result?.output).toContain('Create a Canva design');
|
||||
expect(result?.output).toContain('Input schema');
|
||||
expect(result?.output).toContain('"title"');
|
||||
});
|
||||
|
||||
it('returns benign error when MCP subsystem is not initialised', async () => {
|
||||
setMcpToolLookup(null);
|
||||
const result = await executeTool('ReadToolDoc', { name: 'mcp__canva__x' }, {} as never);
|
||||
expect(result?.isError).toBe(true);
|
||||
expect(result?.output).toContain('初期化されていません');
|
||||
});
|
||||
|
||||
it('returns not-found when lookup returns null', async () => {
|
||||
setMcpToolLookup(() => null);
|
||||
const result = await executeTool('ReadToolDoc', { name: 'mcp__canva__unknown_tool' }, {} as never);
|
||||
expect(result?.isError).toBe(false);
|
||||
expect(result?.output).toContain('キャッシュ情報がありません');
|
||||
});
|
||||
|
||||
it('rejects invalid MCP tool name', async () => {
|
||||
setMcpToolLookup(() => null);
|
||||
const result = await executeTool('ReadToolDoc', { name: 'mcp__bad' }, {} as never);
|
||||
expect(result?.isError).toBe(true);
|
||||
expect(result?.output).toContain('不正な MCP ツール名');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user