Snuetl Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Snuetl Mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
SNU eTL(Canvas LMS)을 MCP 도구로 노출하는 Python MCP 서버. 강의·과제·공지·성적을 조회하고, 마감일/공지를 로컬에 누적 저장하며, 강의 자료를 의예과 폴더 구조에 맞춰 다운로드·정리한다.
| 파일 | 역할 |
|---|---|
server.py | FastMCP 진입점 — 도구 등록 |
canvas_client.py | Canvas 인증 + raw 조회 → dict 변환 |
store.py | SQLite 누적 저장소 (courses/announcements/assignments + change_history) |
materials.py | 강의 자료 다운로드(폴더 구조 매핑) + 강의록/과제물 정리 |
links.py | 본문(페이지·공지·과제) 속 링크 자료 스캔 — 파일 탭에 안 뜨는 파일 |
lti.py | LearningX LTI 공통 진입점 (도구 id 탐색·런치·API) |
boards.py | 게시판·열린게시판(LTI 두 종류) 글/첨부/댓글 |
weekly.py | 주차학습 항목과 내 진도·출결 상태 |
snapshot.py | 강의 탭 전체를 훑는 화면 스냅샷 + 커버리지 |
write.py | 쓰기 — 과제 제출·토론 답글·게시판 글/댓글 (기본 비활성) |
video.py | 영상 강의(LearningX→LCMS→유튜브) 대본 추출 |
sync_job.py | 자동 동기화 잡 (cron 실행, 결과를 store/sync.log에 누적) |
DESIGN.md | 설계 문서 |
.env (git 제외):
ETL_BASE_URL=https://myetl.snu.ac.kr
ETL_TOKEN=<개인 액세스 토큰>
ETL_DOWNLOAD_DIR=/Users/.../의예과 # 강의 자료 저장 기본 경로python3 -m venv .venv
.venv/bin/pip install -r requirements.txt.venv/bin/python server.py # MCP 서버 (stdio)
.venv/bin/mcp dev server.py # MCP Inspector 디버깅
.venv/bin/python sync_job.py # 동기화 1회 수동 실행MCP 클라이언트 등록:
claude mcp add snu-etl -- /abs/path/.venv/bin/python /abs/path/server.pywhoami, list_courses, list_assignments, get_upcoming,list_announcements, get_grades, list_files, list_linked_files, get_submissions(과제별 점수·코멘트), get_attendance, list_weekly_items, get_textbooks, get_page, list_conversations, list_boards, list_board_posts, get_board_post
get_course_snapshot(사이트에 보이는 모든 탭을 한 번에),sync_snapshot, get_stored_snapshot
sync_all, sync_courses, sync_announcements, sync_assignments,get_stored_courses, get_stored_announcements, get_stored_assignments, get_change_history
download_course_files(본문 링크 자료·과제 첨부 포함),organize_course_files
list_lecture_videos, get_lecture_transcript(eTL 영상→유튜브 자막),get_youtube_transcript(공개 유튜브 링크 범용)
교수가 페이지·공지 본문에 파일을 링크로만 걸면 Canvas 가 그 파일을 hidden 으로 두어 eTL '파일' 탭과 list_files 에 나오지 않는다. list_linked_files 가 강의계획서·페이지·과제·퀴즈·공지·토론 본문과 모듈 항목을 훑어 이런 파일과 외부 링크(구글드라이브·유튜브·LCMS·직접 파일 URL)를 찾아내고, download_course_files 는 기본값(include_linked=True)으로 이들을 함께 내려받는다(출처는 via 에 표시).
include_embedded_images=True # 본문 삽입 이미지(<img>)까지
include_external_links=True # 외부 사이트 직접 파일 링크(pdf 등)까지GET /courses/{id}/tabs(학생이 실제로 보는 메뉴)를 기준으로 모든 탭을 훑는다. 탭별로 읽기 성공 여부와 건수를 coverage 로 함께 주므로, "사이트엔 있는데 못 읽는 것"이 결과에 그대로 드러난다.
| 탭 | 읽는 경로 |
|---|---|
| 홈·공지·과제·모듈·페이지·수강생·퀴즈 | Canvas API |
| 성적 | students/submissions (과제별 점수·만점·지각·채점 코멘트) |
| 강의계획서 / 강의교재 | sugang cc103ajax / cc103bkajax (Referer 필요, SSO 불필요) |
| 주차학습 / 강의·출결 | LearningX LTI — 항목별 완료·출결 상태 |
| 게시판 / 열린게시판 | LearningX LTI 두 종류 모두 |
| 파일 | 파일 탭 + 본문 링크 자료(hidden) |
sync_snapshot 은 이 스냅샷을 SQLite 에 저장하고, 다음 동기화 때 달라진 항목을 change_history 에 남긴다(예: 성적 / 추가 2 → "Homework 3; Homework 4"). sync_all 은 기본으로 이번 학기 과목의 스냅샷까지 갱신한다(snapshot=False 로 생략).
기본은 꺼져 있다. .env 에 ETL_ENABLE_WRITE=1 을 넣고 서버를 다시 시작해야 쓰기 도구가 등록된다. 그 위에 한 겹 더: 모든 쓰기 도구는 confirm=True 가 없으면 무엇이 어디에 올라가는지 미리보기만 돌려준다.
| 도구 | 되돌리기 |
|---|---|
submit_assignment(파일/텍스트/URL) | 불가 — 재제출은 새 시도로 쌓임 |
reply_discussion, create_discussion | eTL 설정에 따라 다름 |
create_board_post / create_board_comment | delete_board_post / delete_board_comment |
출결·영상 시청 기록 쓰기는 넣지 않았다(시청하지 않은 강의를 시청 처리하는 용도가 되어 출결 기록 위조가 된다). 진도 확인은 읽기 쪽 get_attendance·list_weekly_items 로 한다.
sync_job.py를 cron으로 매시 실행하여 공지·마감 변경을 자동 누적한다:
0 * * * * /abs/.venv/bin/python /abs/sync_job.py >> /abs/store/cron.out 2>&1 # snuETL-mcp-sync~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.