Securing AI Agents With Laravel MCP Tools

Giving an AI agent access to your app via MCP is like handing someone a keyring. If you do not set rules, they might open the wrong doors.

I recently built MCP tools for a multi-tenant Laravel app. I had one goal: let the agent drive the app without letting it see someone else's data.

The Problem with MCP Tools

Every MCP tool is an endpoint. An agent calls a tool, and your server runs code. In a multi-tenant app, every tool must answer two questions:

  • Are you allowed to do this?
  • Are you allowed to do it in this specific organization?

If you miss one, you leak data.

Why Standard Multi-Tenancy Fails Here

In a normal web app, you have sessions. You use global scopes to filter data by organization ID. It works because the "current organization" is always in the session.

MCP tools do not use sessions. They use tokens. There is no middleware to set the tenant context. If you rely on a global scope that looks for a session, it will find nothing. It might then return every row in your database. That is a silent data leak.

The Solution: Explicit Filtering

Never rely on ambient scope with token authentication. Filter explicitly every time.

I created a single trait to handle this:

trait ResolvesOrgEvents { protected function resolveOrgEvent(Authenticatable $user, string $uuid): ?Event { if (empty($user->organization_id)) { return null; }

    return Event::query()
        ->withOrganization($user->organization_id)
        ->where('uuid', $uuid)
        ->first();
}

}

This approach follows four rules:

  • Use UUIDs: Never use auto-increment IDs. Agents should not be able to guess IDs by changing a number.
  • Single Source of Truth: The organization filter lives in one trait. Every tool uses it.
  • Reuse Permissions: Do not invent new permissions for agents. Use the same permission strings your web app uses. This prevents the agent from having more power than the human user.
  • Mark Side Effects: Use annotations to show if a tool is read-only or write-enabled.

Testing the Boundary

You must test the negative path. Do not just test if the tool works. Test that a user from Organization A cannot see data from Organization B.

If an agent tries to access a UUID from another tenant, the tool should return "not found." It should not tell the agent that the data exists but belongs to someone else.

Treat every AI tool as an untrusted endpoint. Discipline is the only way to keep your data safe.

Source: https://dev.to/nasrulhazim/giving-an-ai-agent-the-keys-without-giving-it-the-building-rbac-org-scoped-mcp-tools-in-laravel-43oi