diff --git a/src/monarch_mcp_custom.egg-info/PKG-INFO b/src/monarch_mcp_custom.egg-info/PKG-INFO index 8cc0d56..7fbc176 100644 --- a/src/monarch_mcp_custom.egg-info/PKG-INFO +++ b/src/monarch_mcp_custom.egg-info/PKG-INFO @@ -9,13 +9,14 @@ Classifier: Programming Language :: Python :: 3.12 Requires-Python: >=3.12 Description-Content-Type: text/markdown Requires-Dist: mcp[cli]>=1.0.0 +Requires-Dist: fastmcp>=0.4.1 Requires-Dist: monarchmoney>=0.1.15 Requires-Dist: gql<4.0,>=3.4 -Requires-Dist: keyring>=24.0.0 Requires-Dist: python-dotenv>=1.0.0 Requires-Dist: pydantic>=2.0.0 Requires-Dist: starlette>=0.35.0 Requires-Dist: uvicorn>=0.27.0 +Requires-Dist: pyotp>=2.9.0 # Monarch Money Custom MCP Server @@ -49,7 +50,7 @@ docker-compose up -d ## 🔌 Connection The server will be available at: -- **SSE Endpoint**: `http://localhost:8000/mcp/sse` +- **MCP Endpoint**: `http://localhost:8000/mcp` - **Health Check**: `http://localhost:8000/health` ## 🛠️ Tools Included diff --git a/src/monarch_mcp_custom.egg-info/requires.txt b/src/monarch_mcp_custom.egg-info/requires.txt index 12da163..1e4070c 100644 --- a/src/monarch_mcp_custom.egg-info/requires.txt +++ b/src/monarch_mcp_custom.egg-info/requires.txt @@ -1,8 +1,9 @@ mcp[cli]>=1.0.0 +fastmcp>=0.4.1 monarchmoney>=0.1.15 gql<4.0,>=3.4 -keyring>=24.0.0 python-dotenv>=1.0.0 pydantic>=2.0.0 starlette>=0.35.0 uvicorn>=0.27.0 +pyotp>=2.9.0 diff --git a/src/monarch_mcp_custom/__pycache__/auth.cpython-313.pyc b/src/monarch_mcp_custom/__pycache__/auth.cpython-313.pyc index 6240e18..09c4d8e 100644 Binary files a/src/monarch_mcp_custom/__pycache__/auth.cpython-313.pyc and b/src/monarch_mcp_custom/__pycache__/auth.cpython-313.pyc differ diff --git a/src/monarch_mcp_custom/server.py b/src/monarch_mcp_custom/server.py index 9bd0f9d..9d68d1a 100644 --- a/src/monarch_mcp_custom/server.py +++ b/src/monarch_mcp_custom/server.py @@ -110,7 +110,21 @@ async def get_transactions( async def get_budgets(reason: Optional[str] = None) -> str: """Get current budget information.""" client = await get_authenticated_client() - budgets = await client.get_budgets() + + try: + budgets = await client.get_budgets() + except Exception as e: + error_msg = str(e) + # Check if this is a Monarch API error about budgets not being set up + if "Something went wrong" in error_msg: + return serialize_json( + { + "error": "Budget data unavailable", + "detail": "The Monarch Money API returned an error. This may occur if budgets are not configured in your account.", + "raw_error": error_msg, + } + ) + raise # Build a category lookup from categoryGroups category_lookup = {} diff --git a/test_token.py b/test_token.py new file mode 100755 index 0000000..e7e6737 --- /dev/null +++ b/test_token.py @@ -0,0 +1,56 @@ +import asyncio +import os +from monarchmoney import MonarchMoney +from dotenv import load_dotenv + + +async def test_token(): + load_dotenv(override=True) + + token = os.getenv("MONARCH_TOKEN") + + email = os.getenv("MONARCH_EMAIL") + + password = os.getenv("MONARCH_PASSWORD") + + if token and token.strip(): + # Strip potential prefix + + if token.startswith("MONARCH_TOKEN="): + token = token.replace("MONARCH_TOKEN=", "") + + print(f"Testing with TOKEN: {token[:10]}...") + + mm = MonarchMoney(token=token) + + elif email and password: + print(f"Testing with EMAIL: {email}") + + mm = MonarchMoney() + + try: + await mm.login(email, password) + + print("✅ Login successful with email/password!") + + except Exception as e: + print(f"❌ Login failed: {e}") + + return + + else: + print("❌ No credentials found in .env") + + return + + try: + accounts = await mm.get_accounts() + + print(f"Success! Found {len(accounts.get('accounts', []))} accounts.") + + except Exception as e: + print(f"Error fetching accounts: {e}") + + +if __name__ == "__main__": + asyncio.run(test_token()) diff --git a/uv.lock b/uv.lock index e0246de..6d5c67f 100644 --- a/uv.lock +++ b/uv.lock @@ -913,6 +913,7 @@ dependencies = [ { name = "mcp", extra = ["cli"] }, { name = "monarchmoney" }, { name = "pydantic" }, + { name = "pyotp" }, { name = "python-dotenv" }, { name = "starlette" }, { name = "uvicorn" }, @@ -925,6 +926,7 @@ requires-dist = [ { name = "mcp", extras = ["cli"], specifier = ">=1.0.0" }, { name = "monarchmoney", specifier = ">=0.1.15" }, { name = "pydantic", specifier = ">=2.0.0" }, + { name = "pyotp", specifier = ">=2.9.0" }, { name = "python-dotenv", specifier = ">=1.0.0" }, { name = "starlette", specifier = ">=0.35.0" }, { name = "uvicorn", specifier = ">=0.27.0" }, @@ -1485,6 +1487,15 @@ crypto = [ { name = "cryptography" }, ] +[[package]] +name = "pyotp" +version = "2.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/b2/1d5994ba2acde054a443bd5e2d384175449c7d2b6d1a0614dbca3a63abfc/pyotp-2.9.0.tar.gz", hash = "sha256:346b6642e0dbdde3b4ff5a930b664ca82abfa116356ed48cc42c7d6590d36f63", size = 17763, upload-time = "2023-07-27T23:41:03.295Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/c0/c33c8792c3e50193ef55adb95c1c3c2786fe281123291c2dbf0eaab95a6f/pyotp-2.9.0-py3-none-any.whl", hash = "sha256:81c2e5865b8ac55e825b0358e496e1d9387c811e85bb40e71a3b29b288963612", size = 13376, upload-time = "2023-07-27T23:41:01.685Z" }, +] + [[package]] name = "pyperclip" version = "1.11.0"