Personal finance tracking usually means switching between an app, a spreadsheet, or a browser tab just to log a single transaction. I wanted a lower-friction way to capture and query expenses — ideally through natural conversation with an AI assistant, without giving up structured storage or control over my own data.
The Model Context Protocol (MCP) made this possible: it lets an AI client like Claude Desktop call tools exposed by a server I own and control, over a standard interface.
The Solution
I built and deployed a remote MCP server that exposes an expense tracker as a set of callable tools, then connected it directly to Claude Desktop as a live data source.
Stack and architecture:
FastMCP to define and expose tools (add_expense, get_summary, list_transactions, category management) over the MCP protocol
aiosqlite for async-safe database access, migrated from a synchronous sqlite3 connection to avoid blocking calls in an async server
A temporary, writable directory (via Python's tempfile) for database storage, since the deployment target does not guarantee a writable path at a fixed location
Deployed remotely on Horizon, then registered as a connector in Claude Desktop's configuration
The repository is public here: github.com/AliRaza3485/test-remote-mcp-server
Problems Along the Way
Two issues took real debugging time and aren't well covered in introductory MCP material:
1. Locating the client configuration on Windows.
Claude Desktop on Windows is distributed as an MSIX package — Microsoft's sandboxed app packaging format, similar in spirit to how Android isolates APKs. MSIX apps don't write to the file paths a normal Windows install would use; the OS virtualizes the path so the app sees what looks like a standard location, while the actual file lives in an isolated, package-specific directory. This meant the config file wasn't where standard documentation implied — finding the correct sandboxed path required checking the MSIX virtualization behavior directly rather than trusting the "expected" path.
2. OAuth handling beyond the basics.
Most MCP quick-start guides demonstrate local, tool-only servers with no external authentication. As soon as a server needs to authorize against an external service, the flow requires handling the redirect, exchanging the returned code for a token, storing it safely, and refreshing it when it expires — none of which the beginner-level guides walk through. Getting this right meant working from the OAuth spec directly rather than a tutorial.
The Outcome
The result is a working setup where a plain-language instruction in Claude Desktop — for example, logging an expense with an amount and category — is routed through MCP to the deployed server and persisted in the database, with no separate app or manual form involved.
More importantly, building this end-to-end (real deployment, real client integration, real platform-specific failure modes) surfaced problems that toy examples don't: packaging quirks, async database access, and authentication lifecycle management.
What's Next
I'm currently extending this into more general agentic workflows using LangGraph — state graphs, checkpointing, and human-in-the-loop interrupts — with the goal of combining stateful agent logic with MCP-exposed tools.
If you're building with MCP or LangGraph and want to compare notes, I'd welcome the conversation.
GitHub: github.com/AliRaza3485
Repo: github.com/AliRaza3485/test-remote-mcp-server

Top comments (2)
The MSIX section is the most useful thing I've read about Claude Desktop on Windows, and I can corroborate it from a completely different angle, because we hit the same class of problem this week.
We build Claude Code plugins, and one of ours has a SessionStart hook that simply would not fire on Windows. Same shape as your finding: the docs imply one thing, the platform does another. Our hook command was bash "...session-start.sh". It turns out the hook runner on Windows executes through cmd.exe against the system PATH, while the Bash tool inside the session resolves its own Git Bash by a different route. So bash was installed and perfectly usable by the tool, but invisible to the hook runner, because the Git installer only puts Git\cmd on PATH, not Git\bin. The command failed, the hook exited non-zero, and it was silently dropped. No error surfaced anywhere. We only caught it by running where.exe bash instead of trusting that "bash works here."
Your framing nails it: problems that toy examples don't surface. Both cases are the same lesson, that Windows plus Claude Desktop diverges from the documented happy path in packaging and execution, and you only find it by checking what actually resolves.
Taking you up on the compare-notes offer, since you have actually shipped remote MCP with OAuth. We're weighing wrapping our generators as an MCP server, the appeal being one build reaching Claude Desktop and Cursor and anything else that speaks MCP, instead of maintaining a plugin format per editor. Ours would be local and keyless so we'd likely dodge most of the auth lifecycle you describe, but I'm curious whether deployment bit you harder than the protocol itself, and whether you'd make the same remote-versus-local call again.
Context for the Windows story: github.com/localplugins/plugins. Good writeup.
Really appreciate the detail here — the Git\cmd vs Git\bin PATH split is a great catch, and where.exe as the actual source of truth instead of trusting "it works in my shell" is exactly the kind of check I wish more docs mentioned upfront. Silent non-zero exits with no surfaced error is the worst version of this bug too, since there's nothing pointing you toward PATH as the culprit.
To your question directly: deployment bit harder than the protocol itself, by a decent margin. MCP as a spec is straightforward once you've read it — tools, resources, the JSON-RPC shape. The pain was almost entirely environmental: the Windows MSIX sandboxing (covered in the post), plus getting async DB access right under a server that has to survive being invoked from a client I don't control the lifecycle of. The protocol was the easy 20%.
On remote vs local — if your case is genuinely local and keyless, I'd lean toward staying local. You're right that you'd dodge most of what made this hard for me: token storage, refresh, and redirect handling only exist because I needed the server reachable from outside my machine. If "one build reaching Claude Desktop, Cursor, anything MCP-aware" is the main appeal and you don't need remote reachability, local gets you that portability without inheriting the auth lifecycle or the deployment surface at all. I'd only push toward remote if you expect the server to need to run somewhere you don't control, or be shared across users — otherwise it's overhead you don't need yet.
Took a look at the plugins repo — will dig in properly and follow up if I spot anything relevant to the MCP wrapping question.