DEV Community

A0mineTV
A0mineTV

Posted on

How I Built a Support Ticket MCP Server in Laravel

Model Context Protocol is much easier to understand when it is attached to a real workflow.

In my Laravel project, I wanted a support-focused MCP server that could do three concrete things:

  • create a support ticket
  • read a ticket from a resource URI
  • generate a polished summary that is ready to send to a customer or teammate

Instead of building a generic demo, I kept the scope narrow and useful. The result is a local support MCP server powered by laravel/mcp, with one tool, one resource, and one prompt working together as a small support system.

Why this is a good MCP use case

A lot of MCP examples stop at "here is a tool that returns some JSON." That is fine for learning the API, but it does not show why the protocol is interesting.

Support workflows are a better fit because they combine three different kinds of interaction:

  • an action that changes state,
  • a resource that can be fetched again later,
  • a prompt that turns raw operational data into something a human can actually use.

That maps cleanly to MCP:

  • Tool: create the ticket.
  • Resource: fetch the ticket by identifier.
  • Prompt: transform the ticket into a professional summary.

This separation is what I like most about the approach. The AI client does not need custom knowledge about my Laravel app. It only needs to know which MCP capabilities are available.

Registering the server in Laravel

The first step in my project was registering a local MCP server in routes/ai.php:

use App\Mcp\Servers\SupportServer;
use Laravel\Mcp\Facades\Mcp;

Mcp::local('support', SupportServer::class);
Enter fullscreen mode Exit fullscreen mode

That gives the app a dedicated MCP entry point for support operations. I also have a separate CRM server in the same project, which keeps responsibilities isolated instead of pushing every AI capability into one giant surface area.

Defining the support server

The server itself is intentionally small. Its job is to compose capabilities, not to hold business logic. In app/Mcp/Servers/SupportServer.php, I register the three pieces that make the workflow useful:

class SupportServer extends Server
{
    protected array $tools = [
        CreateTicketTool::class,
    ];

    protected array $resources = [
        TicketResource::class,
    ];

    protected array $prompts = [
        SummarizeTicketPrompt::class,
    ];
}
Enter fullscreen mode Exit fullscreen mode

That structure is one of the reasons Laravel feels comfortable here. The framework already encourages small classes with clear responsibilities, and laravel/mcp fits that style well.

The ticket creation tool

The first capability I needed was a way for an AI client to create a support ticket safely.

In app/Mcp/Tools/CreateTicketTool.php, the tool validates three inputs:

  • title
  • description
  • priority

It then creates the ticket and returns a structured response with the fields an agent actually needs next:

return Response::structured([
    'id' => $ticket->id,
    'title' => $ticket->title,
    'status' => $ticket->status,
    'priority' => $ticket->priority,
]);
Enter fullscreen mode Exit fullscreen mode

This is an important detail. I do not want the tool to return a blob of unstructured text when the next step may depend on the new ticket ID. MCP tools are much more useful when they return predictable data that another step can consume immediately.

I also defined the schema explicitly. That gives the client a machine-readable contract instead of forcing it to guess the arguments:

public function schema(JsonSchema $schema): array
{
    return [
        'title' => $schema->string()->required(),
        'description' => $schema->string()->required(),
        'priority' => $schema->string()
            ->enum(['low', 'medium', 'high'])
            ->default('medium'),
    ];
}
Enter fullscreen mode Exit fullscreen mode

For support workflows, this matters a lot. Validation rules are not just a backend concern anymore. They become part of the interface between your Laravel app and the AI client.

The ticket resource

Creating a ticket is only half of the story. Once the ticket exists, an agent should be able to retrieve it again without calling a mutation tool.

That is where the resource comes in. In app/Mcp/Resources/TicketResource.php, the resource receives a ticket identifier, loads the record, and returns a readable text representation:

return Response::text(
    "Ticket #{$ticket->id}\n".
    "Titre: {$ticket->title}\n".
    "Statut: {$ticket->status}\n".
    "PrioritΓ©: {$ticket->priority}\n\n".
    "Description:\n{$ticket->description}"
);
Enter fullscreen mode Exit fullscreen mode

I like this pattern because it makes the ticket easy to reuse in multiple contexts:

  • an assistant can read it before answering a user
  • another prompt can summarize it
  • a human can inspect the output without decoding JSON

This is a small design choice, but it makes the system more composable. A tool changes state. A resource exposes state. Keeping those responsibilities separate leads to cleaner MCP surfaces.

The prompt that turns raw data into a ready-to-send summary

The third piece is the one that makes the demo feel like a real support workflow rather than a CRUD exercise.

In app/Mcp/Prompts/SummarizeTicketPrompt.php, I created a prompt that takes:

  • a tone
  • the raw ticket_text

The prompt then instructs the model to return a structured support summary covering:

  • the problem
  • the impact
  • the suggested priority
  • the next action.

Here is the core idea:

return [
    Response::text(
        "You are a support assistant. Summarize the ticket below in a {$tone} tone. ".
        "Return: problem, impact, suggested priority, next action."
    )->asAssistant(),
    Response::text($ticket_text),
];
Enter fullscreen mode Exit fullscreen mode

This is the part many teams skip. They expose data, but they do not shape the final output. In support, the final wording matters:

  • internal teams may want concise operational summaries
  • customer-facing teams may want a more professional tone
  • some situations need a friendlier message without losing the technical facts.

Packaging that as an MCP prompt means the formatting logic is reusable instead of being reinvented in every client.

Why I like this architecture

What I ended up with is not a huge system. It is a very small one. That is exactly why it works well.

Each MCP primitive has a clear role:

  • the tool creates the record,
  • the resource exposes the record,
  • the prompt transforms the record into communication.

That separation makes the project easier to extend. If I want to go further, I already know the next logical steps:

  • add a tool to update ticket status,
  • expose a resource for recent tickets,
  • create prompts for escalation notes or customer replies,
  • connect the same support server to external AI clients.

Laravel gives me the application structure, validation, and persistence patterns I already trust. MCP adds a clean interface layer on top of that.

Final thoughts

The main lesson from this project is that MCP becomes much more compelling when you model a real workflow instead of isolated demo commands.

In my Laravel app, the support server is useful because it does three connected things well:

  1. it creates a ticket with validated input,
  2. it lets the client read that ticket back as a resource,
  3. it provides a prompt that converts raw ticket data into a professional summary.

That is enough to move from "AI can call my backend" to "AI can participate in a support flow with clear boundaries."

If you are experimenting with laravel/mcp, I would strongly recommend starting with a narrow domain like support, sales handoff, onboarding, or incident triage. Pick one workflow, then model it with one tool, one resource, and one prompt. You will learn much more than you would from a generic demo.

If I keep iterating on this project, the next step will be turning this support server into a fuller ticket operations layer with richer resources and multi-step support actions. But even in its current form, it already shows why Laravel and MCP fit together so naturally.

Top comments (0)