redash-mcp 2.2.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Jiro Developers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.ko.md ADDED
@@ -0,0 +1,223 @@
1
+ # redash-mcp
2
+
3
+ [Redash](https://redash.io)를 Claude AI에 연결하는 MCP 서버 — 자연어로 데이터를 조회하고 대시보드를 관리하세요.
4
+
5
+ **[English Documentation](README.md)**
6
+
7
+ ---
8
+
9
+ ## 기능
10
+
11
+ ### 툴 목록
12
+
13
+ | 카테고리 | 툴 | 설명 |
14
+ |---|---|---|
15
+ | 데이터소스 | `list_data_sources` | 연결된 데이터소스 목록 조회 |
16
+ | 스키마 | `list_tables` | 테이블 목록 조회 (키워드 검색 가능) |
17
+ | 스키마 | `get_table_columns` | 테이블 컬럼명 및 타입 조회 |
18
+ | 쿼리 실행 | `run_query` | SQL 직접 실행 후 결과 반환 |
19
+ | 저장 쿼리 | `list_queries` | 저장된 쿼리 목록 조회 |
20
+ | 저장 쿼리 | `get_query` | 쿼리 상세 정보 (SQL, 시각화 등) 조회 |
21
+ | 저장 쿼리 | `get_query_result` | 저장된 쿼리 실행 결과 조회 |
22
+ | 저장 쿼리 | `create_query` | 새 쿼리 저장 |
23
+ | 저장 쿼리 | `update_query` | 쿼리 수정 |
24
+ | 저장 쿼리 | `fork_query` | 쿼리 복제 |
25
+ | 저장 쿼리 | `archive_query` | 쿼리 삭제 (아카이브) |
26
+ | 대시보드 | `list_dashboards` | 대시보드 목록 조회 |
27
+ | 대시보드 | `get_dashboard` | 대시보드 상세 및 위젯 목록 조회 |
28
+ | 대시보드 | `create_dashboard` | 새 대시보드 생성 |
29
+ | 대시보드 | `add_widget` | 대시보드에 시각화 위젯 추가 |
30
+ | 알림 | `list_alerts` | 알림 목록 조회 |
31
+ | 알림 | `get_alert` | 알림 상세 정보 조회 |
32
+ | 알림 | `create_alert` | 새 알림 생성 |
33
+
34
+ ### SQL 안전 가드
35
+
36
+ 위험한 쿼리로부터 데이터베이스를 보호합니다:
37
+
38
+ - **항상 차단**: `DROP`, `TRUNCATE`, `ALTER TABLE`, `GRANT/REVOKE`, `WHERE` 없는 `DELETE/UPDATE`
39
+ - **경고 (warn 모드)** / **차단 (strict 모드)**: `SELECT *`, `WHERE`·`LIMIT` 없는 쿼리, PII 컬럼 접근
40
+ - **자동 LIMIT**: `REDASH_AUTO_LIMIT` 설정 시 LIMIT 없는 쿼리에 자동으로 `LIMIT N` 추가
41
+
42
+ ### 쿼리 캐시
43
+
44
+ 중복 API 호출을 줄이기 위해 결과를 메모리에 캐싱합니다:
45
+
46
+ - TTL: `REDASH_MCP_CACHE_TTL` 환경변수로 설정 (기본값: 300초)
47
+ - 최대 메모리: `REDASH_MCP_CACHE_MAX_MB` 환경변수로 설정 (기본값: 50MB)
48
+
49
+ ---
50
+
51
+ ## 설치
52
+
53
+ ### 자동 설치 (권장)
54
+
55
+ ```bash
56
+ npx redash-mcp setup
57
+ ```
58
+
59
+ 설치 마법사가 실행되며 Claude Desktop, Claude Code(CLI), 또는 둘 다 선택하여 설정할 수 있습니다.
60
+
61
+ ### 셸 스크립트로 설치
62
+
63
+ Node.js, Claude Desktop, MCP 설정을 한번에 처리합니다:
64
+
65
+ ```bash
66
+ curl -fsSL https://raw.githubusercontent.com/seob717/redash-mcp/main/install.sh | bash
67
+ ```
68
+
69
+ ### 수동 설치
70
+
71
+ #### 1. Redash API 키 발급
72
+
73
+ Redash → 우측 상단 프로필 → **Edit Profile** → **API Key** 복사
74
+
75
+ #### 2-A. Claude Desktop 설정
76
+
77
+ `~/Library/Application Support/Claude/claude_desktop_config.json` 파일을 열고 아래 내용을 추가합니다:
78
+
79
+ ```json
80
+ {
81
+ "mcpServers": {
82
+ "redash-mcp": {
83
+ "command": "npx",
84
+ "args": ["-y", "redash-mcp"],
85
+ "env": {
86
+ "REDASH_URL": "https://your-redash-instance.com",
87
+ "REDASH_API_KEY": "your_api_key_here"
88
+ }
89
+ }
90
+ }
91
+ }
92
+ ```
93
+
94
+ 저장 후 Claude Desktop을 완전히 종료했다가 다시 시작합니다.
95
+
96
+ #### 2-B. Claude Code (CLI) 설정
97
+
98
+ `~/.claude/settings.json` 파일을 열고 아래 내용을 추가합니다:
99
+
100
+ ```json
101
+ {
102
+ "mcpServers": {
103
+ "redash-mcp": {
104
+ "command": "npx",
105
+ "args": ["-y", "redash-mcp"],
106
+ "env": {
107
+ "REDASH_URL": "https://your-redash-instance.com",
108
+ "REDASH_API_KEY": "your_api_key_here"
109
+ }
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ > **macOS**: `npx`를 못 찾는 경우 `which npx` 명령어로 전체 경로를 확인 후 대체하세요.
116
+
117
+ ---
118
+
119
+ ## 환경 변수
120
+
121
+ ### 필수
122
+
123
+ | 변수 | 설명 |
124
+ |---|---|
125
+ | `REDASH_URL` | Redash 인스턴스 URL (예: `https://redash.example.com`) |
126
+ | `REDASH_API_KEY` | Redash 사용자 API 키 |
127
+
128
+ ### 선택
129
+
130
+ | 변수 | 기본값 | 설명 |
131
+ |---|---|---|
132
+ | `REDASH_SAFETY_MODE` | `warn` | SQL 안전 수준: `off` / `warn` / `strict` |
133
+ | `REDASH_SAFETY_DISABLE_PII` | `false` | PII 감지 비활성화 |
134
+ | `REDASH_SAFETY_DISABLE_COST` | `false` | 비용 경고 비활성화 |
135
+ | `REDASH_AUTO_LIMIT` | `0` | LIMIT 없는 쿼리에 자동으로 `LIMIT N` 추가 (0 = 비활성화) |
136
+ | `REDASH_DEFAULT_MAX_AGE` | `0` | Redash 캐시 TTL (초) |
137
+ | `REDASH_MCP_CACHE_TTL` | `300` | MCP 쿼리 캐시 TTL (초, 0 = 비활성화) |
138
+ | `REDASH_MCP_CACHE_MAX_MB` | `50` | MCP 쿼리 캐시 최대 메모리 (MB) |
139
+
140
+ ---
141
+
142
+ ## 사용 예시
143
+
144
+ Claude에게 자연어로 요청하면 됩니다:
145
+
146
+ - "users 테이블 컬럼 보여줘"
147
+ - "최근 7일 주문 수를 SQL로 조회해줘"
148
+ - "저장된 쿼리 목록 보여줘"
149
+ - "매출 대시보드 위젯 목록 알려줘"
150
+ - "일별 가입자 수가 100명 이하로 떨어지면 알림 만들어줘"
151
+
152
+ ### 예제 1: 자연어로 데이터 조회
153
+
154
+ > **프롬프트**: "이번 달 신규 가입자 수 알려줘"
155
+
156
+ **실행 흐름:**
157
+ 1. `list_data_sources` → 데이터소스 목록 확인
158
+ 2. `smart_query` → 질문 분석, 관련 테이블(`User`) 자동 선택, SQL 생성 가이드 제공
159
+ 3. `run_query` → 생성된 SQL 실행
160
+
161
+ **결과:**
162
+ ```
163
+ 이번 달 신규 가입자는 18,197명입니다.
164
+ ```
165
+
166
+ ### 예제 2: 복잡한 비즈니스 질문
167
+
168
+ > **프롬프트**: "지난주 신규 가입자 중 결제한 사용자 비율은?"
169
+
170
+ **실행 흐름:**
171
+ 1. `smart_query` → 질문 분석, `User`·`Payment` 테이블 자동 선택, JOIN 쿼리 가이드 제공
172
+ 2. `run_query` → SQL 실행
173
+
174
+ **결과:**
175
+ ```
176
+ 지난주 신규 가입자 1,204명 중 결제한 사용자는 312명 (25.9%)입니다.
177
+ ```
178
+
179
+ ### 예제 3: 쿼리 저장 + 대시보드 생성
180
+
181
+ > **프롬프트**: "월별 매출 추이 쿼리를 만들고 대시보드에 추가해줘"
182
+
183
+ **실행 흐름:**
184
+ 1. `smart_query` → 매출 관련 테이블 분석
185
+ 2. `create_query` → "월별 매출 추이" 쿼리 저장
186
+ 3. `create_dashboard` → "매출 대시보드" 생성
187
+ 4. `get_query` → 저장된 쿼리의 시각화 ID 확인
188
+ 5. `add_widget` → 대시보드에 차트 위젯 추가
189
+
190
+ **결과:**
191
+ ```
192
+ "매출 대시보드"가 생성되었고, 월별 매출 추이 차트가 추가되었습니다.
193
+ Redash에서 확인: https://your-redash.com/dashboard/monthly-revenue
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Privacy Policy
199
+
200
+ ### 데이터 수집 및 처리
201
+
202
+ redash-mcp는 **로컬 MCP 서버**로, 사용자의 Redash 인스턴스와 직접 통신합니다. 중간 서버를 거치지 않습니다.
203
+
204
+ | 항목 | 설명 |
205
+ |------|------|
206
+ | **Redash API Key** | 로컬 환경변수(`REDASH_API_KEY`)로만 저장. 외부 전송 없음 |
207
+ | **쿼리 내용 및 결과** | MCP 프로토콜을 통해 로컬 클라이언트(Claude Desktop/Code)에만 전달 |
208
+ | **BIRD SQL 설정** | 로컬 파일(`~/.redash-mcp/`)에만 저장. Few-shot 예제, 키워드 맵, 피드백 등 |
209
+ | **LLM Fallback** | `ANTHROPIC_API_KEY` 설정 시, 테이블명 목록만 Anthropic API로 전송. 쿼리 데이터·결과는 전송하지 않음 |
210
+
211
+ ### 제3자 공유
212
+
213
+ 사용자 데이터를 제3자에게 판매하거나 공유하지 않습니다. LLM Fallback 기능 사용 시 Anthropic API로 테이블명 목록만 전송되며, 이는 사용자가 `ANTHROPIC_API_KEY`를 설정한 경우에만 활성화됩니다.
214
+
215
+ ### 데이터 보존
216
+
217
+ - 설정 파일: `~/.redash-mcp/` 디렉토리에 로컬 저장 (사용자가 직접 삭제 가능)
218
+ - 쿼리 캐시: 메모리에만 저장, 서버 종료 시 소멸
219
+ - 스키마 캐시: 메모리에만 저장, 10분 TTL 후 자동 소멸
220
+
221
+ ### 연락처
222
+
223
+ 문의 및 보안 이슈 보고: [GitHub Issues](https://github.com/seob717/redash-mcp/issues)
package/README.md CHANGED
@@ -1,49 +1,80 @@
1
1
  # redash-mcp
2
2
 
3
- [Redash](https://redash.io) Claude AI에서 직접 조회하고 관리할 있는 MCP(Model Context Protocol) 서버입니다.
3
+ MCP server that connects [Redash](https://redash.io) to Claude AI query data, manage dashboards, and run SQL with natural language.
4
4
 
5
- ## 기능
5
+ **[한국어 문서](README.ko.md)**
6
6
 
7
- | 카테고리 | 툴 | 설명 |
7
+ ---
8
+
9
+ ## Features
10
+
11
+ ### Tools
12
+
13
+ | Category | Tool | Description |
8
14
  |---|---|---|
9
- | 데이터소스 | `list_data_sources` | 연결된 데이터소스 목록 조회 |
10
- | 스키마 | `list_tables` | 테이블 목록 조회 (키워드 검색 가능) |
11
- | 스키마 | `get_table_columns` | 테이블 컬럼명/타입 조회 |
12
- | 쿼리 실행 | `run_query` | SQL 직접 실행 결과 반환 |
13
- | 저장 쿼리 | `list_queries` | 저장된 쿼리 목록 조회 |
14
- | 저장 쿼리 | `get_query` | 쿼리 상세 정보(SQL, 시각화 등) 조회 |
15
- | 저장 쿼리 | `get_query_result` | 저장된 쿼리 실행 결과 조회 |
16
- | 저장 쿼리 | `create_query` | 쿼리 저장 |
17
- | 저장 쿼리 | `update_query` | 쿼리 수정 |
18
- | 저장 쿼리 | `fork_query` | 쿼리 복제 |
19
- | 저장 쿼리 | `archive_query` | 쿼리 삭제 |
20
- | 대시보드 | `list_dashboards` | 대시보드 목록 조회 |
21
- | 대시보드 | `get_dashboard` | 대시보드 상세 위젯 목록 조회 |
22
- | 대시보드 | `create_dashboard` | 대시보드 생성 |
23
- | 대시보드 | `add_widget` | 대시보드에 시각화 위젯 추가 |
24
- | 알림 | `list_alerts` | 알림 목록 조회 |
25
- | 알림 | `get_alert` | 알림 상세 정보 조회 |
26
- | 알림 | `create_alert` | 알림 생성 |
27
-
28
- ## 설치
29
-
30
- ### 자동 설치 (권장)
15
+ | Data Sources | `list_data_sources` | List connected data sources |
16
+ | Schema | `list_tables` | List tables (supports keyword search) |
17
+ | Schema | `get_table_columns` | Get column names and types |
18
+ | Query | `run_query` | Execute SQL and return results |
19
+ | Saved Queries | `list_queries` | List saved queries |
20
+ | Saved Queries | `get_query` | Get query details (SQL, visualizations) |
21
+ | Saved Queries | `get_query_result` | Run a saved query and get results |
22
+ | Saved Queries | `create_query` | Save a new query |
23
+ | Saved Queries | `update_query` | Update a saved query |
24
+ | Saved Queries | `fork_query` | Fork a saved query |
25
+ | Saved Queries | `archive_query` | Archive (delete) a query |
26
+ | Dashboards | `list_dashboards` | List dashboards |
27
+ | Dashboards | `get_dashboard` | Get dashboard details and widgets |
28
+ | Dashboards | `create_dashboard` | Create a new dashboard |
29
+ | Dashboards | `add_widget` | Add a visualization widget to a dashboard |
30
+ | Alerts | `list_alerts` | List alerts |
31
+ | Alerts | `get_alert` | Get alert details |
32
+ | Alerts | `create_alert` | Create a new alert |
33
+
34
+ ### SQL Safety Guard
35
+
36
+ Protects your database from dangerous queries:
37
+
38
+ - **Blocked always**: `DROP`, `TRUNCATE`, `ALTER TABLE`, `GRANT/REVOKE`, `DELETE/UPDATE` without `WHERE`
39
+ - **Warned (warn mode)** / **Blocked (strict mode)**: `SELECT *`, queries without `WHERE` or `LIMIT`, PII column access
40
+ - **Auto-LIMIT**: Automatically appends `LIMIT N` when `REDASH_AUTO_LIMIT` is set
41
+
42
+ ### Query Cache
43
+
44
+ Results are cached in-memory to reduce redundant API calls:
45
+
46
+ - TTL: configurable via `REDASH_MCP_CACHE_TTL` (default: 300s)
47
+ - Max memory: configurable via `REDASH_MCP_CACHE_MAX_MB` (default: 50MB)
48
+
49
+ ---
50
+
51
+ ## Installation
52
+
53
+ ### Auto Setup (Recommended)
31
54
 
32
55
  ```bash
33
56
  npx redash-mcp setup
34
57
  ```
35
58
 
36
- 설치 마법사가 실행되며 Claude Desktop, Claude Code(CLI), 또는 둘 다 선택하여 설정할 수 있습니다.
59
+ The setup wizard will guide you through configuring Claude Desktop, Claude Code (CLI), or both.
37
60
 
38
- ### 수동 설치
61
+ ### Shell Script Install
39
62
 
40
- #### 1. Redash API 발급
63
+ Installs Node.js, Claude Desktop, and MCP config all at once:
41
64
 
42
- Redash → 우측 상단 프로필 → **Edit Profile** → **API Key** 복사
65
+ ```bash
66
+ curl -fsSL https://raw.githubusercontent.com/seob717/redash-mcp/main/install.sh | bash
67
+ ```
68
+
69
+ ### Manual Setup
70
+
71
+ #### 1. Get your Redash API Key
43
72
 
44
- #### 2-A. Claude Desktop 설정
73
+ Go to Redash Profile (top right) → **Edit Profile** → Copy **API Key**
45
74
 
46
- `~/Library/Application Support/Claude/claude_desktop_config.json` 파일을 열고 아래 내용을 추가합니다:
75
+ #### 2-A. Claude Desktop
76
+
77
+ Open `~/Library/Application Support/Claude/claude_desktop_config.json` and add:
47
78
 
48
79
  ```json
49
80
  {
@@ -60,11 +91,11 @@ Redash → 우측 상단 프로필 → **Edit Profile** → **API Key** 복사
60
91
  }
61
92
  ```
62
93
 
63
- 설정 저장 Claude Desktop 완전히 종료했다가 다시 시작합니다.
94
+ Fully quit and restart Claude Desktop after saving.
64
95
 
65
- #### 2-B. Claude Code (CLI) 설정
96
+ #### 2-B. Claude Code (CLI)
66
97
 
67
- `~/.claude/settings.json` 파일을 열고 아래 내용을 추가합니다:
98
+ Open `~/.claude/settings.json` and add:
68
99
 
69
100
  ```json
70
101
  {
@@ -81,23 +112,112 @@ Redash → 우측 상단 프로필 → **Edit Profile** → **API Key** 복사
81
112
  }
82
113
  ```
83
114
 
84
- Claude Code 세션에서 바로 사용할 있습니다.
115
+ > **macOS**: If `npx` is not found, run `which npx` to get the full path and use that instead.
116
+
117
+ ---
118
+
119
+ ## Environment Variables
85
120
 
86
- > **Windows/Linux**: `command`를 `npx`로 그대로 사용하면 됩니다.
87
- > **macOS**: `npx`를 못 찾는 경우 `which npx` 명령어로 전체 경로를 확인 후 대체하세요.
121
+ ### Required
88
122
 
89
- ## 환경 변수
123
+ | Variable | Description |
124
+ |---|---|
125
+ | `REDASH_URL` | Redash instance URL (e.g. `https://redash.example.com`) |
126
+ | `REDASH_API_KEY` | Redash user API key |
90
127
 
91
- | 변수 | 필수 | 설명 |
128
+ ### Optional
129
+
130
+ | Variable | Default | Description |
92
131
  |---|---|---|
93
- | `REDASH_URL` | | Redash 인스턴스 URL (예: `https://redash.example.com`) |
94
- | `REDASH_API_KEY` | | Redash 사용자 API |
132
+ | `REDASH_SAFETY_MODE` | `warn` | SQL safety level: `off` / `warn` / `strict` |
133
+ | `REDASH_SAFETY_DISABLE_PII` | `false` | Disable PII detection |
134
+ | `REDASH_SAFETY_DISABLE_COST` | `false` | Disable cost warnings |
135
+ | `REDASH_AUTO_LIMIT` | `0` | Auto-append `LIMIT N` to queries without one (0 = disabled) |
136
+ | `REDASH_DEFAULT_MAX_AGE` | `0` | Redash cache TTL in seconds |
137
+ | `REDASH_MCP_CACHE_TTL` | `300` | MCP query cache TTL in seconds (0 = disabled) |
138
+ | `REDASH_MCP_CACHE_MAX_MB` | `50` | Max memory for MCP query cache in MB |
139
+
140
+ ---
141
+
142
+ ## Usage Examples
143
+
144
+ Just ask Claude in natural language:
145
+
146
+ - "Show me the columns in the users table"
147
+ - "Run a query to get order counts for the last 7 days"
148
+ - "List all saved queries"
149
+ - "Show widgets in the revenue dashboard"
150
+ - "Create an alert when daily signups drop below 100"
151
+
152
+ ### Example 1: Query data with natural language
153
+
154
+ > **Prompt**: "How many new users signed up this month?"
155
+
156
+ **Tool flow:**
157
+ 1. `list_data_sources` → Identify the target data source
158
+ 2. `smart_query` → Analyze the question, auto-select the `User` table, provide SQL generation guidance
159
+ 3. `run_query` → Execute the generated SQL
160
+
161
+ **Result:**
162
+ ```
163
+ There were 18,197 new signups this month.
164
+ ```
165
+
166
+ ### Example 2: Complex business questions
167
+
168
+ > **Prompt**: "What percentage of last week's new users made a purchase?"
169
+
170
+ **Tool flow:**
171
+ 1. `smart_query` → Analyze the question, auto-select `User` and `Payment` tables, provide JOIN query guidance
172
+ 2. `run_query` → Execute the SQL
173
+
174
+ **Result:**
175
+ ```
176
+ Out of 1,204 new users last week, 312 made a purchase (25.9%).
177
+ ```
178
+
179
+ ### Example 3: Create a query and dashboard
180
+
181
+ > **Prompt**: "Create a monthly revenue trend query and add it to a dashboard"
182
+
183
+ **Tool flow:**
184
+ 1. `smart_query` → Analyze revenue-related tables
185
+ 2. `create_query` → Save the "Monthly Revenue Trend" query
186
+ 3. `create_dashboard` → Create a "Revenue Dashboard"
187
+ 4. `get_query` → Get the visualization ID from the saved query
188
+ 5. `add_widget` → Add the chart widget to the dashboard
189
+
190
+ **Result:**
191
+ ```
192
+ Created "Revenue Dashboard" with the monthly revenue trend chart.
193
+ View in Redash: https://your-redash.com/dashboard/monthly-revenue
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Privacy Policy
199
+
200
+ ### Data Collection and Processing
201
+
202
+ redash-mcp is a **local MCP server** that communicates directly with your Redash instance. No intermediate servers are involved.
203
+
204
+ | Item | Description |
205
+ |------|-------------|
206
+ | **Redash API Key** | Stored only as a local environment variable (`REDASH_API_KEY`). Never transmitted externally. |
207
+ | **Query content & results** | Delivered only to the local MCP client (Claude Desktop/Code) via the MCP protocol. |
208
+ | **BIRD SQL settings** | Stored only in local files (`~/.redash-mcp/`). Includes few-shot examples, keyword maps, and feedback. |
209
+ | **LLM Fallback** | When `ANTHROPIC_API_KEY` is set, only table name lists are sent to the Anthropic API. Query data and results are never transmitted. |
210
+
211
+ ### Third-Party Sharing
212
+
213
+ We do not sell or share user data with third parties. When the LLM Fallback feature is active, only table name lists are sent to the Anthropic API, and only when the user has explicitly configured an `ANTHROPIC_API_KEY`.
214
+
215
+ ### Data Retention
95
216
 
96
- ## 사용 예시
217
+ - **Config files**: Stored locally in `~/.redash-mcp/` (user can delete at any time)
218
+ - **Query cache**: In-memory only, cleared on server shutdown
219
+ - **Schema cache**: In-memory only, auto-expires after 10-minute TTL
97
220
 
98
- Claude에게 자연어로 요청하면 됩니다:
221
+ ### Contact
99
222
 
100
- - "Redash에서 users 테이블 컬럼 보여줘"
101
- - "최근 7일 주문 수를 SQL로 조회해줘"
102
- - "저장된 쿼리 목록 보여줘"
103
- - "매출 대시보드 위젯 목록 알려줘"
223
+ For inquiries and security reports: [GitHub Issues](https://github.com/seob717/redash-mcp/issues)
@@ -0,0 +1,87 @@
1
+ export function assessComplexity(question, prunedTables) {
2
+ let score = 0;
3
+ const hints = [];
4
+ const reasons = [];
5
+ const q = question.toLowerCase();
6
+ const relevantTables = prunedTables.filter((t) => t.score > 0).length;
7
+ if (relevantTables > 1) {
8
+ score += (relevantTables - 1) * 2;
9
+ reasons.push(`${relevantTables} tables involved`);
10
+ }
11
+ const joinKeywords = ["join", "combine", "relate", "between tables", "연결", "결합", "조인"];
12
+ if (joinKeywords.some((kw) => q.includes(kw))) {
13
+ score += 2;
14
+ reasons.push("explicit join requested");
15
+ hints.push("Verify JOIN conditions match the correct foreign keys.");
16
+ }
17
+ const subqueryKeywords = [
18
+ "for each", "among those", "within", "nested", "sub",
19
+ "중에서", "각각", "그중", "해당하는",
20
+ ];
21
+ if (subqueryKeywords.some((kw) => q.includes(kw))) {
22
+ score += 2;
23
+ reasons.push("subquery likely needed");
24
+ hints.push("Consider using CTEs for readability.");
25
+ }
26
+ const windowKeywords = [
27
+ "ranking", "rank", "running total", "cumulative", "previous",
28
+ "percentile", "top n", "nth",
29
+ "순위", "누적", "이전", "상위",
30
+ ];
31
+ if (windowKeywords.some((kw) => q.includes(kw))) {
32
+ score += 3;
33
+ reasons.push("window functions may be needed");
34
+ hints.push("Use window functions (ROW_NUMBER, RANK, LAG/LEAD) as appropriate.");
35
+ }
36
+ const pivotKeywords = [
37
+ "compare across", "breakdown by", "pivot", "crosstab",
38
+ "비교", "교차", "피벗", "대비",
39
+ ];
40
+ if (pivotKeywords.some((kw) => q.includes(kw))) {
41
+ score += 2;
42
+ reasons.push("cross-comparison or pivoting needed");
43
+ hints.push("Consider CASE WHEN for pivot-style queries.");
44
+ }
45
+ const timeCompareKeywords = [
46
+ "year over year", "month over month", "yoy", "mom",
47
+ "growth rate", "change rate", "trend",
48
+ "전년", "전월", "증감", "추이", "변화율", "성장률",
49
+ ];
50
+ if (timeCompareKeywords.some((kw) => q.includes(kw))) {
51
+ score += 2;
52
+ reasons.push("time-based comparison needed");
53
+ hints.push("Use LAG() or self-join for period-over-period comparisons.");
54
+ }
55
+ const ratioKeywords = [
56
+ "ratio", "rate", "conversion", "percentage", "proportion",
57
+ "비율", "전환율", "퍼센트", "점유율",
58
+ ];
59
+ if (ratioKeywords.some((kw) => q.includes(kw))) {
60
+ score += 1;
61
+ reasons.push("ratio calculation involved");
62
+ }
63
+ let level;
64
+ if (score <= 2) {
65
+ level = "simple";
66
+ }
67
+ else if (score <= 5) {
68
+ level = "medium";
69
+ }
70
+ else {
71
+ level = "complex";
72
+ }
73
+ if (level === "simple" && hints.length === 0) {
74
+ hints.push("Straightforward query. Focus on correct column names and filter values.");
75
+ }
76
+ if (level === "medium" && hints.length === 0) {
77
+ hints.push("Moderate complexity. Double-check JOIN conditions and GROUP BY columns.");
78
+ }
79
+ if (level === "complex" && hints.length === 0) {
80
+ hints.push("Complex query. Consider breaking into CTEs for clarity and correctness.");
81
+ }
82
+ return {
83
+ level,
84
+ reasoning: reasons.length > 0 ? reasons.join("; ") : "single table with basic filtering",
85
+ hints,
86
+ };
87
+ }
@@ -0,0 +1,73 @@
1
+ import { readFile, mkdir, writeFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import path from "node:path";
4
+ import os from "node:os";
5
+ const DEFAULT_CONFIG = {
6
+ bird: {
7
+ enabled: true,
8
+ schemaPruning: {
9
+ enabled: true,
10
+ topK: 7,
11
+ includeSampleValues: true,
12
+ maxSampleValues: 5,
13
+ },
14
+ fewShot: {
15
+ enabled: true,
16
+ maxExamplesPerQuery: 3,
17
+ },
18
+ feedback: {
19
+ enabled: true,
20
+ autoPromoteThreshold: 3,
21
+ },
22
+ complexity: {
23
+ enabled: true,
24
+ },
25
+ },
26
+ };
27
+ export function getConfigDir() {
28
+ return process.env.REDASH_MCP_CONFIG_DIR || path.join(os.homedir(), ".redash-mcp");
29
+ }
30
+ export async function ensureConfigDir() {
31
+ const dir = getConfigDir();
32
+ for (const sub of ["", "few-shot", "feedback", "eval", "keyword-map"]) {
33
+ const p = path.join(dir, sub);
34
+ if (!existsSync(p)) {
35
+ await mkdir(p, { recursive: true });
36
+ }
37
+ }
38
+ }
39
+ export function getDataSourcePath(subdir, dataSourceId) {
40
+ return path.join(getConfigDir(), subdir, `${dataSourceId}.json`);
41
+ }
42
+ export async function loadConfig() {
43
+ const configPath = path.join(getConfigDir(), "config.json");
44
+ try {
45
+ const raw = await readFile(configPath, "utf-8");
46
+ const parsed = JSON.parse(raw);
47
+ return deepMerge(DEFAULT_CONFIG, parsed);
48
+ }
49
+ catch {
50
+ return DEFAULT_CONFIG;
51
+ }
52
+ }
53
+ export async function saveConfig(config) {
54
+ await ensureConfigDir();
55
+ const configPath = path.join(getConfigDir(), "config.json");
56
+ await writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
57
+ }
58
+ function deepMerge(target, source) {
59
+ const result = { ...target };
60
+ for (const key of Object.keys(source)) {
61
+ if (source[key] &&
62
+ typeof source[key] === "object" &&
63
+ !Array.isArray(source[key]) &&
64
+ target[key] &&
65
+ typeof target[key] === "object") {
66
+ result[key] = deepMerge(target[key], source[key]);
67
+ }
68
+ else {
69
+ result[key] = source[key];
70
+ }
71
+ }
72
+ return result;
73
+ }