portfolio-mcp-server 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint-and-test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12"]
15
+
16
+ steps:
17
+ - name: Check out repository
18
+ uses: actions/checkout@v4
19
+
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+ cache: "pip"
25
+
26
+ - name: Install dependencies
27
+ run: |
28
+ python -m pip install --upgrade pip
29
+ pip install -r requirements.txt
30
+ pip install -r requirements-dev.txt
31
+
32
+ - name: Lint (ruff)
33
+ run: ruff check .
34
+
35
+ - name: Type check (mypy)
36
+ run: mypy server.py
37
+
38
+ - name: Run tests
39
+ run: pytest -v
40
+
41
+ - name: Smoke test MCP tools over stdio
42
+ run: python -m tests.smoke_test
@@ -0,0 +1,10 @@
1
+ venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .env
5
+
6
+ # Packaging build artifacts
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ .pytest_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ayush Tomar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: portfolio-mcp-server
3
+ Version: 1.0.0
4
+ Summary: MCP server exposing Ayush Tomar's AI project portfolio as queryable tools for Claude Desktop and other MCP clients
5
+ Project-URL: Homepage, https://github.com/ayush-s-tomar/portfolio-mcp-server
6
+ Project-URL: Repository, https://github.com/ayush-s-tomar/portfolio-mcp-server
7
+ Project-URL: Issues, https://github.com/ayush-s-tomar/portfolio-mcp-server/issues
8
+ Author: Ayush Tomar
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agents,claude,mcp,model-context-protocol,portfolio
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: mcp[cli]>=1.0.0
20
+ Description-Content-Type: text/markdown
21
+
22
+ <div align="center">
23
+
24
+ # Portfolio MCP Server
25
+
26
+ **An MCP server that turns my AI project portfolio into something you can *query*, not just read.**
27
+
28
+ Point any MCP client (Claude Desktop, Cursor, custom agents) at it and ask
29
+ *"What has Ayush built with LangGraph?"* or *"What's his flagship project?"*
30
+ — it answers from live structured data, not a static PDF.
31
+
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
33
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
34
+ [![MCP](https://img.shields.io/badge/protocol-MCP-5A67D8.svg)](https://modelcontextprotocol.io)
35
+ [![CI](https://github.com/ayush-s-tomar/portfolio-mcp-server/actions/workflows/ci.yml/badge.svg)](https://github.com/ayush-s-tomar/portfolio-mcp-server/actions/workflows/ci.yml)
36
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-261230.svg)](https://github.com/astral-sh/ruff)
37
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
38
+
39
+ <img src="assets/MCP.gif" alt="Portfolio MCP Server demo" width="720">
40
+
41
+ [Watch the full walkthrough ▶](https://github.com/user-attachments/assets/4c1b844a-087f-48a6-b156-bdef27282acc)
42
+
43
+ </div>
44
+
45
+ ---
46
+
47
+ ## Table of contents
48
+
49
+ - [Why this exists](#why-this-exists)
50
+ - [Demo](#demo)
51
+ - [Tools exposed](#tools-exposed)
52
+ - [Quickstart](#quickstart)
53
+ - [Connect to Claude Desktop](#connect-to-claude-desktop)
54
+ - [Stack](#stack)
55
+ - [Testing & CI](#testing--ci)
56
+ - [Project structure](#project-structure)
57
+ - [Roadmap](#roadmap)
58
+ - [License](#license)
59
+ - [Author](#author)
60
+
61
+ ## Why this exists
62
+
63
+ Most AI-developer portfolios are a list of links. This is a working MCP
64
+ server — the same protocol agentic products use to connect to tools — built
65
+ around my own portfolio. It's both a real implementation of the spec and an
66
+ answer to *"show me you've actually built with MCP,"* not just talked about it.
67
+
68
+ ## Demo
69
+
70
+ | MCP Inspector — tool discovery | Live chat demo |
71
+ |---|---|
72
+ | ![MCP Inspector Demo](assets/MCP_Inspector_Demo.png) | ![MCP Chat Demo](assets/MCP_Chat_Demo.png) |
73
+
74
+ Full video walkthrough (setup → tool calls → live answers): **[watch here ▶](https://github.com/user-attachments/assets/4c1b844a-087f-48a6-b156-bdef27282acc)**
75
+
76
+ ## Tools exposed
77
+
78
+ | Tool | Description |
79
+ |---|---|
80
+ | `list_projects` | Short summary of all 9 projects |
81
+ | `get_project_details(project_name)` | Full details for one project |
82
+ | `search_projects_by_stack(technology)` | Find projects using a given technology |
83
+ | `get_flagship_project` | The single best project to look at first |
84
+ | `get_resume_summary` | Background, target role, and core stack |
85
+
86
+ ## Quickstart
87
+
88
+ ```bash
89
+ git clone https://github.com/ayush-s-tomar/portfolio-mcp-server.git
90
+ cd portfolio-mcp-server
91
+
92
+ python -m venv venv
93
+ source venv/bin/activate # Windows: venv\Scripts\activate
94
+
95
+ pip install -r requirements.txt
96
+ ```
97
+
98
+ Test it interactively with the MCP Inspector before wiring it into a client:
99
+
100
+ ```bash
101
+ mcp dev server.py
102
+ ```
103
+
104
+ This opens a browser UI where you can call each tool manually and inspect
105
+ raw request/response payloads.
106
+
107
+ ## Connect to Claude Desktop
108
+
109
+ Open your Claude Desktop config file:
110
+
111
+ | OS | Path |
112
+ |---|---|
113
+ | macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
114
+ | Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
115
+
116
+ If the file already has an `mcpServers` key with other servers in it, add
117
+ the `"portfolio"` entry inside the existing object rather than overwriting
118
+ the file. Use the **absolute path** to `server.py` on your machine:
119
+
120
+ ```json
121
+ {
122
+ "mcpServers": {
123
+ "portfolio": {
124
+ "command": "python",
125
+ "args": ["/absolute/path/to/portfolio-mcp-server/server.py"]
126
+ }
127
+ }
128
+ }
129
+ ```
130
+
131
+ Restart Claude Desktop, then ask it something like:
132
+
133
+ > "What projects has Ayush built with FastAPI?"
134
+
135
+ Claude will call `search_projects_by_stack` and answer from the live data.
136
+
137
+ ## Stack
138
+
139
+ - **Python 3.10+**
140
+ - **[MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk)** (`FastMCP`)
141
+ - **stdio transport**
142
+
143
+ ## Testing & CI
144
+
145
+ Every push and pull request runs through GitHub Actions:
146
+
147
+ - **Lint** — `ruff check .`
148
+ - **Type check** — `mypy server.py`
149
+ - **Smoke test** — spins up the server and calls each of the 5 tools over
150
+ stdio to confirm they return valid, schema-matching JSON
151
+
152
+ See [`.github/workflows/ci.yml`](.github/workflows/ci.yml). Run the same
153
+ checks locally before opening a PR:
154
+
155
+ ```bash
156
+ pip install -r requirements-dev.txt
157
+ ruff check .
158
+ mypy server.py
159
+ pytest
160
+ ```
161
+
162
+ ## Project structure
163
+ portfolio-mcp-server/
164
+ ├── server.py # FastMCP server + tool definitions
165
+ ├── data/
166
+ │ └── projects.json # Project data the tools read from
167
+ ├── tests/
168
+ │ └── test_tools.py # Smoke tests for each tool
169
+ ├── requirements.txt
170
+ ├── requirements-dev.txt
171
+ └── .github/workflows/ci.yml
172
+ ## Roadmap
173
+
174
+ - [ ] `search_projects_by_stack` — support matching on multiple technologies at once
175
+ - [ ] Add an HTTP/SSE transport option alongside stdio for remote clients
176
+ - [ ] Publish to the MCP server registry
177
+ - [ ] Cache resume/project data with a lightweight refresh endpoint instead of static JSON
178
+
179
+ ## License
180
+
181
+ Released under the [MIT License](LICENSE).
182
+
183
+ ## Author
184
+
185
+ **Ayush Tomar** — [GitHub](https://github.com/ayush-s-tomar)
186
+
187
+ If this was useful as a reference for building your own MCP server, a ⭐ on the repo is appreciated.
@@ -0,0 +1,166 @@
1
+ <div align="center">
2
+
3
+ # Portfolio MCP Server
4
+
5
+ **An MCP server that turns my AI project portfolio into something you can *query*, not just read.**
6
+
7
+ Point any MCP client (Claude Desktop, Cursor, custom agents) at it and ask
8
+ *"What has Ayush built with LangGraph?"* or *"What's his flagship project?"*
9
+ — it answers from live structured data, not a static PDF.
10
+
11
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
12
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
13
+ [![MCP](https://img.shields.io/badge/protocol-MCP-5A67D8.svg)](https://modelcontextprotocol.io)
14
+ [![CI](https://github.com/ayush-s-tomar/portfolio-mcp-server/actions/workflows/ci.yml/badge.svg)](https://github.com/ayush-s-tomar/portfolio-mcp-server/actions/workflows/ci.yml)
15
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-261230.svg)](https://github.com/astral-sh/ruff)
16
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
17
+
18
+ <img src="assets/MCP.gif" alt="Portfolio MCP Server demo" width="720">
19
+
20
+ [Watch the full walkthrough ▶](https://github.com/user-attachments/assets/4c1b844a-087f-48a6-b156-bdef27282acc)
21
+
22
+ </div>
23
+
24
+ ---
25
+
26
+ ## Table of contents
27
+
28
+ - [Why this exists](#why-this-exists)
29
+ - [Demo](#demo)
30
+ - [Tools exposed](#tools-exposed)
31
+ - [Quickstart](#quickstart)
32
+ - [Connect to Claude Desktop](#connect-to-claude-desktop)
33
+ - [Stack](#stack)
34
+ - [Testing & CI](#testing--ci)
35
+ - [Project structure](#project-structure)
36
+ - [Roadmap](#roadmap)
37
+ - [License](#license)
38
+ - [Author](#author)
39
+
40
+ ## Why this exists
41
+
42
+ Most AI-developer portfolios are a list of links. This is a working MCP
43
+ server — the same protocol agentic products use to connect to tools — built
44
+ around my own portfolio. It's both a real implementation of the spec and an
45
+ answer to *"show me you've actually built with MCP,"* not just talked about it.
46
+
47
+ ## Demo
48
+
49
+ | MCP Inspector — tool discovery | Live chat demo |
50
+ |---|---|
51
+ | ![MCP Inspector Demo](assets/MCP_Inspector_Demo.png) | ![MCP Chat Demo](assets/MCP_Chat_Demo.png) |
52
+
53
+ Full video walkthrough (setup → tool calls → live answers): **[watch here ▶](https://github.com/user-attachments/assets/4c1b844a-087f-48a6-b156-bdef27282acc)**
54
+
55
+ ## Tools exposed
56
+
57
+ | Tool | Description |
58
+ |---|---|
59
+ | `list_projects` | Short summary of all 9 projects |
60
+ | `get_project_details(project_name)` | Full details for one project |
61
+ | `search_projects_by_stack(technology)` | Find projects using a given technology |
62
+ | `get_flagship_project` | The single best project to look at first |
63
+ | `get_resume_summary` | Background, target role, and core stack |
64
+
65
+ ## Quickstart
66
+
67
+ ```bash
68
+ git clone https://github.com/ayush-s-tomar/portfolio-mcp-server.git
69
+ cd portfolio-mcp-server
70
+
71
+ python -m venv venv
72
+ source venv/bin/activate # Windows: venv\Scripts\activate
73
+
74
+ pip install -r requirements.txt
75
+ ```
76
+
77
+ Test it interactively with the MCP Inspector before wiring it into a client:
78
+
79
+ ```bash
80
+ mcp dev server.py
81
+ ```
82
+
83
+ This opens a browser UI where you can call each tool manually and inspect
84
+ raw request/response payloads.
85
+
86
+ ## Connect to Claude Desktop
87
+
88
+ Open your Claude Desktop config file:
89
+
90
+ | OS | Path |
91
+ |---|---|
92
+ | macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
93
+ | Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
94
+
95
+ If the file already has an `mcpServers` key with other servers in it, add
96
+ the `"portfolio"` entry inside the existing object rather than overwriting
97
+ the file. Use the **absolute path** to `server.py` on your machine:
98
+
99
+ ```json
100
+ {
101
+ "mcpServers": {
102
+ "portfolio": {
103
+ "command": "python",
104
+ "args": ["/absolute/path/to/portfolio-mcp-server/server.py"]
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ Restart Claude Desktop, then ask it something like:
111
+
112
+ > "What projects has Ayush built with FastAPI?"
113
+
114
+ Claude will call `search_projects_by_stack` and answer from the live data.
115
+
116
+ ## Stack
117
+
118
+ - **Python 3.10+**
119
+ - **[MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk)** (`FastMCP`)
120
+ - **stdio transport**
121
+
122
+ ## Testing & CI
123
+
124
+ Every push and pull request runs through GitHub Actions:
125
+
126
+ - **Lint** — `ruff check .`
127
+ - **Type check** — `mypy server.py`
128
+ - **Smoke test** — spins up the server and calls each of the 5 tools over
129
+ stdio to confirm they return valid, schema-matching JSON
130
+
131
+ See [`.github/workflows/ci.yml`](.github/workflows/ci.yml). Run the same
132
+ checks locally before opening a PR:
133
+
134
+ ```bash
135
+ pip install -r requirements-dev.txt
136
+ ruff check .
137
+ mypy server.py
138
+ pytest
139
+ ```
140
+
141
+ ## Project structure
142
+ portfolio-mcp-server/
143
+ ├── server.py # FastMCP server + tool definitions
144
+ ├── data/
145
+ │ └── projects.json # Project data the tools read from
146
+ ├── tests/
147
+ │ └── test_tools.py # Smoke tests for each tool
148
+ ├── requirements.txt
149
+ ├── requirements-dev.txt
150
+ └── .github/workflows/ci.yml
151
+ ## Roadmap
152
+
153
+ - [ ] `search_projects_by_stack` — support matching on multiple technologies at once
154
+ - [ ] Add an HTTP/SSE transport option alongside stdio for remote clients
155
+ - [ ] Publish to the MCP server registry
156
+ - [ ] Cache resume/project data with a lightweight refresh endpoint instead of static JSON
157
+
158
+ ## License
159
+
160
+ Released under the [MIT License](LICENSE).
161
+
162
+ ## Author
163
+
164
+ **Ayush Tomar** — [GitHub](https://github.com/ayush-s-tomar)
165
+
166
+ If this was useful as a reference for building your own MCP server, a ⭐ on the repo is appreciated.
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "portfolio": {
4
+ "command": "python",
5
+ "args": ["/absolute/path/to/portfolio-mcp-server/server.py"]
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,3 @@
1
+ """Portfolio MCP Server — exposes Ayush Tomar's AI project portfolio as MCP tools."""
2
+
3
+ __version__ = "1.0.0"
@@ -0,0 +1,250 @@
1
+ """
2
+ Portfolio MCP Server
3
+ ---------------------
4
+ Exposes Ayush Tomar's project portfolio as MCP tools so any MCP-compatible
5
+ client (Claude Desktop, etc.) can query it interactively.
6
+
7
+ Run locally with the inspector:
8
+ mcp dev server.py
9
+
10
+ Run standalone (stdio transport, used by Claude Desktop):
11
+ python server.py
12
+ """
13
+
14
+ from mcp.server.fastmcp import FastMCP
15
+
16
+ mcp = FastMCP("portfolio-server")
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # Data — edit this to keep it current. Later you can swap this for a JSON
20
+ # file or a small DB without changing any tool signatures below.
21
+ # ---------------------------------------------------------------------------
22
+
23
+ PROJECTS: list[dict] = [
24
+ {
25
+ "name": "SalesAgent",
26
+ "description": (
27
+ "Autonomous B2B sales agent. Paste a LinkedIn URL, it researches "
28
+ "the lead, scores them with ML (84/100), and drafts a hyper-"
29
+ "personalized cold email referencing real company events, in "
30
+ "under 45 seconds."
31
+ ),
32
+ "stack": ["LangGraph", "FastAPI", "React", "scikit-learn", "Groq", "Tavily"],
33
+ "github": "https://github.com/ayush-s-tomar/salesagent",
34
+ "demo": "https://salesagent-ai.streamlit.app/",
35
+ "writeup": "https://dev.to/ayushsinghtomar/i-got-tired-of-writing-cold-emails-so-i-built-an-ai-agent-to-do-it-for-me-2m4h",
36
+ "flagship": True,
37
+ },
38
+ {
39
+ "name": "AgentLoop",
40
+ "description": (
41
+ "A multi-step research agent that breaks a question into "
42
+ "sub-questions, searches the live web, reflects on gaps, loops "
43
+ "back, and delivers a fully cited report."
44
+ ),
45
+ "stack": ["FastAPI", "LangGraph", "Groq", "Tavily"],
46
+ "github": "https://github.com/ayush-s-tomar/agentloop",
47
+ "demo": "https://agentloop.onrender.com/",
48
+ "writeup": None,
49
+ "flagship": False,
50
+ },
51
+ {
52
+ "name": "AskMyDocs",
53
+ "description": (
54
+ "RAG pipeline that answers questions over 50-page PDFs in under "
55
+ "3 seconds, with source citations and cosine similarity scores. "
56
+ "No SQL, no code."
57
+ ),
58
+ "stack": ["Next.js", "Supabase", "pgvector", "Cohere"],
59
+ "github": "https://github.com/ayush-s-tomar/intellect-docs-ai",
60
+ "demo": "https://intellect-docs-ai.vercel.app/",
61
+ "writeup": None,
62
+ "flagship": False,
63
+ },
64
+ {
65
+ "name": "AI Data Analyst Agent",
66
+ "description": (
67
+ "Upload any CSV, ask questions in plain English, get instant "
68
+ "charts and insights. No SQL. No code."
69
+ ),
70
+ "stack": ["FastAPI", "React", "Groq", "pandas", "matplotlib"],
71
+ "github": "https://github.com/ayush-s-tomar/ai-data-analyst",
72
+ "demo": "https://ai-data-analyst-six-sooty.vercel.app/",
73
+ "writeup": None,
74
+ "flagship": False,
75
+ },
76
+ {
77
+ "name": "JobHunt",
78
+ "description": (
79
+ "Multi-user Telegram job aggregator. AI scores every post and "
80
+ "auto-applies via email or form-fill. Watches job channels 24/7."
81
+ ),
82
+ "stack": ["FastAPI", "PostgreSQL", "Groq", "Playwright"],
83
+ "github": "https://github.com/ayush-s-tomar/jobhunt",
84
+ "demo": "https://jobhunt-demo.vercel.app/",
85
+ "writeup": None,
86
+ "flagship": False,
87
+ },
88
+ {
89
+ "name": "Email Agent",
90
+ "description": (
91
+ "AI agent that reads Gmail, classifies emails, drafts context-"
92
+ "aware replies, and lets you approve or edit before sending."
93
+ ),
94
+ "stack": ["FastAPI", "React", "LLaMA 3.3"],
95
+ "github": "https://github.com/ayush-s-tomar/Email-agent",
96
+ "demo": "https://ai-inbox-agent.streamlit.app/",
97
+ "writeup": None,
98
+ "flagship": False,
99
+ },
100
+ {
101
+ "name": "ARIA - Voice AI Assistant",
102
+ "description": (
103
+ "Speech-to-speech AI assistant with 99-language support and "
104
+ "conversation memory. Speak in any language, ARIA transcribes, "
105
+ "thinks, and talks back."
106
+ ),
107
+ "stack": ["FastAPI", "Faster-Whisper", "Groq", "LLaMA", "gTTS"],
108
+ "github": "https://github.com/ayush-s-tomar/aria-voice-assistant",
109
+ "demo": "https://aria-bot.streamlit.app/",
110
+ "writeup": None,
111
+ "flagship": False,
112
+ },
113
+ {
114
+ "name": "StartupScope",
115
+ "description": (
116
+ "Multi-agent CrewAI crew: Researcher, Analyst, and Writer agents "
117
+ "collaborate to search the web and generate structured startup "
118
+ "intelligence reports."
119
+ ),
120
+ "stack": ["CrewAI", "Groq", "SerperDev"],
121
+ "github": "https://github.com/ayush-s-tomar/startupscope",
122
+ "demo": "https://startupscope-ephq.onrender.com/",
123
+ "writeup": None,
124
+ "flagship": False,
125
+ },
126
+ {
127
+ "name": "n8n Email to Slack",
128
+ "description": (
129
+ "No-code AI automation pipeline: fetches unread Gmail, "
130
+ "summarizes with Groq LLaMA, detects priority, pushes digest to "
131
+ "Slack."
132
+ ),
133
+ "stack": ["n8n", "Groq", "Gmail", "Slack"],
134
+ "github": "https://github.com/ayush-s-tomar/n8n-email-slack",
135
+ "demo": None,
136
+ "writeup": None,
137
+ "flagship": False,
138
+ },
139
+ {
140
+ "name": "ResumeIQ",
141
+ "description": (
142
+ "AI resume screener that scores ATS compatibility, identifies "
143
+ "gaps, and exports detailed PDF reports."
144
+ ),
145
+ "stack": ["Python", "Flask", "Groq"],
146
+ "github": "https://github.com/ayush-s-tomar/ResumeIQ",
147
+ "demo": "https://resumeiq-55h8.onrender.com/",
148
+ "writeup": None,
149
+ "flagship": False,
150
+ },
151
+ ]
152
+
153
+ PROFILE = {
154
+ "name": "Ayush Tomar",
155
+ "role_target": "AI Developer (early-stage startups)",
156
+ "education": "B.Tech IT, MITS Gwalior (2023-2027, final year)",
157
+ "core_stack": [
158
+ "Python", "LangChain", "LangGraph", "CrewAI", "FastAPI", "Groq",
159
+ "Supabase pgvector", "Tavily", "React", "Render", "Vercel",
160
+ ],
161
+ "github": "https://github.com/ayush-s-tomar",
162
+ "highlights": [
163
+ "9 independently built and deployed AI projects, no prior internship",
164
+ "Built and shipped an MCP server (this one) to expose the portfolio itself",
165
+ "Active LinkedIn cold-outreach campaign targeting Indian AI startups",
166
+ ],
167
+ }
168
+
169
+
170
+ # ---------------------------------------------------------------------------
171
+ # Tools
172
+ # ---------------------------------------------------------------------------
173
+
174
+ @mcp.tool()
175
+ def list_projects() -> list[dict]:
176
+ """List all projects in Ayush's portfolio with a short summary of each.
177
+
178
+ Returns a compact list (name, one-line description, stack) — use
179
+ get_project_details for the full description, GitHub link, and demo URL.
180
+ """
181
+ return [
182
+ {
183
+ "name": p["name"],
184
+ "description": p["description"][:120] + "...",
185
+ "stack": p["stack"],
186
+ }
187
+ for p in PROJECTS
188
+ ]
189
+
190
+
191
+ @mcp.tool()
192
+ def get_project_details(project_name: str) -> dict:
193
+ """Get full details for a single project by name (case-insensitive,
194
+ partial match allowed, e.g. 'sales' matches 'SalesAgent').
195
+
196
+ Returns description, full stack, GitHub repo, live demo link, and
197
+ writeup link if one exists.
198
+ """
199
+ query = project_name.lower().strip()
200
+ for p in PROJECTS:
201
+ if query in p["name"].lower():
202
+ return p
203
+ return {
204
+ "error": f"No project found matching '{project_name}'",
205
+ "available_projects": [p["name"] for p in PROJECTS],
206
+ }
207
+
208
+
209
+ @mcp.tool()
210
+ def search_projects_by_stack(technology: str) -> list[dict]:
211
+ """Find all projects that use a given technology or tool, e.g.
212
+ 'LangGraph', 'Groq', 'FastAPI', 'React'. Case-insensitive, partial match.
213
+
214
+ Useful for answering "does Ayush have experience with X?"
215
+ """
216
+ query = technology.lower().strip()
217
+ matches = [
218
+ {"name": p["name"], "stack": p["stack"], "github": p["github"]}
219
+ for p in PROJECTS
220
+ if any(query in tech.lower() for tech in p["stack"])
221
+ ]
222
+ if not matches:
223
+ return [{"message": f"No projects found using '{technology}'"}]
224
+ return matches
225
+
226
+
227
+ @mcp.tool()
228
+ def get_flagship_project() -> dict:
229
+ """Get Ayush's flagship/best project — the one to look at first for a
230
+ quick sense of his skill level."""
231
+ for p in PROJECTS:
232
+ if p.get("flagship"):
233
+ return p
234
+ return PROJECTS[0]
235
+
236
+
237
+ @mcp.tool()
238
+ def get_resume_summary() -> dict:
239
+ """Get a summary of Ayush's background: education, target role, core
240
+ tech stack, and career highlights."""
241
+ return PROFILE
242
+
243
+
244
+ def main() -> None:
245
+ """Entry point used by the `portfolio-mcp-server` console script."""
246
+ mcp.run(transport="stdio")
247
+
248
+
249
+ if __name__ == "__main__":
250
+ main()
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "portfolio-mcp-server"
7
+ version = "1.0.0"
8
+ description = "MCP server exposing Ayush Tomar's AI project portfolio as queryable tools for Claude Desktop and other MCP clients"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Ayush Tomar" }
14
+ ]
15
+ keywords = ["mcp", "model-context-protocol", "portfolio", "ai-agents", "claude"]
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "License :: OSI Approved :: MIT License",
22
+ "Operating System :: OS Independent",
23
+ ]
24
+ dependencies = [
25
+ "mcp[cli]>=1.0.0",
26
+ ]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/ayush-s-tomar/portfolio-mcp-server"
30
+ Repository = "https://github.com/ayush-s-tomar/portfolio-mcp-server"
31
+ Issues = "https://github.com/ayush-s-tomar/portfolio-mcp-server/issues"
32
+
33
+ [project.scripts]
34
+ portfolio-mcp-server = "portfolio_mcp_server.server:main"
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["portfolio_mcp_server"]
@@ -0,0 +1,3 @@
1
+ ruff>=0.6.0
2
+ mypy>=1.11.0
3
+ pytest>=8.0.0
@@ -0,0 +1 @@
1
+ mcp[cli]>=1.0.0
@@ -0,0 +1,18 @@
1
+ """
2
+ Portfolio MCP Server -- root-level entry point.
3
+
4
+ The actual implementation lives in portfolio_mcp_server/server.py so the
5
+ project can be packaged and published to PyPI / the MCP Registry. This file
6
+ is kept so existing instructions keep working unchanged:
7
+
8
+ mcp dev server.py
9
+ python server.py
10
+
11
+ and so the Claude Desktop config snippet (which points at this file's
12
+ absolute path) continues to work without edits.
13
+ """
14
+
15
+ from portfolio_mcp_server.server import main, mcp # noqa: F401
16
+
17
+ if __name__ == "__main__":
18
+ main()
File without changes
@@ -0,0 +1,69 @@
1
+ """
2
+ End-to-end smoke test: spins up the portfolio MCP server as a subprocess
3
+ and calls each of the 5 tools over real stdio transport, confirming each
4
+ returns valid, non-empty JSON-serializable output.
5
+
6
+ Run with:
7
+ python -m tests.smoke_test
8
+ """
9
+
10
+ import asyncio
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ from mcp import ClientSession, StdioServerParameters
15
+ from mcp.client.stdio import stdio_client
16
+
17
+ SERVER_PATH = str(Path(__file__).resolve().parent.parent / "server.py")
18
+
19
+ EXPECTED_TOOLS = {
20
+ "list_projects",
21
+ "get_project_details",
22
+ "search_projects_by_stack",
23
+ "get_flagship_project",
24
+ "get_resume_summary",
25
+ }
26
+
27
+
28
+ async def run_smoke_test() -> None:
29
+ params = StdioServerParameters(command=sys.executable, args=[SERVER_PATH])
30
+
31
+ async with stdio_client(params) as (read, write):
32
+ async with ClientSession(read, write) as session:
33
+ await session.initialize()
34
+
35
+ tools_result = await session.list_tools()
36
+ tool_names = {t.name for t in tools_result.tools}
37
+ missing = EXPECTED_TOOLS - tool_names
38
+ assert not missing, f"Missing expected tools: {missing}"
39
+ print(f"Discovered all {len(EXPECTED_TOOLS)} expected tools")
40
+
41
+ result = await session.call_tool("list_projects", {})
42
+ assert result.content, "list_projects returned empty content"
43
+ print("list_projects returned content")
44
+
45
+ result = await session.call_tool(
46
+ "get_project_details", {"project_name": "SalesAgent"}
47
+ )
48
+ assert result.content, "get_project_details returned empty content"
49
+ print("get_project_details returned content")
50
+
51
+ result = await session.call_tool(
52
+ "search_projects_by_stack", {"technology": "Groq"}
53
+ )
54
+ assert result.content, "search_projects_by_stack returned empty content"
55
+ print("search_projects_by_stack returned content")
56
+
57
+ result = await session.call_tool("get_flagship_project", {})
58
+ assert result.content, "get_flagship_project returned empty content"
59
+ print("get_flagship_project returned content")
60
+
61
+ result = await session.call_tool("get_resume_summary", {})
62
+ assert result.content, "get_resume_summary returned empty content"
63
+ print("get_resume_summary returned content")
64
+
65
+ print("\nAll 5 tools responded correctly over stdio. Smoke test passed.")
66
+
67
+
68
+ if __name__ == "__main__":
69
+ asyncio.run(run_smoke_test())
@@ -0,0 +1,73 @@
1
+ """
2
+ Smoke/unit tests for the portfolio MCP server's tools.
3
+
4
+ These call the underlying tool functions directly (FastMCP's @mcp.tool()
5
+ decorator doesn't wrap them, so they're plain callables) rather than going
6
+ through the MCP protocol layer -- fast, no transport needed.
7
+ """
8
+
9
+ from portfolio_mcp_server.server import (
10
+ PROJECTS,
11
+ get_flagship_project,
12
+ get_project_details,
13
+ get_resume_summary,
14
+ list_projects,
15
+ search_projects_by_stack,
16
+ )
17
+
18
+
19
+ def test_list_projects_returns_all_projects():
20
+ result = list_projects()
21
+ assert isinstance(result, list)
22
+ assert len(result) == len(PROJECTS)
23
+
24
+
25
+ def test_list_projects_has_expected_keys():
26
+ result = list_projects()
27
+ for item in result:
28
+ assert "name" in item
29
+ assert "description" in item
30
+ assert "stack" in item
31
+
32
+
33
+ def test_get_project_details_found():
34
+ result = get_project_details("SalesAgent")
35
+ assert result["name"] == "SalesAgent"
36
+ assert "stack" in result
37
+
38
+
39
+ def test_get_project_details_case_insensitive_partial_match():
40
+ result = get_project_details("sales")
41
+ assert result["name"] == "SalesAgent"
42
+
43
+
44
+ def test_get_project_details_not_found():
45
+ result = get_project_details("NonexistentProjectXYZ")
46
+ assert "error" in result
47
+ assert "available_projects" in result
48
+
49
+
50
+ def test_search_projects_by_stack_found():
51
+ result = search_projects_by_stack("Groq")
52
+ assert isinstance(result, list)
53
+ assert len(result) > 0
54
+ assert all("stack" in item for item in result)
55
+
56
+
57
+ def test_search_projects_by_stack_not_found():
58
+ result = search_projects_by_stack("NonexistentTechXYZ")
59
+ assert isinstance(result, list)
60
+ assert "message" in result[0]
61
+
62
+
63
+ def test_get_flagship_project_returns_a_project():
64
+ result = get_flagship_project()
65
+ assert "name" in result
66
+ assert result in PROJECTS
67
+
68
+
69
+ def test_get_resume_summary_has_expected_keys():
70
+ result = get_resume_summary()
71
+ assert isinstance(result, dict)
72
+ assert "github" in result
73
+ assert "highlights" in result