Eric Dai commited on
Commit
e51b1cb
·
1 Parent(s): dbef77f

Add travel mcp server and client

Browse files
Files changed (6) hide show
  1. README.md +96 -2
  2. mcp_config.json +11 -0
  3. requirements.txt +5 -0
  4. setup.sh +22 -0
  5. travel_mcp_client.py +50 -0
  6. travel_mcp_server.py +208 -0
README.md CHANGED
@@ -1,2 +1,96 @@
1
- # hf-mcp-2025
2
- Repo for Huggingface MCP hackathon 2025
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Travel Documentation MCP Server
2
+
3
+ > An AI-powered travel documentation assistant built with Gradio's MCP server capabilities and smolagents for the Hugging Face MCP Hackathon 2025.
4
+
5
+ ## What This Project Does
6
+
7
+ This project creates an intelligent travel documentation agent that helps travelers understand what documents they need for international trips. It consists of:
8
+
9
+ 1. **MCP Server** (`travel_mcp_server.py`) - A Gradio interface that exposes travel documentation analysis as an MCP tool, mocking data is used for demonstration
10
+ 2. **MCP Client** (`travel_mcp_client.py`) - An AI agent that uses smolagents to interact with the MCP server and provide conversational assistance
11
+
12
+
13
+ ## Technical Implementation
14
+ - **Gradio MCP Server**: Uses Gradio's built-in MCP server functionality
15
+ - **smolagents Integration**: AI agent framework for tool execution and conversation
16
+ - **Local LLM Support**: Uses Ollama for local AI inference
17
+
18
+ ## Installation & Setup
19
+
20
+ ### Prerequisites
21
+ - Python 3.8+
22
+ - Ollama (for local LLM)
23
+
24
+ ### 1. Install Dependencies
25
+ ```bash
26
+ pip install -r requirements.txt
27
+ ```
28
+
29
+ ### 2. Set up Ollama (for the AI agent)
30
+ ```bash
31
+ # Install Ollama from https://ollama.ai
32
+ # Pull the required model
33
+ ollama pull gemma3:4b
34
+ ```
35
+
36
+ ### 3. Start the MCP Server
37
+ ```bash
38
+ python travel_mcp_server.py
39
+ ```
40
+ The server will start at `http://127.0.0.1:7861` with MCP endpoint at `http://127.0.0.1:7861/gradio_api/mcp/sse`
41
+
42
+ ### 4. Start the AI Agent Client
43
+ ```bash
44
+ python travel_mcp_client.py
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ### Direct Web Interface
50
+ Visit `http://127.0.0.1:7861` to use the travel documentation tool directly:
51
+
52
+ **Input Parameters:**
53
+ - **Your Citizenship Country**: e.g., "Canada", "China", "India"
54
+ - **Destination Country**: e.g., "Japan", "USA", "Germany"
55
+ - **Trip Duration**: Number of days (1-365)
56
+ - **Trip Purpose**: tourism, business, study, transit, work, family visit
57
+
58
+ **Example Output:**
59
+ ```json
60
+ {
61
+ "trip_info": {
62
+ "from_country": "Canada",
63
+ "to_country": "Japan",
64
+ "duration_days": 30,
65
+ "purpose": "Tourism"
66
+ },
67
+ "visa_requirements": {
68
+ "visa_required": false,
69
+ "max_stay": "90 days"
70
+ },
71
+ "required_documents": [
72
+ {
73
+ "document_type": "Passport",
74
+ "required": true,
75
+ "description": "Valid passport with at least 6 months validity remaining"
76
+ }
77
+ ],
78
+ "summary": {
79
+ "required_count": 5,
80
+ "optional_count": 1,
81
+ "visa_needed": false
82
+ }
83
+ }
84
+ ```
85
+
86
+ ### AI Agent Chat Interface
87
+ Use the conversational agent for natural language queries:
88
+
89
+ **Example Conversations:**
90
+ ```
91
+ User: "I'm a Canadian citizen planning to visit Japan for 2 weeks for tourism"
92
+ Agent: "..."
93
+
94
+ User: "What documents do I need for business travel from China to USA?"
95
+ Agent: "..."
96
+ ```
mcp_config.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "servers": {
3
+ "travel-docs": {
4
+ "command": "python",
5
+ "args": ["travel_docs_server.py"],
6
+ "env": {
7
+ "PYTHONPATH": "."
8
+ }
9
+ }
10
+ }
11
+ }
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ mcp
2
+ gradio[mcp]
3
+ smolagents
4
+ smolagents[mcp]
5
+ fastmcp
setup.sh ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Setup script for Travel Documentation MCP Server
3
+
4
+ echo "Setting up Travel Documentation MCP Server"
5
+ echo "=============================================="
6
+
7
+ # Check Python version
8
+ python_version=$(python3 --version 2>&1)
9
+ echo "Python version: $python_version"
10
+
11
+ # Install dependencies
12
+ echo "Installing dependencies..."
13
+ pip3 install -r requirements.txt
14
+
15
+
16
+ echo "Setup complete!"
17
+ echo ""
18
+ echo "To run the applications:"
19
+ echo " MCP Server: python travel_mcp_server.py"
20
+ echo " MCP Client with Agent: python travel_mcp_client.py"
21
+ echo ""
22
+ echo "Access the web interface at: http://127.0.0.1:7860"
travel_mcp_client.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ from smolagents import CodeAgent, tool, LiteLLMModel
4
+ from smolagents.mcp_client import MCPClient
5
+
6
+ @tool
7
+ def self_introduction() -> str:
8
+ """
9
+ Provides information about the agent's identity and capabilities.
10
+
11
+ This tool should be triggered when the user asks questions like:
12
+ - "What's your name?"
13
+ - "Who are you?"
14
+ - "What can you do?"
15
+ - "Tell me about yourself"
16
+ - "What are your capabilities?"
17
+ - Any other introductory or identity-related queries
18
+
19
+ Returns:
20
+ str: A friendly introduction explaining the agent's purpose and capabilities. You are free to rewrite the introduction but need to keep the same meaning.
21
+ """
22
+ return "Hello! I am your travel documentation Agent. I can help you find out what documetations are required for your trip, get me your original coutry, destionation country, trip duration and purpose and I can help you."
23
+
24
+
25
+ mcp_client = MCPClient(
26
+ {"url": "http://127.0.0.1:7861/gradio_api/mcp/sse"}
27
+ )
28
+
29
+ try:
30
+ tools = mcp_client.get_tools()
31
+
32
+ # for local testing
33
+ model = LiteLLMModel(
34
+ model_id="ollama_chat/gemma3:4b",
35
+ api_base="http://127.0.0.1:11434",
36
+ num_ctx=8192,
37
+ )
38
+ agent = CodeAgent(tools=[*tools], model=model)
39
+
40
+ demo = gr.ChatInterface(
41
+ fn=lambda message, history: str(agent.run(message)),
42
+ type="messages",
43
+ examples=["Your trip plan..."],
44
+ title="Travel documentation agent",
45
+ description="This is a simple agent that uses MCP tools to help you find out required documentations for your international trip.",
46
+ )
47
+
48
+ demo.launch()
49
+ finally:
50
+ mcp_client.disconnect()
travel_mcp_server.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from typing import Dict, List, Any
3
+
4
+ class TravelDocumentationService:
5
+ """Service for fetching travel documentation requirements"""
6
+
7
+ def get_visa_requirements(
8
+ self,
9
+ from_country: str,
10
+ to_country: str,
11
+ trip_duration: int = 30
12
+ ) -> Dict[str, Any]:
13
+ """Get visa requirements for travel between countries"""
14
+
15
+ # Mock data
16
+ visa_free_combinations = {
17
+ ("canada", "japan"): {"visa_required": False, "max_stay": "90 days"},
18
+ ("canada", "uk"): {"visa_required": False, "max_stay": "6 months"},
19
+ ("canada", "usa"): {"visa_required": False, "max_stay": "6 months"},
20
+ ("canada", "germany"): {"visa_required": False, "max_stay": "90 days"},
21
+ ("usa", "japan"): {"visa_required": False, "max_stay": "90 days"},
22
+ ("usa", "uk"): {"visa_required": False, "max_stay": "6 months"},
23
+ ("usa", "germany"): {"visa_required": False, "max_stay": "90 days"},
24
+ ("china", "japan"): {"visa_required": True, "visa_type": "Tourist Visa", "max_stay": "30 days", "processing_time": "5-7 business days", "fee": "$30 USD"},
25
+ ("india", "japan"): {"visa_required": True, "visa_type": "Tourist Visa", "max_stay": "90 days", "processing_time": "5-10 business days", "fee": "$50 USD"},
26
+ ("china", "usa"): {"visa_required": True, "visa_type": "B-2 Tourist Visa", "max_stay": "6 months", "processing_time": "3-5 weeks", "fee": "$160 USD"},
27
+ ("india", "usa"): {"visa_required": True, "visa_type": "B-2 Tourist Visa", "max_stay": "6 months", "processing_time": "3-5 weeks", "fee": "$160 USD"},
28
+ }
29
+
30
+ key = (from_country.lower(), to_country.lower())
31
+
32
+ if key in visa_free_combinations:
33
+ return visa_free_combinations[key]
34
+ else:
35
+ return {
36
+ "visa_required": True,
37
+ "visa_type": "Tourist Visa",
38
+ "max_stay": "30 days",
39
+ "processing_time": "5-10 business days",
40
+ "fee": "$50-150 USD"
41
+ }
42
+
43
+ def get_document_requirements(
44
+ self,
45
+ from_country: str,
46
+ to_country: str,
47
+ trip_duration: int = 30,
48
+ trip_purpose: str = "tourism"
49
+ ) -> List[Dict[str, Any]]:
50
+ """Get comprehensive document requirements for travel"""
51
+
52
+ requirements = []
53
+
54
+ requirements.append({
55
+ "document_type": "Passport",
56
+ "required": True,
57
+ "description": "Valid passport with at least 6 months validity remaining",
58
+ "validity_period": "At least 6 months from travel date",
59
+ "additional_notes": "Must have at least 2 blank pages for stamps"
60
+ })
61
+
62
+ visa_info = self.get_visa_requirements(from_country, to_country, trip_duration)
63
+
64
+ if visa_info.get("visa_required", False):
65
+ requirements.append({
66
+ "document_type": "Visa",
67
+ "required": True,
68
+ "description": f"Required {visa_info.get('visa_type', 'Tourist Visa')}",
69
+ "processing_time": visa_info.get("processing_time", "5-10 business days"),
70
+ "additional_notes": f"Fee: {visa_info.get('fee', 'Varies by embassy')}"
71
+ })
72
+
73
+ insurance_required_countries = ["schengen", "germany", "france", "italy", "spain", "netherlands", "austria", "belgium"]
74
+ if to_country.lower() in insurance_required_countries:
75
+ requirements.append({
76
+ "document_type": "Travel Insurance",
77
+ "required": True,
78
+ "description": "Travel insurance with minimum €30,000 coverage",
79
+ "additional_notes": "Required for Schengen area countries"
80
+ })
81
+ else:
82
+ requirements.append({
83
+ "document_type": "Travel Insurance",
84
+ "required": False,
85
+ "description": "Travel insurance (highly recommended)",
86
+ "additional_notes": "Covers medical emergencies, trip cancellation, etc."
87
+ })
88
+
89
+ requirements.append({
90
+ "document_type": "Return/Onward Ticket",
91
+ "required": True,
92
+ "description": "Proof of return or onward travel",
93
+ "additional_notes": "Flight confirmation or travel itinerary"
94
+ })
95
+
96
+ financial_amount = "$100-150 per day" if to_country.lower() in ["japan", "switzerland", "norway"] else "$50-100 per day"
97
+ requirements.append({
98
+ "document_type": "Financial Proof",
99
+ "required": True,
100
+ "description": f"Proof of sufficient funds ({financial_amount})",
101
+ "additional_notes": "Bank statements, credit cards, or traveler's checks"
102
+ })
103
+
104
+ requirements.append({
105
+ "document_type": "Accommodation Proof",
106
+ "required": True,
107
+ "description": "Hotel booking or invitation letter",
108
+ "additional_notes": "Confirmation of where you'll be staying"
109
+ })
110
+
111
+ if trip_purpose.lower() == "business":
112
+ requirements.append({
113
+ "document_type": "Business Invitation Letter",
114
+ "required": True,
115
+ "description": "Letter from host company",
116
+ "additional_notes": "Must include company details and purpose of visit"
117
+ })
118
+ elif trip_purpose.lower() == "study":
119
+ requirements.append({
120
+ "document_type": "Student Visa/Permit",
121
+ "required": True,
122
+ "description": "Student visa or study permit",
123
+ "additional_notes": "Issued by educational institution"
124
+ })
125
+ requirements.append({
126
+ "document_type": "Acceptance Letter",
127
+ "required": True,
128
+ "description": "Letter of acceptance from educational institution",
129
+ "additional_notes": "Must be from recognized institution"
130
+ })
131
+
132
+ return requirements
133
+
134
+ travel_service = TravelDocumentationService()
135
+
136
+ def get_requirements(from_country, to_country, trip_duration, trip_purpose):
137
+ """
138
+ Analyze the documentation requirment for user's travel plan
139
+
140
+ Args:
141
+ from_country (str): User's original country
142
+ to_country (str): The destination country that the user plans to travel to
143
+ trip_duration (number): The days length that the user plans to stay in the destination country
144
+ trip_purpose (str): The purpose of the travel, the user is go for business, tourism, study or other purposes
145
+
146
+ Returns:
147
+ json: Contains the comprehensive documentation requires and suggestions based on the user input
148
+ """
149
+ try:
150
+ requirements = travel_service.get_document_requirements(
151
+ from_country, to_country, int(trip_duration), trip_purpose
152
+ )
153
+
154
+ visa_info = travel_service.get_visa_requirements(from_country, to_country, int(trip_duration))
155
+
156
+ required_docs = [req for req in requirements if req['required']]
157
+ optional_docs = [req for req in requirements if not req['required']]
158
+
159
+ result = {
160
+ "trip_info": {
161
+ "from_country": from_country.title(),
162
+ "to_country": to_country.title(),
163
+ "duration_days": int(trip_duration),
164
+ "purpose": trip_purpose.title()
165
+ },
166
+ "visa_requirements": {
167
+ "visa_required": visa_info.get("visa_required", False),
168
+ "visa_type": visa_info.get("visa_type"),
169
+ "max_stay": visa_info.get("max_stay"),
170
+ "processing_time": visa_info.get("processing_time"),
171
+ "fee": visa_info.get("fee")
172
+ },
173
+ "required_documents": required_docs,
174
+ "optional_documents": optional_docs,
175
+ "total_documents": len(requirements),
176
+ "summary": {
177
+ "required_count": len(required_docs),
178
+ "optional_count": len(optional_docs),
179
+ "visa_needed": visa_info.get("visa_required", False)
180
+ }
181
+ }
182
+
183
+ return result
184
+
185
+ except Exception as e:
186
+ return {"error": str(e)}
187
+
188
+
189
+ travel_demo = gr.Interface(
190
+ fn=get_requirements,
191
+ inputs=[
192
+ gr.Textbox(label="Your Citizenship Country", placeholder="e.g., Canada", value="Canada"),
193
+ gr.Textbox(label="Destination Country", placeholder="e.g., Japan", value="Japan"),
194
+ gr.Number(label="Trip Duration (days)", value=30, minimum=1, maximum=365),
195
+ gr.Dropdown(label="Trip Purpose", choices=["tourism", "business", "transit", "study", "work", "family visit"], value="tourism")
196
+ ],
197
+ outputs=gr.JSON(),
198
+ title="Travel Documentation Requirements",
199
+ description="Get the comprehensive travel documentation requirements for international travel"
200
+ )
201
+
202
+ if __name__ == "__main__":
203
+ travel_demo.launch(
204
+ # share=True,
205
+ show_error=True,
206
+ mcp_server=True,
207
+ server_port=7861
208
+ )