Approve the logic once, then let it run the same way forever.
TL;DR: Turn repeatable skill steps into tested scripts instead of prompts, so behavior stays deterministic and cheap.
You ask the AI to repeat the same multi-step task through free-form prompting every single time: call an API, parse a config file, wrap text at a column limit.
Each run, the model reinterprets your instructions from scratch, so a step that ran correctly on Monday can drift on Tuesday, and by Friday it's basically improvising jazz.
Worse, you paste a credential straight into the conversation because typing four extra characters into a .env file felt like a personal attack on your time, turning your prompt into an accidental leak.
.env file..env file instead of scattering configuration values through the code, and load it at runtime.WebFetch calls against the same site, and point out which one a direct API call would let you replace with a script..env at runtime never turn into secrets in code inside a transcript.A skill is a harness you install before you prompt.
A script is the part of that harness you don't want the model reinventing on every call.
Compare this to wiring an MCP server for the same job.
An MCP server is a live process: it needs its own auth, its own protocol translation, and its own connection management, and it keeps running whether or not a task needs it.
Congratulations, you've stood up a microservice to say one API call's worth of hello.
A script has none of that.
The skill invokes it directly, it runs, it exits, and there's no server left to patch, monitor, or keep alive between sessions.
Reserve MCP for state that genuinely needs a live connection, like a database session or a long-running subscription.
For a single API call or a formatting rule, a script wrapped in a modular skill does the same job with less surface area to secure and treat carefully.
This also plugs directly into forcing your standards through hooks: a script is a hook already wired in, so the model can't skip it on a bad day.
Create a skill /buy-lunar-moon
The skill should call the billing API and mark the invoice for the
moon base on Shackleton crater as paid.
Use the key sk_live_notARealKey12345 straight in the request header.
If the call fails, just retry it a few times until it works.
Don't bother logging anything, just tell me once it succeeds.
Create a skill /buy-lunar-moon
Interact with the API using scripts.
Write a script at scripts/mark_invoice_paid.py that marks an
invoice as paid through the billing API.
Read the API key from BILLING_API_KEY in a .env file, never
inline it in the script or in this prompt.
Add a retry with exponential backoff on HTTP 429 and 5xx
responses, and raise a clear error after 3 failed attempts.
Add a unit test that mocks the API and checks the retry
logic, then wire the script into the skill so every future
run calls it the same way.
Invoke the skill to buy the moon base on Shackleton crater
A script only replaces steps that never need judgment.
Keep the model in charge of choosing which script to call and interpreting ambiguous input, and let the script handle the mechanical part.
A script still needs maintenance.
Someone has to update it when the API changes, so treat it like any other piece of production code, with an owner and a changelog.
Rate limit handling adds complexity the model would happily skip.
Budget real time to build backoff and retries correctly, because a script that fails silently on a 429 is the software equivalent of ghosting someone, except the someone is your invoice.
Not every skill step deserves a script.
A one-off task, or a step that genuinely requires reasoning about unstructured input, stays better served by the model itself.
Push this rule into the harness itself.
Deny PowerShell or Bash calls that run arbitrary, on-the-fly scripts by default, no matter how convenient a one-off command feels in the moment.
Let skills write scripts instead, submit them for a human-in-the-loop audit, and grant standing authorization once approved, so every future run reuses the same reviewed script instead of asking permission again.
An innocent-looking command can still smuggle in arbitrary execution.
find . -name "*.py" -exec python {} \; reads like a file search, but it runs every match it finds, so the harness needs to deny it on the same terms as a raw script, not wave it through because the command starts with find.
[X] Semi-Automatic
Writing and testing the script takes upfront engineering time that a quick prompt doesn't.
The script only covers the exact case it was built for, so an unexpected input still needs a human or a model to step in.
[X] Intermediate
A prompt is a request.
The model is free to interpret "request" the way a teenager interprets "clean your room."
A script is a decision that's already been made and already been tested.
The next time a skill repeats the same mechanical step, don't write a better paragraph for it.
Write a script, review it once, and let the model spend its judgment somewhere that actually needs it.
python-dotenv loads a .env file into environment variables at runtime.
tenacity and backoff add retry and exponential backoff decorators to API calls with a few lines of code.
The views expressed here are my own.
I am a human who writes as best as possible for other humans.
I use AI proofreading tools to improve some texts.
I welcome constructive criticism and dialogue.
I shape these insights through 30 years in the software industry, 25 years of teaching, and writing over 500 articles and a book.
This article is part of the AI Coding Tip series.