Skip to main content
Glama

MariaDB Reader MCP Server

by moosin76
index.js10.2 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, } from "@modelcontextprotocol/sdk/types.js"; import mysql from 'mysql2/promise'; // 비동기/await 지원을 위해 mysql2/promise 사용 // --- MariaDB 연결 설정 --- // 이 값들은 MCP 설정에서 제공하는 환경 변수로부터 채워집니다. const dbConfig = { host: process.env.MARIADB_HOST || 'localhost', // 호스트 주소 (기본값: localhost) port: process.env.MARIADB_PORT ? parseInt(process.env.MARIADB_PORT, 10) : 3306, // 포트 번호 (기본값: 3306) user: process.env.MARIADB_USER, // 사용자 이름 (필수) password: process.env.MARIADB_PASSWORD, // 비밀번호 (선택) database: process.env.MARIADB_DATABASE, // 기본 데이터베이스 (선택) }; // 필수 환경 변수 기본 검증 if (!dbConfig.user) { console.error("MARIADB_USER 환경 변수가 필요합니다."); process.exit(1); // 가능하다면 MCP 클라이언트가 이해할 수 있는 오류를 발생시키는 것을 고려 } // 비밀번호는 DB 설정에 따라 선택 사항일 수 있으므로 엄격한 검사는 하지 않음 /** * 데이터베이스 연결을 생성하는 헬퍼 함수. * 사용 후에는 반드시 연결을 닫아야 합니다. * @param dbName - 연결할 특정 데이터베이스 이름 (선택 사항) * @returns 생성된 데이터베이스 연결 객체 */ async function createDbConnection(dbName) { try { const connection = await mysql.createConnection({ ...dbConfig, database: dbName || dbConfig.database, // 특정 DB가 제공되면 사용, 아니면 기본값 사용 }); return connection; } catch (error) { console.error("데이터베이스 연결 오류:", error.message); // MCP 클라이언트에게 더 구체적인 오류 제공 throw new McpError(ErrorCode.InternalError, `데이터베이스 연결 실패: ${error.message}`); } } /** * MCP 서버 인스턴스 생성. */ const server = new Server({ // 이름과 설명은 create-server 단계에서 제공됨 name: "mcp_server_mariadb_reader", version: "0.1.0", description: "MCP server mariadb reader" // 제공된 설명과 일치 }, { capabilities: { // 이 버전에서는 도구만 구현됨 tools: {}, }, }); /** * 사용 가능한 MariaDB 도구 목록을 반환하는 핸들러. */ server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "list_databases", description: "접근 가능한 모든 데이터베이스 목록을 보여줍니다.", inputSchema: { type: "object", properties: {} } // 입력 필요 없음 }, { name: "list_tables", description: "특정 데이터베이스 내의 모든 테이블 목록을 보여줍니다.", inputSchema: { type: "object", properties: { database: { type: "string", description: "데이터베이스의 이름입니다." } }, required: ["database"] } }, { name: "get_table_schema", description: "특정 테이블의 스키마(컬럼 정의)를 가져옵니다.", inputSchema: { type: "object", properties: { database: { type: "string", description: "데이터베이스의 이름입니다." }, table: { type: "string", description: "테이블의 이름입니다." } }, required: ["database", "table"] } }, { name: "query_table", description: "특정 테이블에서 데이터를 조회합니다 (제한된 행 반환).", inputSchema: { type: "object", properties: { database: { type: "string", description: "데이터베이스의 이름입니다." }, table: { type: "string", description: "테이블의 이름입니다." }, limit: { type: "number", description: "반환할 최대 행 수 (기본값 100).", default: 100 }, // 향후 개선: where_clause, columns, order_by 등 추가 }, required: ["database", "table"] } } ] }; }); /** * MariaDB 도구를 실행하는 핸들러. */ server.setRequestHandler(CallToolRequestSchema, async (request) => { let connection = null; // 연결 객체 초기화 const args = request.params.arguments || {}; // 인자 객체 (없으면 빈 객체) try { switch (request.params.name) { case "list_databases": { connection = await createDbConnection(); // 특정 DB 없이 연결 const [rows] = await connection.query('SHOW DATABASES;'); // 데이터베이스 목록 조회 쿼리 // 결과를 JSON 문자열로 변환하여 반환 (가독성을 위해 null, 2 사용) return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] }; } case "list_tables": { const dbName = args.database; // 데이터베이스 이름 추출 if (!dbName) throw new McpError(ErrorCode.InvalidParams, "필수 파라미터 누락: database"); connection = await createDbConnection(dbName); // 지정된 DB로 연결 // 테이블 목록 조회 쿼리 (백틱 사용은 안전하지만 SHOW TABLES에서는 필수는 아님) const [rows] = await connection.query(`SHOW TABLES;`); return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] }; } case "get_table_schema": { const dbName = args.database; // 데이터베이스 이름 추출 const tableName = args.table; // 테이블 이름 추출 if (!dbName) throw new McpError(ErrorCode.InvalidParams, "필수 파라미터 누락: database"); if (!tableName) throw new McpError(ErrorCode.InvalidParams, "필수 파라미터 누락: table"); connection = await createDbConnection(dbName); // 지정된 DB로 연결 // 테이블 스키마 조회 쿼리 (예약어 또는 특수문자 가능성을 위해 백틱 사용) const [rows] = await connection.query(`DESCRIBE \`${tableName}\`;`); return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] }; } case "query_table": { const dbName = args.database; // 데이터베이스 이름 추출 const tableName = args.table; // 테이블 이름 추출 // 반환할 행 수 제한 (기본값 100) const limit = typeof args.limit === 'number' && args.limit > 0 ? args.limit : 100; if (!dbName) throw new McpError(ErrorCode.InvalidParams, "필수 파라미터 누락: database"); if (!tableName) throw new McpError(ErrorCode.InvalidParams, "필수 파라미터 누락: table"); connection = await createDbConnection(dbName); // 지정된 DB로 연결 // 데이터 조회 쿼리 (안전을 위해 백틱 사용 및 LIMIT 적용) const query = `SELECT * FROM \`${tableName}\` LIMIT ?;`; const [rows] = await connection.query(query, [limit]); // 쿼리 실행 (limit 값 바인딩) return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] }; } default: // 알려지지 않은 도구 요청 시 오류 발생 throw new McpError(ErrorCode.MethodNotFound, `알 수 없는 도구: ${request.params.name}`); } } catch (error) { // 도구 실행 중 발생한 오류 로깅 console.error(`도구 ${request.params.name} 실행 오류:`, error.message); // 이미 McpError 인스턴스인지 확인, 아니면 래핑 if (error instanceof McpError) { throw error; } // 잠재적인 MySQL 오류를 구체적으로 처리 if (error.sqlMessage) { throw new McpError(ErrorCode.InternalError, `데이터베이스 쿼리 실패: ${error.sqlMessage} (코드: ${error.code})`); } // 일반적인 내부 오류 throw new McpError(ErrorCode.InternalError, `도구 ${request.params.name} 실행 실패: ${error.message}`); } finally { // 연결이 생성되었다면 항상 닫도록 보장 if (connection) { await connection.end(); } } }); // --- 서버 시작 --- async function main() { // 오류 처리 설정 server.onerror = (error) => console.error('[MCP 서버 오류]', error); // SIGINT (Ctrl+C) 및 SIGTERM 신호 처리하여 정상 종료 process.on('SIGINT', async () => { console.log("서버 종료 중..."); await server.close(); process.exit(0); }); process.on('SIGTERM', async () => { console.log("서버 종료 중..."); await server.close(); process.exit(0); }); // 표준 입출력(stdio) 전송 계층 사용 const transport = new StdioServerTransport(); await server.connect(transport); // 서버 실행 로그는 stderr로 출력하여 stdout JSON 통신 방해 방지 console.error('MariaDB 리더 MCP 서버가 stdio에서 실행 중입니다.'); } // 주 함수 실행 및 오류 처리 main().catch((error) => { console.error("서버 시작 실패:", error); process.exit(1); });

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/moosin76/mcp_server_mariadb_reader'

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