Skip to content

MCP Tools

MCP (Model Context Protocol) tools are the only way an AI agent reads or writes business data in this repo. No AI system calls Eloquent models or hits internal endpoints directly; every read and every write goes through a Laravel\Mcp\Server\Tool subclass registered on one of two MCP servers, so the tool boundary is the entire surface area an LLM has access to. This repo defines that boundary through two distinct servers rather than one, because the two consumers on the other side of it — an external AI vendor and the internal team — need very different levels of trust.

Two servers, two trust levels

YayaServer is the public-facing server. It is mounted at /mcp/yaya behind auth.admin_token middleware and exists specifically for Dify, the AI chat vendor that currently powers user-facing conversations, to call [@yaya-server] [@ai-routes]. Its instructions tell the calling model to "read and update user actions in Yaya Engine," and it registers exactly three tools — GetUserActionsTool, GetUserActionDetailsTool, and UpdateActionStepStatusTool — a deliberately narrow surface matched to what a conversational agent helping a user through their preparation tasks actually needs [@yaya-server].

InternalYayaServer is a much larger, much more sensitive server: 20 tools covering users, verifications, payouts, alerts, message delivery, FSPs, and an operations dashboard [@internal-yaya-server]. It is mounted at /mcp/internal-yaya behind auth:api and a can:mcp.access gate, and its OAuth routes are registered through Mcp::oauthRoutes(), which is how the team authenticates to it from Claude Desktop or Claude Code rather than as an unattended service account [@ai-routes]. Its instructions describe a full investigation workflow — search for a user, check an alert's delivery report, list the users assigned to an alert, then drill into conversations — because this server is meant for the team to interactively investigate production issues, not for a single-purpose conversational reply.

Permission gating inside the internal server

Every tool on InternalYayaServer extends the abstract InternalTool base class, which adds an authorization check on top of whatever OAuth/session authentication already passed. InternalTool::eligibleForRegistration() requires that the resolved admin user both can() the general mcp.access permission and can() a tool-specific $requiredPermission, which defaults to mcp.tools.read but is overridden to mcp.tools.write on tools that mutate data [@internal-tool]. This means read tools and write tools are gated by two separate permission grants, so an AdminUser role can be given read access to the whole internal MCP surface without also being able to approve evidence or reject payouts.

InternalTool also stamps every tool's response with an MCP annotation reflecting that same read/write distinction: isReadOnly tools get readOnlyHint: true, and mutating tools get destructiveHint: true instead, added directly into the tool's toArray() output [@internal-tool]. This lets the calling AI client itself reason about which tool calls are safe to make without confirmation and which are destructive, independent of whatever permission check already gated registration.

Not to be confused with Prism

openspec/project.md describes an aspirational future architecture where a "Prism PHP" layer replaces Dify as the LLM orchestration layer, routing through Portkey and calling the same MCP tools [@project-md]. That target architecture is documented but not implemented: the actual code in this repo has no Prism package installed, and the live AI layer that calls YayaServer's tools today is DifyService, a Saloon-based connector to the Dify vendor API. When reading MCP tool code, treat Dify as the real, current caller, and treat any Prism references in project docs as forward-looking intent rather than a description of what runs today.

For the broader vocabulary this concept sits inside, see Atram, Yaya, FSP, and Rada vocabulary.