Letting an LLM drive an internal app without going behind its back.
The agency runs its own task manager, built in-house by a colleague. I gave it an MCP server with OAuth 2.1 so Claude on web, mobile and desktop can operate the tracker headlessly. Crucially, those operations still fire the app's real side effects instead of writing to the database behind them.
The problem
The agency replaced a paid task tracker with one built in-house: per-client boards, recurring tasks, templates, saved views, reports. Good app, built by a colleague on the team. Not my product.
The gap was access. Everyone on the team was already working with Claude all day, and the tracker was the one system you had to stop and go use by hand. Reading a task list, checking what's blocked, drafting an update: all of it meant leaving the tool you were thinking in.
The tempting shortcut is to give the model database credentials and let it read and write tables directly. It works immediately, and it is exactly the wrong answer. Every write that bypasses the application also bypasses everything the application does around the write: the notification to the assignee, the activity-log entry, the attribution that records which human did this. Do that for a week and the tracker is no longer trustworthy for the people still using it normally. Things change and nobody knows who changed them or why they weren't told.
What I did
Built the integration as a proper MCP server over the app's own API surface, so the model is a client of the application rather than a peer of its database.
- MCP server with OAuth 2.1, remote transport. Claude on web, mobile, and desktop can all connect. Each connection is authenticated as a specific person, not as a service account with god rights.
- Every operation routes through the application's real paths. Creating a task fires the notification. Changing a status writes the activity log. Every write carries the attribution of the human whose token it is. The model gets no capability the person driving it doesn't already have.
- A self-updating tool catalog, so the tools exposed to the model track the app's actual capabilities instead of drifting into a stale hand-maintained list.
- Guidance shipped from the server side, so the model arrives already knowing the app's conventions: that statuses are per-pipeline and a bare label means nothing, that comments render plain text and a markdown table comes out as unreadable pipes, and that colleague-visible writes need the operator's explicit go because nothing here can be deleted by anyone but a human in the app.
The detail that mattered: the permission model is the person, not the integration. It would have been easier to give the MCP server broad rights and trust the prompt to behave. Instead the server acts as the authenticated user, which means the blast radius of a confused model is bounded by what that person could have done by hand, and every trace of it lands in the activity log under their name, where it can be found and fixed.
Outcome
- The team's tracker is reachable from wherever they're already working, without a second interface to learn.
- Automated and human activity are indistinguishable to the app, which is the point: notifications, logs, and attribution all still work.
- No credentials with database-level reach handed to a language model.
- Scope note, stated plainly: the task manager is a colleague's build. The MCP and integration layer is mine.