惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
博客园 - Franky
H
Help Net Security
A
About on SuperTechFans
Know Your Adversary
Know Your Adversary
罗磊的独立博客
Help Net Security
Help Net Security
腾讯CDC
博客园 - 三生石上(FineUI控件)
月光博客
月光博客
Project Zero
Project Zero
有赞技术团队
有赞技术团队
Blog — PlanetScale
Blog — PlanetScale
T
Threat Research - Cisco Blogs
The Hacker News
The Hacker News
Engineering at Meta
Engineering at Meta
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Simon Willison's Weblog
Simon Willison's Weblog
T
Threatpost
Google DeepMind News
Google DeepMind News
V
V2EX
B
Blog
人人都是产品经理
人人都是产品经理
J
Java Code Geeks
N
Netflix TechBlog - Medium
P
Privacy International News Feed
Recorded Future
Recorded Future
D
Darknet – Hacking Tools, Hacker News & Cyber Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Stack Overflow Blog
Stack Overflow Blog
Cisco Talos Blog
Cisco Talos Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
S
Securelist
NISL@THU
NISL@THU
The GitHub Blog
The GitHub Blog
T
Troy Hunt's Blog
S
Security @ Cisco Blogs
Vercel News
Vercel News
L
LINUX DO - 热门话题
博客园_首页
The Register - Security
The Register - Security
GbyAI
GbyAI
TaoSecurity Blog
TaoSecurity Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
V2EX - 技术
V2EX - 技术
L
LangChain Blog
T
Tor Project blog
P
Privacy & Cybersecurity Law Blog
Security Latest
Security Latest
K
Kaspersky official blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
The Generic MCP Toolbox: Tools That Register Themselves
Nasrul Hazim Bin Mohamad · 2026-06-19 · via DEV Community

I've now built MCP servers into enough Laravel apps to notice the pattern: I keep rewriting the same tools. Every server needs a whoami. Every server needs to tail logs, peek at failed jobs, retry one, check whether the queue is alive. None of that is domain logic — it's the same generic ops surface, copy-pasted and lightly mutated, in project after project.

So I pulled it into the kit. Today cleaniquecoders/laravel-mcp-kit got a generic, opt-in toolbox — the handful of tools that have zero domain coupling and therefore belong in a package, not in your app. The interesting part isn't the tools themselves. It's the two rules that decide whether a tool even exists in a given install.

The problem with shipping a toolbox

A toolbox package has a tension baked in. You want to ship list_audits, issue_mcp_token, list_roles — genuinely useful tools. But list_audits only makes sense if the host installed owen-it/laravel-auditing. issue_mcp_token needs Sanctum. list_roles needs spatie/laravel-permission.

The naive approach — register them all — blows up the moment the agent calls a tool whose backing package isn't there: a fatal Class not found, or worse, a tool that looks available in the MCP tool list and then errors only when invoked. An agent has no way to know that list_roles is a lie until it's mid-task.

So the toolbox needs to be honest about itself. A host should get exactly the tools its stack can support — no half-wired features, no phantom entries.

Rule one: opt-in by presence

The fix is that a tool registers only when its backing package (and, where it matters, its table) actually exist. Each conditional tool answers one static question:

class ListAuditsTool extends McpKitTool
{
    public static function isAvailable(): bool
    {
        return class_exists(static::model());
    }

    protected static function model(): string
    {
        // resolved from the package's own config, so a host's
        // custom Audit model is honoured, not hard-coded.
        return config('audit.implementation', 'OwenIt\\Auditing\\Models\\Audit');
    }
}

And the registry — the single source of truth for what the server exposes — filters on it:

public static function tools(): array
{
    return array_values(array_filter([
        // Tier 1 — pure-generic, always on (zero dependencies).
        WhoAmITool::class,
        SystemHealthTool::class,
        TailLogsTool::class,
        ListFailedJobsTool::class,
        RetryFailedJobTool::class,
        QueueStatusTool::class,

        // Tier 2 — registered only when the backing package is present.
        ListAuditsTool::isAvailable() ? ListAuditsTool::class : null,
        IssueMcpTokenTool::isAvailable() ? IssueMcpTokenTool::class : null,
        ListRolesTool::isAvailable() ? ListRolesTool::class : null,
        GetUserPermissionsTool::isAvailable() ? GetUserPermissionsTool::class : null,
    ]));
}

array_filter drops the nulls; the server boots with whatever survived. Install Sanctum later and the token tools appear on the next boot with no code change. Uninstall auditing and list_audits quietly vanishes — the server degrades gracefully instead of throwing.

The key design choice is keeping this in one place. TaskServer::boot() reads ToolRegistry::tools() and nothing else decides registration. When you want to know "what does this server actually expose on this install?", there's exactly one list to read. Scatter the class_exists checks across twenty tool constructors and you've built a system nobody can reason about.

There's a subtlety worth naming: isAvailable() is a static gate on capability, and the tool also re-checks inside handle():

public function handle(Request $request): Response
{
    if (! static::isAvailable()) {
        return Response::error('Audit reading is unavailable — owen-it/laravel-auditing is not installed.');
    }
    // ...
}

Belt and suspenders. The registry should mean an unavailable tool never reaches handle() — but if something ever wires it up directly, the tool refuses cleanly rather than fatalling. Think of it like a contract: the registry promises not to call you when you can't work, and you still check the promise was kept.

Rule two: gated and uuid-only, every single tool

Presence decides whether a tool exists. Authorization decides whether this caller may use it. Those are different axes and the toolbox keeps them separate.

Every tool reads its required ability from config rather than hard-coding a permission name:

protected function ability(): string
{
    return $this->configuredAbility('view-audits');
}

That indirection matters more than it looks. Your app probably doesn't call its permissions view-audits — it has its own scheme, its own guard, its own role names. By routing every ability through config('mcp-kit.abilities.*'), the host remaps the kit's generic ability onto whatever its permission system actually uses. The package ships an opinion about what needs guarding; the host keeps full control over who clears the gate.

The other half of the rule: reads are tagged #[IsReadOnly] and writes funnel through an invokable Action — never inline in the tool. retry_failed_job doesn't re-dispatch a job itself; it calls a RetryFailedJob action. The tool is the MCP-shaped doorway; the Action is the thing that actually mutates state, and it's independently testable and reusable outside MCP entirely.

And identity stays uuid-only. Tools emit and accept public UUIDs, never the internal auto-increment id. An agent — or anyone reading the transcript — never sees your sequential primary keys, which leak row counts and make enumeration trivial. The public id is the uuid; the internal id stays internal.

A nice payoff: the export-by-signature pattern

One tool worth calling out because it generalizes well. export_logs doesn't inline a giant blob of log text into the MCP response — that's how you blow a context window. Instead it writes the slice to disk and hands back a short-lived signed download URL:

// inside the tool:
return $this->download($contents, 'failed-jobs-export.json');

The signature is the capability. Laravel's signed middleware rejects a tampered or expired link, so you don't need a second auth layer on the download route — the URL either verifies or it doesn't. The agent gets a link, the human (or the calling system) fetches it once, and the link dies on a timer. Large payloads leave through a side door instead of clogging the conversation.

Testing the registry, not just the tools

The thing most worth a test here isn't any single tool — it's the registration logic, because that's the part with branches. A tool's happy path you'll notice when it breaks; a tool silently missing from the registry you won't.

it('registers tier-2 tools only when the backing package is present', function () {
    expect(ListAuditsTool::isAvailable())
        ->toBe(class_exists(\OwenIt\Auditing\Models\Audit::class));
});

it('keeps the server bootable when an optional package is absent', function () {
    $tools = ToolRegistry::tools();

    // Tier-1 tools are unconditional — always there.
    expect($tools)
        ->toContain(WhoAmITool::class)
        ->toContain(SystemHealthTool::class);
});

it('never exposes an unavailable tool', function () {
    foreach (ToolRegistry::tools() as $tool) {
        if (method_exists($tool, 'isAvailable')) {
            expect($tool::isAvailable())->toBeTrue();
        }
    }
});

That last test is the one I'd fight to keep. It asserts the invariant the whole design rests on: if it's in the registry, it works. Everything else is downstream of that promise.

The line I won't cross

The toolbox stops at generic. Anything domain-coupled — identity sync, gateway provisioning, directory-presence checks, your business rules — stays in the host app behind project-specific Actions. The kit gives you the generic spine: ops, jobs, logs, tokens, the gate-first pattern, the signed-URL helper. The domain is yours, and it should be, because the day a "generic" package starts knowing about your business logic is the day it stops being reusable.

That's the whole philosophy in one sentence: ship the spine, not the organs. A toolbox that registers itself, gates itself, and refuses to pretend it can do things it can't — and leaves your domain exactly where it belongs.

It's open source, so the tier tables and the full tool list are in the repo: github.com/cleaniquecoders/laravel-mcp-kit.