Initial commit — clean history (removed large test files, browser profiles, Medidata/Clario downloads)
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
mcp_jnjemails.py | v1.0 | 2026-06-01
|
||||
MCP server pro dotazování SQLite DB jnjemails (EmailsImport pipeline).
|
||||
Automaticky načte nejnovější jnjemails_*.db z \\tower\JNJEMAILS\db\.
|
||||
"""
|
||||
import sqlite3
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
DB_DIR = Path(r"\\tower\JNJEMAILS\db")
|
||||
|
||||
|
||||
def log(msg: str):
|
||||
print(msg, file=sys.stderr, flush=True)
|
||||
|
||||
|
||||
def get_latest_db() -> Path:
|
||||
files = sorted(DB_DIR.glob("jnjemails_*.db"), key=lambda f: f.name)
|
||||
if not files:
|
||||
raise FileNotFoundError(f"Žádný jnjemails_*.db soubor v {DB_DIR}")
|
||||
return files[-1]
|
||||
|
||||
|
||||
def query(sql: str, params=()) -> list[dict]:
|
||||
db_path = get_latest_db()
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.row_factory = sqlite3.Row
|
||||
try:
|
||||
cur = conn.execute(sql, params)
|
||||
return [dict(row) for row in cur.fetchall()]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
mcp = FastMCP("jnjemails")
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def db_info() -> dict:
|
||||
"""Informace o aktuální DB: cesta, velikost, název souboru."""
|
||||
db_path = get_latest_db()
|
||||
size_kb = db_path.stat().st_size // 1024
|
||||
return {
|
||||
"path": str(db_path),
|
||||
"file": db_path.name,
|
||||
"size_kb": size_kb,
|
||||
}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def last_run() -> list[dict]:
|
||||
"""Kompletní log posledního běhu (runs + log tabulky)."""
|
||||
return query("""
|
||||
SELECT r.script, r.version, r.started_at, r.finished_at,
|
||||
r.transferred, r.skipped, r.errors,
|
||||
l.level, l.event, l.subject, l.folder, l.detail, l.created_at
|
||||
FROM log l
|
||||
JOIN runs r ON r.id = l.run_id
|
||||
WHERE l.run_id = (SELECT MAX(id) FROM runs)
|
||||
ORDER BY l.created_at
|
||||
""")
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def list_runs(limit: int = 20) -> list[dict]:
|
||||
"""Přehled všech běhů skriptů (nejnovější nahoře)."""
|
||||
return query("""
|
||||
SELECT id, script, version, started_at, finished_at,
|
||||
transferred, skipped, errors
|
||||
FROM runs
|
||||
ORDER BY started_at DESC
|
||||
LIMIT ?
|
||||
""", (limit,))
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def errors_last_run() -> list[dict]:
|
||||
"""Chyby z posledního běhu."""
|
||||
return query("""
|
||||
SELECT l.event, l.subject, l.folder, l.detail, l.created_at
|
||||
FROM log l
|
||||
WHERE l.run_id = (SELECT MAX(id) FROM runs)
|
||||
AND l.level = 'ERROR'
|
||||
ORDER BY l.created_at
|
||||
""")
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def run_log(run_id: int) -> list[dict]:
|
||||
"""Log konkrétního běhu podle ID (z list_runs)."""
|
||||
return query("""
|
||||
SELECT level, event, subject, folder, graph_id, detail, created_at
|
||||
FROM log
|
||||
WHERE run_id = ?
|
||||
ORDER BY created_at
|
||||
""", (run_id,))
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def search_messages(subject: str = "", sender: str = "", folder: str = "", limit: int = 50) -> list[dict]:
|
||||
"""Hledání v přenesených emailech. Všechny parametry jsou volitelné (LIKE, case-insensitive)."""
|
||||
conditions = []
|
||||
params = []
|
||||
if subject:
|
||||
conditions.append("subject LIKE ?")
|
||||
params.append(f"%{subject}%")
|
||||
if sender:
|
||||
conditions.append("sender LIKE ?")
|
||||
params.append(f"%{sender}%")
|
||||
if folder:
|
||||
conditions.append("jnj_folder LIKE ?")
|
||||
params.append(f"%{folder}%")
|
||||
where = f"WHERE {' AND '.join(conditions)}" if conditions else ""
|
||||
params.append(limit)
|
||||
return query(f"""
|
||||
SELECT id, message_id, subject, sender, received_at,
|
||||
jnj_folder, graph_id, is_read, source, uploaded_at
|
||||
FROM messages
|
||||
{where}
|
||||
ORDER BY received_at DESC
|
||||
LIMIT ?
|
||||
""", params)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def messages_stats() -> dict:
|
||||
"""Statistiky: celkový počet emailů, podle source, podle složky (top 10)."""
|
||||
total = query("SELECT COUNT(*) as cnt FROM messages")[0]["cnt"]
|
||||
by_source = query("SELECT source, COUNT(*) as cnt FROM messages GROUP BY source ORDER BY cnt DESC")
|
||||
by_folder = query("""
|
||||
SELECT jnj_folder, COUNT(*) as cnt FROM messages
|
||||
WHERE jnj_folder IS NOT NULL
|
||||
GROUP BY jnj_folder
|
||||
ORDER BY cnt DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
no_graph = query("SELECT COUNT(*) as cnt FROM messages WHERE graph_id IS NULL")[0]["cnt"]
|
||||
return {
|
||||
"total": total,
|
||||
"no_graph_id": no_graph,
|
||||
"by_source": by_source,
|
||||
"top_folders": by_folder,
|
||||
}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def sql(query_str: str) -> list[dict]:
|
||||
"""Libovolný SELECT dotaz proti DB. Pouze SELECT (ostatní jsou blokovány)."""
|
||||
stripped = query_str.strip().upper()
|
||||
if not stripped.startswith("SELECT"):
|
||||
return [{"error": "Povoleny jsou pouze SELECT dotazy."}]
|
||||
try:
|
||||
return query(query_str)
|
||||
except Exception as e:
|
||||
return [{"error": str(e)}]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
db_path = get_latest_db()
|
||||
log(f"jnjemails MCP server — DB: {db_path.name}")
|
||||
mcp.run()
|
||||
Reference in New Issue
Block a user