Writing 路 28 August 2026
Hardening Your Agentic Development Setup: Rules, Permissions, and Tooling
How to harden your agentic development setup with rules-with-reasons, permissions discipline, and disciplined choices about skills and Model Context Protocol servers.

In Part 1 of this series, I covered the foundation of agentic development: how to onboard an agent to a project, how to brief it within a conversation, and how to keep that shared understanding honest as the codebase evolves. In Part 2, I covered the three workflows I reach for to get actual engineering work done: a lightweight plan-and-execute pattern for day-to-day tasks, GitHub’s spec-kit for substantive features, and the BMAD-Method for solo or underbacked work. Between them, those two posts cover most of what makes agentic development work.
This post is about what makes it safe.
I have been doing this since early 2022 across personal projects, open-source contributions, and client production work, and the lesson I keep coming back to is that a working setup is not the same as a safe one. The same agent that can write good code can also leak credentials, drop a destructive command into a terminal session it cannot retrieve, install a skill from a repository nobody on the team has read, or burn through your context budget loading Model Context Protocol (MCP) server descriptions before you have sent your first message. Hardening your agentic development setup is how you close that gap between works and safe, and it is, I think, the part of the series that matters most.
I maintain OwaspHeaders.Core, an open-source security-headers middleware for ASP.NET Core that has been downloaded more than 1.9 million times from NuGet (as of mid-2026) and runs in production at organisations whose names I will not be naming in a blog post. Microsoft recognised that work with the Most Valuable Professional (MVP) award for Developer Technologies over four consecutive years (a recognition I no longer hold). Both credentials are about the same skill: building systems that hold up long after the original author has stopped paying attention to them. Agentic development is no different.
The argument has three parts. The first is rules, which Part 1 introduced and this post takes considerably deeper, including where the secure-coding rules I rely on actually come from. The second is permissions, and why I have never once run Claude Code with --dangerously-skip-permissions. The third is tooling discipline: what to add to your setup, what to leave out, and how to tell the difference. Each is straightforward in isolation; the discipline is in applying all three consistently, on every project, for as long as the project is live.
Rules with Reasons, and Where Secure-by-Default Rules Come From
Part 1 introduced the rules-with-reasons pattern in passing: each rule in CLAUDE.md paired with the why of its existence, on the grounds that an agent applies a rule with a reason more intelligently than it applies one given by edict. In practice, the rules section is where most of the day-to-day discipline of agentic development actually lives. The right CLAUDE.md rules turn the agent into a collaborator who reaches the right conclusion without being told to; the wrong ones, or their absence, produce work that has to be redone.
The rules I add to every project fall into three groups: behavioural, workflow, and secure-coding. I will show two or three from each group, in my own voice.
鈩癸笍 Note
A small caveat first: the examples below are deliberately a little longer than the versions I actually keep in my working
CLAUDE.mdfiles. The extra words are there to make the reason in each rule fully explicit for you, the reader; in a working file, the reason is often as short as a single subordinate clause. The point of the examples, in any case, is the form, not the content; if you adopt the form, your own rules will look nothing like mine.
Behavioural rules
These shape what the agent does and does not do in the conversation itself. Part 1’s example was “do not run git add, because I want to review every change before it is committed”, and that remains the single most important behavioural rule I deploy. A close second is this one:
“When we are working from a plan or TODO list, do not list the plan or TODO list step in comments like
// plan x, section y, task 4.2: .... These can be useful when working through the plan, but age like milk once committed to source control. If these comments are required to keep track of where we are, add a TODO for cleaning them up before telling me you are done.”
The reason is in the rule itself: comments that reference the plan or task list are scaffolding, and scaffolding belongs on a building site, not in the finished building. The agent will, given the chance, leave that scaffolding behind as // step 3.2 complete comments scattered through the diff. I added this rule after reviewing a pull request littered with comments like // T003: Added authentication header. I bounced the pull request asking for the comments to be removed; the contributor’s defence was that “the agent added those”.
That defence underlined two problems at once. The first is the surface one the rule names: comments tagged to a plan task become noise the moment the plan is finished. The second is deeper, and no rule on its own can solve it: the contributor had either not read what their agent committed, or had decided it was not worth reading. The rule makes the first kind of comment temporary by design; the embedded TODO is the prompt that catches them before review.
Workflow rules
These describe the order of operations the agent should follow when working. Two examples from mine:
“After you have confirmed that the build completes and tests pass, ALWAYS run the linter, because I want all code to match the team-approved coding standards. Neither of us wants to waste time dealing with a pull request comment related to coding style. Let’s run the linter and never have to deal with comments like that.”
“Tests are non-optional because we want to be able to verify that our code works the way we say it does. This also sets us up to be able to add regression tests when we fix a bug. The only get-out for tests being non-optional is when we are working with legacy code that has no tests, because this can be non-trivial to set up. If we are working with untested code, I would like you to investigate the over-under on making it testable first; that way I can push back against the PM on estimates.”
The linter rule does what it says. The tests rule is more interesting because of the get-out clause. A naively absolute rule (“tests are mandatory, full stop”) breaks on contact with the brownfield code most working engineers spend most of their time in: written before testing was the norm, by people who have since moved on, with no easy seam to test against. A rule with no get-out forces the agent either to write tests that pass without verifying anything, or to refuse the work; the first is bad, the second worse. The get-out clause names the exception explicitly and turns it into a useful action: investigate the cost of making the code testable, surface it to me, and let me have an informed conversation with the Product Manager (PM) about estimates. The rule respects the reality of the work.
Secure-coding rules. These are the rules where the why does most of the work, because the rules themselves often look obvious. Two from mine:
“Whenever you are about to write any code, I’d like you to read through the secure coding rules found in
docs/secure-coding.mdbecause security is our gold standard. If it is written as secure-by-default, our colleagues can only fall into the pit of success when using our code.”
“You are working with a senior developer here, so I do not want any ‘clever’ hacks or tricks that a junior or less-experienced person might create. Simple always beats clever, and simple helps with secure-by-default because you are not going off the rails. Try to find a solution that you could explain in an elevator ride.”
The first points the agent at a file that lives in the repository; I will come back to where its contents come from in a moment. The second is the rule I rely on most often to catch the mistake that does not look like a mistake: cleverness. By which I mean optimisations that obscure intent, tricks that work only because of some non-obvious property of the language, abstractions that compress complexity rather than removing it. Clever code is often correct; it is also often the code that hides the security flaw the next engineer cannot see, because they cannot read it cleanly enough to spot it. The elevator-ride test catches it: if you could not explain the solution before the doors open, it is too clever for production.
Where the secure-coding rules come from.
The docs/secure-coding.md file referenced in the first rule above is not something I write from scratch on every project. I draw heavily on three industry references, and so should you.
The first is TikiTribe’s claude-secure-coding-rules, the closest thing the open-source community currently has to a vendored, audit-ready set of secure-coding rules for agentic use. It covers more than a hundred rule sets across twelve programming languages, and the Artificial Intelligence (AI) and Machine Learning (ML) rules are grounded in the National Institute of Standards and Technology (NIST) AI Risk Management Framework, MITRE ATLAS, and Google’s Secure AI Framework. Engineers sometimes arrive, see that several of the canonical examples are written in Python, and dismiss the lot because their project is in a different language. That is a mistake: the repository spans Python, JavaScript, TypeScript, Go, Rust, Java, C#, Ruby, R, C++, Julia, and SQL, and even where your stack is not listed, the principle a rule encodes is rarely language-specific. A competent agent can translate the form; the value is in reading the rules, understanding what they protect against, and adopting the equivalents in your own stack. Reading other people’s secure-by-default rules is, incidentally, one of the better ways to learn to write your own.
The second is the OWASP Agentic Skills Top 10, which catalogues the security risks specific to agentic skills themselves. The framing OWASP use is the one I find most useful: MCP describes how the model talks to tools; the Agentic Skills Top 10 describes what those tools actually do. The list covers categories such as Malicious Skills, Supply Chain Compromise, Over-Privileged Skills, Insecure Metadata, and Weak Isolation. I will come back to several in the tooling section.
The third is the OWASP AI Agent Security Cheat Sheet, the broadest of the three and the one I send to engineers who want a single document to read on the subject. It identifies thirteen categories of risk: prompt injection, tool abuse, memory poisoning, data exfiltration, denial-of-wallet attacks, and several others, each with practical recommendations grounded in real engineering practice. If you read only one of the three references, read this one.
馃挕 Tip
You might remember when I wrote about Chipotle’s Pepper facing a denial-of-wallet attack in “When Your AI Chatbot Becomes Someone Else鈥檚 Free Compute: The Pepper Lesson”. Worth a read if you haven’t yet.
Permissions and the One Flag I Have Never Used
The flag in question is --dangerously-skip-permissions, sometimes called “YOLO mode” in the community, and I have never used it. I am not saying that as a boast or a moral position; I am saying it because the framing it imposes on the conversation is incompatible with how I actually use agentic tools.
The framing I work with is pair programming. The agent is the driver; my hands are off the keyboard. I am the navigator; I am watching the road, reading the map, calling out turns. The model in my head is the classic pair programming one (swap it for chauffeur driving if you prefer; the metaphor is the same): two people, one keyboard, and a constant negotiation about where the work is going. The agent proposes an action; I see the proposal; I approve, redirect, or reject. The conversation accumulates into work I have actually navigated through, not work I happened to find at the end of a session.
What --dangerously-skip-permissions does is take the navigator out of the car. The driver is left to read their own map at sixty miles an hour. They might do it well; many sessions do in fact go fine. But the value of a navigator is most visible in the cases where they prevent a disaster, and you cannot harvest that value if the navigator is asleep in the back.
That is the primary argument. Three supporting ones sit behind it.
The first is the obvious one. The permission system is what catches the irreversible action you did not expect the agent to take. “Delete that branch.” “Drop that table.” “Force-push to main.” “Run that database migration against the production environment.” All of these are recoverable with effort; many only with significant effort; some not at all. The permission prompt is the moment of friction that gives you the chance to say “not yet, walk me through what you are about to do first”. Skipping it does not save time; it transfers cost to the moment something goes wrong, which is the worst possible moment to be paying for it.
If that sounds abstract, the PocketOS incident from April this year is the very concrete version of it. Jer Crane, the founder of PocketOS, had an agent (Cursor running Claude Opus 4.6) decide on its own initiative to delete a Railway volume containing the company’s production database. Because Railway stores volume-level backups inside the same volume, the backups went with it. The deletion took nine seconds. The most recent recoverable backup was three months old. Customers running rental businesses on PocketOS spent the following Saturday reconstructing bookings from Stripe payment histories and email confirmations.
The detail that should travel back to the rules conversation is the agent’s own answer when it was asked to explain itself. Crane published the confession verbatim: the agent enumerates the rules it had been given (“NEVER run destructive/irreversible commands… unless the user explicitly requests them”) and then explains, item by item, how it violated each. It guessed instead of verifying. It ran a destructive action without being asked. The rules-with-reasons pattern is not the failure mode here; the agent had been given both rules and reasons, and proceeded anyway.
The failure was that the enforcement layer was not where it needed to be. Cursor’s Plan Mode, marketed as “restricting agents to read-only operations until approval is granted,” did not catch the action; Railway’s Application Programming Interface (API) allowed the destructive call with no confirmation step at all. Crane’s own conclusion is the one-line summary of this entire section: “AI-agent vendor system prompts cannot be the only safety layer.” They are necessary; they are not sufficient. The Claude Code permission prompts are an example of what sufficient looks like for the typical engineering session: each destructive or potentially-destructive action requires explicit approval before it executes. The point of --dangerously-skip-permissions is, by design, to disable that approval.
The second is the audit-trail argument, which is the same one I made for spec-kit’s specifications in Part 2. You cannot review what you did not see. Every action the agent takes that you did not see is one your future self has no way to reconstruct when the question lands six weeks later in a code review. The permission prompts are, in effect, the highlight reel of the session; reading them as they appear builds the institutional memory of what the agent did and why.
The third is about how trust is built. Trust with any collaborator, human or not, is earned through visible competence. You start by reviewing everything; over time, as patterns emerge and the agent demonstrates reliability in a given domain, you can permit a wider band of actions without breaking step. The skip-permissions flag erases that curve; you go straight from full review to no review, with no intermediate state where you have learned which behaviours to trust and which still warrant a closer look.
A friend of mine has taken to calling the flag “YOLO mode”, and his framing of why is the sharpest version of the argument I have heard: “It can work to undermine all of the engineering that is put into getting to the last stage (manufacturing the code). Why bother with all that process just to break the world at the last step?” The discipline of this entire series, the foundation from Part 1, the workflows from Part 2, the rules and tooling here, is the engineering process that gets you to the point where the agent is producing trustworthy work. The permission prompt is the last guard rail before that work touches reality. Disabling it concedes everything you built the process to protect against.
If you want a formal frame for it, the OWASP AI Agent Security Cheat Sheet sets out a risk-classification scheme, running from auto-approving low-risk reads up to step-up authentication for critical, irreversible operations. The Claude Code permission prompt is the informal version of that framework, and it has always been enough for day-to-day engineering. What I have never felt the need to do is disable it.
Tooling Discipline: Skills, Commands, and MCP Servers
The third leg of the discipline is what you add to your agentic setup beyond the agent itself: custom skills and slash commands, Model Context Protocol servers, plugins, extensions, integrations. Each is a piece of software that consumes context, makes API calls on your behalf, executes code in some scope, and adds a category of risk you did not have before. Treat them like dependencies in a build manifest: chosen deliberately, audited, and pruned as carefully as you would prune your package.json.
Skills and slash commands
The most useful piece of agentic tooling I have built into a client engagement was a custom /code-review slash command at Ligentia. It was built on Anthropic’s own code-review skill, which runs four parallel review sub-agents over a pull request (two checking against the project’s CLAUDE.md, one focused on bug detection, one analysing git blame and history), scores each finding on a 0-to-100 confidence scale, and filters out anything below 80. The custom version was also informed by a deep-dive analysis I did of how GitHub Copilot’s own code review system goes about the same problem.
The capability the custom version added was integration with the team’s source-control system. I am leaving the provider unnamed for contractual reasons, but the command used that system’s own command-line interface (CLI) to fetch the open pull request, pull the code locally, run the tests, run the linter, perform the review, and post the resulting comments back into the pull request through the same CLI. Routing the comments through the source-control system’s own infrastructure meant the existing email notification machinery did the work of telling the author the review had happened. The team did not have to learn a new tool; they received review comments in the channel they were already paying attention to.
That worked example illustrates something the abstract argument does not. A custom skill is not, or should not be, a one-off script you write once and forget. It is software with a defined scope of capability; in the /code-review case, that scope was “read the open pull request, run the local toolchain, post a comment back”, and nothing else. It did not need filesystem-wide write access; it did not need network egress beyond the source-control system; it did not need the credentials for any other service. Least privilege per skill is the principle, and the OWASP Agentic Skills Top 10 reinforces it explicitly: AST03 (Over-Privileged Skills) is one of the highest-severity items on the list, sitting alongside AST01 (Malicious Skills) and AST02 (Supply Chain Compromise).
The pattern of treating skills as dependencies, with reviewable scope and a clear list of the capabilities they require, is the practical answer to those three risks. Read what you install. Audit what you build.
MCP servers and the sub-five heuristic
The other thing worth being deliberate about is the set of MCP servers you configure into your agent. MCP is the protocol the agent uses to talk to external tools; each server you configure makes its capabilities available to every conversation. There is a real temptation, particularly in the early enthusiasm of adopting agentic tooling, to install one MCP server for every capability you can imagine wanting. “There’s an MCP server for that” becomes the developer’s version of the “there’s an app for that” reflex from the early days of the smartphone.
My heuristic is to keep the number of active MCP servers under five. Like the five-deep limit on progressive discovery from Part 1, it is not a rule; it is a shape that has held up well across the projects I work on. The short version of the why: every configured server loads its tool definitions into the session’s context at the start of the conversation, and most teams have far more configured than they ever actually use. The longer version is two reasons.
The first is that unused servers are pure cost. “There’s an MCP server for that, I’ll install it” is a sentence often not followed by actually using the server in question, and every server in your configuration that you do not use is dead weight: you carry the cost of it and get no value back. Worse, each one is a separate process with its own dependencies, security posture, and potential for failure, so the more you configure, the more surface area there is for something to go wrong, and the harder it is to diagnose what went wrong when it does.
The second is the one I find most often gets missed, and it has to do with how the protocol actually works. When an MCP server connects to a client, the client requests its list of available tools via the protocol’s tools/list call. The tool definitions returned (their names, descriptions, and input schemas) are included in the session’s context, alongside the system prompt, and they remain there for every subsequent turn. The descriptions are not refetched per message; they are loaded once and consume context budget thereafter.
Mike Kistler covered the protocol-level mechanics in detail when he came on The Modern .NET Show recently, and the episode is worth your time for the deeper picture. The practical implication is straightforward: more configured MCP servers means more context burned at the start of every session, which is exactly the resource Part 1 told you to spend carefully.
The context budget is, for teams paying API rates, also a money budget. Most agentic tooling is billed per million tokens; that sounds generous until you multiply by your seat count and the number of sessions each engineer runs in a day. A handful of unused MCP servers, each consuming a few hundred tokens of description, across forty engineers running ten sessions a day, is no longer a rounding error on the invoice. It is, in effect, a self-inflicted version of the denial-of-wallet risk listed in the OWASP cheat sheet I cited earlier; the difference is that there is no attacker, just an accumulation of installs nobody has pruned.
At one client, a developer I was working with had installed 30 MCP servers using exactly the “there’s an MCP server for that” logic above. They came to me confused about why their system prompt had grown so large compared to mine: “I never touched it.” They had not touched it directly; they had touched it by accumulating MCP servers over the months without ever removing one. Going through the list together was instructive. “Do you really need a git MCP server? Not a GitHub one, but one for the local git CLI?” They did not; they had a perfectly serviceable git command line already, and the server was a wrapper around it that they had installed and never used. After the audit they were back down to a working set they could explain in a sentence, and the context budget they had been quietly losing was returned to them.
If your team is up at 15 or 20 MCP servers, you are very likely paying for surface area you are not using; the developer above was at 30 and had no idea where the context cost was coming from until they sat down to look. The right move is not to ban MCP servers; it is to audit them, the same way you would audit your application’s dependency manifest. Which of these have I actually used in the last month? Which duplicate capabilities I have through other means? Which are doing meaningful work and worth their place? The same audit applies to skills and slash commands.
The two layers reinforce each other: the Agentic Skills Top 10 covers the risks of the skills themselves, MCP discipline the risks of the protocol layer they sit on, and together they map the substantive ground of what good agentic tooling discipline looks like.
Where This Leaves Us, and Where This Is Going
Three components, three sections. The rules you write into CLAUDE.md, each with the reason it exists, and a docs/secure-coding.md file that draws on the industry references rather than improvising from scratch. The permissions you keep enabled, every single one, every single session, because pair programming is what this is and the navigator does not get out of the car. The tooling you add deliberately, audit periodically, and treat as dependencies in their own right rather than free additions to your toolkit. None of it is complex; all of it requires that you keep doing it.
The argument that runs through all three parts of the series is the same one I made in Part 1, and which Part 2 reinforced. Software engineering has always been about exercising judgement over a system you are responsible for. We wrote in assembly, then in higher-level languages, and we trusted the compiler. We wrote frameworks, then we wrote against frameworks, and we trusted other people’s libraries. Each abstraction layer changed what we typed; none of them changed what we were. Agentic development is the next abstraction layer. The judgement is still ours.
The wrinkle I want to add, in closing, is on experimentation. The discipline I have described across these three posts can read, on first encounter, as restrictive: do not skip permissions, do not hoard MCP servers, do not let scaffolding into source control. That reading is wrong. Discipline is the thing that enables experimentation, because it is the thing that keeps experiments from turning into production disasters. The engineer who has hardened their setup can try a new MCP server in the morning, deprecate two old skills in the afternoon, and switch a project from spec-kit to BMAD-Method when the work changes shape, all without breaking the work in front of them. The engineer who has not cannot do any of it safely, and therefore does not do it at all; they settle on the tooling that worked six months ago, and watch the industry move past them.
That, in the end, is the difference between agentic development and vibe coding. Vibe coding is what happens when engineers forget that the layer below is still their responsibility. Agentic development is the practice of staying responsible for it; at speed, with help, and with the discipline to know when to add a tool, when to remove one, and when to leave the system alone.
If you would like help getting a team to this point, the work I do with clients includes exactly that: the choice of tools, and the slower work of building the discipline around them, over months rather than weeks, in the same way you would build any other capability your business depends on. Fractional Chief Technology Officer (CTO) engagements often include exactly this work. Get in touch.