Skip to main content
Glama
index.ts12.3 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { QaseClient } from './client.js'; class QaseMcpServer { private server: Server; private qaseClient: QaseClient; constructor() { this.server = new Server( { name: 'qase-mcp-server', version: '0.1.0', }, { capabilities: { tools: {}, }, } ); try { this.qaseClient = new QaseClient(); } catch (error) { console.error('Failed to initialize Qase client:', error); throw error; } this.setupRequestHandlers(); } private setupRequestHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'get_projects', description: 'プロジェクト一覧を取得します', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_test_cases', description: '指定したプロジェクトのテストケース一覧を取得します', inputSchema: { type: 'object', properties: { project_code: { type: 'string', description: 'プロジェクトコード', }, suite_id: { type: 'number', description: 'スイートID(指定した場合、そのスイートに属するテストケースのみ取得します)', }, }, required: ['project_code'], }, }, { name: 'create_test_case', description: 'テストケースを作成します', inputSchema: { type: 'object', properties: { project_code: { type: 'string', description: 'プロジェクトコード', }, title: { type: 'string', description: 'テストケースのタイトル', }, description: { type: 'string', description: 'テストケースの説明', }, }, required: ['project_code', 'title'], }, }, { name: 'create_suite', description: 'テストスイートを作成します', inputSchema: { type: 'object', properties: { project_code: { type: 'string', description: 'プロジェクトコード', }, title: { type: 'string', description: 'テストスイートのタイトル', }, description: { type: 'string', description: 'テストスイートの説明', }, preconditions: { type: 'string', description: 'テストスイートの前提条件', }, parent_id: { type: 'number', description: '親スイートのID', }, }, required: ['project_code', 'title'], }, }, { name: 'create_test_run', description: 'テスト実行を作成します', inputSchema: { type: 'object', properties: { project_code: { type: 'string', description: 'プロジェクトコード', }, title: { type: 'string', description: 'テスト実行のタイトル', }, description: { type: 'string', description: 'テスト実行の説明', }, cases: { type: 'array', items: { type: 'number', }, description: 'テスト実行に含めるテストケースのID一覧', }, }, required: ['project_code', 'title'], }, }, { name: 'create_test_cases_in_bulk', description: '複数のテストケースを一括で作成します', inputSchema: { type: 'object', properties: { project_code: { type: 'string', description: 'プロジェクトコード', }, cases: { type: 'array', description: '作成するテストケースの配列', items: { type: 'object', properties: { title: { type: 'string', description: 'テストケースのタイトル' }, description: { type: 'string', description: 'テストケースの説明' }, steps: { type: 'array', description: 'テストケースの手順', items: { type: 'object', properties: { action: { type: 'string', description: '手順のアクション' }, }, } }, suite_id: { type: 'number', description: '所属するスイートのID' } }, required: ['title'] } } }, required: ['project_code', 'cases'], }, }, ], })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { switch (request.params.name) { case 'get_projects': { const response = await this.qaseClient.getProjects(); return { content: [ { type: 'text', text: JSON.stringify(response.result, null, 2), }, ], }; } case 'get_test_cases': { const args = request.params.arguments; if (!args || typeof args.project_code !== 'string') { throw new McpError( ErrorCode.InvalidParams, 'project_code must be a string' ); } if (args.suite_id !== undefined && typeof args.suite_id !== 'number') { throw new McpError( ErrorCode.InvalidParams, 'suite_id must be a number' ); } const options = args.suite_id !== undefined ? { suite_id: args.suite_id } : undefined; const response = await this.qaseClient.getTestCases(args.project_code, options); return { content: [ { type: 'text', text: JSON.stringify(response.result, null, 2), }, ], }; } case 'create_test_case': { const args = request.params.arguments; if (!args || typeof args.project_code !== 'string') { throw new McpError( ErrorCode.InvalidParams, 'project_code must be a string' ); } if (typeof args.title !== 'string') { throw new McpError( ErrorCode.InvalidParams, 'title must be a string' ); } const { project_code, ...testCase } = args; const completeTestCase = { ...testCase, title: typeof testCase.title === 'string' ? testCase.title : 'Default Title', // Ensure title is a string }; const response = await this.qaseClient.createTestCase( project_code, completeTestCase ); return { content: [ { type: 'text', text: JSON.stringify(response.result, null, 2), }, ], }; } case 'create_suite': { const args = request.params.arguments; if (!args || typeof args.project_code !== 'string') { throw new McpError( ErrorCode.InvalidParams, 'project_code must be a string' ); } if (typeof args.title !== 'string') { throw new McpError( ErrorCode.InvalidParams, 'title must be a string' ); } const { project_code, ...suite } = args; const response = await this.qaseClient.createSuite( project_code, suite as { title: string; description?: string; preconditions?: string; parent_id?: number } ); return { content: [ { type: 'text', text: JSON.stringify(response.result, null, 2), }, ], }; } case 'create_test_run': { const args = request.params.arguments; if (!args || typeof args.project_code !== 'string') { throw new McpError( ErrorCode.InvalidParams, 'project_code must be a string' ); } if (typeof args.title !== 'string') { throw new McpError( ErrorCode.InvalidParams, 'title must be a string' ); } const { project_code, ...run } = args; const response = await this.qaseClient.createTestRun( project_code, run as { title: string; description?: string; cases?: number[] } ); return { content: [ { type: 'text', text: JSON.stringify(response.result, null, 2), }, ], }; } case 'create_test_cases_in_bulk': { const args = request.params.arguments; if (!args || typeof args.project_code !== 'string') { throw new McpError( ErrorCode.InvalidParams, 'project_code must be a string' ); } if (!Array.isArray(args.cases)) { throw new McpError( ErrorCode.InvalidParams, 'cases must be an array' ); } const response = await this.qaseClient.createTestCasesInBulk( args.project_code, args.cases ); return { content: [ { type: 'text', text: JSON.stringify(response.result, null, 2), }, ], }; } default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } } catch (error) { console.error('Error executing tool:', error); return { content: [ { type: 'text', text: error instanceof Error ? error.message : String(error), }, ], isError: true, }; } }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Qase MCP server running on stdio'); } } const server = new QaseMcpServer(); server.run().catch(console.error);

Latest Blog Posts

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/RayYokoyama/qase-mcp-server'

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