sumitasthaai commited on
Commit
928a62e
·
unverified ·
0 Parent(s):
Files changed (8) hide show
  1. .gitignore +10 -0
  2. .python-version +1 -0
  3. Dockerfile +36 -0
  4. README.md +0 -0
  5. main.py +6 -0
  6. pyproject.toml +10 -0
  7. server.py +159 -0
  8. uv.lock +0 -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.13
Dockerfile ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.13-slim
2
+
3
+ # Install uv
4
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
5
+
6
+ # Change the working directory to the `app` directory
7
+ WORKDIR /app
8
+
9
+ # Install dependencies (separate layer for better caching)
10
+ RUN --mount=type=cache,target=/root/.cache/uv \
11
+ --mount=type=bind,source=uv.lock,target=uv.lock \
12
+ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
13
+ uv sync --locked --no-install-project
14
+
15
+ # Copy the project into the image
16
+ COPY . /app
17
+
18
+ # Sync the project
19
+ RUN --mount=type=cache,target=/root/.cache/uv \
20
+ uv sync --locked
21
+
22
+ # Expose port 7860 for Hugging Face Spaces
23
+ EXPOSE 7860
24
+
25
+ # Set port environment variable for Hugging Face Spaces
26
+ ENV PORT=7860
27
+
28
+ # Set UV cache directory to writable location for non-root user
29
+ ENV UV_CACHE_DIR=/app/.cache/uv
30
+
31
+ # Create cache directory with proper permissions
32
+ RUN mkdir -p /app/.cache/uv && chmod -R 777 /app/.cache
33
+
34
+ # Run the server
35
+ CMD ["uv", "run", "server.py"]
36
+
README.md ADDED
File without changes
main.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ def main():
2
+ print("Hello from sumit-mcp-server!")
3
+
4
+
5
+ if __name__ == "__main__":
6
+ main()
pyproject.toml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "sumit-mcp-server"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ dependencies = [
8
+ "bs4>=0.0.2",
9
+ "fastmcp>=2.12.4",
10
+ ]
server.py ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from fastmcp import FastMCP
3
+ import os
4
+ import requests
5
+ from bs4 import BeautifulSoup
6
+ from urllib.parse import urljoin, urlparse
7
+ import json
8
+ from typing import List, Dict, Optional
9
+
10
+ mcp = FastMCP("Tatva Website Data Access 🌐")
11
+
12
+ BASE_URL = "https://tatva.sumityadav.com.np"
13
+
14
+ def _fetch_page_content(url: str) -> Dict[str, str]:
15
+ """Helper function to fetch and parse content from a specific page URL"""
16
+ try:
17
+ response = requests.get(url, timeout=10)
18
+ response.raise_for_status()
19
+
20
+ soup = BeautifulSoup(response.content, 'html.parser')
21
+
22
+ # Extract title
23
+ title = soup.find('title')
24
+ title_text = title.get_text().strip() if title else "No title found"
25
+
26
+ # Extract main content (try common content selectors)
27
+ content_selectors = ['main', 'article', '.content', '.post-content', '.entry-content', 'body']
28
+ content_text = ""
29
+
30
+ for selector in content_selectors:
31
+ content = soup.select_one(selector)
32
+ if content:
33
+ # Remove script and style elements
34
+ for script in content(["script", "style"]):
35
+ script.decompose()
36
+ content_text = content.get_text().strip()
37
+ break
38
+
39
+ if not content_text:
40
+ content_text = soup.get_text().strip()
41
+
42
+ return {
43
+ "url": url,
44
+ "title": title_text,
45
+ "content": content_text[:5000], # Limit content length
46
+ "status": "success"
47
+ }
48
+ except Exception as e:
49
+ return {
50
+ "url": url,
51
+ "title": "",
52
+ "content": "",
53
+ "status": f"error: {str(e)}"
54
+ }
55
+
56
+ @mcp.tool
57
+ def get_page_content(url: str) -> Dict[str, str]:
58
+ """Fetch and parse content from a specific page URL"""
59
+ return _fetch_page_content(url)
60
+
61
+ @mcp.tool
62
+ def get_homepage_content() -> Dict[str, str]:
63
+ """Get content from the homepage"""
64
+ return _fetch_page_content(BASE_URL)
65
+
66
+ @mcp.tool
67
+ def get_about_page() -> Dict[str, str]:
68
+ """Get content from the about page"""
69
+ return _fetch_page_content(f"{BASE_URL}/about/")
70
+
71
+ @mcp.tool
72
+ def get_post_content(post_path: str) -> Dict[str, str]:
73
+ """Get content from a specific blog post by providing the post path (e.g., '2025/09/22/advance_scanner/')"""
74
+ url = f"{BASE_URL}/posts/{post_path}"
75
+ return _fetch_page_content(url)
76
+
77
+ @mcp.tool
78
+ def get_all_posts_summary() -> List[Dict[str, str]]:
79
+ """Get a summary of all available blog posts"""
80
+ posts = [
81
+ {"path": "2025/09/22/advance_scanner/", "title": "Advance Scanner"},
82
+ {"path": "2025/09/16/layers-travel/", "title": "Layers Travel"},
83
+ {"path": "2025/09/13/The Legend of Jimutavahana: A Detailed Story of Sacrifice and Compassion/", "title": "The Legend of Jimutavahana"},
84
+ {"path": "2025/09/13/mcp-proxy-sse-to-sse/", "title": "MCP Proxy SSE to SSE"},
85
+ {"path": "2025/09/05/embeddinggemma-300m/", "title": "EmbeddingGemma 300M"},
86
+ {"path": "2025/09/02/Ten Mahavidyas/", "title": "Ten Mahavidyas"},
87
+ {"path": "2025/08/06/gpt-oss/", "title": "GPT OSS"},
88
+ {"path": "2025/08/10/Kakbhushundi and Thoth/", "title": "Kakbhushundi and Thoth"},
89
+ {"path": "2025/07/29/lagpachai-maithili-festival-types-of-nagas/", "title": "Lagpachai Maithili Festival Types of Nagas"},
90
+ {"path": "2025/07/23/The Churia Forests/", "title": "The Churia Forests"},
91
+ {"path": "2025/07/17/The Brain's Thinking Cycle: From Thought to Reply/", "title": "The Brain's Thinking Cycle"},
92
+ {"path": "2025/07/14/clock/", "title": "Clock"},
93
+ {"path": "2025/07/13/WanderingMind/", "title": "Wandering Mind"},
94
+ {"path": "2025/07/07/love/", "title": "Love"},
95
+ {"path": "2025/07/06/Go:Backend Programming For ML Service/", "title": "Go: Backend Programming For ML Service"},
96
+ {"path": "2025/01/07/tatva-development-journey/", "title": "Tatva Development Journey"},
97
+ {"path": "2025/06/02/agentsAI/", "title": "Agents AI"},
98
+ {"path": "2025/06/02/bio/", "title": "Bio"},
99
+ {"path": "2025/06/03/tatva/", "title": "Tatva"},
100
+ {"path": "2024/06/01/agents/", "title": "Agents"},
101
+ {"path": "2024/05/30/lines/", "title": "Lines"},
102
+ {"path": "2024/01/21/life/", "title": "Life"},
103
+ {"path": "2025/05/30/familyTree/", "title": "Family Tree"},
104
+ {"path": "2021/10/07/dashain/", "title": "Dashain"},
105
+ {"path": "2025/09/03/moments/", "title": "Moments"}
106
+ ]
107
+ return posts
108
+
109
+ @mcp.tool
110
+ def search_posts_by_keyword(keyword: str) -> List[Dict[str, str]]:
111
+ """Search for posts containing a specific keyword in title or path"""
112
+ all_posts = get_all_posts_summary()
113
+ keyword_lower = keyword.lower()
114
+
115
+ matching_posts = []
116
+ for post in all_posts:
117
+ if keyword_lower in post["title"].lower() or keyword_lower in post["path"].lower():
118
+ matching_posts.append(post)
119
+
120
+ return matching_posts
121
+
122
+ @mcp.tool
123
+ def get_posts_by_year(year: str) -> List[Dict[str, str]]:
124
+ """Get all posts from a specific year"""
125
+ all_posts = get_all_posts_summary()
126
+ year_posts = [post for post in all_posts if post["path"].startswith(f"{year}/")]
127
+ return year_posts
128
+
129
+ @mcp.resource("tatva://homepage")
130
+ def get_homepage_resource():
131
+ """Resource for homepage content"""
132
+ content = get_homepage_content()
133
+ return json.dumps(content, indent=2)
134
+
135
+ @mcp.resource("tatva://about")
136
+ def get_about_resource():
137
+ """Resource for about page content"""
138
+ content = get_about_page()
139
+ return json.dumps(content, indent=2)
140
+
141
+ @mcp.resource("tatva://posts/{post_path}")
142
+ def get_post_resource(post_path: str):
143
+ """Resource for specific post content"""
144
+ content = get_post_content(post_path)
145
+ return json.dumps(content, indent=2)
146
+
147
+ @mcp.prompt
148
+ def analyze_post_prompt(post_path: str) -> str:
149
+ """Generate a prompt for analyzing a specific blog post"""
150
+ return f"Please analyze the following blog post from Tatva website and provide insights about its content, themes, and key takeaways. Post path: {post_path}"
151
+
152
+ @mcp.prompt
153
+ def compare_posts_prompt(post_path1: str, post_path2: str) -> str:
154
+ """Generate a prompt for comparing two blog posts"""
155
+ return f"Please compare and contrast these two blog posts from Tatva website, highlighting similarities, differences, and unique perspectives. Post 1: {post_path1}, Post 2: {post_path2}"
156
+
157
+ if __name__ == "__main__":
158
+ port = int(os.environ.get("PORT", 7860))
159
+ mcp.run(transport="sse", host="0.0.0.0", port=port,path="/api/mcp/")
uv.lock ADDED
The diff for this file is too large to render. See raw diff