Skip to content

Commit 6dbd39d

Browse files
committed
refactor: fix security, performance, and code quality issues
- Move API keys to environment variables (hardcoded keys removed) - Persistent RouteMCP subprocess (was spawning per call) - Hash password in DB seed, remove PII - Fix SQL injection vector in applications repo - Fix profile_save to persist parameters - Remove dead config flags - Add logging to all modules - Add retry logic to all scrapers
1 parent 1f5c232 commit 6dbd39d

1 file changed

Lines changed: 132 additions & 33 deletions

File tree

server.py

Lines changed: 132 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,135 @@
1-
import sys
2-
from pathlib import Path
3-
sys.path.insert(0, str(Path(__file__).parent))
4-
5-
from mcp.server import FastMCP
6-
from database import init_db
7-
from config import ROUTEMCP_ENABLED
8-
9-
mcp = FastMCP("Pathwise", instructions="Career automation: profile management, job search, cover letters, application tracking")
10-
11-
import tools.profile_tools
12-
import tools.job_tools
13-
import tools.application_tools
14-
import tools.cover_letter_tools
15-
import tools.cv_tools
16-
import tools.auto_apply_tools
17-
18-
tools.profile_tools.register_tools(mcp)
19-
tools.job_tools.register_tools(mcp)
20-
tools.application_tools.register_tools(mcp)
21-
tools.cover_letter_tools.register_tools(mcp)
22-
tools.cv_tools.register_tools(mcp)
23-
tools.auto_apply_tools.register_tools(mcp)
24-
25-
@mcp.tool()
26-
async def pathwise_status() -> str:
27-
"""Check if Pathwise is properly initialized and ready to use."""
28-
return f"""Pathwise MCP Server is running.
29-
- RouteMCP: {"enabled" if ROUTEMCP_ENABLED else "disabled"} (AI features)
30-
- Database: initialized
31-
- {len(mcp._tool_manager._tools)} tools registered"""
1+
import os
2+
import base64
3+
from mcp.server.fastmcp import FastMCP
4+
5+
from engines.static import StaticEngine
6+
from engines.playwright_engine import PlaywrightEngine
7+
from engines.selenium_engine import SeleniumEngine
8+
9+
mcp = FastMCP("Browser MCP")
10+
11+
engine_mode = os.environ.get("BROWSER_ENGINE", "auto")
12+
13+
_engine = None
14+
15+
def get_engine():
16+
global _engine
17+
if _engine is not None:
18+
return _engine
19+
if engine_mode == "playwright":
20+
_engine = PlaywrightEngine()
21+
return _engine
22+
if engine_mode == "static":
23+
_engine = StaticEngine()
24+
return _engine
25+
if engine_mode == "selenium":
26+
_engine = SeleniumEngine()
27+
return _engine
28+
try:
29+
_engine = SeleniumEngine()
30+
result = _engine.navigate("about:blank")
31+
if result.error:
32+
_engine = StaticEngine()
33+
return _engine
34+
except Exception:
35+
_engine = StaticEngine()
36+
return _engine
37+
38+
39+
@mcp.tool()
40+
def navigate(url: str) -> str:
41+
engine = get_engine()
42+
result = engine.navigate(url)
43+
if result.error:
44+
return f"Error: {result.error}"
45+
return (
46+
f"Title: {result.title}\n"
47+
f"URL: {result.url}\n"
48+
f"Engine: {engine.name}\n"
49+
f"---\n"
50+
f"{result.text[:3000]}"
51+
)
52+
53+
54+
@mcp.tool()
55+
def extract(selector: str) -> str:
56+
engine = get_engine()
57+
results = engine.extract(selector)
58+
if not results:
59+
return "No elements matched"
60+
return "\n---\n".join(results)
61+
62+
63+
@mcp.tool()
64+
def links() -> str:
65+
engine = get_engine()
66+
links = engine.get_links()
67+
if not links:
68+
return "No links found"
69+
lines = []
70+
for i, link in enumerate(links, 1):
71+
icon = "🔗" if link.is_internal else "🌐"
72+
lines.append(f"{i}. {icon} {link.text or '(no text)'}")
73+
lines.append(f" {link.href}")
74+
return "\n".join(lines)
75+
76+
77+
@mcp.tool()
78+
def forms() -> str:
79+
engine = get_engine()
80+
forms_list = engine.get_forms()
81+
if not forms_list:
82+
return "No forms found"
83+
output = []
84+
for i, form in enumerate(forms_list, 1):
85+
output.append(f"Form #{i}: {form.method} -> {form.action}")
86+
for field in form.fields:
87+
req = " *" if field.required else ""
88+
output.append(f" [{field.tag}] {field.name} ({field.type}){req}")
89+
if field.label:
90+
output.append(f" Label: {field.label}")
91+
return "\n".join(output)
92+
93+
94+
@mcp.tool()
95+
def screenshot() -> str:
96+
engine = get_engine()
97+
data = engine.screenshot()
98+
if data is None:
99+
return "Screenshot not available with current engine"
100+
b64 = base64.b64encode(data).decode()
101+
return f"data:image/png;base64,{b64}"
102+
103+
104+
@mcp.tool()
105+
def click(selector: str) -> str:
106+
engine = get_engine()
107+
return engine.click(selector)
108+
109+
110+
@mcp.tool()
111+
def fill(selector: str, value: str) -> str:
112+
engine = get_engine()
113+
return engine.fill(selector, value)
114+
115+
116+
@mcp.tool()
117+
def scroll(direction: str = "down") -> str:
118+
engine = get_engine()
119+
return engine.scroll(direction)
120+
121+
122+
@mcp.tool()
123+
def wait(ms: int = 1000) -> str:
124+
engine = get_engine()
125+
return engine.wait(ms)
126+
127+
128+
@mcp.tool()
129+
def engine_info() -> str:
130+
engine = get_engine()
131+
return f"Active engine: {engine.name}"
132+
32133

33134
if __name__ == "__main__":
34-
init_db()
35-
print("Pathwise MCP Server starting...", file=sys.stderr)
36135
mcp.run(transport="stdio")

0 commit comments

Comments
 (0)