DEV Community

Bruno Vieira
Bruno Vieira

Posted on

What Still Required Human Judgment When Adding MCP to an AI-Assisted SaaS

I am building a very small SaaS for Brazilian businesses that still manage employee work hours in notebooks or spreadsheets.

The product is called PontoBarato. It was built quickly and with substantial AI assistance.

This is not a story about a perfectly engineered, hyperscale system. It is also not a tutorial claiming that a prompt can replace security design. It is a product-level account of what still required human judgment when I added a Model Context Protocol (MCP) interface to the application.

The surprising part was not getting an assistant to call a tool. The difficult part was deciding what the assistant should be allowed to do.

The feature sounded simpler than it was

The initial idea was straightforward:

Let a business owner connect the product to ChatGPT or Claude and manage routine tasks with natural language.

The public MCP interface now supports workflows such as:

  • creating and updating employees;
  • viewing and editing weekly schedules;
  • listing, creating, and assigning shifts;
  • checking recent time entries;
  • viewing expected, worked, and balance hours;
  • reading overtime and night-work totals;
  • viewing or updating selected company settings.

On paper, this is just another interface to existing features.

In practice, a conversational interface introduces ambiguity that normal forms avoid. A form has required fields, dropdowns, validation messages, and visible state. A user can see which company, employee, or shift is selected.

A model receives a sentence.

“Move Ana to the morning shift” sounds clear until the system has two employees named Ana, several companies in the same account, or no shift literally named “morning.”

The integration therefore needed boundaries before it needed more tools.

A model should not choose the tenant

PontoBarato is multi-tenant: each company must only access its own employees, schedules, and time records.

The most important product rule is simple:

Company scope must come from the authenticated session, not from an identifier invented or supplied by the model.

Even if a tool receives an employee identifier, the server still has to verify that the employee belongs to the authenticated company.

This sounds obvious. It is also the kind of detail that can disappear when development becomes a rapid sequence of generated endpoints and happy-path tests.

AI assistance made implementation faster, but it did not decide the trust boundary. That remained a human responsibility.

Narrow tools were easier to reason about

A generic tool such as update_record would have been quick to expose. It would also have been difficult to explain, authorize, validate, and audit.

I preferred tools that map to actions a business owner recognizes:

  • create an employee;
  • assign a shift;
  • change a weekly schedule;
  • inspect an hours balance;
  • update the geofence requirement.

Narrow tools create more work at the beginning, but they reduce ambiguity later.

They also produce better conversations. If the assistant needs an employee name, a shift, and an optional role, the tool schema can say exactly that. If information is missing, the assistant has a reason to ask.

The goal was not to give the model a flexible path into the database. The goal was to expose a small set of business capabilities.

Read and write operations should feel different

“Show this month's balance” and “deactivate this employee” should not have the same level of friction.

I started thinking about the tool surface in three groups:

Type Example Main concern
Read list shifts or recent time entries scope and data minimization
Reversible write assign a shift or update a role validation and a clear result
Sensitive write deactivate an employee or change location rules explicit intent and auditability

This is not a sophisticated framework. It is a practical reminder that not every successful tool call has the same consequence.

For example, deactivation is usually safer than deletion in a time-tracking product because historical records still matter. The conversational interface should not make destructive actions easier than they are in the main application.

The application should calculate; the model should explain

Time tracking involves calculations such as expected time, worked time, balances, overtime categories, and night work.

It is tempting to give raw entries to a model and ask it to calculate the result. I avoided making that the normal path.

If the application already has domain logic for a calculation, the MCP tool should return that application's result. The assistant can then summarize or explain it.

Otherwise, the dashboard, exported report, and AI response can disagree.

This distinction also made debugging easier:

  • the product remains responsible for business rules;
  • the MCP layer is responsible for authentication, validation, and translation;
  • the model is responsible for choosing tools and communicating results.

The model should not become an unofficial payroll engine.

Smaller responses were better responses

My first instinct was to return complete objects. That is easy during development because the data is already available.

It is rarely what the assistant needs.

For an hours-balance question, a useful response contains:

  • employee;
  • period;
  • expected minutes;
  • worked minutes;
  • balance minutes;
  • relevant overtime totals.

It does not need internal flags, unrelated settings, framework metadata, or every stored field.

Compact responses reduce accidental exposure and make the assistant's final answer easier to verify.

Explicit units matter too. Returning 75 is ambiguous. Returning balanceMinutes: 75 is much safer.

AI-assisted development increased the need for verification

AI assistance was excellent for:

  • producing repetitive handlers;
  • drafting schemas;
  • suggesting validation cases;
  • generating test scaffolding;
  • refactoring similar tool definitions;
  • writing initial documentation.

It was not a substitute for deciding:

  • where tenant scope comes from;
  • which actions are too destructive;
  • whether a response exposes unnecessary data;
  • which calculation is authoritative;
  • what the user should confirm;
  • how a changed record can be audited.

Generated code often looks complete before the underlying decision is complete.

That was the central lesson of this feature: faster implementation increases the importance of slowing down at trust boundaries.

What I would do before adding more tools

The current interface is intentionally limited, and the product itself is still evolving.

Before expanding it, my checklist is:

  1. verify tenant isolation for every tool;
  2. test ambiguous names and missing resources;
  3. keep write results concise and inspectable;
  4. prefer reversible changes;
  5. minimize returned employee data;
  6. reuse the application's calculation logic;
  7. make sensitive actions require clear intent;
  8. record enough information to understand what changed;
  9. test failures, not only successful demos;
  10. add capabilities gradually.

None of these points is unique to MCP. MCP simply makes them easier to overlook because the conversational demo feels magical.

The honest takeaway

You can build a useful SaaS with substantial AI assistance. You can also create serious problems very quickly if generated code is treated as proof that authorization and business rules are correct.

For me, the valuable work was not typing every line manually. It was deciding which lines should exist, which operations should not be exposed, and which claims I was actually able to verify.

PontoBarato's MCP server is available at:

https://pontobarato.com/mcp
Enter fullscreen mode Exit fullscreen mode

The main product is at pontobarato.com.

I would be especially interested in feedback from people building MCP interfaces for other multi-tenant products:

Which operations did you decide not to expose, even though they were technically easy to implement?

Top comments (0)