cai-mcp-server 0.1.1__tar.gz
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.
- cai_mcp_server-0.1.1/.env.example +8 -0
- cai_mcp_server-0.1.1/.gitignore +7 -0
- cai_mcp_server-0.1.1/.ipynb_checkpoints/AGENT_GUIDE-checkpoint.md +92 -0
- cai_mcp_server-0.1.1/.ipynb_checkpoints/README-checkpoint.md +183 -0
- cai_mcp_server-0.1.1/AGENT_GUIDE.md +92 -0
- cai_mcp_server-0.1.1/PKG-INFO +197 -0
- cai_mcp_server-0.1.1/README.md +183 -0
- cai_mcp_server-0.1.1/agent-studio.json +22 -0
- cai_mcp_server-0.1.1/cai-mcp-starter-latest.zip +0 -0
- cai_mcp_server-0.1.1/pyproject.toml +39 -0
- cai_mcp_server-0.1.1/scripts/verify_uvx.py +45 -0
- cai_mcp_server-0.1.1/src/cai_mcp_server/__init__.py +3 -0
- cai_mcp_server-0.1.1/src/cai_mcp_server/config.py +54 -0
- cai_mcp_server-0.1.1/src/cai_mcp_server/server.py +268 -0
- cai_mcp_server-0.1.1/src/cai_mcp_server/service.py +535 -0
- cai_mcp_server-0.1.1/src/cai_mcp_server/workbench_client.py +189 -0
- cai_mcp_server-0.1.1/tests/conftest.py +6 -0
- cai_mcp_server-0.1.1/tests/test_config.py +48 -0
- cai_mcp_server-0.1.1/tests/test_server.py +122 -0
- cai_mcp_server-0.1.1/tests/test_service.py +291 -0
- cai_mcp_server-0.1.1/tests/test_stdio.py +29 -0
- cai_mcp_server-0.1.1/tests/test_workbench_client.py +140 -0
- cai_mcp_server-0.1.1/uv.lock +952 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Cloudera AI Workbench MCP 에이전트 가이드
|
|
2
|
+
|
|
3
|
+
이 MCP는 Cloudera AI Workbench API v2를 업무 단위 tool로 제공한다. 사용자는 API 경로,
|
|
4
|
+
project ID, job ID, page token을 입력하지 않는다. 에이전트는 자연어 이름과 선택적 필터만
|
|
5
|
+
받아 tool을 호출한다.
|
|
6
|
+
|
|
7
|
+
## 도구 선택
|
|
8
|
+
|
|
9
|
+
| 사용자 요청 | 호출할 tool |
|
|
10
|
+
|---|---|
|
|
11
|
+
| Workbench 전체 상태, 최근 실패, 현재 실행 workload, 리소스 요약 | `get_workbench_overview` |
|
|
12
|
+
| workload를 이름/프로젝트/상태/유형/생성자/기간으로 찾기 | `find_workloads` |
|
|
13
|
+
| 현재 CPU·메모리·GPU 사용량 및 집계 | `get_current_resource_usage` |
|
|
14
|
+
| 이름으로 job 상태와 최근 run 확인 | `get_job_status` |
|
|
15
|
+
| 허용된 job 실행 | `run_job` |
|
|
16
|
+
|
|
17
|
+
프로젝트 자체 목록을 직접 반환하는 공개 tool은 없다. 프로젝트 이름이 필요한 경우
|
|
18
|
+
workload 결과의 `project_name`을 사용하고, job 조회/실행 시에는 사람이 아는 job 이름으로
|
|
19
|
+
질의한다.
|
|
20
|
+
|
|
21
|
+
## 기본 호출 규칙
|
|
22
|
+
|
|
23
|
+
- 상태 요약은 입력 없이 `get_workbench_overview(window_hours=24)`를 호출한다.
|
|
24
|
+
- workload 검색은 먼저 `find_workloads`를 입력 없이 호출한다. `lookback_hours`는 사용자가
|
|
25
|
+
기간을 말했을 때만 지정한다.
|
|
26
|
+
- 리소스 질문은 `get_current_resource_usage(status="running")`를 기본으로 사용한다.
|
|
27
|
+
- 사용자가 말한 상태나 workload 유형을 임의로 번역하지 않는다. 서버가 Workbench의 현재
|
|
28
|
+
허용 목록을 검증하므로 오류가 나오면 반환된 허용 값으로 다시 질문한다.
|
|
29
|
+
- `job_id`, `project_id`, API filter JSON, page token을 사용자에게 요구하지 않는다.
|
|
30
|
+
|
|
31
|
+
## 이름 해석
|
|
32
|
+
|
|
33
|
+
`get_job_status`와 `run_job`은 `job_name`만 필수다.
|
|
34
|
+
|
|
35
|
+
- `project_name`을 생략하고 job 이름이 한 개면 프로젝트를 자동 선택한다.
|
|
36
|
+
- 같은 이름이 여러 프로젝트에 있으면 후보 프로젝트명을 사용자에게 보여주고 선택을
|
|
37
|
+
요청한다.
|
|
38
|
+
- 부분 일치나 임의의 유사 이름을 선택하지 않는다.
|
|
39
|
+
- 조회는 대소문자 무시의 유일한 일치만 허용하지만, 실행은 allowlist의 정확한 이름과
|
|
40
|
+
프로젝트 조합만 허용한다.
|
|
41
|
+
|
|
42
|
+
## 실행 tool 안전 규칙
|
|
43
|
+
|
|
44
|
+
`run_job`은 새 run을 만들 수 있는 non-idempotent 작업이다.
|
|
45
|
+
|
|
46
|
+
- `CAI_ENABLE_JOB_RUNS=true`이고 `CAI_ALLOWED_JOBS`에 정확히 허용된 조합이어야 한다.
|
|
47
|
+
- 사용자가 명시적으로 실행을 요청하지 않으면 호출하지 않는다.
|
|
48
|
+
- 사용자에게 실행할 job과 프로젝트를 다시 확인받은 뒤 호출한다.
|
|
49
|
+
- `confirm=true` 같은 임의 확인 파라미터를 만들어 보안 경계로 사용하지 않는다.
|
|
50
|
+
- 실패 후 자동 재시도하지 않는다. 재시도하면 중복 run이 생성될 수 있다.
|
|
51
|
+
- 비허용, 모호한 이름, 프로젝트 불일치가 반환되면 실행하지 않고 설명한다.
|
|
52
|
+
|
|
53
|
+
## 결과 해석
|
|
54
|
+
|
|
55
|
+
`get_workbench_overview`의 `execution_summary`와 `recent_failures`는 이 환경에서 접근 가능한
|
|
56
|
+
`/api/v2/usage`의 상태와 생성 시각으로 계산된다. `/api/v2/workloads/executions`의 상세
|
|
57
|
+
실행 이력이 아니다. 결과에 `history_source: "usage"`가 있으면 이 의미를 사용자에게
|
|
58
|
+
명시한다.
|
|
59
|
+
|
|
60
|
+
`running_resource_totals`는 현재 workload가 보고한 CPU 코어, 메모리 GB, NVIDIA GPU 수의
|
|
61
|
+
합계다. 사용률 퍼센트나 과거 누적 비용으로 해석하지 않는다.
|
|
62
|
+
|
|
63
|
+
## 오류 처리
|
|
64
|
+
|
|
65
|
+
- 인증서/연결 오류: 사용자에게 일시적 연결 문제로 알리고 토큰을 다시 출력하거나
|
|
66
|
+
노출하지 않는다.
|
|
67
|
+
- HTTP 401: API Key가 없거나 잘못된 상태다.
|
|
68
|
+
- HTTP 403: 현재 사용자가 해당 Workbench API operation 권한이 없는 상태다. 다른 tool로
|
|
69
|
+
대체 가능한지 먼저 확인한다.
|
|
70
|
+
- 빈 결과: "없음"으로 단정하기 전에 적용된 필터와 기간을 함께 표시한다.
|
|
71
|
+
- API 오류 원문에 토큰, Cookie, private key가 포함되지 않도록 한다.
|
|
72
|
+
|
|
73
|
+
## 대표 대화 예시
|
|
74
|
+
|
|
75
|
+
사용자: `현재 Workbench 상태 보여줘`
|
|
76
|
+
|
|
77
|
+
→ `get_workbench_overview`를 입력 없이 호출하고, 실행 중 workload 수·리소스 합계·최근
|
|
78
|
+
실패를 요약한다.
|
|
79
|
+
|
|
80
|
+
사용자: `ksy-agent 프로젝트에서 실행 중인 애플리케이션 찾아줘`
|
|
81
|
+
|
|
82
|
+
→ `find_workloads(project_name="ksy-agent", workload_type="application", status="running")`
|
|
83
|
+
|
|
84
|
+
사용자: `Nightly Train job 상태 알려줘`
|
|
85
|
+
|
|
86
|
+
→ `get_job_status(job_name="Nightly Train")`. 중복 후보가 반환되면 프로젝트를 질문한다.
|
|
87
|
+
|
|
88
|
+
사용자: `Nightly Train 실행해줘`
|
|
89
|
+
|
|
90
|
+
→ 먼저 정확한 프로젝트를 확인하고, allowlist/전역 실행 스위치 오류가 없는 경우에만
|
|
91
|
+
`run_job`을 호출한다.
|
|
92
|
+
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Cloudera AI Workbench MCP Server
|
|
2
|
+
|
|
3
|
+
Cloudera Agent Studio가 `uvx`로 실행하는 로컬 Python MCP 서버입니다. MCP 통신은
|
|
4
|
+
`stdio`만 사용하고, 서버 프로세스가 인증된 Cloudera AI Workbench API v2를 직접
|
|
5
|
+
호출합니다. 별도의 원격 MCP HTTP 서버는 운영하지 않습니다.
|
|
6
|
+
|
|
7
|
+
이 서버는 API 목록을 그대로 노출하지 않습니다. Agent가 실제 운영 질문과 작업을
|
|
8
|
+
처리할 수 있도록 여러 API 호출·이름 해석·집계를 묶은 5개 업무형 tool만 제공합니다.
|
|
9
|
+
|
|
10
|
+
## 기술 기준
|
|
11
|
+
|
|
12
|
+
- Python 3.11 이상
|
|
13
|
+
- 공식 [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) v2
|
|
14
|
+
- `mcp>=2.0.0,<3`
|
|
15
|
+
- MCP transport: `stdio`
|
|
16
|
+
- REST client: `httpx.AsyncClient`
|
|
17
|
+
- API prefix: 코드에 고정된 `/api/v2`
|
|
18
|
+
|
|
19
|
+
stdout은 MCP wire message 전용이며 애플리케이션 로그는 stderr로 출력됩니다. API path나
|
|
20
|
+
HTTP method를 모델 입력으로 받는 범용 호출 도구는 없습니다.
|
|
21
|
+
|
|
22
|
+
## 제공 tool과 API 매핑
|
|
23
|
+
|
|
24
|
+
| MCP tool | 역할 | 내부 Workbench API v2 |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `get_workbench_overview` | 최근 workload 상태·실패·현재 실행 중 workload·자원 합계 요약 | `GET /usage` |
|
|
27
|
+
| `find_workloads` | 이름, 시간, 상태, 프로젝트, 유형, 생성자로 workload 검색 | `GET /usage` |
|
|
28
|
+
| `get_current_resource_usage` | 현재 workload의 CPU·메모리·GPU 값을 전체/프로젝트/유형별 집계 | `GET /usage` |
|
|
29
|
+
| `get_job_status` | job 이름을 전체 접근 가능 job에서 찾아 최근 run 상태 조회 | 내부 `GET /jobs`, `GET .../runs` |
|
|
30
|
+
| `run_job` | 허용 목록의 job 이름을 안전하게 해석하여 새 run 시작 | 내부 `GET /jobs`, `POST .../runs` |
|
|
31
|
+
|
|
32
|
+
`get_current_resource_usage` 결과는 `/api/v2/usage`가 현재 각 workload에 보고한 CPU,
|
|
33
|
+
memory, GPU 수치의 합계입니다. 사용률 퍼센트나 과거 시간 구간의 누적 소비량으로
|
|
34
|
+
해석하면 안 되며, 응답의 `metric_semantics`에도 이 제한을 표시합니다.
|
|
35
|
+
|
|
36
|
+
`get_workbench_overview`도 `/api/v2/usage`만 사용합니다. `/api/v2/workloads/executions`는
|
|
37
|
+
Cloudera Observability machine user로 구성된 환경에서만 접근 가능한 경우가 있어 기본
|
|
38
|
+
Workload 사용자 인증으로는 호출하지 않습니다. 따라서 overview의 `execution_summary`와
|
|
39
|
+
`recent_failures`는 usage 응답의 상태/생성 시각을 기준으로 계산됩니다.
|
|
40
|
+
|
|
41
|
+
job 목록은 공개 tool이 아닙니다. `get_job_status`와 `run_job`이 사람이 입력한 이름을 API
|
|
42
|
+
ID로 변환할 때만 내부에서 사용합니다. `project_id`, `job_id`, page token과 API filter
|
|
43
|
+
JSON은 어떤 MCP tool도 입력받지 않습니다.
|
|
44
|
+
|
|
45
|
+
## Tool 파라미터
|
|
46
|
+
|
|
47
|
+
| Tool | 필수 입력 | Optional 입력과 기본값 |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `get_workbench_overview` | 없음 | `window_hours=24` (1~168) |
|
|
50
|
+
| `find_workloads` | 없음 | `query=null`, `project_name=null`, `status=null`, `workload_type=null`, `creator=null`, `lookback_hours=null`, `max_results=20` |
|
|
51
|
+
| `get_current_resource_usage` | 없음 | `project_name=null`, `workload_type=null`, `status="running"` |
|
|
52
|
+
| `get_job_status` | `job_name` | `project_name=null`, `recent_runs=5` |
|
|
53
|
+
| `run_job` | `job_name` | `project_name=null` |
|
|
54
|
+
|
|
55
|
+
`find_workloads`의 `lookback_hours`는 workload 생성 시각 필터입니다. 생략하면 시간 필터를
|
|
56
|
+
적용하지 않으므로 오래 실행 중인 workload도 상태 검색에서 누락되지 않습니다. `status`와
|
|
57
|
+
`workload_type`은 서버가 Workbench의 `/workloadstatus`, `/workloadtypes` 목록과
|
|
58
|
+
대소문자 무시 방식으로 검증하며, 잘못된 값이면 현재 사용할 수 있는 값을 반환합니다.
|
|
59
|
+
|
|
60
|
+
`get_job_status`는 전체 접근 가능 job을 이름으로 검색합니다. `project_name`을 생략해도
|
|
61
|
+
job 이름이 유일하면 자동으로 프로젝트를 찾습니다. 동일 이름이 여러 프로젝트에 있으면
|
|
62
|
+
ID가 아닌 프로젝트명 후보를 반환하므로 Agent가 사용자에게 선택을 요청할 수 있습니다.
|
|
63
|
+
조회는 유일한 대소문자 무시 일치도 허용하지만 임의의 부분 일치를 선택하지 않습니다.
|
|
64
|
+
|
|
65
|
+
## 인증 및 환경변수
|
|
66
|
+
|
|
67
|
+
`CAI_BASE_URL`에는 Workbench 도메인만 입력합니다. `CAI_TOKEN`은 Workbench에서
|
|
68
|
+
`audience=API`로 생성한 API Key여야 합니다. AI Inference workload의 `/tmp/jwt`는 이
|
|
69
|
+
API의 자격증명이 아니므로 사용하지 않습니다.
|
|
70
|
+
|
|
71
|
+
| 이름 | 필수 | 기본값 | 설명 |
|
|
72
|
+
|---|---:|---|---|
|
|
73
|
+
| `CAI_BASE_URL` | 예 | 없음 | Workbench origin. 예: `https://ml-xxxx.example.com` |
|
|
74
|
+
| `CAI_TOKEN` | 예 | 없음 | Workbench API v2 Bearer API Key (`audience=API`) |
|
|
75
|
+
| `CAI_ENABLE_JOB_RUNS` | 아니요 | `false` | `run_job` 기능의 운영자 전역 스위치 |
|
|
76
|
+
| `CAI_ALLOWED_JOBS` | 실행 시 | `[]` | 정확한 `project/job` 쌍의 JSON 배열 또는 comma 목록 |
|
|
77
|
+
| `CAI_MAX_RECORDS` | 아니요 | `100` | 내부 API 한 번에 읽는 최대 record 수(1~500) |
|
|
78
|
+
| `CAI_TIMEOUT_SECONDS` | 아니요 | `30` | REST timeout(초) |
|
|
79
|
+
| `CAI_VERIFY_TLS` | 아니요 | `true` | TLS 인증서 검증 |
|
|
80
|
+
| `CAI_LOG_LEVEL` | 아니요 | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
|
|
81
|
+
|
|
82
|
+
운영 환경에서는 `CAI_VERIFY_TLS=true`를 유지하십시오. `CAI_TOKEN`은 로그와 tool 결과에
|
|
83
|
+
출력하지 않습니다.
|
|
84
|
+
|
|
85
|
+
## `run_job` 안전 정책
|
|
86
|
+
|
|
87
|
+
`run_job`은 MCP annotation에서 destructive/non-idempotent tool로 선언되어 있고, 다음 두
|
|
88
|
+
조건을 모두 만족해야 실행됩니다.
|
|
89
|
+
|
|
90
|
+
```dotenv
|
|
91
|
+
CAI_ENABLE_JOB_RUNS=true
|
|
92
|
+
CAI_ALLOWED_JOBS=["Fraud Detection/Nightly Train","Forecast/Daily Refresh"]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`job_name`은 항상 필수입니다. `project_name`을 생략했을 때 allowlist에 같은 job 이름이
|
|
96
|
+
정확히 하나면 프로젝트를 자동 선택합니다. 둘 이상의 프로젝트에 같은 job 이름이
|
|
97
|
+
등록되어 있을 때만 `project_name`을 요구합니다. 실행 시에는 대소문자를 포함한 정확한
|
|
98
|
+
allowlist 일치만 허용하고 부분 일치나 추정은 하지 않습니다.
|
|
99
|
+
|
|
100
|
+
`confirm=true` 같은 모델 입력을 보안 경계로 사용하지 않습니다. 재시도하면 새 run이
|
|
101
|
+
중복 생성될 수 있으므로 POST 요청은 자동 재시도하지 않습니다. 초기 버전은 임의
|
|
102
|
+
환경변수나 실행 인자도 모델에게 받지 않습니다.
|
|
103
|
+
|
|
104
|
+
## 로컬 개발과 테스트
|
|
105
|
+
|
|
106
|
+
[uv](https://docs.astral.sh/uv/)를 설치한 뒤 다음을 실행합니다.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
cp .env.example .env
|
|
110
|
+
# .env의 Workbench URL과 API Key를 수정
|
|
111
|
+
|
|
112
|
+
uv sync --all-extras
|
|
113
|
+
uv run ruff check .
|
|
114
|
+
uv run pytest
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
MCP Inspector로 tool schema를 확인할 수 있습니다.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
uv run --env-file .env mcp dev src/cai_mcp_server/server.py:mcp --with-editable .
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
서버 자체는 stdio peer가 연결해야 하므로 실행 중 터미널에 문자를 직접 입력하지
|
|
124
|
+
마십시오.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
uv run --env-file .env cai-mcp-server
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## wheel 및 uvx 검증
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
uv build
|
|
134
|
+
uvx --from ./dist/cai_mcp_server-0.1.0-py3-none-any.whl cai-mcp-server
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
개발 중에는 현재 소스를 바로 설치할 수도 있습니다.
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
uvx --from . cai-mcp-server
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
패키지 저장소에 배포한 뒤 Agent Studio에는 다음 형태로 등록합니다.
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"command": "uvx",
|
|
148
|
+
"args": [
|
|
149
|
+
"--from",
|
|
150
|
+
"cai-mcp-server==0.1.0",
|
|
151
|
+
"cai-mcp-server"
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
환경변수를 포함한 전체 예시는 `agent-studio.json`에 있습니다. Agent Studio에는 MCP
|
|
157
|
+
server 정의를 등록한 뒤 workflow 설정에서 실제 secret 값을 넣어야 합니다. private
|
|
158
|
+
package index를 사용한다면 Agent Studio runtime에서 접근 가능한 uv/Python index도
|
|
159
|
+
구성해야 합니다.
|
|
160
|
+
|
|
161
|
+
## 테스트 범위와 live 검증
|
|
162
|
+
|
|
163
|
+
자동 테스트는 다음을 검증합니다.
|
|
164
|
+
|
|
165
|
+
- 필수 API Key, 실행 스위치, 정확한 job allowlist parsing
|
|
166
|
+
- 공식 API v2 path, JSON query filter, Bearer header, POST body
|
|
167
|
+
- API 응답 정규화, 실행/현재 자원 집계, 동적 status/type 검증
|
|
168
|
+
- project 이름 생략, 중복 후보 반환, 읽기용 대소문자 무시 해석
|
|
169
|
+
- 실행용 exact allowlist와 유일한 allowlisted project 추론
|
|
170
|
+
- upstream 오류의 민감 정보 제거
|
|
171
|
+
- 공개 tool이 승인된 5개뿐인지와 tool 호출 성공/차단
|
|
172
|
+
- 모든 공개 tool input에서 `project_id`, `job_id`가 제외되는지
|
|
173
|
+
- 실제 subprocess의 stdio MCP initialize 및 tool 목록 조회
|
|
174
|
+
|
|
175
|
+
Workbench 배포 버전에 따라 인스턴스의 정확한 계약은
|
|
176
|
+
`https://<workbench-domain>/api/v2/swagger.json`에서 확인할 수 있습니다. 실제 endpoint를
|
|
177
|
+
사용한 live 검증은 유효한 `CAI_BASE_URL`과 `CAI_TOKEN`이 있을 때 별도로 수행하십시오.
|
|
178
|
+
|
|
179
|
+
관련 공식 문서:
|
|
180
|
+
|
|
181
|
+
- [Cloudera AI Workbench API v2 시작 안내](https://docs.cloudera.com/machine-learning/cloud/api/index.html)
|
|
182
|
+
- [Cloudera AI API v2 REST reference](https://docs.cloudera.com/machine-learning/cloud/rest-api-reference/index.html)
|
|
183
|
+
- [Agent Studio MCP server 등록](https://docs.cloudera.com/machine-learning/cloud/use-ai-studios/topics/ml-register-mcp-server.html)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Cloudera AI Workbench MCP 에이전트 가이드
|
|
2
|
+
|
|
3
|
+
이 MCP는 Cloudera AI Workbench API v2를 업무 단위 tool로 제공한다. 사용자는 API 경로,
|
|
4
|
+
project ID, job ID, page token을 입력하지 않는다. 에이전트는 자연어 이름과 선택적 필터만
|
|
5
|
+
받아 tool을 호출한다.
|
|
6
|
+
|
|
7
|
+
## 도구 선택
|
|
8
|
+
|
|
9
|
+
| 사용자 요청 | 호출할 tool |
|
|
10
|
+
|---|---|
|
|
11
|
+
| Workbench 전체 상태, 최근 실패, 현재 실행 workload, 리소스 요약 | `get_workbench_overview` |
|
|
12
|
+
| workload를 이름/프로젝트/상태/유형/생성자/기간으로 찾기 | `find_workloads` |
|
|
13
|
+
| 현재 CPU·메모리·GPU 사용량 및 집계 | `get_current_resource_usage` |
|
|
14
|
+
| 이름으로 job 상태와 최근 run 확인 | `get_job_status` |
|
|
15
|
+
| 허용된 job 실행 | `run_job` |
|
|
16
|
+
|
|
17
|
+
프로젝트 자체 목록을 직접 반환하는 공개 tool은 없다. 프로젝트 이름이 필요한 경우
|
|
18
|
+
workload 결과의 `project_name`을 사용하고, job 조회/실행 시에는 사람이 아는 job 이름으로
|
|
19
|
+
질의한다.
|
|
20
|
+
|
|
21
|
+
## 기본 호출 규칙
|
|
22
|
+
|
|
23
|
+
- 상태 요약은 입력 없이 `get_workbench_overview(window_hours=24)`를 호출한다.
|
|
24
|
+
- workload 검색은 먼저 `find_workloads`를 입력 없이 호출한다. `lookback_hours`는 사용자가
|
|
25
|
+
기간을 말했을 때만 지정한다.
|
|
26
|
+
- 리소스 질문은 `get_current_resource_usage(status="running")`를 기본으로 사용한다.
|
|
27
|
+
- 사용자가 말한 상태나 workload 유형을 임의로 번역하지 않는다. 서버가 Workbench의 현재
|
|
28
|
+
허용 목록을 검증하므로 오류가 나오면 반환된 허용 값으로 다시 질문한다.
|
|
29
|
+
- `job_id`, `project_id`, API filter JSON, page token을 사용자에게 요구하지 않는다.
|
|
30
|
+
|
|
31
|
+
## 이름 해석
|
|
32
|
+
|
|
33
|
+
`get_job_status`와 `run_job`은 `job_name`만 필수다.
|
|
34
|
+
|
|
35
|
+
- `project_name`을 생략하고 job 이름이 한 개면 프로젝트를 자동 선택한다.
|
|
36
|
+
- 같은 이름이 여러 프로젝트에 있으면 후보 프로젝트명을 사용자에게 보여주고 선택을
|
|
37
|
+
요청한다.
|
|
38
|
+
- 부분 일치나 임의의 유사 이름을 선택하지 않는다.
|
|
39
|
+
- 조회는 대소문자 무시의 유일한 일치만 허용하지만, 실행은 allowlist의 정확한 이름과
|
|
40
|
+
프로젝트 조합만 허용한다.
|
|
41
|
+
|
|
42
|
+
## 실행 tool 안전 규칙
|
|
43
|
+
|
|
44
|
+
`run_job`은 새 run을 만들 수 있는 non-idempotent 작업이다.
|
|
45
|
+
|
|
46
|
+
- `CAI_ENABLE_JOB_RUNS=true`이고 `CAI_ALLOWED_JOBS`에 정확히 허용된 조합이어야 한다.
|
|
47
|
+
- 사용자가 명시적으로 실행을 요청하지 않으면 호출하지 않는다.
|
|
48
|
+
- 사용자에게 실행할 job과 프로젝트를 다시 확인받은 뒤 호출한다.
|
|
49
|
+
- `confirm=true` 같은 임의 확인 파라미터를 만들어 보안 경계로 사용하지 않는다.
|
|
50
|
+
- 실패 후 자동 재시도하지 않는다. 재시도하면 중복 run이 생성될 수 있다.
|
|
51
|
+
- 비허용, 모호한 이름, 프로젝트 불일치가 반환되면 실행하지 않고 설명한다.
|
|
52
|
+
|
|
53
|
+
## 결과 해석
|
|
54
|
+
|
|
55
|
+
`get_workbench_overview`의 `execution_summary`와 `recent_failures`는 이 환경에서 접근 가능한
|
|
56
|
+
`/api/v2/usage`의 상태와 생성 시각으로 계산된다. `/api/v2/workloads/executions`의 상세
|
|
57
|
+
실행 이력이 아니다. 결과에 `history_source: "usage"`가 있으면 이 의미를 사용자에게
|
|
58
|
+
명시한다.
|
|
59
|
+
|
|
60
|
+
`running_resource_totals`는 현재 workload가 보고한 CPU 코어, 메모리 GB, NVIDIA GPU 수의
|
|
61
|
+
합계다. 사용률 퍼센트나 과거 누적 비용으로 해석하지 않는다.
|
|
62
|
+
|
|
63
|
+
## 오류 처리
|
|
64
|
+
|
|
65
|
+
- 인증서/연결 오류: 사용자에게 일시적 연결 문제로 알리고 토큰을 다시 출력하거나
|
|
66
|
+
노출하지 않는다.
|
|
67
|
+
- HTTP 401: API Key가 없거나 잘못된 상태다.
|
|
68
|
+
- HTTP 403: 현재 사용자가 해당 Workbench API operation 권한이 없는 상태다. 다른 tool로
|
|
69
|
+
대체 가능한지 먼저 확인한다.
|
|
70
|
+
- 빈 결과: "없음"으로 단정하기 전에 적용된 필터와 기간을 함께 표시한다.
|
|
71
|
+
- API 오류 원문에 토큰, Cookie, private key가 포함되지 않도록 한다.
|
|
72
|
+
|
|
73
|
+
## 대표 대화 예시
|
|
74
|
+
|
|
75
|
+
사용자: `현재 Workbench 상태 보여줘`
|
|
76
|
+
|
|
77
|
+
→ `get_workbench_overview`를 입력 없이 호출하고, 실행 중 workload 수·리소스 합계·최근
|
|
78
|
+
실패를 요약한다.
|
|
79
|
+
|
|
80
|
+
사용자: `ksy-agent 프로젝트에서 실행 중인 애플리케이션 찾아줘`
|
|
81
|
+
|
|
82
|
+
→ `find_workloads(project_name="ksy-agent", workload_type="application", status="running")`
|
|
83
|
+
|
|
84
|
+
사용자: `Nightly Train job 상태 알려줘`
|
|
85
|
+
|
|
86
|
+
→ `get_job_status(job_name="Nightly Train")`. 중복 후보가 반환되면 프로젝트를 질문한다.
|
|
87
|
+
|
|
88
|
+
사용자: `Nightly Train 실행해줘`
|
|
89
|
+
|
|
90
|
+
→ 먼저 정확한 프로젝트를 확인하고, allowlist/전역 실행 스위치 오류가 없는 경우에만
|
|
91
|
+
`run_job`을 호출한다.
|
|
92
|
+
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cai-mcp-server
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Task-level MCP tools for Cloudera AI Workbench API v2
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: httpx<1,>=0.28.0
|
|
7
|
+
Requires-Dist: mcp<3,>=2.0.0
|
|
8
|
+
Requires-Dist: pydantic-settings<3,>=2.7.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: mcp[cli]<3,>=2.0.0; extra == 'dev'
|
|
11
|
+
Requires-Dist: pytest<10,>=8.4; extra == 'dev'
|
|
12
|
+
Requires-Dist: ruff<1,>=0.12; extra == 'dev'
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Cloudera AI Workbench MCP Server
|
|
16
|
+
|
|
17
|
+
Cloudera Agent Studio가 `uvx`로 실행하는 로컬 Python MCP 서버입니다. MCP 통신은
|
|
18
|
+
`stdio`만 사용하고, 서버 프로세스가 인증된 Cloudera AI Workbench API v2를 직접
|
|
19
|
+
호출합니다. 별도의 원격 MCP HTTP 서버는 운영하지 않습니다.
|
|
20
|
+
|
|
21
|
+
이 서버는 API 목록을 그대로 노출하지 않습니다. Agent가 실제 운영 질문과 작업을
|
|
22
|
+
처리할 수 있도록 여러 API 호출·이름 해석·집계를 묶은 5개 업무형 tool만 제공합니다.
|
|
23
|
+
|
|
24
|
+
## 기술 기준
|
|
25
|
+
|
|
26
|
+
- Python 3.11 이상
|
|
27
|
+
- 공식 [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) v2
|
|
28
|
+
- `mcp>=2.0.0,<3`
|
|
29
|
+
- MCP transport: `stdio`
|
|
30
|
+
- REST client: `httpx.AsyncClient`
|
|
31
|
+
- API prefix: 코드에 고정된 `/api/v2`
|
|
32
|
+
|
|
33
|
+
stdout은 MCP wire message 전용이며 애플리케이션 로그는 stderr로 출력됩니다. API path나
|
|
34
|
+
HTTP method를 모델 입력으로 받는 범용 호출 도구는 없습니다.
|
|
35
|
+
|
|
36
|
+
## 제공 tool과 API 매핑
|
|
37
|
+
|
|
38
|
+
| MCP tool | 역할 | 내부 Workbench API v2 |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `get_workbench_overview` | 최근 workload 상태·실패·현재 실행 중 workload·자원 합계 요약 | `GET /usage` |
|
|
41
|
+
| `find_workloads` | 이름, 시간, 상태, 프로젝트, 유형, 생성자로 workload 검색 | `GET /usage` |
|
|
42
|
+
| `get_current_resource_usage` | 현재 workload의 CPU·메모리·GPU 값을 전체/프로젝트/유형별 집계 | `GET /usage` |
|
|
43
|
+
| `get_job_status` | job 이름을 전체 접근 가능 job에서 찾아 최근 run 상태 조회 | 내부 `GET /jobs`, `GET .../runs` |
|
|
44
|
+
| `run_job` | 허용 목록의 job 이름을 안전하게 해석하여 새 run 시작 | 내부 `GET /jobs`, `POST .../runs` |
|
|
45
|
+
|
|
46
|
+
`get_current_resource_usage` 결과는 `/api/v2/usage`가 현재 각 workload에 보고한 CPU,
|
|
47
|
+
memory, GPU 수치의 합계입니다. 사용률 퍼센트나 과거 시간 구간의 누적 소비량으로
|
|
48
|
+
해석하면 안 되며, 응답의 `metric_semantics`에도 이 제한을 표시합니다.
|
|
49
|
+
|
|
50
|
+
`get_workbench_overview`도 `/api/v2/usage`만 사용합니다. `/api/v2/workloads/executions`는
|
|
51
|
+
Cloudera Observability machine user로 구성된 환경에서만 접근 가능한 경우가 있어 기본
|
|
52
|
+
Workload 사용자 인증으로는 호출하지 않습니다. 따라서 overview의 `execution_summary`와
|
|
53
|
+
`recent_failures`는 usage 응답의 상태/생성 시각을 기준으로 계산됩니다.
|
|
54
|
+
|
|
55
|
+
job 목록은 공개 tool이 아닙니다. `get_job_status`와 `run_job`이 사람이 입력한 이름을 API
|
|
56
|
+
ID로 변환할 때만 내부에서 사용합니다. `project_id`, `job_id`, page token과 API filter
|
|
57
|
+
JSON은 어떤 MCP tool도 입력받지 않습니다.
|
|
58
|
+
|
|
59
|
+
## Tool 파라미터
|
|
60
|
+
|
|
61
|
+
| Tool | 필수 입력 | Optional 입력과 기본값 |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| `get_workbench_overview` | 없음 | `window_hours=24` (1~168) |
|
|
64
|
+
| `find_workloads` | 없음 | `query=null`, `project_name=null`, `status=null`, `workload_type=null`, `creator=null`, `lookback_hours=null`, `max_results=20` |
|
|
65
|
+
| `get_current_resource_usage` | 없음 | `project_name=null`, `workload_type=null`, `status="running"` |
|
|
66
|
+
| `get_job_status` | `job_name` | `project_name=null`, `recent_runs=5` |
|
|
67
|
+
| `run_job` | `job_name` | `project_name=null` |
|
|
68
|
+
|
|
69
|
+
`find_workloads`의 `lookback_hours`는 workload 생성 시각 필터입니다. 생략하면 시간 필터를
|
|
70
|
+
적용하지 않으므로 오래 실행 중인 workload도 상태 검색에서 누락되지 않습니다. `status`와
|
|
71
|
+
`workload_type`은 서버가 Workbench의 `/workloadstatus`, `/workloadtypes` 목록과
|
|
72
|
+
대소문자 무시 방식으로 검증하며, 잘못된 값이면 현재 사용할 수 있는 값을 반환합니다.
|
|
73
|
+
|
|
74
|
+
`get_job_status`는 전체 접근 가능 job을 이름으로 검색합니다. `project_name`을 생략해도
|
|
75
|
+
job 이름이 유일하면 자동으로 프로젝트를 찾습니다. 동일 이름이 여러 프로젝트에 있으면
|
|
76
|
+
ID가 아닌 프로젝트명 후보를 반환하므로 Agent가 사용자에게 선택을 요청할 수 있습니다.
|
|
77
|
+
조회는 유일한 대소문자 무시 일치도 허용하지만 임의의 부분 일치를 선택하지 않습니다.
|
|
78
|
+
|
|
79
|
+
## 인증 및 환경변수
|
|
80
|
+
|
|
81
|
+
`CAI_BASE_URL`에는 Workbench 도메인만 입력합니다. `CAI_TOKEN`은 Workbench에서
|
|
82
|
+
`audience=API`로 생성한 API Key여야 합니다. AI Inference workload의 `/tmp/jwt`는 이
|
|
83
|
+
API의 자격증명이 아니므로 사용하지 않습니다.
|
|
84
|
+
|
|
85
|
+
| 이름 | 필수 | 기본값 | 설명 |
|
|
86
|
+
|---|---:|---|---|
|
|
87
|
+
| `CAI_BASE_URL` | 예 | 없음 | Workbench origin. 예: `https://ml-xxxx.example.com` |
|
|
88
|
+
| `CAI_TOKEN` | 예 | 없음 | Workbench API v2 Bearer API Key (`audience=API`) |
|
|
89
|
+
| `CAI_ENABLE_JOB_RUNS` | 아니요 | `false` | `run_job` 기능의 운영자 전역 스위치 |
|
|
90
|
+
| `CAI_ALLOWED_JOBS` | 실행 시 | `[]` | 정확한 `project/job` 쌍의 JSON 배열 또는 comma 목록 |
|
|
91
|
+
| `CAI_MAX_RECORDS` | 아니요 | `100` | 내부 API 한 번에 읽는 최대 record 수(1~500) |
|
|
92
|
+
| `CAI_TIMEOUT_SECONDS` | 아니요 | `30` | REST timeout(초) |
|
|
93
|
+
| `CAI_VERIFY_TLS` | 아니요 | `true` | TLS 인증서 검증 |
|
|
94
|
+
| `CAI_LOG_LEVEL` | 아니요 | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
|
|
95
|
+
|
|
96
|
+
운영 환경에서는 `CAI_VERIFY_TLS=true`를 유지하십시오. `CAI_TOKEN`은 로그와 tool 결과에
|
|
97
|
+
출력하지 않습니다.
|
|
98
|
+
|
|
99
|
+
## `run_job` 안전 정책
|
|
100
|
+
|
|
101
|
+
`run_job`은 MCP annotation에서 destructive/non-idempotent tool로 선언되어 있고, 다음 두
|
|
102
|
+
조건을 모두 만족해야 실행됩니다.
|
|
103
|
+
|
|
104
|
+
```dotenv
|
|
105
|
+
CAI_ENABLE_JOB_RUNS=true
|
|
106
|
+
CAI_ALLOWED_JOBS=["Fraud Detection/Nightly Train","Forecast/Daily Refresh"]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`job_name`은 항상 필수입니다. `project_name`을 생략했을 때 allowlist에 같은 job 이름이
|
|
110
|
+
정확히 하나면 프로젝트를 자동 선택합니다. 둘 이상의 프로젝트에 같은 job 이름이
|
|
111
|
+
등록되어 있을 때만 `project_name`을 요구합니다. 실행 시에는 대소문자를 포함한 정확한
|
|
112
|
+
allowlist 일치만 허용하고 부분 일치나 추정은 하지 않습니다.
|
|
113
|
+
|
|
114
|
+
`confirm=true` 같은 모델 입력을 보안 경계로 사용하지 않습니다. 재시도하면 새 run이
|
|
115
|
+
중복 생성될 수 있으므로 POST 요청은 자동 재시도하지 않습니다. 초기 버전은 임의
|
|
116
|
+
환경변수나 실행 인자도 모델에게 받지 않습니다.
|
|
117
|
+
|
|
118
|
+
## 로컬 개발과 테스트
|
|
119
|
+
|
|
120
|
+
[uv](https://docs.astral.sh/uv/)를 설치한 뒤 다음을 실행합니다.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
cp .env.example .env
|
|
124
|
+
# .env의 Workbench URL과 API Key를 수정
|
|
125
|
+
|
|
126
|
+
uv sync --all-extras
|
|
127
|
+
uv run ruff check .
|
|
128
|
+
uv run pytest
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
MCP Inspector로 tool schema를 확인할 수 있습니다.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
uv run --env-file .env mcp dev src/cai_mcp_server/server.py:mcp --with-editable .
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
서버 자체는 stdio peer가 연결해야 하므로 실행 중 터미널에 문자를 직접 입력하지
|
|
138
|
+
마십시오.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
uv run --env-file .env cai-mcp-server
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## wheel 및 uvx 검증
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
uv build
|
|
148
|
+
uvx --from ./dist/cai_mcp_server-0.1.0-py3-none-any.whl cai-mcp-server
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
개발 중에는 현재 소스를 바로 설치할 수도 있습니다.
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
uvx --from . cai-mcp-server
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
패키지 저장소에 배포한 뒤 Agent Studio에는 다음 형태로 등록합니다.
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"command": "uvx",
|
|
162
|
+
"args": [
|
|
163
|
+
"--from",
|
|
164
|
+
"cai-mcp-server==0.1.0",
|
|
165
|
+
"cai-mcp-server"
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
환경변수를 포함한 전체 예시는 `agent-studio.json`에 있습니다. Agent Studio에는 MCP
|
|
171
|
+
server 정의를 등록한 뒤 workflow 설정에서 실제 secret 값을 넣어야 합니다. private
|
|
172
|
+
package index를 사용한다면 Agent Studio runtime에서 접근 가능한 uv/Python index도
|
|
173
|
+
구성해야 합니다.
|
|
174
|
+
|
|
175
|
+
## 테스트 범위와 live 검증
|
|
176
|
+
|
|
177
|
+
자동 테스트는 다음을 검증합니다.
|
|
178
|
+
|
|
179
|
+
- 필수 API Key, 실행 스위치, 정확한 job allowlist parsing
|
|
180
|
+
- 공식 API v2 path, JSON query filter, Bearer header, POST body
|
|
181
|
+
- API 응답 정규화, 실행/현재 자원 집계, 동적 status/type 검증
|
|
182
|
+
- project 이름 생략, 중복 후보 반환, 읽기용 대소문자 무시 해석
|
|
183
|
+
- 실행용 exact allowlist와 유일한 allowlisted project 추론
|
|
184
|
+
- upstream 오류의 민감 정보 제거
|
|
185
|
+
- 공개 tool이 승인된 5개뿐인지와 tool 호출 성공/차단
|
|
186
|
+
- 모든 공개 tool input에서 `project_id`, `job_id`가 제외되는지
|
|
187
|
+
- 실제 subprocess의 stdio MCP initialize 및 tool 목록 조회
|
|
188
|
+
|
|
189
|
+
Workbench 배포 버전에 따라 인스턴스의 정확한 계약은
|
|
190
|
+
`https://<workbench-domain>/api/v2/swagger.json`에서 확인할 수 있습니다. 실제 endpoint를
|
|
191
|
+
사용한 live 검증은 유효한 `CAI_BASE_URL`과 `CAI_TOKEN`이 있을 때 별도로 수행하십시오.
|
|
192
|
+
|
|
193
|
+
관련 공식 문서:
|
|
194
|
+
|
|
195
|
+
- [Cloudera AI Workbench API v2 시작 안내](https://docs.cloudera.com/machine-learning/cloud/api/index.html)
|
|
196
|
+
- [Cloudera AI API v2 REST reference](https://docs.cloudera.com/machine-learning/cloud/rest-api-reference/index.html)
|
|
197
|
+
- [Agent Studio MCP server 등록](https://docs.cloudera.com/machine-learning/cloud/use-ai-studios/topics/ml-register-mcp-server.html)
|