Files
owa-mcp/exchange_mcp/tools/auth.py
T
2026-07-06 01:46:14 +03:00

118 lines
3.6 KiB
Python

"""Authentication tool for the Exchange MCP server.
Provides a `login` tool that accepts session cookies from the user,
saves them to disk, loads them into the OWA client, and verifies the session.
The user obtains cookies from their browser (DevTools → Application → Cookies),
pastes them as a ``name=value`` string (one cookie per line), and passes them
to this tool.
"""
import json
from mcp.server.fastmcp import Context
from exchange_mcp.server import mcp, AppContext
from exchange_mcp.owa_client import OWAClient, SessionExpiredError
from exchange_mcp.server_lifecycle import require_client
def _get_app_ctx(ctx: Context) -> AppContext:
"""Extract the AppContext from the MCP lifespan context."""
return ctx.request_context.lifespan_context
def _get_client(ctx: Context) -> OWAClient:
"""Extract the OWAClient from the MCP lifespan context."""
return require_client(_get_app_ctx(ctx).client)
def _session_is_valid(client: OWAClient) -> bool:
"""Quick check: can we reach the inbox?"""
try:
client._ensure_loaded()
payload = {
"__type": "GetFolderJsonRequest:#Exchange",
"Header": {
"__type": "JsonRequestHeaders:#Exchange",
"RequestServerVersion": "Exchange2013",
},
"Body": {
"__type": "GetFolderRequest:#Exchange",
"FolderShape": {
"__type": "FolderResponseShape:#Exchange",
"BaseShape": "IdOnly",
},
"FolderIds": [
{
"__type": "DistinguishedFolderId:#Exchange",
"Id": "inbox",
}
],
},
}
data = client.request("GetFolder", payload)
items = client.extract_items(data)
return bool(items)
except Exception:
return False
@mcp.tool()
async def login(
cookies: str,
ctx: Context = None,
) -> str:
"""Authenticate to Exchange OWA using session cookies from the browser.
Paste the cookies you copied from your browser (DevTools → Application →
Cookies) as a ``name=value`` string — one cookie per line.
Example cookies string::
X-OWA-CANARY=abc123
CookieAuth1=def456
...
Args:
cookies: Session cookies in ``name=value`` format, one per line.
Obtain from browser DevTools → Application → Cookies → copy all
cookies as ``name=value`` pairs.
Returns:
JSON result with success status and message.
"""
client = _get_client(ctx)
# Parse and validate cookies
cookies_str = cookies.strip()
if not cookies_str:
return json.dumps({
"success": False,
"error": "Cookies string is empty. Paste your browser cookies as name=value pairs, one per line.",
})
# Save to disk
try:
client.cookie_file.write_text(cookies_str + "\n")
except Exception as e:
return json.dumps({"success": False, "error": f"Failed to write cookie file: {e}"})
# Load into memory
try:
client.load_cookies_from_string(cookies_str)
except SessionExpiredError as e:
return json.dumps({"success": False, "error": str(e)})
# Verify session
if _session_is_valid(client):
return json.dumps({
"success": True,
"message": f"Logged in. Session cookies saved to {client.cookie_file}.",
})
else:
return json.dumps({
"success": False,
"error": "Cookies loaded but session verification failed. The cookies may have expired.",
})