Spaces:
Sleeping
Sleeping
Meet Patel
commited on
Commit
·
c466cf2
0
Parent(s):
Step 1: Initial TutorX MCP server setup with basic core features
Browse files- .gitignore +10 -0
- .python-version +1 -0
- README.md +55 -0
- docs/mcp.md +0 -0
- docs/prd.md +145 -0
- docs/sdk.md +646 -0
- main.py +132 -0
- pyproject.toml +9 -0
- uv.lock +371 -0
.gitignore
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Python-generated files
|
2 |
+
__pycache__/
|
3 |
+
*.py[oc]
|
4 |
+
build/
|
5 |
+
dist/
|
6 |
+
wheels/
|
7 |
+
*.egg-info
|
8 |
+
|
9 |
+
# Virtual environments
|
10 |
+
.venv
|
.python-version
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
3.12
|
README.md
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# TutorX-MCP Server
|
2 |
+
|
3 |
+
A Model Context Protocol (MCP) server for educational AI tutoring as specified in the PRD.
|
4 |
+
|
5 |
+
## Overview
|
6 |
+
|
7 |
+
TutorX-MCP is an adaptive, multi-modal, and collaborative AI tutoring platform that leverages the Model Context Protocol (MCP) for tool integration and provides APIs for educational features.
|
8 |
+
|
9 |
+
## Features
|
10 |
+
|
11 |
+
- **Adaptive Learning Engine**: Concept graph, skill assessment, and personalized learning paths
|
12 |
+
- **Assessment Suite**: Quiz generation, solution analysis
|
13 |
+
- **Feedback System**: Error pattern analysis and contextual suggestions
|
14 |
+
- **Multi-Modal Interaction**: Text-based Q&A (with planned voice and handwriting recognition)
|
15 |
+
|
16 |
+
## Getting Started
|
17 |
+
|
18 |
+
### Prerequisites
|
19 |
+
|
20 |
+
- Python 3.12 or higher
|
21 |
+
- Dependencies as listed in pyproject.toml
|
22 |
+
|
23 |
+
### Installation
|
24 |
+
|
25 |
+
```bash
|
26 |
+
# Clone the repository
|
27 |
+
git clone https://github.com/yourusername/tutorx-mcp.git
|
28 |
+
cd tutorx-mcp
|
29 |
+
|
30 |
+
# Install dependencies
|
31 |
+
uv install
|
32 |
+
```
|
33 |
+
|
34 |
+
### Running the Server
|
35 |
+
|
36 |
+
```bash
|
37 |
+
python main.py
|
38 |
+
```
|
39 |
+
|
40 |
+
By default, the server will run in development mode and you can access it at http://localhost:8000.
|
41 |
+
|
42 |
+
## MCP Tool Integration
|
43 |
+
|
44 |
+
The server exposes MCP tools for:
|
45 |
+
- Skill assessment
|
46 |
+
- Quiz generation
|
47 |
+
- Error pattern analysis
|
48 |
+
|
49 |
+
And MCP resources for:
|
50 |
+
- Concept graph
|
51 |
+
- Learning paths
|
52 |
+
|
53 |
+
## License
|
54 |
+
|
55 |
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
docs/mcp.md
ADDED
The diff for this file is too large to render.
See raw diff
|
|
docs/prd.md
ADDED
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# TutorX-MCP
|
2 |
+
# Product Requirements Document (PRD)
|
3 |
+
|
4 |
+
**Educational AI Tutor MCP Server**
|
5 |
+
|
6 |
+
## 1. Overview
|
7 |
+
|
8 |
+
**Product Name:**
|
9 |
+
|
10 |
+
Educational AI Tutor MCP Server
|
11 |
+
|
12 |
+
**Purpose:**
|
13 |
+
|
14 |
+
To provide an adaptive, multi-modal, and collaborative AI tutoring platform accessible via browser, leveraging Model Context Protocol (MCP) for tool integration and Gradio for user-friendly interfaces.
|
15 |
+
|
16 |
+
**Target Users:**
|
17 |
+
|
18 |
+
- **Students:** K-12, higher education, lifelong learners
|
19 |
+
- **Teachers:** For classroom integration and progress monitoring
|
20 |
+
- **Administrators:** For curriculum management and analytics
|
21 |
+
|
22 |
+
---
|
23 |
+
|
24 |
+
## 2. Objectives
|
25 |
+
|
26 |
+
- **Deliver personalized, adaptive learning experiences**
|
27 |
+
- **Enable collaborative and interactive learning**
|
28 |
+
- **Support multiple input/output modalities (text, voice, handwriting, AR/VR)**
|
29 |
+
- **Ensure privacy and data security**
|
30 |
+
- **Integrate with external educational tools and standards**
|
31 |
+
- **Provide actionable insights and analytics for students and teachers**
|
32 |
+
|
33 |
+
---
|
34 |
+
|
35 |
+
## 3. Features
|
36 |
+
|
37 |
+
## 3.1 Core Features
|
38 |
+
|
39 |
+
- **Adaptive Learning Engine**
|
40 |
+
- Concept graph with 50,000+ nodes (STEM and humanities)
|
41 |
+
- Dynamic skill assessment and competency tracking
|
42 |
+
- Personalized learning paths based on real-time feedback
|
43 |
+
- **Multi-Modal Interaction**
|
44 |
+
- Text-based Q&A with error pattern recognition
|
45 |
+
- Voice recognition with ASR and TTS
|
46 |
+
- Handwriting recognition and digital ink processing
|
47 |
+
- **Assessment Suite**
|
48 |
+
- Automated quiz and problem generation
|
49 |
+
- Step-by-step solution analysis
|
50 |
+
- Plagiarism and similarity detection
|
51 |
+
- **Feedback System**
|
52 |
+
- Contextual error analysis and suggestions
|
53 |
+
- Multimodal feedback (text, audio, visual)
|
54 |
+
- Emotional state recognition (via webcam/EEG)
|
55 |
+
|
56 |
+
## 3.2 Advanced Features
|
57 |
+
|
58 |
+
- **Neurological Engagement Monitor**
|
59 |
+
- Integration with consumer-grade EEG devices
|
60 |
+
- Attention, cognitive load, and stress detection
|
61 |
+
- **Cross-Institutional Knowledge Fusion**
|
62 |
+
- Curriculum alignment with 10+ national standards
|
63 |
+
- Textbook content reconciliation
|
64 |
+
- Cultural adaptation engine
|
65 |
+
- **Automated Lesson Authoring**
|
66 |
+
- AI-powered content generation from PDFs, videos, web
|
67 |
+
|
68 |
+
## 3.3 User Experience
|
69 |
+
|
70 |
+
- **Custom Dashboard**
|
71 |
+
- Knowledge growth map
|
72 |
+
- Temporal performance heatmap
|
73 |
+
- Cognitive profile radar
|
74 |
+
- **Accessibility**
|
75 |
+
- Screen reader compatibility
|
76 |
+
- Text-to-speech and adjustable interface
|
77 |
+
- **Gamification**
|
78 |
+
- Badges, leaderboards, token-based rewards
|
79 |
+
|
80 |
+
---
|
81 |
+
|
82 |
+
## 4. Technical Requirements
|
83 |
+
|
84 |
+
- **MCP Server:** Exposes all core and advanced features as MCP tools
|
85 |
+
- **Gradio Interface:** User-friendly, customizable, and accessible
|
86 |
+
- **Microservices Architecture:** Modular design for scalability
|
87 |
+
- **Real-Time Data Processing:** Asynchronous task queues and caching
|
88 |
+
- **Cloud Deployment:** Browser-based access with no local setup
|
89 |
+
- **Integration:** Supports external APIs, LMS, and educational tools
|
90 |
+
|
91 |
+
---
|
92 |
+
|
93 |
+
## 5. Non-Functional Requirements
|
94 |
+
|
95 |
+
- **Performance:** Supports 100+ concurrent users with <1s response time for core features
|
96 |
+
- **Security:** End-to-end encryption, role-based access control
|
97 |
+
- **Privacy:** Compliant with GDPR, COPPA, and FERPA
|
98 |
+
- **Scalability:** Horizontally scalable to support large institutions
|
99 |
+
- **Accessibility:** WCAG 2.1 AA compliant
|
100 |
+
|
101 |
+
---
|
102 |
+
|
103 |
+
## 6. Success Metrics
|
104 |
+
|
105 |
+
- **User Engagement:** Average session duration, repeat usage
|
106 |
+
- **Learning Outcomes:** Improvement in quiz/test scores
|
107 |
+
- **Adoption Rate:** Number of active users and institutions
|
108 |
+
- **Teacher Satisfaction:** Feedback on usability and effectiveness
|
109 |
+
- **Technical Performance:** Uptime, response time, error rates
|
110 |
+
|
111 |
+
---
|
112 |
+
|
113 |
+
## 7. Roadmap
|
114 |
+
|
115 |
+
1. **Phase 1:** Core adaptive learning engine and MCP integration
|
116 |
+
2. **Phase 2:** Multi-modal interaction and collaborative tools
|
117 |
+
3. **Phase 3:** Advanced features (engagement monitor, lesson authoring)
|
118 |
+
4. **Phase 4:** Deployment, analytics, and gamification
|
119 |
+
|
120 |
+
---
|
121 |
+
|
122 |
+
## 8. Risks and Mitigation
|
123 |
+
|
124 |
+
- **Privacy Concerns:** Implement strict data controls and transparency
|
125 |
+
- **Technical Complexity:** Modular design and clear documentation
|
126 |
+
- **User Adoption:** Provide training materials and support
|
127 |
+
|
128 |
+
---
|
129 |
+
|
130 |
+
## 9. Stakeholders
|
131 |
+
|
132 |
+
- **Product Manager:** Oversees development and alignment with educational goals
|
133 |
+
- **Developers:** Build and maintain the MCP server and Gradio interface
|
134 |
+
- **Designers:** Ensure intuitive and accessible user experience
|
135 |
+
- **Educators:** Provide feedback and guide curriculum alignment
|
136 |
+
- **Students:** End users and primary beneficiaries
|
137 |
+
|
138 |
+
---
|
139 |
+
|
140 |
+
## 10. Appendix
|
141 |
+
|
142 |
+
- **Glossary:** MCP, Gradio, adaptive learning, etc.
|
143 |
+
- **References:** Educational standards, privacy regulations, technical documentation
|
144 |
+
|
145 |
+
---
|
docs/sdk.md
ADDED
@@ -0,0 +1,646 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# MCP Python SDK
|
2 |
+
|
3 |
+
<div align="center">
|
4 |
+
|
5 |
+
<strong>Python implementation of the Model Context Protocol (MCP)</strong>
|
6 |
+
|
7 |
+
[![PyPI][pypi-badge]][pypi-url]
|
8 |
+
[![MIT licensed][mit-badge]][mit-url]
|
9 |
+
[![Python Version][python-badge]][python-url]
|
10 |
+
[![Documentation][docs-badge]][docs-url]
|
11 |
+
[![Specification][spec-badge]][spec-url]
|
12 |
+
[![GitHub Discussions][discussions-badge]][discussions-url]
|
13 |
+
|
14 |
+
</div>
|
15 |
+
|
16 |
+
<!-- omit in toc -->
|
17 |
+
## Table of Contents
|
18 |
+
|
19 |
+
- [Overview](#overview)
|
20 |
+
- [Installation](#installation)
|
21 |
+
- [Quickstart](#quickstart)
|
22 |
+
- [What is MCP?](#what-is-mcp)
|
23 |
+
- [Core Concepts](#core-concepts)
|
24 |
+
- [Server](#server)
|
25 |
+
- [Resources](#resources)
|
26 |
+
- [Tools](#tools)
|
27 |
+
- [Prompts](#prompts)
|
28 |
+
- [Images](#images)
|
29 |
+
- [Context](#context)
|
30 |
+
- [Running Your Server](#running-your-server)
|
31 |
+
- [Development Mode](#development-mode)
|
32 |
+
- [Claude Desktop Integration](#claude-desktop-integration)
|
33 |
+
- [Direct Execution](#direct-execution)
|
34 |
+
- [Mounting to an Existing ASGI Server](#mounting-to-an-existing-asgi-server)
|
35 |
+
- [Examples](#examples)
|
36 |
+
- [Echo Server](#echo-server)
|
37 |
+
- [SQLite Explorer](#sqlite-explorer)
|
38 |
+
- [Advanced Usage](#advanced-usage)
|
39 |
+
- [Low-Level Server](#low-level-server)
|
40 |
+
- [Writing MCP Clients](#writing-mcp-clients)
|
41 |
+
- [MCP Primitives](#mcp-primitives)
|
42 |
+
- [Server Capabilities](#server-capabilities)
|
43 |
+
- [Documentation](#documentation)
|
44 |
+
- [Contributing](#contributing)
|
45 |
+
- [License](#license)
|
46 |
+
|
47 |
+
[pypi-badge]: https://img.shields.io/pypi/v/mcp.svg
|
48 |
+
[pypi-url]: https://pypi.org/project/mcp/
|
49 |
+
[mit-badge]: https://img.shields.io/pypi/l/mcp.svg
|
50 |
+
[mit-url]: https://github.com/modelcontextprotocol/python-sdk/blob/main/LICENSE
|
51 |
+
[python-badge]: https://img.shields.io/pypi/pyversions/mcp.svg
|
52 |
+
[python-url]: https://www.python.org/downloads/
|
53 |
+
[docs-badge]: https://img.shields.io/badge/docs-modelcontextprotocol.io-blue.svg
|
54 |
+
[docs-url]: https://modelcontextprotocol.io
|
55 |
+
[spec-badge]: https://img.shields.io/badge/spec-spec.modelcontextprotocol.io-blue.svg
|
56 |
+
[spec-url]: https://spec.modelcontextprotocol.io
|
57 |
+
[discussions-badge]: https://img.shields.io/github/discussions/modelcontextprotocol/python-sdk
|
58 |
+
[discussions-url]: https://github.com/modelcontextprotocol/python-sdk/discussions
|
59 |
+
|
60 |
+
## Overview
|
61 |
+
|
62 |
+
The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This Python SDK implements the full MCP specification, making it easy to:
|
63 |
+
|
64 |
+
- Build MCP clients that can connect to any MCP server
|
65 |
+
- Create MCP servers that expose resources, prompts and tools
|
66 |
+
- Use standard transports like stdio and SSE
|
67 |
+
- Handle all MCP protocol messages and lifecycle events
|
68 |
+
|
69 |
+
## Installation
|
70 |
+
|
71 |
+
### Adding MCP to your python project
|
72 |
+
|
73 |
+
We recommend using [uv](https://docs.astral.sh/uv/) to manage your Python projects. In a uv managed python project, add mcp to dependencies by:
|
74 |
+
|
75 |
+
```bash
|
76 |
+
uv add "mcp[cli]"
|
77 |
+
```
|
78 |
+
|
79 |
+
Alternatively, for projects using pip for dependencies:
|
80 |
+
```bash
|
81 |
+
pip install mcp
|
82 |
+
```
|
83 |
+
|
84 |
+
### Running the standalone MCP development tools
|
85 |
+
|
86 |
+
To run the mcp command with uv:
|
87 |
+
|
88 |
+
```bash
|
89 |
+
uv run mcp
|
90 |
+
```
|
91 |
+
|
92 |
+
## Quickstart
|
93 |
+
|
94 |
+
Let's create a simple MCP server that exposes a calculator tool and some data:
|
95 |
+
|
96 |
+
```python
|
97 |
+
# server.py
|
98 |
+
from mcp.server.fastmcp import FastMCP
|
99 |
+
|
100 |
+
# Create an MCP server
|
101 |
+
mcp = FastMCP("Demo")
|
102 |
+
|
103 |
+
|
104 |
+
# Add an addition tool
|
105 |
+
@mcp.tool()
|
106 |
+
def add(a: int, b: int) -> int:
|
107 |
+
"""Add two numbers"""
|
108 |
+
return a + b
|
109 |
+
|
110 |
+
|
111 |
+
# Add a dynamic greeting resource
|
112 |
+
@mcp.resource("greeting://{name}")
|
113 |
+
def get_greeting(name: str) -> str:
|
114 |
+
"""Get a personalized greeting"""
|
115 |
+
return f"Hello, {name}!"
|
116 |
+
```
|
117 |
+
|
118 |
+
You can install this server in [Claude Desktop](https://claude.ai/download) and interact with it right away by running:
|
119 |
+
```bash
|
120 |
+
mcp install server.py
|
121 |
+
```
|
122 |
+
|
123 |
+
Alternatively, you can test it with the MCP Inspector:
|
124 |
+
```bash
|
125 |
+
mcp dev server.py
|
126 |
+
```
|
127 |
+
|
128 |
+
## What is MCP?
|
129 |
+
|
130 |
+
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions. MCP servers can:
|
131 |
+
|
132 |
+
- Expose data through **Resources** (think of these sort of like GET endpoints; they are used to load information into the LLM's context)
|
133 |
+
- Provide functionality through **Tools** (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)
|
134 |
+
- Define interaction patterns through **Prompts** (reusable templates for LLM interactions)
|
135 |
+
- And more!
|
136 |
+
|
137 |
+
## Core Concepts
|
138 |
+
|
139 |
+
### Server
|
140 |
+
|
141 |
+
The FastMCP server is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing:
|
142 |
+
|
143 |
+
```python
|
144 |
+
# Add lifespan support for startup/shutdown with strong typing
|
145 |
+
from contextlib import asynccontextmanager
|
146 |
+
from dataclasses import dataclass
|
147 |
+
from typing import AsyncIterator
|
148 |
+
|
149 |
+
from fake_database import Database # Replace with your actual DB type
|
150 |
+
|
151 |
+
from mcp.server.fastmcp import Context, FastMCP
|
152 |
+
|
153 |
+
# Create a named server
|
154 |
+
mcp = FastMCP("My App")
|
155 |
+
|
156 |
+
# Specify dependencies for deployment and development
|
157 |
+
mcp = FastMCP("My App", dependencies=["pandas", "numpy"])
|
158 |
+
|
159 |
+
|
160 |
+
@dataclass
|
161 |
+
class AppContext:
|
162 |
+
db: Database
|
163 |
+
|
164 |
+
|
165 |
+
@asynccontextmanager
|
166 |
+
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
|
167 |
+
"""Manage application lifecycle with type-safe context"""
|
168 |
+
# Initialize on startup
|
169 |
+
db = await Database.connect()
|
170 |
+
try:
|
171 |
+
yield AppContext(db=db)
|
172 |
+
finally:
|
173 |
+
# Cleanup on shutdown
|
174 |
+
await db.disconnect()
|
175 |
+
|
176 |
+
|
177 |
+
# Pass lifespan to server
|
178 |
+
mcp = FastMCP("My App", lifespan=app_lifespan)
|
179 |
+
|
180 |
+
|
181 |
+
# Access type-safe lifespan context in tools
|
182 |
+
@mcp.tool()
|
183 |
+
def query_db(ctx: Context) -> str:
|
184 |
+
"""Tool that uses initialized resources"""
|
185 |
+
db = ctx.request_context.lifespan_context["db"]
|
186 |
+
return db.query()
|
187 |
+
```
|
188 |
+
|
189 |
+
### Resources
|
190 |
+
|
191 |
+
Resources are how you expose data to LLMs. They're similar to GET endpoints in a REST API - they provide data but shouldn't perform significant computation or have side effects:
|
192 |
+
|
193 |
+
```python
|
194 |
+
from mcp.server.fastmcp import FastMCP
|
195 |
+
|
196 |
+
mcp = FastMCP("My App")
|
197 |
+
|
198 |
+
|
199 |
+
@mcp.resource("config://app")
|
200 |
+
def get_config() -> str:
|
201 |
+
"""Static configuration data"""
|
202 |
+
return "App configuration here"
|
203 |
+
|
204 |
+
|
205 |
+
@mcp.resource("users://{user_id}/profile")
|
206 |
+
def get_user_profile(user_id: str) -> str:
|
207 |
+
"""Dynamic user data"""
|
208 |
+
return f"Profile data for user {user_id}"
|
209 |
+
```
|
210 |
+
|
211 |
+
### Tools
|
212 |
+
|
213 |
+
Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects:
|
214 |
+
|
215 |
+
```python
|
216 |
+
import httpx
|
217 |
+
from mcp.server.fastmcp import FastMCP
|
218 |
+
|
219 |
+
mcp = FastMCP("My App")
|
220 |
+
|
221 |
+
|
222 |
+
@mcp.tool()
|
223 |
+
def calculate_bmi(weight_kg: float, height_m: float) -> float:
|
224 |
+
"""Calculate BMI given weight in kg and height in meters"""
|
225 |
+
return weight_kg / (height_m**2)
|
226 |
+
|
227 |
+
|
228 |
+
@mcp.tool()
|
229 |
+
async def fetch_weather(city: str) -> str:
|
230 |
+
"""Fetch current weather for a city"""
|
231 |
+
async with httpx.AsyncClient() as client:
|
232 |
+
response = await client.get(f"https://api.weather.com/{city}")
|
233 |
+
return response.text
|
234 |
+
```
|
235 |
+
|
236 |
+
### Prompts
|
237 |
+
|
238 |
+
Prompts are reusable templates that help LLMs interact with your server effectively:
|
239 |
+
|
240 |
+
```python
|
241 |
+
from mcp.server.fastmcp import FastMCP
|
242 |
+
from mcp.server.fastmcp.prompts import base
|
243 |
+
|
244 |
+
mcp = FastMCP("My App")
|
245 |
+
|
246 |
+
|
247 |
+
@mcp.prompt()
|
248 |
+
def review_code(code: str) -> str:
|
249 |
+
return f"Please review this code:\n\n{code}"
|
250 |
+
|
251 |
+
|
252 |
+
@mcp.prompt()
|
253 |
+
def debug_error(error: str) -> list[base.Message]:
|
254 |
+
return [
|
255 |
+
base.UserMessage("I'm seeing this error:"),
|
256 |
+
base.UserMessage(error),
|
257 |
+
base.AssistantMessage("I'll help debug that. What have you tried so far?"),
|
258 |
+
]
|
259 |
+
```
|
260 |
+
|
261 |
+
### Images
|
262 |
+
|
263 |
+
FastMCP provides an `Image` class that automatically handles image data:
|
264 |
+
|
265 |
+
```python
|
266 |
+
from mcp.server.fastmcp import FastMCP, Image
|
267 |
+
from PIL import Image as PILImage
|
268 |
+
|
269 |
+
mcp = FastMCP("My App")
|
270 |
+
|
271 |
+
|
272 |
+
@mcp.tool()
|
273 |
+
def create_thumbnail(image_path: str) -> Image:
|
274 |
+
"""Create a thumbnail from an image"""
|
275 |
+
img = PILImage.open(image_path)
|
276 |
+
img.thumbnail((100, 100))
|
277 |
+
return Image(data=img.tobytes(), format="png")
|
278 |
+
```
|
279 |
+
|
280 |
+
### Context
|
281 |
+
|
282 |
+
The Context object gives your tools and resources access to MCP capabilities:
|
283 |
+
|
284 |
+
```python
|
285 |
+
from mcp.server.fastmcp import FastMCP, Context
|
286 |
+
|
287 |
+
mcp = FastMCP("My App")
|
288 |
+
|
289 |
+
|
290 |
+
@mcp.tool()
|
291 |
+
async def long_task(files: list[str], ctx: Context) -> str:
|
292 |
+
"""Process multiple files with progress tracking"""
|
293 |
+
for i, file in enumerate(files):
|
294 |
+
ctx.info(f"Processing {file}")
|
295 |
+
await ctx.report_progress(i, len(files))
|
296 |
+
data, mime_type = await ctx.read_resource(f"file://{file}")
|
297 |
+
return "Processing complete"
|
298 |
+
```
|
299 |
+
|
300 |
+
## Running Your Server
|
301 |
+
|
302 |
+
### Development Mode
|
303 |
+
|
304 |
+
The fastest way to test and debug your server is with the MCP Inspector:
|
305 |
+
|
306 |
+
```bash
|
307 |
+
mcp dev server.py
|
308 |
+
|
309 |
+
# Add dependencies
|
310 |
+
mcp dev server.py --with pandas --with numpy
|
311 |
+
|
312 |
+
# Mount local code
|
313 |
+
mcp dev server.py --with-editable .
|
314 |
+
```
|
315 |
+
|
316 |
+
### Claude Desktop Integration
|
317 |
+
|
318 |
+
Once your server is ready, install it in Claude Desktop:
|
319 |
+
|
320 |
+
```bash
|
321 |
+
mcp install server.py
|
322 |
+
|
323 |
+
# Custom name
|
324 |
+
mcp install server.py --name "My Analytics Server"
|
325 |
+
|
326 |
+
# Environment variables
|
327 |
+
mcp install server.py -v API_KEY=abc123 -v DB_URL=postgres://...
|
328 |
+
mcp install server.py -f .env
|
329 |
+
```
|
330 |
+
|
331 |
+
### Direct Execution
|
332 |
+
|
333 |
+
For advanced scenarios like custom deployments:
|
334 |
+
|
335 |
+
```python
|
336 |
+
from mcp.server.fastmcp import FastMCP
|
337 |
+
|
338 |
+
mcp = FastMCP("My App")
|
339 |
+
|
340 |
+
if __name__ == "__main__":
|
341 |
+
mcp.run()
|
342 |
+
```
|
343 |
+
|
344 |
+
Run it with:
|
345 |
+
```bash
|
346 |
+
python server.py
|
347 |
+
# or
|
348 |
+
mcp run server.py
|
349 |
+
```
|
350 |
+
|
351 |
+
### Mounting to an Existing ASGI Server
|
352 |
+
|
353 |
+
You can mount the SSE server to an existing ASGI server using the `sse_app` method. This allows you to integrate the SSE server with other ASGI applications.
|
354 |
+
|
355 |
+
```python
|
356 |
+
from starlette.applications import Starlette
|
357 |
+
from starlette.routes import Mount, Host
|
358 |
+
from mcp.server.fastmcp import FastMCP
|
359 |
+
|
360 |
+
|
361 |
+
mcp = FastMCP("My App")
|
362 |
+
|
363 |
+
# Mount the SSE server to the existing ASGI server
|
364 |
+
app = Starlette(
|
365 |
+
routes=[
|
366 |
+
Mount('/', app=mcp.sse_app()),
|
367 |
+
]
|
368 |
+
)
|
369 |
+
|
370 |
+
# or dynamically mount as host
|
371 |
+
app.router.routes.append(Host('mcp.acme.corp', app=mcp.sse_app()))
|
372 |
+
```
|
373 |
+
|
374 |
+
For more information on mounting applications in Starlette, see the [Starlette documentation](https://www.starlette.io/routing/#submounting-routes).
|
375 |
+
|
376 |
+
## Examples
|
377 |
+
|
378 |
+
### Echo Server
|
379 |
+
|
380 |
+
A simple server demonstrating resources, tools, and prompts:
|
381 |
+
|
382 |
+
```python
|
383 |
+
from mcp.server.fastmcp import FastMCP
|
384 |
+
|
385 |
+
mcp = FastMCP("Echo")
|
386 |
+
|
387 |
+
|
388 |
+
@mcp.resource("echo://{message}")
|
389 |
+
def echo_resource(message: str) -> str:
|
390 |
+
"""Echo a message as a resource"""
|
391 |
+
return f"Resource echo: {message}"
|
392 |
+
|
393 |
+
|
394 |
+
@mcp.tool()
|
395 |
+
def echo_tool(message: str) -> str:
|
396 |
+
"""Echo a message as a tool"""
|
397 |
+
return f"Tool echo: {message}"
|
398 |
+
|
399 |
+
|
400 |
+
@mcp.prompt()
|
401 |
+
def echo_prompt(message: str) -> str:
|
402 |
+
"""Create an echo prompt"""
|
403 |
+
return f"Please process this message: {message}"
|
404 |
+
```
|
405 |
+
|
406 |
+
### SQLite Explorer
|
407 |
+
|
408 |
+
A more complex example showing database integration:
|
409 |
+
|
410 |
+
```python
|
411 |
+
import sqlite3
|
412 |
+
|
413 |
+
from mcp.server.fastmcp import FastMCP
|
414 |
+
|
415 |
+
mcp = FastMCP("SQLite Explorer")
|
416 |
+
|
417 |
+
|
418 |
+
@mcp.resource("schema://main")
|
419 |
+
def get_schema() -> str:
|
420 |
+
"""Provide the database schema as a resource"""
|
421 |
+
conn = sqlite3.connect("database.db")
|
422 |
+
schema = conn.execute("SELECT sql FROM sqlite_master WHERE type='table'").fetchall()
|
423 |
+
return "\n".join(sql[0] for sql in schema if sql[0])
|
424 |
+
|
425 |
+
|
426 |
+
@mcp.tool()
|
427 |
+
def query_data(sql: str) -> str:
|
428 |
+
"""Execute SQL queries safely"""
|
429 |
+
conn = sqlite3.connect("database.db")
|
430 |
+
try:
|
431 |
+
result = conn.execute(sql).fetchall()
|
432 |
+
return "\n".join(str(row) for row in result)
|
433 |
+
except Exception as e:
|
434 |
+
return f"Error: {str(e)}"
|
435 |
+
```
|
436 |
+
|
437 |
+
## Advanced Usage
|
438 |
+
|
439 |
+
### Low-Level Server
|
440 |
+
|
441 |
+
For more control, you can use the low-level server implementation directly. This gives you full access to the protocol and allows you to customize every aspect of your server, including lifecycle management through the lifespan API:
|
442 |
+
|
443 |
+
```python
|
444 |
+
from contextlib import asynccontextmanager
|
445 |
+
from typing import AsyncIterator
|
446 |
+
|
447 |
+
from fake_database import Database # Replace with your actual DB type
|
448 |
+
|
449 |
+
from mcp.server import Server
|
450 |
+
|
451 |
+
|
452 |
+
@asynccontextmanager
|
453 |
+
async def server_lifespan(server: Server) -> AsyncIterator[dict]:
|
454 |
+
"""Manage server startup and shutdown lifecycle."""
|
455 |
+
# Initialize resources on startup
|
456 |
+
db = await Database.connect()
|
457 |
+
try:
|
458 |
+
yield {"db": db}
|
459 |
+
finally:
|
460 |
+
# Clean up on shutdown
|
461 |
+
await db.disconnect()
|
462 |
+
|
463 |
+
|
464 |
+
# Pass lifespan to server
|
465 |
+
server = Server("example-server", lifespan=server_lifespan)
|
466 |
+
|
467 |
+
|
468 |
+
# Access lifespan context in handlers
|
469 |
+
@server.call_tool()
|
470 |
+
async def query_db(name: str, arguments: dict) -> list:
|
471 |
+
ctx = server.request_context
|
472 |
+
db = ctx.lifespan_context["db"]
|
473 |
+
return await db.query(arguments["query"])
|
474 |
+
```
|
475 |
+
|
476 |
+
The lifespan API provides:
|
477 |
+
- A way to initialize resources when the server starts and clean them up when it stops
|
478 |
+
- Access to initialized resources through the request context in handlers
|
479 |
+
- Type-safe context passing between lifespan and request handlers
|
480 |
+
|
481 |
+
```python
|
482 |
+
import mcp.server.stdio
|
483 |
+
import mcp.types as types
|
484 |
+
from mcp.server.lowlevel import NotificationOptions, Server
|
485 |
+
from mcp.server.models import InitializationOptions
|
486 |
+
|
487 |
+
# Create a server instance
|
488 |
+
server = Server("example-server")
|
489 |
+
|
490 |
+
|
491 |
+
@server.list_prompts()
|
492 |
+
async def handle_list_prompts() -> list[types.Prompt]:
|
493 |
+
return [
|
494 |
+
types.Prompt(
|
495 |
+
name="example-prompt",
|
496 |
+
description="An example prompt template",
|
497 |
+
arguments=[
|
498 |
+
types.PromptArgument(
|
499 |
+
name="arg1", description="Example argument", required=True
|
500 |
+
)
|
501 |
+
],
|
502 |
+
)
|
503 |
+
]
|
504 |
+
|
505 |
+
|
506 |
+
@server.get_prompt()
|
507 |
+
async def handle_get_prompt(
|
508 |
+
name: str, arguments: dict[str, str] | None
|
509 |
+
) -> types.GetPromptResult:
|
510 |
+
if name != "example-prompt":
|
511 |
+
raise ValueError(f"Unknown prompt: {name}")
|
512 |
+
|
513 |
+
return types.GetPromptResult(
|
514 |
+
description="Example prompt",
|
515 |
+
messages=[
|
516 |
+
types.PromptMessage(
|
517 |
+
role="user",
|
518 |
+
content=types.TextContent(type="text", text="Example prompt text"),
|
519 |
+
)
|
520 |
+
],
|
521 |
+
)
|
522 |
+
|
523 |
+
|
524 |
+
async def run():
|
525 |
+
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
|
526 |
+
await server.run(
|
527 |
+
read_stream,
|
528 |
+
write_stream,
|
529 |
+
InitializationOptions(
|
530 |
+
server_name="example",
|
531 |
+
server_version="0.1.0",
|
532 |
+
capabilities=server.get_capabilities(
|
533 |
+
notification_options=NotificationOptions(),
|
534 |
+
experimental_capabilities={},
|
535 |
+
),
|
536 |
+
),
|
537 |
+
)
|
538 |
+
|
539 |
+
|
540 |
+
if __name__ == "__main__":
|
541 |
+
import asyncio
|
542 |
+
|
543 |
+
asyncio.run(run())
|
544 |
+
```
|
545 |
+
|
546 |
+
### Writing MCP Clients
|
547 |
+
|
548 |
+
The SDK provides a high-level client interface for connecting to MCP servers:
|
549 |
+
|
550 |
+
```python
|
551 |
+
from mcp import ClientSession, StdioServerParameters, types
|
552 |
+
from mcp.client.stdio import stdio_client
|
553 |
+
|
554 |
+
# Create server parameters for stdio connection
|
555 |
+
server_params = StdioServerParameters(
|
556 |
+
command="python", # Executable
|
557 |
+
args=["example_server.py"], # Optional command line arguments
|
558 |
+
env=None, # Optional environment variables
|
559 |
+
)
|
560 |
+
|
561 |
+
|
562 |
+
# Optional: create a sampling callback
|
563 |
+
async def handle_sampling_message(
|
564 |
+
message: types.CreateMessageRequestParams,
|
565 |
+
) -> types.CreateMessageResult:
|
566 |
+
return types.CreateMessageResult(
|
567 |
+
role="assistant",
|
568 |
+
content=types.TextContent(
|
569 |
+
type="text",
|
570 |
+
text="Hello, world! from model",
|
571 |
+
),
|
572 |
+
model="gpt-3.5-turbo",
|
573 |
+
stopReason="endTurn",
|
574 |
+
)
|
575 |
+
|
576 |
+
|
577 |
+
async def run():
|
578 |
+
async with stdio_client(server_params) as (read, write):
|
579 |
+
async with ClientSession(
|
580 |
+
read, write, sampling_callback=handle_sampling_message
|
581 |
+
) as session:
|
582 |
+
# Initialize the connection
|
583 |
+
await session.initialize()
|
584 |
+
|
585 |
+
# List available prompts
|
586 |
+
prompts = await session.list_prompts()
|
587 |
+
|
588 |
+
# Get a prompt
|
589 |
+
prompt = await session.get_prompt(
|
590 |
+
"example-prompt", arguments={"arg1": "value"}
|
591 |
+
)
|
592 |
+
|
593 |
+
# List available resources
|
594 |
+
resources = await session.list_resources()
|
595 |
+
|
596 |
+
# List available tools
|
597 |
+
tools = await session.list_tools()
|
598 |
+
|
599 |
+
# Read a resource
|
600 |
+
content, mime_type = await session.read_resource("file://some/path")
|
601 |
+
|
602 |
+
# Call a tool
|
603 |
+
result = await session.call_tool("tool-name", arguments={"arg1": "value"})
|
604 |
+
|
605 |
+
|
606 |
+
if __name__ == "__main__":
|
607 |
+
import asyncio
|
608 |
+
|
609 |
+
asyncio.run(run())
|
610 |
+
```
|
611 |
+
|
612 |
+
### MCP Primitives
|
613 |
+
|
614 |
+
The MCP protocol defines three core primitives that servers can implement:
|
615 |
+
|
616 |
+
| Primitive | Control | Description | Example Use |
|
617 |
+
|-----------|-----------------------|-----------------------------------------------------|------------------------------|
|
618 |
+
| Prompts | User-controlled | Interactive templates invoked by user choice | Slash commands, menu options |
|
619 |
+
| Resources | Application-controlled| Contextual data managed by the client application | File contents, API responses |
|
620 |
+
| Tools | Model-controlled | Functions exposed to the LLM to take actions | API calls, data updates |
|
621 |
+
|
622 |
+
### Server Capabilities
|
623 |
+
|
624 |
+
MCP servers declare capabilities during initialization:
|
625 |
+
|
626 |
+
| Capability | Feature Flag | Description |
|
627 |
+
|-------------|------------------------------|------------------------------------|
|
628 |
+
| `prompts` | `listChanged` | Prompt template management |
|
629 |
+
| `resources` | `subscribe`<br/>`listChanged`| Resource exposure and updates |
|
630 |
+
| `tools` | `listChanged` | Tool discovery and execution |
|
631 |
+
| `logging` | - | Server logging configuration |
|
632 |
+
| `completion`| - | Argument completion suggestions |
|
633 |
+
|
634 |
+
## Documentation
|
635 |
+
|
636 |
+
- [Model Context Protocol documentation](https://modelcontextprotocol.io)
|
637 |
+
- [Model Context Protocol specification](https://spec.modelcontextprotocol.io)
|
638 |
+
- [Officially supported servers](https://github.com/modelcontextprotocol/servers)
|
639 |
+
|
640 |
+
## Contributing
|
641 |
+
|
642 |
+
We are passionate about supporting contributors of all levels of experience and would love to see you get involved in the project. See the [contributing guide](CONTRIBUTING.md) to get started.
|
643 |
+
|
644 |
+
## License
|
645 |
+
|
646 |
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
main.py
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# TutorX MCP Server
|
2 |
+
from mcp.server.fastmcp import FastMCP
|
3 |
+
import json
|
4 |
+
from typing import List, Dict, Any, Optional
|
5 |
+
from datetime import datetime
|
6 |
+
|
7 |
+
# Create the TutorX MCP server
|
8 |
+
mcp = FastMCP("TutorX")
|
9 |
+
|
10 |
+
# ------------------ Core Features ------------------
|
11 |
+
|
12 |
+
# Adaptive Learning Engine
|
13 |
+
@mcp.tool()
|
14 |
+
def assess_skill(student_id: str, concept_id: str) -> Dict[str, Any]:
|
15 |
+
"""
|
16 |
+
Assess student's skill level on a specific concept
|
17 |
+
|
18 |
+
Args:
|
19 |
+
student_id: The unique identifier for the student
|
20 |
+
concept_id: The concept to assess
|
21 |
+
|
22 |
+
Returns:
|
23 |
+
Dictionary containing skill level and recommendations
|
24 |
+
"""
|
25 |
+
# Simulated skill assessment
|
26 |
+
return {
|
27 |
+
"student_id": student_id,
|
28 |
+
"concept_id": concept_id,
|
29 |
+
"skill_level": 0.75,
|
30 |
+
"confidence": 0.85,
|
31 |
+
"recommendations": [
|
32 |
+
"Practice more complex problems",
|
33 |
+
"Review related concept: algebra_linear_equations"
|
34 |
+
],
|
35 |
+
"timestamp": datetime.now().isoformat()
|
36 |
+
}
|
37 |
+
|
38 |
+
@mcp.resource("concept-graph://")
|
39 |
+
def get_concept_graph() -> Dict[str, Any]:
|
40 |
+
"""Get the full knowledge concept graph"""
|
41 |
+
return {
|
42 |
+
"nodes": [
|
43 |
+
{"id": "math_algebra_basics", "name": "Algebra Basics", "difficulty": 1},
|
44 |
+
{"id": "math_algebra_linear_equations", "name": "Linear Equations", "difficulty": 2},
|
45 |
+
{"id": "math_algebra_quadratic_equations", "name": "Quadratic Equations", "difficulty": 3},
|
46 |
+
],
|
47 |
+
"edges": [
|
48 |
+
{"from": "math_algebra_basics", "to": "math_algebra_linear_equations", "weight": 1.0},
|
49 |
+
{"from": "math_algebra_linear_equations", "to": "math_algebra_quadratic_equations", "weight": 0.8},
|
50 |
+
]
|
51 |
+
}
|
52 |
+
|
53 |
+
@mcp.resource("learning-path://{student_id}")
|
54 |
+
def get_learning_path(student_id: str) -> Dict[str, Any]:
|
55 |
+
"""Get personalized learning path for a student"""
|
56 |
+
return {
|
57 |
+
"student_id": student_id,
|
58 |
+
"current_concepts": ["math_algebra_linear_equations"],
|
59 |
+
"recommended_next": ["math_algebra_quadratic_equations"],
|
60 |
+
"mastered": ["math_algebra_basics"],
|
61 |
+
"estimated_completion_time": "2 weeks"
|
62 |
+
}
|
63 |
+
|
64 |
+
# Assessment Suite
|
65 |
+
@mcp.tool()
|
66 |
+
def generate_quiz(concept_ids: List[str], difficulty: int = 2) -> Dict[str, Any]:
|
67 |
+
"""
|
68 |
+
Generate a quiz based on specified concepts and difficulty
|
69 |
+
|
70 |
+
Args:
|
71 |
+
concept_ids: List of concept IDs to include in the quiz
|
72 |
+
difficulty: Difficulty level from 1-5
|
73 |
+
|
74 |
+
Returns:
|
75 |
+
Quiz object with questions and answers
|
76 |
+
"""
|
77 |
+
return {
|
78 |
+
"quiz_id": "q12345",
|
79 |
+
"concept_ids": concept_ids,
|
80 |
+
"difficulty": difficulty,
|
81 |
+
"questions": [
|
82 |
+
{
|
83 |
+
"id": "q1",
|
84 |
+
"text": "Solve for x: 2x + 3 = 7",
|
85 |
+
"type": "algebraic_equation",
|
86 |
+
"answer": "x = 2",
|
87 |
+
"solution_steps": [
|
88 |
+
"2x + 3 = 7",
|
89 |
+
"2x = 7 - 3",
|
90 |
+
"2x = 4",
|
91 |
+
"x = 4/2 = 2"
|
92 |
+
]
|
93 |
+
}
|
94 |
+
]
|
95 |
+
}
|
96 |
+
|
97 |
+
# Feedback System
|
98 |
+
@mcp.tool()
|
99 |
+
def analyze_error_patterns(student_id: str, concept_id: str) -> Dict[str, Any]:
|
100 |
+
"""
|
101 |
+
Analyze common error patterns for a student on a specific concept
|
102 |
+
|
103 |
+
Args:
|
104 |
+
student_id: The student's unique identifier
|
105 |
+
concept_id: The concept to analyze
|
106 |
+
|
107 |
+
Returns:
|
108 |
+
Error pattern analysis
|
109 |
+
"""
|
110 |
+
return {
|
111 |
+
"student_id": student_id,
|
112 |
+
"concept_id": concept_id,
|
113 |
+
"common_errors": [
|
114 |
+
{
|
115 |
+
"type": "sign_error",
|
116 |
+
"frequency": 0.65,
|
117 |
+
"example": "2x - 3 = 5 → 2x = 5 - 3 → 2x = 2 → x = 1 (should be x = 4)"
|
118 |
+
},
|
119 |
+
{
|
120 |
+
"type": "arithmetic_error",
|
121 |
+
"frequency": 0.35,
|
122 |
+
"example": "2x = 8 → x = 8/2 = 3 (should be x = 4)"
|
123 |
+
}
|
124 |
+
],
|
125 |
+
"recommendations": [
|
126 |
+
"Practice more sign manipulation problems",
|
127 |
+
"Review basic arithmetic operations"
|
128 |
+
]
|
129 |
+
}
|
130 |
+
|
131 |
+
if __name__ == "__main__":
|
132 |
+
mcp.run()
|
pyproject.toml
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[project]
|
2 |
+
name = "tutorx-mcp"
|
3 |
+
version = "0.1.0"
|
4 |
+
description = "Add your description here"
|
5 |
+
readme = "README.md"
|
6 |
+
requires-python = ">=3.12"
|
7 |
+
dependencies = [
|
8 |
+
"mcp[cli]>=1.9.3",
|
9 |
+
]
|
uv.lock
ADDED
@@ -0,0 +1,371 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version = 1
|
2 |
+
revision = 2
|
3 |
+
requires-python = ">=3.12"
|
4 |
+
|
5 |
+
[[package]]
|
6 |
+
name = "annotated-types"
|
7 |
+
version = "0.7.0"
|
8 |
+
source = { registry = "https://pypi.org/simple" }
|
9 |
+
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
|
10 |
+
wheels = [
|
11 |
+
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
|
12 |
+
]
|
13 |
+
|
14 |
+
[[package]]
|
15 |
+
name = "anyio"
|
16 |
+
version = "4.9.0"
|
17 |
+
source = { registry = "https://pypi.org/simple" }
|
18 |
+
dependencies = [
|
19 |
+
{ name = "idna" },
|
20 |
+
{ name = "sniffio" },
|
21 |
+
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
22 |
+
]
|
23 |
+
sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" }
|
24 |
+
wheels = [
|
25 |
+
{ url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" },
|
26 |
+
]
|
27 |
+
|
28 |
+
[[package]]
|
29 |
+
name = "certifi"
|
30 |
+
version = "2025.4.26"
|
31 |
+
source = { registry = "https://pypi.org/simple" }
|
32 |
+
sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" }
|
33 |
+
wheels = [
|
34 |
+
{ url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" },
|
35 |
+
]
|
36 |
+
|
37 |
+
[[package]]
|
38 |
+
name = "click"
|
39 |
+
version = "8.2.1"
|
40 |
+
source = { registry = "https://pypi.org/simple" }
|
41 |
+
dependencies = [
|
42 |
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
43 |
+
]
|
44 |
+
sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" }
|
45 |
+
wheels = [
|
46 |
+
{ url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" },
|
47 |
+
]
|
48 |
+
|
49 |
+
[[package]]
|
50 |
+
name = "colorama"
|
51 |
+
version = "0.4.6"
|
52 |
+
source = { registry = "https://pypi.org/simple" }
|
53 |
+
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
54 |
+
wheels = [
|
55 |
+
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
56 |
+
]
|
57 |
+
|
58 |
+
[[package]]
|
59 |
+
name = "h11"
|
60 |
+
version = "0.16.0"
|
61 |
+
source = { registry = "https://pypi.org/simple" }
|
62 |
+
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
|
63 |
+
wheels = [
|
64 |
+
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
65 |
+
]
|
66 |
+
|
67 |
+
[[package]]
|
68 |
+
name = "httpcore"
|
69 |
+
version = "1.0.9"
|
70 |
+
source = { registry = "https://pypi.org/simple" }
|
71 |
+
dependencies = [
|
72 |
+
{ name = "certifi" },
|
73 |
+
{ name = "h11" },
|
74 |
+
]
|
75 |
+
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
|
76 |
+
wheels = [
|
77 |
+
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
78 |
+
]
|
79 |
+
|
80 |
+
[[package]]
|
81 |
+
name = "httpx"
|
82 |
+
version = "0.28.1"
|
83 |
+
source = { registry = "https://pypi.org/simple" }
|
84 |
+
dependencies = [
|
85 |
+
{ name = "anyio" },
|
86 |
+
{ name = "certifi" },
|
87 |
+
{ name = "httpcore" },
|
88 |
+
{ name = "idna" },
|
89 |
+
]
|
90 |
+
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
|
91 |
+
wheels = [
|
92 |
+
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
|
93 |
+
]
|
94 |
+
|
95 |
+
[[package]]
|
96 |
+
name = "httpx-sse"
|
97 |
+
version = "0.4.0"
|
98 |
+
source = { registry = "https://pypi.org/simple" }
|
99 |
+
sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624, upload-time = "2023-12-22T08:01:21.083Z" }
|
100 |
+
wheels = [
|
101 |
+
{ url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819, upload-time = "2023-12-22T08:01:19.89Z" },
|
102 |
+
]
|
103 |
+
|
104 |
+
[[package]]
|
105 |
+
name = "idna"
|
106 |
+
version = "3.10"
|
107 |
+
source = { registry = "https://pypi.org/simple" }
|
108 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
|
109 |
+
wheels = [
|
110 |
+
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
|
111 |
+
]
|
112 |
+
|
113 |
+
[[package]]
|
114 |
+
name = "markdown-it-py"
|
115 |
+
version = "3.0.0"
|
116 |
+
source = { registry = "https://pypi.org/simple" }
|
117 |
+
dependencies = [
|
118 |
+
{ name = "mdurl" },
|
119 |
+
]
|
120 |
+
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" }
|
121 |
+
wheels = [
|
122 |
+
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" },
|
123 |
+
]
|
124 |
+
|
125 |
+
[[package]]
|
126 |
+
name = "mcp"
|
127 |
+
version = "1.9.3"
|
128 |
+
source = { registry = "https://pypi.org/simple" }
|
129 |
+
dependencies = [
|
130 |
+
{ name = "anyio" },
|
131 |
+
{ name = "httpx" },
|
132 |
+
{ name = "httpx-sse" },
|
133 |
+
{ name = "pydantic" },
|
134 |
+
{ name = "pydantic-settings" },
|
135 |
+
{ name = "python-multipart" },
|
136 |
+
{ name = "sse-starlette" },
|
137 |
+
{ name = "starlette" },
|
138 |
+
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
|
139 |
+
]
|
140 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f2/df/8fefc0c6c7a5c66914763e3ff3893f9a03435628f6625d5e3b0dc45d73db/mcp-1.9.3.tar.gz", hash = "sha256:587ba38448e81885e5d1b84055cfcc0ca56d35cd0c58f50941cab01109405388", size = 333045, upload-time = "2025-06-05T15:48:25.681Z" }
|
141 |
+
wheels = [
|
142 |
+
{ url = "https://files.pythonhosted.org/packages/79/45/823ad05504bea55cb0feb7470387f151252127ad5c72f8882e8fe6cf5c0e/mcp-1.9.3-py3-none-any.whl", hash = "sha256:69b0136d1ac9927402ed4cf221d4b8ff875e7132b0b06edd446448766f34f9b9", size = 131063, upload-time = "2025-06-05T15:48:24.171Z" },
|
143 |
+
]
|
144 |
+
|
145 |
+
[package.optional-dependencies]
|
146 |
+
cli = [
|
147 |
+
{ name = "python-dotenv" },
|
148 |
+
{ name = "typer" },
|
149 |
+
]
|
150 |
+
|
151 |
+
[[package]]
|
152 |
+
name = "mdurl"
|
153 |
+
version = "0.1.2"
|
154 |
+
source = { registry = "https://pypi.org/simple" }
|
155 |
+
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
|
156 |
+
wheels = [
|
157 |
+
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
158 |
+
]
|
159 |
+
|
160 |
+
[[package]]
|
161 |
+
name = "pydantic"
|
162 |
+
version = "2.11.5"
|
163 |
+
source = { registry = "https://pypi.org/simple" }
|
164 |
+
dependencies = [
|
165 |
+
{ name = "annotated-types" },
|
166 |
+
{ name = "pydantic-core" },
|
167 |
+
{ name = "typing-extensions" },
|
168 |
+
{ name = "typing-inspection" },
|
169 |
+
]
|
170 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" }
|
171 |
+
wheels = [
|
172 |
+
{ url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" },
|
173 |
+
]
|
174 |
+
|
175 |
+
[[package]]
|
176 |
+
name = "pydantic-core"
|
177 |
+
version = "2.33.2"
|
178 |
+
source = { registry = "https://pypi.org/simple" }
|
179 |
+
dependencies = [
|
180 |
+
{ name = "typing-extensions" },
|
181 |
+
]
|
182 |
+
sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" }
|
183 |
+
wheels = [
|
184 |
+
{ url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" },
|
185 |
+
{ url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" },
|
186 |
+
{ url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" },
|
187 |
+
{ url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" },
|
188 |
+
{ url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" },
|
189 |
+
{ url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" },
|
190 |
+
{ url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" },
|
191 |
+
{ url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" },
|
192 |
+
{ url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" },
|
193 |
+
{ url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" },
|
194 |
+
{ url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" },
|
195 |
+
{ url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" },
|
196 |
+
{ url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" },
|
197 |
+
{ url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" },
|
198 |
+
{ url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" },
|
199 |
+
{ url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" },
|
200 |
+
{ url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" },
|
201 |
+
{ url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" },
|
202 |
+
{ url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" },
|
203 |
+
{ url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" },
|
204 |
+
{ url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" },
|
205 |
+
{ url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" },
|
206 |
+
{ url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" },
|
207 |
+
{ url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" },
|
208 |
+
{ url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" },
|
209 |
+
{ url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" },
|
210 |
+
{ url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" },
|
211 |
+
{ url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" },
|
212 |
+
{ url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" },
|
213 |
+
{ url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" },
|
214 |
+
{ url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
|
215 |
+
]
|
216 |
+
|
217 |
+
[[package]]
|
218 |
+
name = "pydantic-settings"
|
219 |
+
version = "2.9.1"
|
220 |
+
source = { registry = "https://pypi.org/simple" }
|
221 |
+
dependencies = [
|
222 |
+
{ name = "pydantic" },
|
223 |
+
{ name = "python-dotenv" },
|
224 |
+
{ name = "typing-inspection" },
|
225 |
+
]
|
226 |
+
sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234, upload-time = "2025-04-18T16:44:48.265Z" }
|
227 |
+
wheels = [
|
228 |
+
{ url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356, upload-time = "2025-04-18T16:44:46.617Z" },
|
229 |
+
]
|
230 |
+
|
231 |
+
[[package]]
|
232 |
+
name = "pygments"
|
233 |
+
version = "2.19.1"
|
234 |
+
source = { registry = "https://pypi.org/simple" }
|
235 |
+
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" }
|
236 |
+
wheels = [
|
237 |
+
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" },
|
238 |
+
]
|
239 |
+
|
240 |
+
[[package]]
|
241 |
+
name = "python-dotenv"
|
242 |
+
version = "1.1.0"
|
243 |
+
source = { registry = "https://pypi.org/simple" }
|
244 |
+
sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920, upload-time = "2025-03-25T10:14:56.835Z" }
|
245 |
+
wheels = [
|
246 |
+
{ url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" },
|
247 |
+
]
|
248 |
+
|
249 |
+
[[package]]
|
250 |
+
name = "python-multipart"
|
251 |
+
version = "0.0.20"
|
252 |
+
source = { registry = "https://pypi.org/simple" }
|
253 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" }
|
254 |
+
wheels = [
|
255 |
+
{ url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" },
|
256 |
+
]
|
257 |
+
|
258 |
+
[[package]]
|
259 |
+
name = "rich"
|
260 |
+
version = "14.0.0"
|
261 |
+
source = { registry = "https://pypi.org/simple" }
|
262 |
+
dependencies = [
|
263 |
+
{ name = "markdown-it-py" },
|
264 |
+
{ name = "pygments" },
|
265 |
+
]
|
266 |
+
sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" }
|
267 |
+
wheels = [
|
268 |
+
{ url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" },
|
269 |
+
]
|
270 |
+
|
271 |
+
[[package]]
|
272 |
+
name = "shellingham"
|
273 |
+
version = "1.5.4"
|
274 |
+
source = { registry = "https://pypi.org/simple" }
|
275 |
+
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
|
276 |
+
wheels = [
|
277 |
+
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
|
278 |
+
]
|
279 |
+
|
280 |
+
[[package]]
|
281 |
+
name = "sniffio"
|
282 |
+
version = "1.3.1"
|
283 |
+
source = { registry = "https://pypi.org/simple" }
|
284 |
+
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
|
285 |
+
wheels = [
|
286 |
+
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
|
287 |
+
]
|
288 |
+
|
289 |
+
[[package]]
|
290 |
+
name = "sse-starlette"
|
291 |
+
version = "2.3.6"
|
292 |
+
source = { registry = "https://pypi.org/simple" }
|
293 |
+
dependencies = [
|
294 |
+
{ name = "anyio" },
|
295 |
+
]
|
296 |
+
sdist = { url = "https://files.pythonhosted.org/packages/8c/f4/989bc70cb8091eda43a9034ef969b25145291f3601703b82766e5172dfed/sse_starlette-2.3.6.tar.gz", hash = "sha256:0382336f7d4ec30160cf9ca0518962905e1b69b72d6c1c995131e0a703b436e3", size = 18284, upload-time = "2025-05-30T13:34:12.914Z" }
|
297 |
+
wheels = [
|
298 |
+
{ url = "https://files.pythonhosted.org/packages/81/05/78850ac6e79af5b9508f8841b0f26aa9fd329a1ba00bf65453c2d312bcc8/sse_starlette-2.3.6-py3-none-any.whl", hash = "sha256:d49a8285b182f6e2228e2609c350398b2ca2c36216c2675d875f81e93548f760", size = 10606, upload-time = "2025-05-30T13:34:11.703Z" },
|
299 |
+
]
|
300 |
+
|
301 |
+
[[package]]
|
302 |
+
name = "starlette"
|
303 |
+
version = "0.47.0"
|
304 |
+
source = { registry = "https://pypi.org/simple" }
|
305 |
+
dependencies = [
|
306 |
+
{ name = "anyio" },
|
307 |
+
]
|
308 |
+
sdist = { url = "https://files.pythonhosted.org/packages/8b/d0/0332bd8a25779a0e2082b0e179805ad39afad642938b371ae0882e7f880d/starlette-0.47.0.tar.gz", hash = "sha256:1f64887e94a447fed5f23309fb6890ef23349b7e478faa7b24a851cd4eb844af", size = 2582856, upload-time = "2025-05-29T15:45:27.628Z" }
|
309 |
+
wheels = [
|
310 |
+
{ url = "https://files.pythonhosted.org/packages/e3/81/c60b35fe9674f63b38a8feafc414fca0da378a9dbd5fa1e0b8d23fcc7a9b/starlette-0.47.0-py3-none-any.whl", hash = "sha256:9d052d4933683af40ffd47c7465433570b4949dc937e20ad1d73b34e72f10c37", size = 72796, upload-time = "2025-05-29T15:45:26.305Z" },
|
311 |
+
]
|
312 |
+
|
313 |
+
[[package]]
|
314 |
+
name = "tutorx-mcp"
|
315 |
+
version = "0.1.0"
|
316 |
+
source = { virtual = "." }
|
317 |
+
dependencies = [
|
318 |
+
{ name = "mcp", extra = ["cli"] },
|
319 |
+
]
|
320 |
+
|
321 |
+
[package.metadata]
|
322 |
+
requires-dist = [{ name = "mcp", extras = ["cli"], specifier = ">=1.9.3" }]
|
323 |
+
|
324 |
+
[[package]]
|
325 |
+
name = "typer"
|
326 |
+
version = "0.16.0"
|
327 |
+
source = { registry = "https://pypi.org/simple" }
|
328 |
+
dependencies = [
|
329 |
+
{ name = "click" },
|
330 |
+
{ name = "rich" },
|
331 |
+
{ name = "shellingham" },
|
332 |
+
{ name = "typing-extensions" },
|
333 |
+
]
|
334 |
+
sdist = { url = "https://files.pythonhosted.org/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b", size = 102625, upload-time = "2025-05-26T14:30:31.824Z" }
|
335 |
+
wheels = [
|
336 |
+
{ url = "https://files.pythonhosted.org/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855", size = 46317, upload-time = "2025-05-26T14:30:30.523Z" },
|
337 |
+
]
|
338 |
+
|
339 |
+
[[package]]
|
340 |
+
name = "typing-extensions"
|
341 |
+
version = "4.14.0"
|
342 |
+
source = { registry = "https://pypi.org/simple" }
|
343 |
+
sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" }
|
344 |
+
wheels = [
|
345 |
+
{ url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" },
|
346 |
+
]
|
347 |
+
|
348 |
+
[[package]]
|
349 |
+
name = "typing-inspection"
|
350 |
+
version = "0.4.1"
|
351 |
+
source = { registry = "https://pypi.org/simple" }
|
352 |
+
dependencies = [
|
353 |
+
{ name = "typing-extensions" },
|
354 |
+
]
|
355 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" }
|
356 |
+
wheels = [
|
357 |
+
{ url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" },
|
358 |
+
]
|
359 |
+
|
360 |
+
[[package]]
|
361 |
+
name = "uvicorn"
|
362 |
+
version = "0.34.3"
|
363 |
+
source = { registry = "https://pypi.org/simple" }
|
364 |
+
dependencies = [
|
365 |
+
{ name = "click" },
|
366 |
+
{ name = "h11" },
|
367 |
+
]
|
368 |
+
sdist = { url = "https://files.pythonhosted.org/packages/de/ad/713be230bcda622eaa35c28f0d328c3675c371238470abdea52417f17a8e/uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a", size = 76631, upload-time = "2025-06-01T07:48:17.531Z" }
|
369 |
+
wheels = [
|
370 |
+
{ url = "https://files.pythonhosted.org/packages/6d/0d/8adfeaa62945f90d19ddc461c55f4a50c258af7662d34b6a3d5d1f8646f6/uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", size = 62431, upload-time = "2025-06-01T07:48:15.664Z" },
|
371 |
+
]
|