iflow-mcp_sdi2200262-eclass-mcp-server 0.1.0__py3-none-any.whl
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.
- eclass_mcp_server/__init__.py +12 -0
- eclass_mcp_server/authentication.py +197 -0
- eclass_mcp_server/course_management.py +82 -0
- eclass_mcp_server/html_parsing.py +163 -0
- eclass_mcp_server/server.py +237 -0
- eclass_mcp_server/test/__init__.py +1 -0
- eclass_mcp_server/test/run_all_tests.py +185 -0
- eclass_mcp_server/test/test_courses.py +92 -0
- eclass_mcp_server/test/test_login.py +87 -0
- iflow_mcp_sdi2200262_eclass_mcp_server-0.1.0.dist-info/METADATA +158 -0
- iflow_mcp_sdi2200262_eclass_mcp_server-0.1.0.dist-info/RECORD +14 -0
- iflow_mcp_sdi2200262_eclass_mcp_server-0.1.0.dist-info/WHEEL +4 -0
- iflow_mcp_sdi2200262_eclass_mcp_server-0.1.0.dist-info/entry_points.txt +2 -0
- iflow_mcp_sdi2200262_eclass_mcp_server-0.1.0.dist-info/licenses/LICENSE +637 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Run all tests for the eClass MCP Server."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
|
|
10
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
|
|
11
|
+
from eclass_mcp_server.server import (
|
|
12
|
+
handle_authstatus,
|
|
13
|
+
handle_get_courses,
|
|
14
|
+
handle_login,
|
|
15
|
+
handle_logout,
|
|
16
|
+
session_state,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
logging.basicConfig(
|
|
20
|
+
level=logging.INFO,
|
|
21
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
22
|
+
)
|
|
23
|
+
logger = logging.getLogger('run_all_tests')
|
|
24
|
+
|
|
25
|
+
test_results = {
|
|
26
|
+
'login': False,
|
|
27
|
+
'authstatus_after_login': False,
|
|
28
|
+
'get_courses': False,
|
|
29
|
+
'logout': False,
|
|
30
|
+
'authstatus_after_logout': False
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def test_login() -> bool:
|
|
35
|
+
"""Test the login functionality."""
|
|
36
|
+
print("\n=== Testing Login ===")
|
|
37
|
+
|
|
38
|
+
load_dotenv()
|
|
39
|
+
|
|
40
|
+
username = os.getenv('ECLASS_USERNAME')
|
|
41
|
+
password = os.getenv('ECLASS_PASSWORD')
|
|
42
|
+
|
|
43
|
+
if not username or not password:
|
|
44
|
+
print("ERROR: ECLASS_USERNAME and ECLASS_PASSWORD must be set in the .env file.")
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
print("Attempting login...")
|
|
48
|
+
response = await handle_login()
|
|
49
|
+
|
|
50
|
+
if not response:
|
|
51
|
+
print("ERROR: Empty response from login handler.")
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
text_content = response[0]
|
|
55
|
+
print(f"Login response: {text_content.text}")
|
|
56
|
+
|
|
57
|
+
if "Login successful" in text_content.text:
|
|
58
|
+
print("Login test SUCCESS!")
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
print("Login test FAILED!")
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async def test_authstatus() -> bool:
|
|
66
|
+
"""Test the authentication status functionality."""
|
|
67
|
+
print("\n=== Testing Auth Status ===")
|
|
68
|
+
|
|
69
|
+
print("Checking authentication status...")
|
|
70
|
+
response = await handle_authstatus()
|
|
71
|
+
|
|
72
|
+
if not response:
|
|
73
|
+
print("ERROR: Empty response from authstatus handler.")
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
text_content = response[0]
|
|
77
|
+
print(f"Auth status response: {text_content.text}")
|
|
78
|
+
|
|
79
|
+
if "Status: Logged in as" in text_content.text:
|
|
80
|
+
print("Auth status (logged in) test SUCCESS!")
|
|
81
|
+
return True
|
|
82
|
+
if "Status: Not logged in" in text_content.text and not session_state.logged_in:
|
|
83
|
+
print("Auth status (not logged in) test SUCCESS!")
|
|
84
|
+
return True
|
|
85
|
+
if "Status: Session expired" in text_content.text and not session_state.is_session_valid():
|
|
86
|
+
print("Auth status (session expired) test SUCCESS!")
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
print("Auth status test FAILED!")
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
async def test_get_courses() -> bool:
|
|
94
|
+
"""Test the course retrieval functionality."""
|
|
95
|
+
print("\n=== Testing Course Retrieval ===")
|
|
96
|
+
|
|
97
|
+
if not session_state.logged_in:
|
|
98
|
+
print("Not logged in, skipping course retrieval test.")
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
print("Retrieving courses...")
|
|
102
|
+
response = await handle_get_courses()
|
|
103
|
+
|
|
104
|
+
if not response:
|
|
105
|
+
print("ERROR: Empty response from get_courses handler.")
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
text_content = response[0]
|
|
109
|
+
print(f"Course retrieval response: {text_content.text}")
|
|
110
|
+
|
|
111
|
+
if "Error" in text_content.text:
|
|
112
|
+
print("Course retrieval test FAILED!")
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
if "No courses found" in text_content.text:
|
|
116
|
+
print("No courses found, but API worked correctly.")
|
|
117
|
+
print("Course retrieval test SUCCESS!")
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
if "Found" in text_content.text and "courses" in text_content.text:
|
|
121
|
+
if "URL:" in text_content.text:
|
|
122
|
+
print("Course retrieval test SUCCESS!")
|
|
123
|
+
return True
|
|
124
|
+
print("Course retrieval test FAILED! Output format is incorrect (missing URLs).")
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
print("Unexpected response from course retrieval.")
|
|
128
|
+
print("Course retrieval test FAILED!")
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
async def test_logout() -> bool:
|
|
133
|
+
"""Test the logout functionality."""
|
|
134
|
+
print("\n=== Testing Logout ===")
|
|
135
|
+
|
|
136
|
+
if not session_state.logged_in:
|
|
137
|
+
print("Not logged in, skipping logout test.")
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
print("Attempting logout...")
|
|
141
|
+
response = await handle_logout()
|
|
142
|
+
|
|
143
|
+
if not response:
|
|
144
|
+
print("ERROR: Empty response from logout handler.")
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
text_content = response[0]
|
|
148
|
+
print(f"Logout response: {text_content.text}")
|
|
149
|
+
|
|
150
|
+
if "Successfully logged out" in text_content.text:
|
|
151
|
+
print("Logout test SUCCESS!")
|
|
152
|
+
return True
|
|
153
|
+
|
|
154
|
+
print("Logout test FAILED!")
|
|
155
|
+
return False
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
async def run_all_tests() -> None:
|
|
159
|
+
"""Run all tests in sequence."""
|
|
160
|
+
print("\n====== Starting All eClass MCP Server Tests ======\n")
|
|
161
|
+
|
|
162
|
+
test_results['login'] = await test_login()
|
|
163
|
+
|
|
164
|
+
if test_results['login']:
|
|
165
|
+
test_results['authstatus_after_login'] = await test_authstatus()
|
|
166
|
+
test_results['get_courses'] = await test_get_courses()
|
|
167
|
+
test_results['logout'] = await test_logout()
|
|
168
|
+
|
|
169
|
+
test_results['authstatus_after_logout'] = await test_authstatus()
|
|
170
|
+
|
|
171
|
+
print("\n====== Test Results Summary ======")
|
|
172
|
+
for test_name, result in test_results.items():
|
|
173
|
+
status = "PASSED" if result else "FAILED"
|
|
174
|
+
print(f"{test_name}: {status}")
|
|
175
|
+
|
|
176
|
+
if all(test_results.values()):
|
|
177
|
+
print("\nAll tests PASSED!")
|
|
178
|
+
else:
|
|
179
|
+
print("\nSome tests FAILED!")
|
|
180
|
+
|
|
181
|
+
print("\n====== Completed All eClass MCP Server Tests ======\n")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
if __name__ == "__main__":
|
|
185
|
+
asyncio.run(run_all_tests())
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Test course retrieval functionality for eClass MCP Server."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
|
|
10
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
|
|
11
|
+
from eclass_mcp_server.server import handle_get_courses, handle_login, session_state
|
|
12
|
+
|
|
13
|
+
logging.basicConfig(
|
|
14
|
+
level=logging.INFO,
|
|
15
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
16
|
+
)
|
|
17
|
+
logger = logging.getLogger('test_courses')
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def ensure_login() -> bool:
|
|
21
|
+
"""Ensure logged in before testing course retrieval."""
|
|
22
|
+
if session_state.logged_in and session_state.is_session_valid():
|
|
23
|
+
print("Already logged in, proceeding with tests.")
|
|
24
|
+
return True
|
|
25
|
+
|
|
26
|
+
print("Not logged in, attempting login...")
|
|
27
|
+
|
|
28
|
+
load_dotenv()
|
|
29
|
+
|
|
30
|
+
username = os.getenv('ECLASS_USERNAME')
|
|
31
|
+
password = os.getenv('ECLASS_PASSWORD')
|
|
32
|
+
|
|
33
|
+
if not username or not password:
|
|
34
|
+
print("ERROR: ECLASS_USERNAME and ECLASS_PASSWORD must be set in the .env file.")
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
response = await handle_login()
|
|
38
|
+
|
|
39
|
+
if response and "Login successful" in response[0].text:
|
|
40
|
+
print("Login successful, proceeding with tests.")
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
print("Login failed, cannot proceed with course tests.")
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def test_get_courses() -> bool:
|
|
48
|
+
"""Test the course retrieval functionality."""
|
|
49
|
+
print("Testing course retrieval functionality...")
|
|
50
|
+
|
|
51
|
+
response = await handle_get_courses()
|
|
52
|
+
|
|
53
|
+
if not response:
|
|
54
|
+
print("ERROR: Empty response from get_courses handler.")
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
text_content = response[0]
|
|
58
|
+
print(f"Course retrieval response: {text_content.text}")
|
|
59
|
+
|
|
60
|
+
if "Error" in text_content.text:
|
|
61
|
+
print("Course retrieval test FAILED!")
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
if "No courses found" in text_content.text:
|
|
65
|
+
print("No courses found, but API worked correctly.")
|
|
66
|
+
print("Course retrieval test SUCCESS!")
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
if "Found" in text_content.text and "courses" in text_content.text:
|
|
70
|
+
if "URL:" in text_content.text:
|
|
71
|
+
print("Course retrieval test SUCCESS!")
|
|
72
|
+
return True
|
|
73
|
+
print("Course retrieval test FAILED! Output format is incorrect (missing URLs).")
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
print("Unexpected response from course retrieval.")
|
|
77
|
+
print("Course retrieval test FAILED!")
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async def main() -> None:
|
|
82
|
+
"""Run course tests."""
|
|
83
|
+
print("=== Starting eClass MCP Server Course Tests ===")
|
|
84
|
+
|
|
85
|
+
if await ensure_login():
|
|
86
|
+
await test_get_courses()
|
|
87
|
+
|
|
88
|
+
print("=== Completed eClass MCP Server Course Tests ===")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Test login functionality for eClass MCP Server."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
|
|
10
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
|
|
11
|
+
from eclass_mcp_server.server import handle_login, handle_logout, session_state
|
|
12
|
+
|
|
13
|
+
logging.basicConfig(
|
|
14
|
+
level=logging.INFO,
|
|
15
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
16
|
+
)
|
|
17
|
+
logger = logging.getLogger('test_login')
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def test_login() -> bool:
|
|
21
|
+
"""Test the login functionality."""
|
|
22
|
+
print("Testing login functionality...")
|
|
23
|
+
|
|
24
|
+
load_dotenv()
|
|
25
|
+
|
|
26
|
+
username = os.getenv('ECLASS_USERNAME')
|
|
27
|
+
password = os.getenv('ECLASS_PASSWORD')
|
|
28
|
+
|
|
29
|
+
if not username or not password:
|
|
30
|
+
print("ERROR: ECLASS_USERNAME and ECLASS_PASSWORD must be set in the .env file.")
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
response = await handle_login()
|
|
34
|
+
|
|
35
|
+
if not response:
|
|
36
|
+
print("ERROR: Empty response from login handler.")
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
text_content = response[0]
|
|
40
|
+
print(f"Login response: {text_content.text}")
|
|
41
|
+
|
|
42
|
+
if "Login successful" in text_content.text:
|
|
43
|
+
print("Login test SUCCESS!")
|
|
44
|
+
return True
|
|
45
|
+
|
|
46
|
+
print("Login test FAILED!")
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def test_logout() -> bool:
|
|
51
|
+
"""Test the logout functionality."""
|
|
52
|
+
if not session_state.logged_in:
|
|
53
|
+
print("Not logged in, skipping logout test.")
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
print("Testing logout functionality...")
|
|
57
|
+
|
|
58
|
+
response = await handle_logout()
|
|
59
|
+
|
|
60
|
+
if not response:
|
|
61
|
+
print("ERROR: Empty response from logout handler.")
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
text_content = response[0]
|
|
65
|
+
print(f"Logout response: {text_content.text}")
|
|
66
|
+
|
|
67
|
+
if "Successfully logged out" in text_content.text:
|
|
68
|
+
print("Logout test SUCCESS!")
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
print("Logout test FAILED!")
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
async def main() -> None:
|
|
76
|
+
"""Run login tests."""
|
|
77
|
+
print("=== Starting eClass MCP Server Login Tests ===")
|
|
78
|
+
|
|
79
|
+
login_success = await test_login()
|
|
80
|
+
if login_success:
|
|
81
|
+
await test_logout()
|
|
82
|
+
|
|
83
|
+
print("=== Completed eClass MCP Server Login Tests ===")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iflow-mcp_sdi2200262-eclass-mcp-server
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An MCP server for the Open eClass platform by GUnet
|
|
5
|
+
Author-email: CobuterMan <haidemenoss@gmail.com>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: beautifulsoup4>=4.12.3
|
|
9
|
+
Requires-Dist: mcp>=1.24.0
|
|
10
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
11
|
+
Requires-Dist: requests>=2.32.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# eClass MCP Server
|
|
15
|
+
|
|
16
|
+
An MCP server for interacting with the [Open eClass](https://github.com/gunet/openeclass) platform, with support for UoA's SSO authentication.
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="https://github.com/modelcontextprotocol/python-sdk"><img src="https://img.shields.io/badge/MCP-Protocol-blue" alt="MCP Protocol"></a>
|
|
20
|
+
<a href="https://www.gnu.org/licenses/gpl-3.0"><img src="https://img.shields.io/badge/License-GPLv3-blue.svg" alt="License: GPL v3"></a>
|
|
21
|
+
<img src="https://img.shields.io/badge/Python-3.10%2B-blue" alt="Python: 3.10+">
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<img src="assets/example.png" alt="Example Usage" width="600">
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- **SSO Authentication**: Log in through UoA's CAS SSO system
|
|
31
|
+
- **Course Retrieval**: Get list of enrolled courses
|
|
32
|
+
- **Session Management**: Persistent sessions between tool calls
|
|
33
|
+
- **Status Checking**: Verify authentication status
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### Prerequisites
|
|
38
|
+
|
|
39
|
+
- Python 3.10+
|
|
40
|
+
- [uv](https://github.com/astral-sh/uv) (recommended) or pip
|
|
41
|
+
|
|
42
|
+
### Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/sdi2200262/eclass-mcp-server.git
|
|
46
|
+
cd eclass-mcp-server
|
|
47
|
+
uv sync --dev --all-extras
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Configuration
|
|
51
|
+
|
|
52
|
+
Create a `.env` file (or copy `example.env`):
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
ECLASS_USERNAME=your_username
|
|
56
|
+
ECLASS_PASSWORD=your_password
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Optional settings:
|
|
60
|
+
```bash
|
|
61
|
+
ECLASS_URL=https://eclass.uoa.gr # Default
|
|
62
|
+
ECLASS_SSO_DOMAIN=sso.uoa.gr # Default
|
|
63
|
+
ECLASS_SSO_PROTOCOL=https # Default
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Running
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Using the entry point script
|
|
70
|
+
python run_server.py
|
|
71
|
+
|
|
72
|
+
# Or as a module
|
|
73
|
+
python -m src.eclass_mcp_server.server
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## MCP Client Configuration
|
|
77
|
+
|
|
78
|
+
To use this MCP server with Claude Desktop, VS Code, Cursor, or any MCP-compatible client, configure your client to run:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
python3 /absolute/path/to/eclass-mcp-server/run_server.py
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Set the following environment variables in your client's MCP configuration:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"env": {
|
|
89
|
+
"ECLASS_USERNAME": "your_username",
|
|
90
|
+
"ECLASS_PASSWORD": "your_password"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Optional environment variables:**
|
|
96
|
+
- `ECLASS_URL` - OpenEclass instance URL (default: `https://eclass.uoa.gr`)
|
|
97
|
+
- `ECLASS_SSO_DOMAIN` - SSO domain (default: `sso.uoa.gr`)
|
|
98
|
+
- `ECLASS_SSO_PROTOCOL` - SSO protocol (default: `https`)
|
|
99
|
+
|
|
100
|
+
Refer to your specific client's documentation for how to add MCP servers to your configuration.
|
|
101
|
+
|
|
102
|
+
## Available Tools
|
|
103
|
+
|
|
104
|
+
| Tool | Description |
|
|
105
|
+
|------|-------------|
|
|
106
|
+
| `login` | Authenticate using credentials from `.env` |
|
|
107
|
+
| `get_courses` | Retrieve enrolled courses (requires login) |
|
|
108
|
+
| `logout` | End the current session |
|
|
109
|
+
| `authstatus` | Check authentication status |
|
|
110
|
+
|
|
111
|
+
All tools use a dummy `random_string` parameter (MCP protocol requirement).
|
|
112
|
+
|
|
113
|
+
## Standalone Client
|
|
114
|
+
|
|
115
|
+
For non-MCP usage, a standalone client is included:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
python eclass_client.py
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
This demonstrates the core functionality without MCP integration. See [docs/architecture.md](docs/architecture.md) for details.
|
|
122
|
+
|
|
123
|
+
## Documentation
|
|
124
|
+
|
|
125
|
+
- [Architecture](docs/architecture.md) - System design and authentication flow
|
|
126
|
+
- [Wire Protocol](docs/reference/wire-protocol.md) - JSON-RPC message formats
|
|
127
|
+
- [Tools Reference](docs/reference/tools-reference.md) - Detailed tool documentation
|
|
128
|
+
|
|
129
|
+
## Project Structure
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
eclass-mcp-server/
|
|
133
|
+
├── run_server.py # Entry point
|
|
134
|
+
├── eclass_client.py # Standalone client (non-MCP)
|
|
135
|
+
├── src/eclass_mcp_server/ # Main package
|
|
136
|
+
│ ├── server.py # MCP server and tool handlers
|
|
137
|
+
│ ├── authentication.py # SSO authentication
|
|
138
|
+
│ ├── course_management.py # Course operations
|
|
139
|
+
│ ├── html_parsing.py # HTML parsing utilities
|
|
140
|
+
│ └── test/ # Test scripts
|
|
141
|
+
└── docs/ # Documentation
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Security
|
|
145
|
+
|
|
146
|
+
- Credentials are stored locally in `.env` only
|
|
147
|
+
- Never passed as tool parameters (preventing AI provider exposure)
|
|
148
|
+
- Sessions maintained in-memory only
|
|
149
|
+
- No cloud services or remote storage
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
[GNU GPL v3.0](LICENSE) - This ensures transparency in credential handling.
|
|
154
|
+
|
|
155
|
+
## Acknowledgments
|
|
156
|
+
|
|
157
|
+
- [GUnet](https://github.com/gunet) for the [Open eClass platform](https://github.com/gunet/openeclass)
|
|
158
|
+
- This project is an independent interface, not affiliated with GUnet
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
eclass_mcp_server/__init__.py,sha256=dm04z7pStZAy-J_t3dpDxnoNzD0x0RV87GRqbkb3hPw,233
|
|
2
|
+
eclass_mcp_server/authentication.py,sha256=8Ltuba4qG4yiblwK44OboZ8-6R4--E3HpRQ79KYxj_M,6903
|
|
3
|
+
eclass_mcp_server/course_management.py,sha256=8eKkB55O3rJ58IKeycFQACXa0UkP9mh7UIOImMmpfTc,2517
|
|
4
|
+
eclass_mcp_server/html_parsing.py,sha256=ztmiVWqG3Dz_UVhZcBOaGexrJNPPFRY3kSbvNBDu_UY,5303
|
|
5
|
+
eclass_mcp_server/server.py,sha256=J_euIPOCgo5yml8EGRB4-uHXtnoFl7zzyVZOx7frOXc,8117
|
|
6
|
+
eclass_mcp_server/test/__init__.py,sha256=6CWwwhfzisuSlkknsm8jKnhCPTkw68816RmL9EbiYUk,42
|
|
7
|
+
eclass_mcp_server/test/run_all_tests.py,sha256=mT_fqSjhgEUmb1MAxPEi-ZyNwYTErxcRgRGVvM1C8_M,5354
|
|
8
|
+
eclass_mcp_server/test/test_courses.py,sha256=AYv6Tt_ZE1XRbX73L8rYC53rTcNJcoR4b_5Mk7diQOc,2734
|
|
9
|
+
eclass_mcp_server/test/test_login.py,sha256=3wRstWBLe7c5HInDk4wrz3jDBMXFu5hpbz3UAzwP2wo,2218
|
|
10
|
+
iflow_mcp_sdi2200262_eclass_mcp_server-0.1.0.dist-info/METADATA,sha256=iVXJ5fd1h19_iGOWMXOUTs_p6bBJH8C2-SNrxsEgHBA,4694
|
|
11
|
+
iflow_mcp_sdi2200262_eclass_mcp_server-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
12
|
+
iflow_mcp_sdi2200262_eclass_mcp_server-0.1.0.dist-info/entry_points.txt,sha256=FVYwA1FQMTcR3NcwQECbhKwt6DsUd5ZbYte4YYbUf70,82
|
|
13
|
+
iflow_mcp_sdi2200262_eclass_mcp_server-0.1.0.dist-info/licenses/LICENSE,sha256=BOrRwhm7skCATXYP84QJwMnxb2_hEb61cn7XyrRAUQ4,33188
|
|
14
|
+
iflow_mcp_sdi2200262_eclass_mcp_server-0.1.0.dist-info/RECORD,,
|