<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Fady Mondy's blog]]></title><description><![CDATA[Fady Mondy's blog]]></description><link>https://3x1.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 14 Oct 2026 15:42:35 GMT</lastBuildDate><atom:link href="https://3x1.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Ship a Laravel Admin Panel in a Day with Filament 5 and TomatoPHP]]></title><description><![CDATA[Every Laravel admin panel I've built starts with the same list: users and roles, app settings, English and Arabic, a media library, menus, some content pages, and sooner or later invoices. None of it ]]></description><link>https://3x1.hashnode.dev/ship-a-laravel-admin-panel-in-a-day-with-filament-5-and-tomatophp</link><guid isPermaLink="true">https://3x1.hashnode.dev/ship-a-laravel-admin-panel-in-a-day-with-filament-5-and-tomatophp</guid><category><![CDATA[Laravel]]></category><category><![CDATA[PHP]]></category><category><![CDATA[Filament]]></category><category><![CDATA[open source]]></category><dc:creator><![CDATA[Fady Mondy]]></dc:creator><pubDate>Mon, 12 Oct 2026 11:22:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64de161bb47a2d7debae7d8b/4cf84ff7-8309-4a85-ac02-28b1e28ca326.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every Laravel admin panel I've built starts with the same list: users and roles, app settings, English and Arabic, a media library, menus, some content pages, and sooner or later invoices. None of it is your product, but all of it has to work before anyone sees your product. I maintain TomatoPHP, 68 MIT-licensed Filament plugins, and this is the exact order I install them in on Filament 5 when I want that part done in a day. <em>(This post first appeared on <a href="https://tomatophp.com/en/blog/laravel-admin-panel-in-a-day-filament-tomatophp?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">tomatophp.com</a>.)</em></p>
<p><a href="https://filamentphp.com">Filament</a> gives you the panel. TomatoPHP gives you those modules as plain composer packages. There are 68 TomatoPHP Filament plugins, all MIT-licensed and released for <strong>Filament 5, Laravel 12–13 and PHP 8.2+</strong>. Each one registers itself with your panel in one line.</p>
<h2>What you end up with</h2>
<table>
<thead>
<tr>
<th>You need</th>
<th>Plugin</th>
<th>Package</th>
</tr>
</thead>
<tbody><tr>
<td>Users and roles</td>
<td><a href="https://tomatophp.com/en/plugins/filament-users?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">User Manager</a></td>
<td><code>tomatophp/filament-users</code></td>
</tr>
<tr>
<td>App settings (site name, logo, colours, social links)</td>
<td><a href="https://tomatophp.com/en/plugins/filament-settings-hub?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">Settings Hub</a></td>
<td><code>tomatophp/filament-settings-hub</code></td>
</tr>
<tr>
<td>English + Arabic UI, translated from the panel</td>
<td><a href="https://tomatophp.com/en/plugins/filament-translations?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">Translation Manager</a> + <a href="https://tomatophp.com/en/plugins/filament-language-switcher?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">Language Switcher</a></td>
<td><code>tomatophp/filament-translations</code>, <code>tomatophp/filament-language-switcher</code></td>
</tr>
<tr>
<td>Files and images</td>
<td><a href="https://tomatophp.com/en/plugins/filament-media-manager?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">Media Manager</a></td>
<td><code>tomatophp/filament-media-manager</code></td>
</tr>
<tr>
<td>Navigation menus</td>
<td><a href="https://tomatophp.com/en/plugins/filament-menus?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">Menu Builder</a></td>
<td><code>tomatophp/filament-menus</code></td>
</tr>
<tr>
<td>Posts, categories and pages</td>
<td><a href="https://tomatophp.com/en/plugins/filament-cms?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">CMS Builder</a></td>
<td><code>tomatophp/filament-cms</code></td>
</tr>
<tr>
<td>Invoices and payments</td>
<td><a href="https://tomatophp.com/en/plugins/filament-invoices?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">Invoice Manager</a></td>
<td><code>tomatophp/filament-invoices</code></td>
</tr>
<tr>
<td>Installable app on phones</td>
<td><a href="https://tomatophp.com/en/plugins/filament-pwa?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">PWA</a></td>
<td><code>tomatophp/filament-pwa</code></td>
</tr>
</tbody></table>
<p><strong>Try before you install:</strong> <a href="https://demo.tomatophp.com?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">demo.tomatophp.com</a> runs 31 TomatoPHP plugins together on Filament 5, with a prefilled demo account. The data resets every hour, so click anything.</p>
<h2>Before you start</h2>
<p>Start from a fresh Laravel app with a Filament 5 panel:</p>
<pre><code class="language-bash">composer require filament/filament:"^5.0"
php artisan filament:install --panels
</code></pre>
<p>Every plugin below follows the same three steps: <code>composer require</code>, run its <code>install</code> command, then add <code>-&gt;plugin(...)</code> to <code>app/Providers/Filament/AdminPanelProvider.php</code>. Some also publish a migration. Each plugin page links to its README with the exact steps, so keep it open while you go.</p>
<h2>Morning: the foundation</h2>
<p><strong>Users.</strong> One package gives you the user resource. If you need roles, the README shows how to pair it with Filament Shield.</p>
<pre><code class="language-bash">composer require tomatophp/filament-users
</code></pre>
<p><strong>Settings.</strong> Settings Hub stores site settings in the database and gives you a settings page in the panel. Several other TomatoPHP plugins (PWA, invoices) add their own pages to it, so install it early.</p>
<pre><code class="language-bash">composer require tomatophp/filament-settings-hub
php artisan filament-settings-hub:install
</code></pre>
<p><strong>Two languages.</strong> The Translation Manager scans your code for <code>trans()</code> and <code>__()</code>, stores the strings in the database with a cache, and lets you translate them from the panel. The Language Switcher adds the toggle. English and Arabic work out of the box, including RTL.</p>
<pre><code class="language-bash">composer require tomatophp/filament-translations tomatophp/filament-language-switcher
php artisan filament-translations:install
php artisan filament-language-switcher:install
php artisan filament-translations:import
</code></pre>
<h2>Midday: content</h2>
<p><strong>Media.</strong> Media Manager puts a GUI on top of Spatie Media Library: folders, uploads, and a picker you can use in your own forms.</p>
<pre><code class="language-bash">composer require tomatophp/filament-media-manager
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="medialibrary-migrations"
php artisan filament-media-manager:install
</code></pre>
<p><strong>Menus and pages.</strong> The Menu Builder stores navigation in the database, so you can render it in the panel or in a Livewire component. The CMS Builder adds posts and categories. Both are translatable, so register <code>lara-zeus/spatie-translatable</code> next to them.</p>
<pre><code class="language-bash">composer require tomatophp/filament-menus tomatophp/filament-cms
php artisan filament-menus:install
php artisan filament-cms:install
</code></pre>
<p>One warning from the README: the CMS creates <code>categories</code> and <code>posts</code> tables. If your app already has those, rename yours first.</p>
<h2>Afternoon: money</h2>
<p><strong>Invoices.</strong> The Invoice Manager handles invoices and payments with multiple currencies and invoice types. Call <code>-&gt;useSettingsHub()</code> and its settings appear next to everything else.</p>
<pre><code class="language-bash">composer require tomatophp/filament-invoices
php artisan filament-invoices:install
</code></pre>
<p>Before the first invoice, tell it which models invoices are <em>from</em> and <em>for</em> (for example your company and your customers). The README shows the facade call to put in a service provider.</p>
<h2>Register everything</h2>
<p>With those installed, the panel provider looks roughly like this (check each README for the options you want):</p>
<pre><code class="language-php">use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        -&gt;plugin(\TomatoPHP\FilamentUsers\FilamentUsersPlugin::make())
        -&gt;plugin(\TomatoPHP\FilamentSettingsHub\FilamentSettingsHubPlugin::make()
            -&gt;allowSiteSettings()
            -&gt;allowSocialMenuSettings())
        -&gt;plugin(\TomatoPHP\FilamentTranslations\FilamentTranslationsPlugin::make())
        -&gt;plugin(\TomatoPHP\FilamentLanguageSwitcher\FilamentLanguageSwitcherPlugin::make())
        -&gt;plugin(\TomatoPHP\FilamentMediaManager\FilamentMediaManagerPlugin::make())
        -&gt;plugin(\LaraZeus\SpatieTranslatable\SpatieTranslatablePlugin::make()
            -&gt;defaultLocales(['en', 'ar']))
        -&gt;plugin(\TomatoPHP\FilamentMenus\FilamentMenusPlugin::make())
        -&gt;plugin(\TomatoPHP\FilamentCms\FilamentCMSPlugin::make()
            -&gt;useCategory()
            -&gt;usePost())
        -&gt;plugin(\TomatoPHP\FilamentInvoices\FilamentInvoicesPlugin::make()
            -&gt;useSettingsHub());
}
</code></pre>
<p>Run <code>php artisan migrate</code>, sign in, and you have a bilingual admin panel with users, settings, media, menus, content and invoices. That is the part every client expects and nobody pays extra for.</p>
<h2>What this won't do for you</h2>
<ul>
<li><strong>It won't design your domain.</strong> The plugins cover the generic modules. Your orders, bookings or patients are still your code, and that's where the day you saved should go.</li>
<li><strong>Read the README of each plugin.</strong> Some publish migrations or need a companion package. The compatibility table at the top tells you which major version matches your Filament.</li>
<li><strong>Not every repo is a finished plugin.</strong> The TomatoPHP organisation has more repos than the 68 released Filament plugins. Use what's listed on the <a href="https://tomatophp.com/en/plugins?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">plugins page</a>, which is the maintained set.</li>
</ul>
<h2>How the plugins are maintained</h2>
<p>During the Filament 5 upgrade, every plugin went through the same release gate before it was tagged: open issues fixed and community PRs merged with credit, a green Pest suite with a regression test for each fix, and then a real install in a sandbox Laravel 13 + Filament 5 app where a smoke test renders every panel page with zero skipped. The same pass found and fixed real security bugs, for example unescaped user HTML in the docs, blog and notes plugins, and payment gateway secrets that were reaching the browser.</p>
<p>The packages have more than 1.7 million installs on Packagist, and the source is on <a href="https://github.com/tomatophp">GitHub</a>.</p>
<p><strong>Next steps:</strong> <a href="https://tomatophp.com/en/plugins?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">browse all plugins</a> · <a href="https://tomatophp.com/en/docs?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">read the docs</a> · <a href="https://demo.tomatophp.com?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">try the live demo</a></p>
<hr />
<p><em>Originally published on <a href="https://tomatophp.com/en/blog/laravel-admin-panel-in-a-day-filament-tomatophp?utm_source=hashnode&amp;utm_medium=syndication&amp;utm_campaign=tomatophp-launch-oct26">tomatophp.com</a>.</em></p>
]]></content:encoded></item><item><title><![CDATA[How to Ship an Official MCP Server for Your SaaS in 2 Weeks]]></title><description><![CDATA[Originally published at fadymondy.com.
Your customers are starting to ask a new question in sales calls and support tickets: "Does it work with Claude?" Sometimes it is ChatGPT or Cursor instead, but ]]></description><link>https://3x1.hashnode.dev/how-to-ship-an-official-mcp-server-for-your-saas-in-2-weeks</link><guid isPermaLink="true">https://3x1.hashnode.dev/how-to-ship-an-official-mcp-server-for-your-saas-in-2-weeks</guid><category><![CDATA[mcp]]></category><category><![CDATA[AI]]></category><category><![CDATA[SaaS]]></category><category><![CDATA[api]]></category><dc:creator><![CDATA[Fady Mondy]]></dc:creator><pubDate>Sun, 11 Oct 2026 13:00:12 GMT</pubDate><content:encoded><![CDATA[<p><em>Originally published at <a href="https://fadymondy.com/en/blog/ship-official-mcp-server-for-saas-in-2-weeks?utm_source=hashnode&amp;utm_medium=social&amp;utm_campaign=launch-oct26">fadymondy.com</a>.</em></p>
<p>Your customers are starting to ask a new question in sales calls and support tickets: <em>"Does it work with Claude?"</em> Sometimes it is ChatGPT or Cursor instead, but the request is the same. They want to ask their AI assistant to do real work inside your product, without copying data between tabs.</p>
<p>The standard way to make that happen is an <strong>MCP server</strong>. The Model Context Protocol is the open protocol AI assistants use to discover and call tools in other products. If you already have a decent API, an official MCP server is a small, well-bounded project. Here is how I plan and ship one in about two weeks, and what to decide before anyone writes code.</p>
<h2>What an MCP server actually is (for a SaaS team)</h2>
<p>An MCP server is a thin layer that sits <strong>beside</strong> your product and talks to your existing API. It exposes a short list of <em>tools</em>, each with a name, a plain-language description and a typed input schema. The assistant reads those descriptions, decides which tool fits the user's request, and calls it.</p>
<p>Three things follow from that:</p>
<ul>
<li><strong>Your backend doesn't change.</strong> The server is a client of your API, like your web app is.</li>
<li><strong>Your language doesn't matter.</strong> I usually build in TypeScript or Go, or PHP for Laravel products, whatever your team can maintain.</li>
<li><strong>Tool design matters more than code volume.</strong> Most of the work is deciding what the assistant should be able to do, and describing it so the model picks the right tool.</li>
</ul>
<p>There are two common ways to run one: <strong>remote</strong> (a hosted HTTP endpoint your cloud customers connect to with OAuth) and <strong>local</strong> (a package the user runs on their machine, which suits self-hosted installs). Many SaaS products start with remote.</p>
<h2>Why "official" matters</h2>
<p>If you don't ship one, someone else might. Community MCP servers for popular products appear quickly, and they usually ask users to paste a full-access API key into a config file. Your customers then run unvetted code with their production credentials, and you get the support tickets.</p>
<p>An official server lets you control three things: which actions are exposed, how authentication works, and how your product is described to the assistant.</p>
<h2>The 2-week plan</h2>
<p>Two weeks is realistic when the API exists and is documented, and the first version focuses on the 10–20 actions people actually ask for. Here is the sequence I use.</p>
<h3>Step 0: a one-page tool spec (before any commitment)</h3>
<p>Before building, I read your API docs and write a one-page spec. It lists the proposed tools, what each one does, its inputs, whether it reads or writes, and which auth scope it needs. It is the cheapest way for both sides to see whether the project makes sense.</p>
<p>A good spec answers:</p>
<ul>
<li>What are the five things a user will ask the assistant to do on day one?</li>
<li>Which of those are read-only and which change data?</li>
<li>Which actions are destructive or expensive and need a clear warning?</li>
<li>What does the assistant need to <em>find</em> things (search, filters, IDs)?</li>
</ul>
<h3>Days 1–3: tool design and scaffolding</h3>
<p>Turn the spec into tool definitions. Keep the list short and the names obvious. <strong>Don't mirror every endpoint.</strong> An assistant does better with <code>find_customer</code> and <code>create_invoice</code> than with forty CRUD endpoints that differ by one field.</p>
<p>Write the descriptions as instructions to the model: when to use the tool, what it returns, and what <em>not</em> to use it for. In my experience this is the work that most changes how well the server performs.</p>
<p>On my own servers, tools live in a <strong>registry</strong>: one list the protocol layer reads. Adding a tool later means adding one entry, not touching the transport or auth plumbing. It keeps the server easy to grow after handover.</p>
<h3>Days 4–7: authentication and permissions</h3>
<p>This is where an official server earns its name.</p>
<ul>
<li><strong>OAuth for remote servers.</strong> The MCP authorization spec is built on OAuth 2.1. On <a href="https://zekra.dev/en/docs/mcp">Zekra</a>'s remote MCP endpoint I implemented the full flow: PKCE (S256), dynamic client registration and per-brain consent, with access re-checked against current membership on every call.</li>
<li><strong>Scoped API keys as a fallback.</strong> In <a href="https://moharrik.com">Moharrik</a>, keys are read-only by default and expire after 90 days.</li>
<li><strong>Read and write separated.</strong> Mark read-only tools as read-only (MCP tool annotations support <code>readOnlyHint</code> and <code>destructiveHint</code>). On Zekra, adding read-only annotations let AI coding agents and other MCP clients run recall and list tools without an approval prompt, while writes still ask.</li>
<li><strong>Make agent actions traceable.</strong> When an assistant writes data, record that it was an agent. On my issue tracker, comments created over MCP are stored with <code>author_kind = 'agent'</code>, so a human can always tell them apart.</li>
</ul>
<h3>Days 8–10: test against real assistants</h3>
<p>Unit tests aren't enough. Connect the server to Claude, ChatGPT and Cursor and run the prompts from the spec. Watch for:</p>
<ul>
<li>the assistant picking the wrong tool (fix the description, not the model);</li>
<li>tools returning too much data (paginate and trim; context is not free);</li>
<li>vague errors (return messages the assistant can act on, like "customer not found, try search_customers").</li>
</ul>
<h3>Days 11–14: docs, listing and handover</h3>
<ul>
<li><strong>Setup guides</strong> for each assistant: one page each, with the exact config.</li>
<li><strong>A permissions page</strong> that explains in plain language what each scope allows. In Moharrik this explainer is generated from the tool registry, so it can't drift from the code.</li>
<li><strong>Listings</strong> in the main MCP directories, so users can find the official server.</li>
<li><strong>Handover.</strong> The repository, deployment notes and a short walkthrough. The code ships under your name and licence.</li>
</ul>
<h2>What stretches past two weeks</h2>
<p>Be honest about scope up front. These usually add time:</p>
<ul>
<li>no public API yet, or an API that can't do what users ask;</li>
<li>complex multi-tenant permissions that aren't modelled in the API;</li>
<li>long-running jobs that need progress reporting or webhooks;</li>
<li>a self-hosted package <em>and</em> a remote endpoint in the first release.</li>
</ul>
<p>None of them blocks a project. They belong in the spec so the plan and the price stay fixed.</p>
<h2>Checklist before you start</h2>
<ul>
<li> Your API is documented and stable enough to build against.</li>
<li> You have listed the top 5 user requests the assistant should handle.</li>
<li> You know which actions must never run without a confirmation.</li>
<li> You have decided on remote, local or both.</li>
<li> Someone on your team will own the server after handover.</li>
</ul>
<h2>Proof, not promises</h2>
<p>I build MCP servers for my own products, and they run in production:</p>
<ul>
<li><strong><a href="https://fadymondy.com/en/projects/orchestra-mcp">Orchestra MCP</a></strong>: an AI-agentic IDE framework with a plugin-host architecture.</li>
<li><strong><a href="https://fadymondy.com/en/projects/cabrain">Zekra</a></strong>: shared memory for AI agents, served over a remote MCP endpoint with OAuth 2.1.</li>
<li><strong><a href="https://moharrik.com">Moharrik</a></strong>: a CRM with inbox and WhatsApp that is fully operable over MCP.</li>
<li><strong><a href="https://mcp.nasaqui.com">Nasaq UI</a></strong>: an open-source design system with its own MCP server.</li>
</ul>
<p>If you want the same for your product, see <a href="https://fadymondy.com/en/services/mcp-server-development?utm_source=hashnode&amp;utm_medium=social&amp;utm_campaign=launch-oct26">MCP server development</a>. The process starts with a free one-page tool spec.</p>
]]></content:encoded></item></channel></rss>