# AWS Athena MCP Server - 部署文档
本文档提供 AWS Athena MCP Server 的完整部署指南,包括本地开发和生产环境部署。
---
## 目录
1. [系统要求](#系统要求)
2. [快速开始](#快速开始)
3. [部署方式](#部署方式)
- [方式一:本地 stdio 模式](#方式一本地-stdio-模式)
- [方式二:Lambda + API Gateway](#方式二lambda--api-gateway)
4. [OAuth 认证配置](#oauth-认证配置)
5. [测试验证](#测试验证)
6. [客户端集成](#客户端集成)
7. [故障排查](#故障排查)
8. [维护和更新](#维护和更新)
---
## 系统要求
### 必需软件
- **Node.js**: >= 16.x
- **npm**: >= 8.x
- **AWS CLI**: >= 2.x
- **AWS SAM CLI**: >= 1.x (仅 Lambda 部署需要)
### AWS 权限要求
- Athena: 查询执行权限
- S3: 读写查询结果桶
- Glue: 数据目录访问权限
- CloudFormation: 创建和管理资源 (Lambda 部署)
- Cognito: 用户池管理 (Lambda 部署)
- Lambda: 函数管理 (Lambda 部署)
- API Gateway: API 管理 (Lambda 部署)
- IAM: 角色和策略管理 (Lambda 部署)
---
## 快速开始
### 1. 克隆项目
```bash
git clone https://github.com/lishenxydlgzs/aws-athena-mcp.git
cd aws-athena-mcp
```
### 2. 安装依赖
```bash
npm install
```
### 3. 配置 AWS 凭证
```bash
# 方式 1: 使用 AWS CLI 配置
aws configure
# 方式 2: 设置环境变量
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export AWS_REGION="us-east-1"
```
### 4. 构建项目
```bash
npm run build
```
---
## 部署方式
### 方式一:本地 stdio 模式
适用于 Claude Desktop、Cline 等 MCP 客户端。
#### 1. 配置环境变量
创建 `.env` 文件:
```bash
# 必需配置
OUTPUT_S3_PATH=s3://your-bucket/athena-results/
# 可选配置
AWS_REGION=us-east-1
AWS_PROFILE=default
ATHENA_WORKGROUP=primary
```
#### 2. 配置 MCP 客户端
在 MCP 客户端配置文件中添加:
```json
{
"mcpServers": {
"athena": {
"command": "npx",
"args": ["-y", "@lishenxydlgzs/aws-athena-mcp"],
"env": {
"OUTPUT_S3_PATH": "s3://your-bucket/athena-results/",
"AWS_REGION": "us-east-1",
"AWS_PROFILE": "default",
"ATHENA_WORKGROUP": "primary"
}
}
}
}
```
#### 3. 测试连接
重启 MCP 客户端,测试连接是否正常。
---
### 方式二:Lambda + API Gateway
适用于远程访问、多用户场景,支持 OAuth 2.0 认证。
#### 1. 准备 S3 存储桶
```bash
# 创建 Athena 查询结果存储桶(如果不存在)
aws s3 mb s3://your-athena-results-bucket --region us-east-1
```
#### 2. 配置部署参数
编辑 `samconfig.toml`:
```toml
[default.deploy.parameters]
stack_name = "aws-athena-mcp-stack"
region = "us-east-1"
capabilities = "CAPABILITY_IAM"
parameter_overrides = [
"OutputS3Path=s3://your-athena-results-bucket/",
"AthenaWorkgroup=primary",
"AwsRegionParam=us-east-1",
"CognitoUserPoolName=athena-mcp-user-pool",
"CognitoAppClientName=athena-mcp-client"
]
```
#### 3. 构建和部署
```bash
# 构建项目
npm run build
# 构建 SAM 应用
sam build
# 部署到 AWS(首次部署使用 --guided)
sam deploy --guided
# 后续部署
sam deploy --resolve-s3 --no-confirm-changeset --capabilities CAPABILITY_IAM
```
#### 4. 获取部署信息
部署完成后,记录输出信息:
```bash
# 查看 CloudFormation 输出
aws cloudformation describe-stacks \
--stack-name aws-athena-mcp-stack \
--query "Stacks[0].Outputs" \
--output table
```
输出示例:
```
----------------------------------------------------------------------------------
| DescribeStacks |
+---------------------------+----------------------------------------------------+
| OutputKey | OutputValue |
+---------------------------+----------------------------------------------------+
| ApiEndpoint | https://xxxxx.execute-api.us-east-1.amazonaws.com/prod/mcp |
| CognitoUserPoolId | us-east-1_xxxxxxxxx |
| CognitoAppClientId | xxxxxxxxxxxxxxxxxxxxxxxxxx |
| CognitoTokenUrl | https://xxxxx.auth.us-east-1.amazoncognito.com/oauth2/token |
| CognitoScopes | athena-mcp-api/read athena-mcp-api/write |
| FunctionArn | arn:aws:lambda:us-east-1:xxxxx:function:xxxxx |
+---------------------------+----------------------------------------------------+
```
---
## OAuth 认证配置
### 1. 获取 Client Secret
```bash
# 从 CloudFormation 获取 User Pool ID 和 Client ID
USER_POOL_ID=$(aws cloudformation describe-stacks \
--stack-name aws-athena-mcp-stack \
--query "Stacks[0].Outputs[?OutputKey=='CognitoUserPoolId'].OutputValue" \
--output text)
CLIENT_ID=$(aws cloudformation describe-stacks \
--stack-name aws-athena-mcp-stack \
--query "Stacks[0].Outputs[?OutputKey=='CognitoAppClientId'].OutputValue" \
--output text)
# 获取 Client Secret
CLIENT_SECRET=$(aws cognito-idp describe-user-pool-client \
--user-pool-id $USER_POOL_ID \
--client-id $CLIENT_ID \
--query "UserPoolClient.ClientSecret" \
--output text)
echo "Client Secret: $CLIENT_SECRET"
```
### 2. 保存配置信息
创建 `.env.oauth` 文件(不要提交到版本控制):
```bash
# Cognito 配置
COGNITO_CLIENT_ID=your-client-id-here
COGNITO_CLIENT_SECRET=your-client-secret-here
COGNITO_TOKEN_URL=https://your-domain.auth.us-east-1.amazoncognito.com/oauth2/token
# API 端点
API_ENDPOINT=https://your-api-id.execute-api.us-east-1.amazonaws.com/prod/mcp
# OAuth Scopes
OAUTH_SCOPES=athena-mcp-api/read athena-mcp-api/write
```
### 3. 测试 OAuth 认证
```bash
# 运行自动化测试脚本
chmod +x test-cognito-auth.sh
./test-cognito-auth.sh
```
预期输出:
```
=== AWS Athena MCP - Cognito OAuth 测试 ===
1. 获取 Cognito 配置...
✓ 配置获取成功
2. 获取 Access Token...
✓ Token 获取成功
3. 测试 MCP initialize...
✓ Initialize 成功
4. 测试 tools/list...
✓ Tools 列表获取成功
5. 测试未授权访问...
✓ 未授权访问被正确拒绝 (401)
=== 测试完成 ===
```
---
## 测试验证
### 1. 测试 OAuth 认证
```bash
./test-cognito-auth.sh
```
### 2. 测试查询执行
```bash
# 基本查询测试
./test-oauth-query.sh "SELECT 1 as test" "default"
# 实际数据查询
./test-oauth-query.sh "SELECT * FROM my_table LIMIT 5" "my_database"
```
### 3. 手动测试 API
```bash
# 1. 获取 Access Token
CLIENT_ID="your-client-id"
CLIENT_SECRET="your-client-secret"
TOKEN_URL="your-token-url"
AUTH_HEADER=$(echo -n "$CLIENT_ID:$CLIENT_SECRET" | base64)
ACCESS_TOKEN=$(curl -s -X POST "$TOKEN_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic $AUTH_HEADER" \
-d "grant_type=client_credentials&scope=athena-mcp-api/read+athena-mcp-api/write" \
| jq -r '.access_token')
# 2. 测试 initialize
curl -X POST "https://your-api-endpoint/prod/mcp" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "test-client", "version": "1.0.0"}
}
}' | jq '.'
# 3. 测试 tools/list
curl -X POST "https://your-api-endpoint/prod/mcp" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}' | jq '.'
# 4. 执行查询
curl -X POST "https://your-api-endpoint/prod/mcp" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "run_query",
"arguments": {
"database": "my_database",
"query": "SELECT * FROM my_table LIMIT 5",
"maxRows": 10
}
}
}' | jq '.'
```
---
## 客户端集成
### TypeScript/Node.js 客户端
参考 `examples/oauth-client-example.ts`:
```typescript
import { AthenaMcpClient } from './examples/oauth-client-example';
// 初始化客户端
const client = new AthenaMcpClient({
clientId: process.env.COGNITO_CLIENT_ID!,
clientSecret: process.env.COGNITO_CLIENT_SECRET!,
tokenUrl: process.env.COGNITO_TOKEN_URL!,
apiEndpoint: process.env.API_ENDPOINT!,
});
// 使用示例
async function main() {
// 初始化连接
await client.initialize();
// 列出可用工具
const tools = await client.listTools();
console.log('可用工具:', tools.tools.map(t => t.name));
// 执行查询
const result = await client.runQuery(
'my_database',
'SELECT * FROM my_table LIMIT 10',
10
);
console.log('查询结果:', result);
}
main().catch(console.error);
```
### Python 客户端
```python
import requests
import base64
import json
import os
class AthenaMcpClient:
def __init__(self, client_id, client_secret, token_url, api_endpoint):
self.client_id = client_id
self.client_secret = client_secret
self.token_url = token_url
self.api_endpoint = api_endpoint
self.access_token = None
def get_access_token(self):
"""获取 OAuth Access Token"""
auth = base64.b64encode(
f"{self.client_id}:{self.client_secret}".encode()
).decode()
response = requests.post(
self.token_url,
headers={
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': f'Basic {auth}'
},
data='grant_type=client_credentials&scope=athena-mcp-api/read+athena-mcp-api/write'
)
self.access_token = response.json()['access_token']
return self.access_token
def call_mcp(self, method, params=None):
"""调用 MCP 方法"""
if not self.access_token:
self.get_access_token()
response = requests.post(
self.api_endpoint,
headers={
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.access_token}'
},
json={
'jsonrpc': '2.0',
'id': 1,
'method': method,
'params': params
}
)
return response.json()
def run_query(self, database, query, max_rows=1000):
"""执行 Athena 查询"""
result = self.call_mcp('tools/call', {
'name': 'run_query',
'arguments': {
'database': database,
'query': query,
'maxRows': max_rows
}
})
return json.loads(result['result']['content'][0]['text'])
# 使用示例
client = AthenaMcpClient(
client_id=os.getenv('COGNITO_CLIENT_ID'),
client_secret=os.getenv('COGNITO_CLIENT_SECRET'),
token_url=os.getenv('COGNITO_TOKEN_URL'),
api_endpoint=os.getenv('API_ENDPOINT')
)
result = client.run_query('my_database', 'SELECT * FROM my_table LIMIT 5')
print(result)
```
---
## 故障排查
### 常见问题
#### 1. 401 Unauthorized
**原因**:
- Access Token 无效或过期
- Authorization header 格式错误
- Token 缺少必需的 scopes
**解决方案**:
```bash
# 检查 token 是否有效
echo $ACCESS_TOKEN | cut -d'.' -f2 | base64 -d 2>/dev/null | jq '.'
# 重新获取 token
./test-cognito-auth.sh
# 确认 header 格式
curl -v -X POST "$API_ENDPOINT" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```
#### 2. 403 Forbidden
**原因**:
- API Gateway Authorizer 配置错误
- Cognito User Pool ARN 不匹配
- Scopes 配置不正确
**解决方案**:
```bash
# 检查 API Gateway 配置
aws apigateway get-authorizers \
--rest-api-id $(aws cloudformation describe-stacks \
--stack-name aws-athena-mcp-stack \
--query "Stacks[0].Outputs[?OutputKey=='ApiEndpoint'].OutputValue" \
--output text | cut -d'/' -f3 | cut -d'.' -f1)
# 重新部署
sam build && sam deploy --resolve-s3 --no-confirm-changeset --capabilities CAPABILITY_IAM
```
#### 3. 查询超时
**原因**:
- 查询执行时间超过 Lambda timeout(300秒)
- 查询复杂度高
**解决方案**:
```bash
# 使用异步模式
# 查询会返回 queryExecutionId,稍后获取结果
# 或增加 Lambda timeout(修改 template.yaml)
Globals:
Function:
Timeout: 600 # 增加到 10 分钟
```
#### 4. S3 权限错误
**原因**:
- Lambda IAM Role 缺少 S3 权限
- S3 bucket 不存在或无法访问
**解决方案**:
```bash
# 检查 S3 bucket
aws s3 ls s3://your-athena-results-bucket/
# 检查 Lambda IAM Role 权限
aws iam get-role-policy \
--role-name $(aws cloudformation describe-stack-resources \
--stack-name aws-athena-mcp-stack \
--logical-resource-id AthenaFunctionRole \
--query "StackResources[0].PhysicalResourceId" \
--output text) \
--policy-name AthenaFunctionRolePolicy0
```
#### 5. Athena 查询失败
**原因**:
- SQL 语法错误
- 数据库或表不存在
- Glue 权限不足
**解决方案**:
```bash
# 在 AWS Console 测试查询
aws athena start-query-execution \
--query-string "SELECT * FROM my_table LIMIT 1" \
--query-execution-context Database=my_database \
--result-configuration OutputLocation=s3://your-bucket/
# 检查 Glue 数据目录
aws glue get-databases
aws glue get-tables --database-name my_database
```
### 查看日志
```bash
# Lambda 函数日志
aws logs tail /aws/lambda/aws-athena-mcp-stack-AthenaFunction-xxxxx --follow
# 查看最近的错误
aws logs filter-log-events \
--log-group-name /aws/lambda/aws-athena-mcp-stack-AthenaFunction-xxxxx \
--filter-pattern "ERROR" \
--max-items 10
```
---
## 维护和更新
### 更新代码
```bash
# 1. 修改代码
vim src/lambda-simple.ts
# 2. 构建
npm run build
# 3. 部署
sam build
sam deploy --resolve-s3 --no-confirm-changeset --capabilities CAPABILITY_IAM
```
### 更新配置
```bash
# 修改 template.yaml 或 samconfig.toml
vim template.yaml
# 重新部署
sam build
sam deploy --resolve-s3 --no-confirm-changeset --capabilities CAPABILITY_IAM
```
### 监控和告警
```bash
# 创建 CloudWatch 告警
aws cloudwatch put-metric-alarm \
--alarm-name athena-mcp-errors \
--alarm-description "Alert on Lambda errors" \
--metric-name Errors \
--namespace AWS/Lambda \
--statistic Sum \
--period 300 \
--threshold 5 \
--comparison-operator GreaterThanThreshold \
--dimensions Name=FunctionName,Value=aws-athena-mcp-stack-AthenaFunction-xxxxx
```
### 备份和恢复
```bash
# 导出 CloudFormation 模板
aws cloudformation get-template \
--stack-name aws-athena-mcp-stack \
--query TemplateBody \
--output text > backup-template.yaml
# 导出 Cognito 配置
aws cognito-idp describe-user-pool \
--user-pool-id us-east-1_xxxxx > backup-cognito.json
```
### 删除部署
```bash
# 删除 CloudFormation Stack(会删除所有资源)
aws cloudformation delete-stack --stack-name aws-athena-mcp-stack
# 等待删除完成
aws cloudformation wait stack-delete-complete --stack-name aws-athena-mcp-stack
# 清理本地构建
rm -rf .aws-sam/ build/
```
---
## 安全最佳实践
### 1. 保护敏感信息
```bash
# 不要提交敏感信息到版本控制
echo ".env.oauth" >> .gitignore
echo "*.secret" >> .gitignore
# 使用 AWS Secrets Manager 存储 Client Secret
aws secretsmanager create-secret \
--name athena-mcp-client-secret \
--secret-string "$CLIENT_SECRET"
```
### 2. 最小权限原则
- 仅授予 Lambda 必需的 IAM 权限
- 限制 S3 bucket 访问范围
- 使用 VPC 端点限制网络访问
### 3. 启用日志和审计
```bash
# 启用 API Gateway 访问日志
aws apigateway update-stage \
--rest-api-id xxxxx \
--stage-name prod \
--patch-operations op=replace,path=/accessLogSettings/destinationArn,value=arn:aws:logs:region:account:log-group:api-gateway-logs
# 启用 CloudTrail
aws cloudtrail create-trail \
--name athena-mcp-trail \
--s3-bucket-name your-cloudtrail-bucket
```
### 4. 定期轮换凭证
```bash
# 创建新的 App Client
aws cognito-idp create-user-pool-client \
--user-pool-id us-east-1_xxxxx \
--client-name athena-mcp-client-new \
--generate-secret
# 更新客户端配置
# 删除旧的 App Client
```
---
## 性能优化
### 1. Lambda 配置优化
```yaml
# template.yaml
Globals:
Function:
Timeout: 300
MemorySize: 1024 # 增加内存可能提高性能
Architectures:
- arm64 # ARM 架构通常更便宜
```
### 2. 查询优化
- 使用分区表减少扫描数据量
- 添加适当的 WHERE 条件
- 使用 LIMIT 限制返回行数
- 考虑使用 Athena 视图
### 3. 缓存策略
- 在客户端缓存 Access Token(有效期 1 小时)
- 缓存频繁查询的结果
- 使用 API Gateway 缓存(可选)
---
## 成本估算
### Lambda 成本
- **请求数**: $0.20 / 100万请求
- **执行时间**: $0.0000166667 / GB-秒 (ARM64)
- **示例**: 10万次请求,平均 2秒,512MB = ~$1.70/月
### Athena 成本
- **数据扫描**: $5.00 / TB
- **示例**: 每天扫描 10GB = ~$1.50/月
### API Gateway 成本
- **请求数**: $3.50 / 100万请求
- **示例**: 10万次请求 = ~$0.35/月
### Cognito 成本
- **MAU**: 前 50,000 免费
- **超出部分**: $0.0055 / MAU
### 总计估算
小规模使用(10万请求/月):约 $3-5/月
---
## 相关资源
- [AWS Athena 文档](https://docs.aws.amazon.com/athena/)
- [AWS SAM 文档](https://docs.aws.amazon.com/serverless-application-model/)
- [AWS Cognito 文档](https://docs.aws.amazon.com/cognito/)
- [MCP 协议规范](https://modelcontextprotocol.io/)
- [项目 GitHub](https://github.com/lishenxydlgzs/aws-athena-mcp)
---
## 支持和反馈
- **Issues**: https://github.com/lishenxydlgzs/aws-athena-mcp/issues
- **Discussions**: https://github.com/lishenxydlgzs/aws-athena-mcp/discussions
- **Email**: 通过 GitHub 联系
---
## 许可证
MIT License - 详见 [LICENSE](./LICENSE) 文件