ANALYSIS.md•17.6 kB
# WhaTap MXQL MCP Server - 분석 및 설계 문서
## 📋 분석 요약
### 1. 기존 MXQL for Claude Code
- **Repository**: https://github.com/kyupid/mxql-for-claude-code
- **Type**: Claude Code Skill (Python 기반)
- **기능**:
- MXQL 쿼리 생성 (자연어 → MXQL)
- 문법 검증 및 최적화 분석
- 테스트 쿼리 생성
- 카테고리 검색 (631개 메트릭)
- **한계**: 실제 WhaTap 서비스 API 연동 없음 (오프라인 도구)
### 2. WhaTap Mobile App 인증 메커니즘
#### 2.1 인증 흐름 (Multi-Step Authentication)
```
Step 1: CSRF 토큰 획득
GET /account/login?lang=en
Response: HTML에서 CSRF 토큰 추출
Step 2: 웹 로그인
POST /account/login
Body: email, password, _csrf, rememberMe=on
Headers: Content-Type: application/x-www-form-urlencoded
Response: Cookies (wa, JSESSIONID)
Step 3: 모바일 API 토큰 획득
POST /mobile/api/login
Body: { email, password, appVersion, deviceInfo, deviceModel, deviceType, fcmToken, osVersion }
Headers: Content-Type: application/json
Cookies: wa, JSESSIONID (from Step 2)
Response: { accountId, apiToken }
Step 4 (Optional): OTP 검증
POST /account/api/checkOtpToken
Body: { accountId, otpToken }
Response: { result: true/false }
```
**코드 위치**:
- Login Provider: `/src/lib/provider/login_provider.dart:196-231`
- Web Login API: `/src/lib/api/web/login_api.dart:22-146`
- Mobile Login API: `/src/lib/api/mobile/login_api.dart:19-44`
#### 2.2 인증 상태 관리
- **쿠키 기반**: `wa`, `JSESSIONID` (CookieJar로 자동 관리)
- **API 토큰**: Open API 호출 시 `x-whatap-token` 헤더 사용
- **저장**: SharedPreferences에 UserStatus 저장 (email, apiToken, accountId)
### 3. MXQL API 호출 메커니즘
#### 3.1 MXQL 데이터 구조
```dart
// /src/lib/model/api/mxql.dart
class MXQL {
String? type; // "mxql"
int? pcode; // Project Code
Params? params;
String? path; // "text" or "path"
}
class Params {
String? mql; // MXQL 쿼리 문자열
Inject? inject; // 변수 주입 (optional)
int? stime; // Start time (milliseconds)
int? etime; // End time (milliseconds)
int? limit; // 결과 제한 (default: 10000)
}
```
**실제 사용 예시** (`/src/lib/api/web/panel_api.dart:118-158`):
```dart
final mxql = MXQL(
type: 'mxql',
pcode: pCode,
params: Params(
mql: '''
TIME-RANGE {duration:1m, etime:\$etime}
CATEGORY container
TAGLOAD { backward: true }
SELECT ["Deployment","containerId","microOid","podName"]
''',
stime: DateTime.now().subtract(Duration(minutes: 1)).millisecondsSinceEpoch,
etime: DateTime.now().millisecondsSinceEpoch,
limit: 1000,
inject: null
),
path: 'text',
);
```
#### 3.2 MXQL API 엔드포인트
**URL**: `POST /yard/api/flush`
**요청**:
```json
{
"type": "mxql",
"pcode": 12345,
"params": {
"mql": "CATEGORY app_counter\nOID [\$oid]\nTAGLOAD",
"stime": 1699900000000,
"etime": 1699900060000,
"limit": 10000
},
"path": "text"
}
```
**Headers**:
```
Content-Type: application/json
Cookie: wa={cookie_value}; JSESSIONID={session_id}
```
**응답**:
```json
[
{
"oid": 123456789,
"oname": "app-server-1",
"value": 42.5,
...
}
]
```
**코드 위치**:
- MXQL 실행: `/src/lib/api/web/product/product_base_api.dart:44-65`
- APM 예시: `/src/lib/api/web/product/apm_api.dart:20-43`
- Container 예시: `/src/lib/api/web/panel_api.dart:111-199`
#### 3.3 API 서비스 구조
**ApiService** (`/src/lib/service/api_service.dart`):
- **Singleton 패턴**: 단일 Dio 인스턴스
- **CookieJar**: 자동 쿠키 관리
- **Interceptors**: Rate limiting, Logging, Curl 변환
- **Headers**: `json()` 메서드로 Content-Type 설정
```dart
Dio dio = ApiService().json(); // Content-Type: application/json
final response = await dio.post('/yard/api/flush', data: mxql.toJson());
```
### 4. Open API 사용 (대안)
**프로젝트 목록 조회**:
```dart
// /src/lib/api/open/project_api.dart:13-32
GET /open/api/json/projects
Headers: x-whatap-token: {apiToken}
```
**특징**:
- API 토큰만으로 인증 가능 (쿠키 불필요)
- 간단한 REST API
- 공식 문서: https://docs.whatap.io/openapi-spec/
---
## 🏗️ MCP 서버 아키텍처 설계
### 1. 기술 스택 선택
**Option A: Node.js + TypeScript (추천)**
- MCP SDK 공식 지원
- WhaTap API와 동일한 환경 (JavaScript/TypeScript)
- 풍부한 HTTP 클라이언트 라이브러리
**Option B: Python**
- 기존 mxql-for-claude-code와 통합 용이
- MCP SDK Python 버전 사용
**선택: Node.js + TypeScript**
- 이유: MCP 생태계 성숙도, 비동기 처리 우수, WhaTap 팀과 기술 스택 유사
### 2. MCP 서버 구조
```
mxql-mcp-server/
├── src/
│ ├── index.ts # MCP 서버 진입점
│ ├── auth/
│ │ ├── AuthManager.ts # 인증 관리자
│ │ └── SessionStore.ts # 세션 저장소
│ ├── api/
│ │ ├── WhatapClient.ts # WhaTap API 클라이언트
│ │ └── MxqlExecutor.ts # MXQL 실행기
│ ├── tools/
│ │ ├── login.ts # Tool: whatap_login
│ │ ├── execute_mxql.ts # Tool: execute_mxql
│ │ ├── list_projects.ts # Tool: list_projects
│ │ └── validate_mxql.ts # Tool: validate_mxql (기존 validator 활용)
│ ├── resources/
│ │ └── mxql_categories.ts # Resource: MXQL 카테고리 정보
│ └── types/
│ └── index.ts # 타입 정의
├── package.json
├── tsconfig.json
└── README.md
```
### 3. MCP Tools 정의
#### Tool 1: `whatap_login`
**목적**: WhaTap 계정으로 로그인하여 세션 생성
```typescript
{
name: "whatap_login",
description: "Login to WhaTap service and create an authenticated session",
inputSchema: {
type: "object",
properties: {
email: { type: "string", description: "WhaTap account email" },
password: { type: "string", description: "WhaTap account password" },
serviceUrl: {
type: "string",
description: "WhaTap service URL (e.g., https://service.whatap.io)",
default: "https://service.whatap.io"
}
},
required: ["email", "password"]
}
}
```
**응답**:
```json
{
"success": true,
"accountId": 12345,
"email": "user@example.com",
"sessionExpiry": "2025-11-12T00:00:00Z"
}
```
#### Tool 2: `execute_mxql`
**목적**: MXQL 쿼리 실행 및 데이터 조회
```typescript
{
name: "execute_mxql",
description: "Execute MXQL query against WhaTap monitoring data",
inputSchema: {
type: "object",
properties: {
pcode: {
type: "number",
description: "Project code (pcode)"
},
mql: {
type: "string",
description: "MXQL query string"
},
stime: {
type: "number",
description: "Start time in milliseconds (optional, defaults to 5 minutes ago)"
},
etime: {
type: "number",
description: "End time in milliseconds (optional, defaults to now)"
},
limit: {
type: "number",
description: "Result limit (default: 10000)",
default: 10000
}
},
required: ["pcode", "mql"]
}
}
```
**응답**:
```json
{
"success": true,
"data": [
{ "oid": 123, "oname": "server-1", "cpu": 45.2 },
{ "oid": 124, "oname": "server-2", "cpu": 38.7 }
],
"rowCount": 2,
"executionTimeMs": 234
}
```
#### Tool 3: `list_projects`
**목적**: 계정이 접근 가능한 프로젝트 목록 조회
```typescript
{
name: "list_projects",
description: "List all accessible WhaTap projects",
inputSchema: {
type: "object",
properties: {}
}
}
```
**응답**:
```json
{
"success": true,
"projects": [
{
"pcode": 12345,
"name": "Production APM",
"type": "APM",
"status": "active"
},
{
"pcode": 67890,
"name": "DB Monitoring",
"type": "DBX",
"status": "active"
}
]
}
```
#### Tool 4: `validate_mxql`
**목적**: MXQL 쿼리 문법 검증 (기존 validator 활용)
```typescript
{
name: "validate_mxql",
description: "Validate MXQL query syntax and suggest improvements",
inputSchema: {
type: "object",
properties: {
mql: { type: "string", description: "MXQL query to validate" }
},
required: ["mql"]
}
}
```
### 4. 인증 처리 전략
#### Option A: 세션 기반 인증 (추천)
**장점**:
- 웹 앱과 동일한 인증 흐름
- 모든 API 엔드포인트 접근 가능
- 쿠키 자동 관리
**구현**:
```typescript
class AuthManager {
private cookies: Map<string, string> = new Map();
private accountId?: number;
async login(email: string, password: string, serviceUrl: string) {
// Step 1: CSRF 토큰 획득
const csrf = await this.getCsrfToken(serviceUrl);
// Step 2: 웹 로그인
const { wa, jsessionid } = await this.webLogin(email, password, csrf);
this.cookies.set('wa', wa);
this.cookies.set('JSESSIONID', jsessionid);
// Step 3: 모바일 API 토큰 획득 (선택적)
const { accountId, apiToken } = await this.getMobileToken(email, password);
this.accountId = accountId;
// 세션 저장
await this.saveSession();
}
getCookieHeader(): string {
return Array.from(this.cookies.entries())
.map(([k, v]) => `${k}=${v}`)
.join('; ');
}
}
```
#### Option B: API 토큰 기반 인증
**장점**:
- 간단한 구조
- 토큰 재사용 가능
**단점**:
- Open API만 사용 가능 (MXQL flush 엔드포인트 사용 불가)
- 제한된 기능
**구현**:
```typescript
class AuthManager {
private apiToken?: string;
async login(email: string, password: string) {
// 웹 로그인 후 API 토큰만 획득
const { apiToken } = await this.getApiToken(email, password);
this.apiToken = apiToken;
}
getAuthHeader(): Record<string, string> {
return { 'x-whatap-token': this.apiToken! };
}
}
```
**선택: Option A (세션 기반)**
- 이유: MXQL flush 엔드포인트 사용 필요, 모든 기능 접근 가능
### 5. 세션 저장소 설계
**보안 고려사항**:
- 쿠키 및 토큰 암호화 저장
- 환경 변수로 비밀키 관리
- 세션 만료 시간 설정 (24시간)
```typescript
interface Session {
email: string;
cookies: Record<string, string>;
apiToken?: string;
accountId: number;
createdAt: Date;
expiresAt: Date;
}
class SessionStore {
private configDir = path.join(os.homedir(), '.whatap-mcp');
private sessionFile = path.join(this.configDir, 'session.enc');
async save(session: Session) {
const encrypted = this.encrypt(JSON.stringify(session));
await fs.writeFile(this.sessionFile, encrypted);
}
async load(): Promise<Session | null> {
if (!await fs.exists(this.sessionFile)) return null;
const encrypted = await fs.readFile(this.sessionFile, 'utf-8');
const decrypted = this.decrypt(encrypted);
const session = JSON.parse(decrypted);
// 만료 확인
if (new Date(session.expiresAt) < new Date()) {
return null;
}
return session;
}
}
```
### 6. WhatapClient 구현
```typescript
import axios, { AxiosInstance } from 'axios';
class WhatapClient {
private axios: AxiosInstance;
private authManager: AuthManager;
constructor(serviceUrl: string, authManager: AuthManager) {
this.authManager = authManager;
this.axios = axios.create({
baseURL: serviceUrl,
timeout: 45000,
headers: {
'User-Agent': 'WhatapMCP/1.0',
},
});
// 쿠키 인터셉터
this.axios.interceptors.request.use((config) => {
const cookieHeader = this.authManager.getCookieHeader();
if (cookieHeader) {
config.headers['Cookie'] = cookieHeader;
}
return config;
});
}
async executeMxql(mxql: MxqlRequest): Promise<any> {
const response = await this.axios.post('/yard/api/flush', mxql);
return response.data;
}
async getProjects(): Promise<Project[]> {
// 웹 API 사용 (쿠키 기반)
const response = await this.axios.get('/v2/project/list');
return response.data;
}
}
```
### 7. MCP 서버 메인 코드
```typescript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new Server({
name: 'whatap-mxql-server',
version: '1.0.0',
}, {
capabilities: {
tools: {},
},
});
// AuthManager 및 Client 초기화
const authManager = new AuthManager();
const sessionStore = new SessionStore();
let whatapClient: WhatapClient | null = null;
// Tool: whatap_login
server.setRequestHandler('tools/call', async (request) => {
if (request.params.name === 'whatap_login') {
const { email, password, serviceUrl } = request.params.arguments;
await authManager.login(email, password, serviceUrl || 'https://service.whatap.io');
whatapClient = new WhatapClient(serviceUrl, authManager);
return {
content: [{
type: 'text',
text: JSON.stringify({
success: true,
accountId: authManager.accountId,
email: email,
}),
}],
};
}
// Tool: execute_mxql
if (request.params.name === 'execute_mxql') {
if (!whatapClient) {
throw new Error('Not authenticated. Please call whatap_login first.');
}
const { pcode, mql, stime, etime, limit } = request.params.arguments;
const mxqlRequest = {
type: 'mxql',
pcode: pcode,
params: {
mql: mql,
stime: stime || Date.now() - 5 * 60 * 1000,
etime: etime || Date.now(),
limit: limit || 10000,
},
path: 'text',
};
const startTime = Date.now();
const data = await whatapClient.executeMxql(mxqlRequest);
const executionTimeMs = Date.now() - startTime;
return {
content: [{
type: 'text',
text: JSON.stringify({
success: true,
data: data,
rowCount: Array.isArray(data) ? data.length : 0,
executionTimeMs,
}),
}],
};
}
// Tool: list_projects
if (request.params.name === 'list_projects') {
if (!whatapClient) {
throw new Error('Not authenticated. Please call whatap_login first.');
}
const projects = await whatapClient.getProjects();
return {
content: [{
type: 'text',
text: JSON.stringify({
success: true,
projects: projects,
}),
}],
};
}
});
// 서버 시작
const transport = new StdioServerTransport();
await server.connect(transport);
```
### 8. 보안 고려사항
1. **비밀번호 저장 금지**
- 세션 파일에 비밀번호 저장 안 함
- 매번 로그인 시 입력 받기
2. **쿠키 암호화**
- AES-256-GCM으로 세션 파일 암호화
- 환경 변수로 마스터 키 관리
3. **HTTPS 강제**
- serviceUrl이 https가 아니면 거부
4. **Rate Limiting**
- API 호출 제한 (모바일 앱과 동일)
5. **세션 만료**
- 24시간 후 자동 만료
- 재로그인 요구
### 9. 설치 및 사용 방법
#### 9.1 설치
```bash
# 레포지토리 클론
git clone https://github.com/yourusername/mxql-mcp-server.git
cd mxql-mcp-server
# 의존성 설치
npm install
# 빌드
npm run build
# Claude Code MCP 설정에 추가
# ~/.config/claude-code/mcp.json
```
#### 9.2 MCP 설정 파일
```json
{
"mcpServers": {
"whatap-mxql": {
"command": "node",
"args": ["/path/to/mxql-mcp-server/dist/index.js"],
"env": {
"WHATAP_SESSION_KEY": "your-secure-key-here"
}
}
}
}
```
#### 9.3 사용 예시 (Claude Code에서)
```
User: WhaTap에 로그인해줘
Claude: [whatap_login tool 호출]
User: pcode 12345의 최근 5분간 CPU 사용률을 조회해줘
Claude: [execute_mxql tool 호출]
Query: CATEGORY app_counter
TAGLOAD
SELECT [cpu]
User: 내가 접근 가능한 프로젝트 목록을 보여줘
Claude: [list_projects tool 호출]
```
### 10. 개발 로드맵
**Phase 1: MVP (1-2주)**
- [ ] AuthManager 구현 (세션 기반)
- [ ] WhatapClient 구현 (MXQL 실행)
- [ ] MCP 서버 기본 구조
- [ ] Tools: login, execute_mxql, list_projects
**Phase 2: 통합 (1주)**
- [ ] 기존 mxql-for-claude-code validator 통합
- [ ] validate_mxql tool 구현
- [ ] 카테고리 검색 기능
**Phase 3: 고도화 (1-2주)**
- [ ] 세션 저장소 암호화
- [ ] 에러 핸들링 강화
- [ ] 로깅 및 디버깅
- [ ] 테스트 코드 작성
**Phase 4: 문서화 및 배포**
- [ ] README 작성
- [ ] 사용 예시 문서
- [ ] npm 패키지 배포
- [ ] GitHub Actions CI/CD
---
## 🔍 주요 결정 사항
1. **인증 방식**: 세션 기반 (쿠키) → MXQL flush API 접근 가능
2. **기술 스택**: Node.js + TypeScript → MCP 생태계 성숙도
3. **저장소**: 암호화된 로컬 파일 → 보안 강화
4. **Tool 구조**: 4개 핵심 tool (login, execute, list, validate)
## 📚 참고 자료
- WhaTap Mobile App: `/tmp/mobile-app/src/lib/`
- MXQL Validator: https://github.com/kyupid/mxql-for-claude-code
- MCP SDK: https://github.com/modelcontextprotocol/typescript-sdk
- WhaTap Docs: https://docs.whatap.io/
## 다음 단계
1. Node.js 프로젝트 초기화
2. AuthManager 구현 시작
3. WhatapClient 기본 구조 구현
4. MCP 서버 뼈대 구축