Dev.to AI πŸ€– Ai πŸ‘ 0 πŸ“– 3 min read

An MCP Tool Schema Is Not an Authorization Check: A Laravel Design Pattern

An MCP tool can be wonderfully easy to call. That is exactly why a write-capable tool deserves more scrutiny than a read-only one. Imagine an assistant with a refund_invoice tool. Its input schema says it accepts an inv

An MCP tool can be wonderfully easy to call. That is exactly why a write-capable tool deserves more scrutiny than a read-only one.

Imagine an assistant with a refund_invoice tool. Its input schema says it accepts an invoice ID and a reason. The schema helps a client construct a call; it does not tell your server which person is allowed to refund which invoice. Those are separate decisions.

The MCP tools specification puts input validation, access controls, rate limiting, and output sanitization on the server. It also recommends client confirmation for sensitive operations. That is a useful division of responsibility: confirmation in the chat interface is helpful, but it is not a substitute for enforcement in your application.

A three-boundary design

1. Protect the endpoint. Authenticate the caller at the HTTP boundary and apply rate limits. An unprotected endpoint is not made safe merely because its URL is hard to guess. In Laravel, put the appropriate authentication and throttling middleware on the route that receives tool calls; choose a guard that fits your client and deployment.

2. Validate the actual arguments. Treat tool arguments like any other external input. Check type, range, length, and business constraints on the server. A client may have seen a perfectly good inputSchema; a malicious or buggy caller can still send a different payload.

3. Authorize the action on the specific record. After looking up the invoice, ask a policy whether this authenticated actor may perform this action on this invoice. Laravel's gates and policies exist for exactly this kind of decision. Do not confuse β€œthe caller can access the MCP endpoint” with β€œthe caller may refund every invoice.”

A simplified flow looks like this:

Authenticate caller
  β†’ Validate invoice_id and reason
  β†’ Load invoice in the caller's tenant scope
  β†’ Authorize refund action on that invoice
  β†’ Check idempotency and current state
  β†’ Execute one mutation
  β†’ Record actor, invoice, outcome, and request ID

That flow is an architecture sketch, not a drop-in payment implementation. Payment providers and accounting systems have their own refund and reconciliation rules.

The retry problem is not theoretical

Tool calls can be retried after a timeout. If a response disappears after the refund succeeded, the assistant may reasonably try again. For a state-changing tool, use a server-side idempotency key bound to the actor and operation, and reject or return the existing result for a duplicate request. Put the durable state transition behind the same application controls you use for non-AI requests.

A useful API shape is a read-only preview_refund that returns the invoice, amount, and consequences, followed by a separate commit_refund that rechecks authorization and current state. A preview is not an approval token by itself; if you use a short-lived confirmation token, bind it to the actor, resource, amount, and expiry on the server. The client can show the preview to a human, but the server still makes the final decision.

Design your tool output as carefully as its input

A successful response should contain only what the assistant needs: the operation ID, status, and a safe summary. Avoid returning a whole customer record or raw exception trace. For failures, distinguish β€œnot authorized,” β€œvalidation failed,” and β€œalready processed” without leaking another tenant's data.

Before exposing a write tool

  • Can an unauthenticated caller reach the endpoint?
  • Is every argument validated on the server, not only described in a schema?
  • Does a policy check this actor against this specific record?
  • Is a retry safe and observable?
  • Can the output leak sensitive data or implementation details?
  • Can the user inspect a preview before a consequential action?

A tool schema makes an action discoverable. A sound authorization design makes it safe to offer.

What is one authorization or retry edge case your team found only after connecting an agent to a real workflow?

Further reading: MCP tools specification Β· Laravel authorization documentation.

Disclosure: This article and its cover illustration were prepared with AI assistance. The technical recommendations were checked against the linked MCP and Laravel documentation; adapt and test them for your application's threat model.

πŸ“° Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.