Ssumcp

Created By
hoeongj2 months ago
숭실대학교의 모든 공개/개인 정보 제공과 자동화 에이전트 기능을 MCP 표준 도구로 제공하여 공개 서버 지속적으로 업데이트 및 기능 추가 중입니다 공개 도구 •학식 - 학생식당·도담식당·기숙사(레지던스홀)등 모든 식당 메뉴 및 식당정보 •시설 - 캠퍼스 내 카페·편의점·복사 등 검색 •도서관 - 좌석 실시간 조회 / 도서 검색 •공지사항 - 최신 공지 목록·키워드검색·학과별공지 등 개인 도구 요청시 로그인 URL을 받아 한 번 로그인하면 이후 모든 개인 도구를 사용 가능 •u-SAINT - 시간표, 성적, 채플 정보, 졸업요건, 장학금 내역 등 •LMS - 현재 학기 미제출 과제·퀴즈목록 등 •도서관 - 대출 현황 및 반납 기한(예약 현황 및 예약 자동화 에이전트 도입 예정) 대화 예시 나 졸업하려면 어떤 조건들이 남았어? 도서관 6층 창가쪽 빈자리 있으면 예약해줘 장학금 관련 공지사항 찾아줘
Overview

ssuMCP — 숭실대학교 MCP 서버

CI

숭실대학교 캠퍼스 정보를 MCP(Model Context Protocol) 표준 도구로 제공하는 공개 서버.
동시에 ssuAI 웹 클라이언트를 위한 REST API 서버다.

MCP 엔드포인트: https://ssumcp.duckdns.org/mcp


왜 만들었나

학식 메뉴, 도서관 좌석, 성적, 시간표처럼 매일 확인하는 정보를 매번 포털에서 찾는 게 번거로웠다. 단순히 크롤러를 만드는 대신, LLM이 직접 학교 데이터를 도구로 호출할 수 있는 MCP 서버를 만들면 챗봇·IDE·자동화 파이프라인 어디서든 재사용할 수 있겠다고 판단했다.

Claude Desktop에 연결하면 "오늘 학식 뭐야", "이번 학기 성적 알려줘" 같은 질문에 LLM이 직접 데이터를 가져와 답한다.


연결하기

Claude Desktop

%APPDATA%\Claude\claude_desktop_config.json (Windows) /
~/Library/Application Support/Claude/claude_desktop_config.json (macOS):

{
  "mcpServers": {
    "ssuMCP": {
      "url": "https://ssumcp.duckdns.org/mcp"
    }
  }
}

재시작 후 채팅창 도구 아이콘에서 도구 목록을 확인한다.

Cursor / 그 외 MCP 클라이언트

.cursor/mcp.json:

{
  "mcpServers": {
    "ssuMCP": {
      "url": "https://ssumcp.duckdns.org/mcp"
    }
  }
}

도구 목록

공개 도구 (인증 불필요)

도구설명
get_today_meal오늘 학식 메뉴
get_meal_by_date날짜별 학식
get_dorm_weekly_meal레지던스홀 주간 식단
search_campus_facilities교내 시설 검색
search_library_book중앙도서관 도서 검색
get_recent_notices학교 공지사항 최신 목록
search_notices공지사항 키워드 검색
list_notice_categories공지 카테고리 목록
get_notice_detail공지 본문 전체 조회
get_active_notices진행 중(마감 전) 공지
get_department_notices학과/부서별 공지

개인 도구 (인증 필요)

인증 흐름:

  1. start_auth(provider="SAINT")loginUrl + mcpSessionId 수신
  2. loginUrl을 브라우저에서 열어 숭실대 계정 로그인
  3. 이후 개인 도구 호출 시 mcp_session_id 파라미터 전달
도구provider설명
start_auth로그인 URL 발급
get_auth_status세션 상태 확인
logout_provider특정 provider 해제
logout_all세션 전체 해제
get_my_scheduleSAINT시간표
get_my_gradesSAINT성적
get_my_chapel_infoSAINT채플 출석 현황
check_graduation_requirementsSAINT졸업 요건
get_my_scholarshipsSAINT장학금 수혜 내역
get_my_assignmentsLMS과제 및 퀴즈 목록
get_library_seat_statusLIBRARY층별 좌석 현황 (2·5·6층)
get_my_library_loansLIBRARY도서관 대출 현황

아키텍처

하나의 Spring Boot 프로세스가 REST API와 MCP 서버를 동시에 제공한다.

ssuAI (웹)          Claude Desktop / Cursor / 그 외 MCP 클라이언트
    │  REST /api/*         │  MCP Streamable HTTP
    ▼                      ▼
┌──────────────────────────────────────┐
│   ssuMCP (Spring Boot 4, 단일 JVM)   │
│                                      │
│   REST Controllers   MCP Server      │
│          └──────────────┘            │
│             Service Layer            │
│         Connectors   Repositories    │
└──────────────────────────────────────┘
       │         │         │        │
    학식 사이트  도서관    LMS    u-SAINT
               (Pyxis)  (Canvas) (rusaint)

REST와 MCP 두 경로는 동일한 Service 레이어를 공유한다. MCP 도구가 별도 비즈니스 로직을 갖지 않는다.

Connector 패턴

모든 외부 시스템 호출은 인터페이스로 추상화된다.

MealConnector (interface)
├── MockMealConnector   — 고정 fixture 반환 (기본값)
└── RealMealConnector   — 실제 학식 사이트 Jsoup 파싱

@ConditionalOnPropertyssuai.connector.meal=mock|real에 따라 주입된다.
기본값이 mock이므로 외부 네트워크 없이 빌드·테스트·로컬 실행이 가능하다.

LLM 채팅 엔진

LlmChatService는 OpenAI 호환 API 포맷으로 10개 프로바이더에 순차 Fallback한다.

사용자 메시지
    → LlmChatService
    → LlmProvider 체인 (OpenRouter → Mistral → Groq → ...)
        Rate limit / 서버 오류 시 자동으로 다음 프로바이더로 전환
    → tool_call 발생 시
        공개 도구: McpSyncClient → 자체 /mcp (self-dogfood)
        개인 도구: 웹 세션 컨텍스트로 Service 직접 호출 (ThreadLocal)
    → 최종 응답

엔지니어링 노트

구현 중 부딪힌 문제와 판단 근거를 기록한다. 상세 내용은 TROUBLESHOOTING.md에 있다.

u-SAINT: SAP WebDynpro 역공학 → rusaint FFI로 전환

u-SAINT가 SAP NetWeaver WebDynpro 기반이라 sap-contextid, sap-ext-sid, SAPEVENTQUEUE 등이 stateful하게 얽혀 있다. 직접 HTTP 역공학을 8일간 시도했지만 wire-level ground truth 없이 protocol을 추측하는 방식은 한계가 명확했다.

검증된 Rust 구현체 rusaint를 JNA(Java Native Access)로 JVM에 FFI 연동하는 방향으로 전환했다. SmartID 콜백의 sToken을 rusaint withToken에 한 번만 전달하고, 결과 세션은 AES-256-GCM으로 암호화해 저장한다.

WAF 쿠키 + CookieManager 격리

대학 보안 장비(WAF)가 특정 쿠키 누락 시 세션을 익명(ANON)으로 강등한다는 사실을 브라우저 DevTools 패킷 캡처로 특정했다. 단순히 MYSAPSSO2만 전달하던 방식에서 WAF 쿠키를 함께 보존하도록 수정했다.

LMS Canvas의 5-hop SSO 체인에서는 수동 쿠키 병합 방식이 서브도메인 간 쿠키를 오염시키는 문제가 있었다. 요청마다 격리된 java.net.CookieManager를 장착한 HttpClient를 생성해 브라우저 수준의 도메인별 쿠키 격리를 구현했다.

Single-flight 캐시

SAP WebDynpro는 학기별 페이지를 이전 버튼으로 하나씩 이동해야 하는 stateful 구조다. 1시간 TTL 캐시를 두되, cold start 시 동시에 몰리는 중복 요청을 첫 요청만 upstream에 보내고 나머지는 결과를 공유하는 single-flight 패턴으로 처리했다.

Spring AI reflection workaround

McpServer.SyncSpecification.toolspackage-private final이라 tool annotation(readOnlyHint, destructiveHint) 주입 공개 API가 없었다. @Primary McpSyncServerCustomizer로 auto-configure 빈을 교체하고 reflection으로 tool list를 재구성했다. Spring AI가 공개 API를 열면 제거할 임시 bridge다.


보안 원칙

  • 비밀 값 비로깅: 비밀번호, 세션 쿠키, JWT, API 키는 로그에 기록하지 않는다.
  • 인증 경계 분리: 좌석 현황처럼 집계 데이터라도 캐시 키에 인증 여부를 포함해 익명 호출자가 인증 결과를 받지 못하게 한다.
  • 읽기 전용 MCP: 20개 도구에 readOnlyHint=true, logout_*destructiveHint=true를 표시한다.
  • Write 도구 설계 원칙: 예약·취소 같은 상태 변경 도구는 prepare_* + confirm_action 2단계 확인 패턴으로만 구현한다. (ADR 0015)

기술 스택

분류기술
언어 / 프레임워크Java 21 + Kotlin 2.3, Spring Boot 4.0
MCP 구현Spring AI 1.1 (Streamable HTTP)
크롤링Jsoup 1.22
u-SAINT 연동rusaint — JNA 5.18로 Rust 라이브러리를 JVM에 연결
인증JJWT 0.13 (HS256), AES-256-GCM
테스트JUnit 5, MockWebServer, WireMock 3
인프라Oracle Cloud ARM64 · k3s · Traefik · ArgoCD · Helm · GHCR
DBPostgreSQL 17 + Flyway

로컬 개발

실행 (mock 모드 — 외부 네트워크 불필요)

./gradlew bootRun
# http://localhost:8080

MCP Inspector로 도구 확인:

npx @modelcontextprotocol/inspector
# Transport: Streamable HTTP  URL: http://localhost:8080/mcp

실행 (실제 데이터)

cp .env.example .env
SSUAI_CONNECTOR_MEAL=real ./gradlew bootRun

검증

./gradlew test
./gradlew build

환경 변수

주요 변수는 .env.example 참조. 실제 값은 저장소에 커밋하지 않는다.

변수설명
SSUAI_JWT_SECRETHS256 서명 키 (32바이트 이상). 미설정 시 재시작마다 갱신
SSUAI_CREDENTIAL_ENCRYPTION_KEYAES-GCM 세션 암호화 키
SSUAI_CONNECTOR_MEALmock (기본) 또는 real
SSUAI_OPENROUTER_API_KEY, ...LLM 프로바이더 키 (각 선택적)

문서


관련 프로젝트

ssuAI — 이 서버를 소비하는 Next.js 웹 클라이언트


라이선스

MIT — LICENSE

Server Config

{
  "mcpServers": {
    "ssuMCP": {
      "url": "https://ssumcp.duckdns.org/mcp"
    }
  }
}
Project Info
Created At
2 months ago
Updated At
2 months ago
Author Name
hoeongj
Star
-
Language
-
License
-
Category
Tags

Recommend Servers

View All
Tavily Mcp
@tavily-ai

JavaScript
a year ago