Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions api/handler/api/common_service.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 54 additions & 0 deletions api/handler/api/common_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package api

import (
"context"
"strings"
"testing"

"github.com/bytedance/mockey"
Expand Down Expand Up @@ -591,3 +592,56 @@ func TestGetTermsList(t *testing.T) {
})
}
}

func TestGetSignedLocationApiUrl(t *testing.T) {
type testCase struct {
name string
mockSignedURL string
mockHeaders map[string]string
mockErr error
body *ut.Body
expectContains string
}

validBody := `{"location":"119.262647,26.106131"}`

testCases := []testCase{
{
name: "success",
mockSignedURL: "https://restapi.amap.com/v3/place/around?key=xxx&scode=abc",
mockHeaders: map[string]string{"User-Agent": "AMAP_Location_SDK_Android"},
body: &ut.Body{Body: strings.NewReader(validBody), Len: len(validBody)},
expectContains: `{"code":"10000","message":"Success","data":`,
},
{
name: "rpc error",
mockErr: errno.InternalServiceError,
body: &ut.Body{Body: strings.NewReader(validBody), Len: len(validBody)},
expectContains: `{"code":"50001","message":"内部服务错误"`,
},
{
name: "bind error",
body: nil,
expectContains: `{"code":"20001","message":"参数错误`,
},
}

router := route.NewEngine(&config.Options{})
router.POST("/api/v1/common/signed-location-api-url", GetSignedLocationApiUrl)

defer mockey.UnPatchAll()
for _, tc := range testCases {
mockey.PatchConvey(tc.name, t, func() {
if tc.name != "bind error" {
mockey.Mock(rpc.GetSignedLocationApiUrlRPC).To(func(ctx context.Context, req *common.GetSignedLocationApiUrlRequest) (string, map[string]string, error) {
return tc.mockSignedURL, tc.mockHeaders, tc.mockErr
}).Build()
}

res := ut.PerformRequest(router, consts.MethodPost, "/api/v1/common/signed-location-api-url", tc.body,
ut.Header{Key: "Content-Type", Value: "application/json"})
assert.Equal(t, consts.StatusOK, res.Result().StatusCode())
assert.Contains(t, string(res.Result().Body()), tc.expectContains)
})
}
}
24 changes: 16 additions & 8 deletions api/mcp/academic.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,40 +29,48 @@ import (

func GetScoresTool() mcpgoserver.ServerTool {
return mcpgoserver.ServerTool{
Tool: mcp.NewTool("get_scores",
Tool: mcp.NewTool(
"get_scores",
mcp.WithDescription(
"Fetch the user's score records for a given academic term. "+
"Use this when the user asks to view grades/scores for a specific term. "+
"Returns the score list for the given term."),
"Returns the score list for the given term.",
),
mcp.WithString("user_id",
mcp.Required(),
mcp.Description(
"user_id data comes from the login method response (user_id field).")),
"user_id data comes from the login method response (user_id field).",
)),
mcp.WithString("user_cookies",
mcp.Required(),
mcp.Description(
"user_cookies data comes from the login method response (user_cookies field).")),
"user_cookies data comes from the login method response (user_cookies field).",
)),
),
Handler: handleGetScores,
}
}

func GetGPATool() mcpgoserver.ServerTool {
return mcpgoserver.ServerTool{
Tool: mcp.NewTool("get_gpa",
Tool: mcp.NewTool(
"get_gpa",
mcp.WithDescription(
"Fetch the user's GPA (Grade Point Average) information for a given academic term. "+
"Use this when the user asks to view GPA or grade point information. "+
"Returns the GPA data including overall and term-specific GPA. "+
"Note: This feature is only available for undergraduate students. Graduate students should use get_scores instead."),
"Note: This feature is only available for undergraduate students. Graduate students should use get_scores instead.",
),
mcp.WithString("user_id",
mcp.Required(),
mcp.Description(
"user_id data comes from the login method response (user_id field).")),
"user_id data comes from the login method response (user_id field).",
)),
mcp.WithString("user_cookies",
mcp.Required(),
mcp.Description(
"user_cookies data comes from the login method response (user_cookies field).")),
"user_cookies data comes from the login method response (user_cookies field).",
)),
),
Handler: handleGetGPA,
}
Expand Down
21 changes: 14 additions & 7 deletions api/mcp/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,25 @@ type IdentifierData struct {

func LoginTool() mcpgoserver.ServerTool {
return mcpgoserver.ServerTool{
Tool: mcp.NewTool("login",
Tool: mcp.NewTool(
"login",
mcp.WithDescription("Use this tool when the user wants to log into the educational system. "+
"Call this when: user mentions logging in, needs to authenticate, "+
"or when other tools fail due to no active session. "+
"If JWCH_STUDENT_ID and JWCH_PASSWORD environment variables are set, "+
"this happens automatically on startup. Returns success message on successful login."),
mcp.WithString("student_id",
mcp.WithString(
"student_id",
mcp.Required(),
mcp.Description("Student ID for authentication (optional if FZUHELPER_STUDENT_ID env var is set)"),
),
mcp.WithString("password",
mcp.WithString(
"password",
mcp.Required(),
mcp.Description("Password for authentication (optional if FZUHELPER_STUDENT_PASSWORD env var is set)"),
),
mcp.WithString("student_type",
mcp.WithString(
"student_type",
mcp.Description("StudentType for authentication. Defaults to \"1\" (Undergraduate student). "+
"Set \"2\" for Postgraduate(optional if FZUHELPER_STUDENT_TYPE env var is set)"),
),
Expand All @@ -62,15 +66,18 @@ func LoginTool() mcpgoserver.ServerTool {

func CheckSessionTool() mcpgoserver.ServerTool {
return mcpgoserver.ServerTool{
Tool: mcp.NewTool("check_session",
Tool: mcp.NewTool(
"check_session",
mcp.WithDescription("Use this tool to verify if the current login session is still valid. "+
"Call this when: user asks about connection status, before performing operations after a long idle "+
"period, or to troubleshoot authentication issues. Returns session validity status."),
mcp.WithString("user_id",
mcp.WithString(
"user_id",
mcp.Required(),
mcp.Description("user_id data comes from login method response, (user_cookies field)"),
),
mcp.WithString("user_cookies",
mcp.WithString(
"user_cookies",
mcp.Required(),
mcp.Description("user_cookies data comes from login method response, (user_cookies field)"),
),
Expand Down
12 changes: 8 additions & 4 deletions api/mcp/calendar.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,24 @@ import (

func GetCalendarTool() mcpgoserver.ServerTool {
return mcpgoserver.ServerTool{
Tool: mcp.NewTool("get_calendar",
Tool: mcp.NewTool(
"get_calendar",
mcp.WithDescription(
"Fetch the academic calendar in ICS format for the current term. "+
"Use this when the user asks to view the academic calendar, course schedule in calendar format, "+
"or wants to import course schedule into calendar applications. "+
"Returns the calendar data in ICS format (base64 encoded)."),
"Returns the calendar data in ICS format (base64 encoded).",
),
mcp.WithString("user_id",
mcp.Required(),
mcp.Description(
"user_id data comes from the login method response (user_id field).")),
"user_id data comes from the login method response (user_id field).",
)),
mcp.WithString("user_cookies",
mcp.Required(),
mcp.Description(
"user_cookies data comes from the login method response (user_cookies field).")),
"user_cookies data comes from the login method response (user_cookies field).",
)),
),
Handler: handleGetCalendar,
}
Expand Down
15 changes: 10 additions & 5 deletions api/mcp/classroom.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,29 @@ import (

func GetExamRoomTool() mcpgoserver.ServerTool {
return mcpgoserver.ServerTool{
Tool: mcp.NewTool("get_exam_room",
Tool: mcp.NewTool(
"get_exam_room",
mcp.WithDescription(
"Fetch the user's exam room information and schedule. "+
"Use this when the user asks to view exam locations, exam schedule, or examination room details. "+
"Returns exam room information including location, time, seat number, and course details."),
"Returns exam room information including location, time, seat number, and course details.",
),
mcp.WithString("user_id",
mcp.Required(),
mcp.Description(
"user_id data comes from the login method response (user_id field).")),
"user_id data comes from the login method response (user_id field).",
)),
mcp.WithString("user_cookies",
mcp.Required(),
mcp.Description(
"user_cookies data comes from the login method response (user_cookies field).")),
"user_cookies data comes from the login method response (user_cookies field).",
)),
mcp.WithString("term",
mcp.Description(
"Academic term code in the form yyyymm. "+
"Examples: 202401 means 2024 Autumn term, 202402 means 2025 Spring term. "+
"Optional: defaults to current term")),
"Optional: defaults to current term",
)),
),
Handler: handleGetExamRoom,
}
Expand Down
21 changes: 14 additions & 7 deletions api/mcp/course.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,38 +32,45 @@ import (

func GetCourseTool() mcpgoserver.ServerTool {
return mcpgoserver.ServerTool{
Tool: mcp.NewTool("get_course_list",
Tool: mcp.NewTool(
"get_course_list",
mcp.WithDescription(
"Fetch the user's course list for a given academic term. "+
"ALWAYS call get_date first to determine the current term and week if the user asks about today's timetable or current-week schedule. "+
"Do NOT guess dates or terms—use get_date to retrieve them. "+
"Use this when the user asks to view courses/timetable for a specific term or the current day/week. "+
"Returns the course list for the given term."),
"Returns the course list for the given term.",
),
mcp.WithString("term",
mcp.Description(
"Academic term code in the form yyyymm. "+
"Examples: 202401 means 2024 Autumn term, 202402 means 2025 Spring term. "+
"If omitted, the tool will use get_date-derived current term automatically.")),
"If omitted, the tool will use get_date-derived current term automatically.",
)),
mcp.WithString("user_id",
mcp.Required(),
mcp.Description(
"user_id data comes from the login method response (user_id field).")),
"user_id data comes from the login method response (user_id field).",
)),
mcp.WithString("user_cookies",
mcp.Required(),
mcp.Description(
"user_cookies data comes from the login method response (user_cookies field).")),
"user_cookies data comes from the login method response (user_cookies field).",
)),
),
Handler: handleGetCourse,
}
}

func GetDateTool() mcpgoserver.ServerTool {
return mcpgoserver.ServerTool{
Tool: mcp.NewTool("get_date",
Tool: mcp.NewTool(
"get_date",
mcp.WithDescription(
"Get the current year, academic term, week number, calendar date, and weekday. "+
"You MUST call this before attempting to fetch today's timetable or current-week schedule. "+
"Returns: year, term, week, date (YYYY-MM-DD), term_formatted, weekday_name (e.g., Monday), weekday_number (1-7)."),
"Returns: year, term, week, date (YYYY-MM-DD), term_formatted, weekday_name (e.g., Monday), weekday_number (1-7).",
),
),
Handler: handleGetDate,
}
Expand Down
3 changes: 2 additions & 1 deletion api/mcp/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ func CreateMCPProxy() *Proxy {
// mcpgoserver.WithLogger(logger.Logger) // TODO: 引入我们自己的 logger
)

server.AddTools(LoginTool(), CheckSessionTool(),
server.AddTools(
LoginTool(), CheckSessionTool(),
GetCourseTool(),
GetDateTool(),
GetScoresTool(),
Expand Down
12 changes: 8 additions & 4 deletions api/mcp/notice.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,21 @@ const defaultNoticePageSize = 10

func GetNoticesTool() mcpgoserver.ServerTool {
return mcpgoserver.ServerTool{
Tool: mcp.NewTool("get_notices",
Tool: mcp.NewTool(
"get_notices",
mcp.WithDescription(
"Fetch notices and announcements from the educational administration office. "+
"Use this when the user asks to view official notices, announcements, or news from the academic affairs office. "+
"Returns a list of notices with pagination support. No login required."),
"Returns a list of notices with pagination support. No login required.",
),
mcp.WithNumber("page",
mcp.Description(
"Page number for pagination. Optional: defaults to 1")),
"Page number for pagination. Optional: defaults to 1",
)),
mcp.WithNumber("page_size",
mcp.Description(
"Number of notices per page. Optional: defaults to 10")),
"Number of notices per page. Optional: defaults to 10",
)),
),
Handler: handleGetNotices,
}
Expand Down
12 changes: 8 additions & 4 deletions api/mcp/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,23 @@ import (

func GetUserInfoTool() mcpgoserver.ServerTool {
return mcpgoserver.ServerTool{
Tool: mcp.NewTool("get_user_info",
Tool: mcp.NewTool(
"get_user_info",
mcp.WithDescription(
"Fetch the user's personal information from the educational system. "+
"Use this when the user asks to view their profile, personal details, or student information. "+
"Returns user information including name, student ID, major, and other personal details."),
"Returns user information including name, student ID, major, and other personal details.",
),
mcp.WithString("user_id",
mcp.Required(),
mcp.Description(
"user_id data comes from the login method response (user_id field).")),
"user_id data comes from the login method response (user_id field).",
)),
mcp.WithString("user_cookies",
mcp.Required(),
mcp.Description(
"user_cookies data comes from the login method response (user_cookies field).")),
"user_cookies data comes from the login method response (user_cookies field).",
)),
),
Handler: handleGetUserInfo,
}
Expand Down
Loading
Loading