The agent wakes on a schedule, on a webhook, or when mail arrives, reads your systems, does the work, and reports back. It runs in your Google Cloud project, on your jobs, billed to your own Claude subscription. This page is every step from an empty project to a first run — about an afternoon, most of it waiting for Google.
One half is the same for everybody. The other is yours, and it is the only one you ever edit.
ghcr.io/meffecta/agent — what starts a job on time, clones what it needs,
runs it, and delivers the result. It brings the skills that reach Gmail, Grafana,
PostHog, Google Ads and the rest. Public, and it names no company: you deploy it into
your own project and move to a newer one when you choose.
One .md per job — a few settings, then what you would have written to a
colleague. Beside them, what the agent should know about your business and which
systems it may reach. Every run clones it fresh, so
a reworded prompt is live on the next run — no build, no deploy.
The whole set-up below is: make a place for the engine to run, give it credentials, point it at your content repo, and tell it when to wake up.
Have all of these in place on the machine you will operate from — macOS or Linux.
brew install --cask gcloud-cli on a Mac, otherwise cloud.google.com/sdk/docs/install. Then gcloud auth login, on a Google account that may create projectsbrew install node on a Mac, otherwise nodejs.orggit --version to checkbrew install gh on a Mac, otherwise cli.github.comNothing else to install. Every command below starts with npx, which fetches the tool for that one run:
npx @meffecta/agent version # names the engine build it deploys
Pin it once you are running — npx @meffecta/agent@1.0.0 rather than plain
npx. Each release of the tool deploys the engine image published alongside
it, so the version you run is how you choose your engine: deliberately, rather than by
surprise.
Nine steps. Each one is checkable before you go on.
Give the agent a project of its own, separate from any product project it will read: everything the agent runs as can read every password stored in this project, and your mail tokens should not sit where another system's machinery can reach them.
npx @meffecta/agent create-project
acme-agent-506513). The ID, not the display name, is what every later command needs.It lists the accounts you can use, marks closed ones as unselectable, links your choice, and then checks the project really is billed — because linking can report success and still leave it unbilled.
npx @meffecta/agent link-billing --project <PROJECT_ID>
A private GitHub repo holding everything about this deployment. Three lines make it and put you inside it, and the first is once ever — every command from here on runs from this folder, which is what says which deployment you mean.
gh auth login # once, ever — opens your browser gh repo create acme-agent-home --private --clone # creates it, copies it here cd acme-agent-home # move into the folder it made
Now fill the folder:
npx @meffecta/agent init
Four files, and anything already there is left alone:
deployment.env # which GCP project and service this is SYSTEM.md # who it works for — read at the start of every run systems/example.md # a file per system it may reach, and the variables jobs/example.md # a file per job: a few settings, then the prompt
That is a working repo. The two example files are the shape of the thing — rename them after something real, and delete what is left.
Open SYSTEM.md now and say who the agent works for. It goes in front of every job's prompt, and a few honest sentences beat a page of instructions.
worlds/ is always a folder, and each thing inside it is one world — a business, product or brand the agent works on. A world holds what would otherwise be repeated in every job: who you sell to, what you may claim, who the competitors are. One world is either a single file (worlds/acme.md) or a folder of them (worlds/acme/) once there is more than a page of it. Most deployments have exactly one. Skip the whole thing to begin with; nothing depends on it.
Then fill in deployment.env. PROJECT is fixed — it is the ID Google handed you in step 1, and it has to match exactly. SERVICE and ARTIFACT_REPO are the opposite: nothing by those names exists yet, and you are inventing them. Anything you write works, and step 5 creates both under it. The examples below only keep them recognisable beside the project.
PROJECT=acme-agent-506513 # the project ID from step 1 — this one is given SERVICE=acme-agent # your name for the service the agent will run as ARTIFACT_REPO=acme-agent-images # your name for the store it gets the engine from REGION=europe-west1 # which Google data centre all of it lives in
Two more are optional. TIMEZONE is the zone every job's schedule is read in — leave it out and they run on UTC. ACCOUNT pins every command to one Google login, which matters only if you are signed into several.
The whole file is committed. None of it is secret — secrets live in Google's Secret Manager, added in step 6 — so a clone of this repo is everything the next person needs to operate the deployment.
Then send it all to GitHub. These same three commands are how every later change reaches the agent — a new job, a reworded prompt, a fact it should know:
git add -A # take everything you just made git commit -m "First set-up" # save it, with a note to your future self git push # send it to GitHub
Two, and the service will not start without either.
npx @anthropic-ai/claude-code setup-token # → CLAUDE_CODE_OAUTH_TOKEN, step 6
Mail, analytics, a CRM, a database — anything the agent reaches — comes later and one at a time: giving it access to a system.
Safe to run as many times as you like — re-run it after any change. It switches on the Google APIs, creates the registry the engine image is served from, the login the agent itself acts as, a bucket for its memory, a write-once bucket for the audit trail (kept a year), and the empty service the engine will run in.
If you would rather see it before it does anything, ask it first. This changes nothing at all — it just prints everything it would create and every permission it would grant, in your project:
npx @meffecta/agent setup-infra --dry-run
Then, for real:
npx @meffecta/agent setup-infra
deployment.env — if not, you are in the wrong directory.Secrets go into Google's Secret Manager and are connected to the service in one step; the value is never shown on screen, and never saved in your command history. Re-running one rotates it.
npx @meffecta/agent set-secret AGENT_API_SECRET --random # generated for you npx @meffecta/agent set-secret CLAUDE_CODE_OAUTH_TOKEN # prompts, hidden npx @meffecta/agent set-secret GITHUB_TOKEN
Non-secret settings go through set-env. The service will not boot without GIT_REPO_URL:
npx @meffecta/agent set-env GIT_REPO_URL=https://github.com/<owner>/<repo>.git
That is everything the engine needs. Anything else — Gmail, analytics, logs, a CRM — is one credential each, added whenever you want the ability, not now.
Rolls out the engine build this version of the tool was published with, and sets the service's size and scaling from deployment.env — so a setting changed by hand in the console cannot quietly survive a deploy. --tag <other> goes to a different build, which is also how you roll back.
npx @meffecta/agent deploy --dry-run # see the target first
npx @meffecta/agent deploy
It then creates the triggers, because the service sleeps when nothing is happening and is only awake while answering a request — so nothing inside it can run on a timer of its own. One Cloud Scheduler job per cron: in your jobs, and a housekeeping sweep. It can only do this after the engine is running, since the running engine is the only thing that knows which jobs exist.
<service>-sweep, and npx @meffecta/agent triggers agrees.Every later rollout re-syncs them, so a changed cron: needs nothing extra. To re-sync without a rollout — the only time you need it on its own — run npx @meffecta/agent sync-triggers.
npx @meffecta/agent status npx @meffecta/agent jobs npx @meffecta/agent doctor
Seeing your jobs listed is what proves the service could read your content repo. An empty list means GIT_REPO_URL is wrong or GITHUB_TOKEN cannot clone it.
To see what all of this actually created inside Google Cloud — and what each piece is for, in plain words — there is one more:
npx @meffecta/agent resources
It lists everything, says which parts cost anything, and gives you a link straight into the Google Cloud console for each one. Nothing you have to remember the name of.
Then test every connection you have set up, from inside the deployment:
npx @meffecta/agent verify-credentials
skip only means you have not set that one up yet.The same thing has a web page, for anyone who would rather not use a terminal: open /ask on the service URL that status printed. Any username, the secret as the password.
The job init scaffolded touches nothing real. It reports who it works for, which systems it can and cannot reach, and where its memory is — which is every part of the path a real job depends on.
npx @meffecta/agent run example npx @meffecta/agent logs --limit 50
SYSTEM.md, and with the systems you have registered.Then write a real one — what a job is is next, and create-job writes the file from a sentence. Read what the first one produces before giving it a cron:.
Everything about your deployment. Nothing about the engine.
acme-agent/ deployment.env # which GCP project and service your deployment is SYSTEM.md # the rules every job gets, before its own instructions jobs/ # one file per job morning-brief.md weekly-review.md systems/ # one file per system it can reach gmail.md google-analytics.md worlds/ # optional — what it should know about the business acme.md skills/ # optional — procedures of your own, or overrides accounting/SKILL.md
| deployment.env | Which GCP project, service, region and image repository this is. Committed on purpose — none of it is secret, so a clone of the repo is everything the next operator needs. |
| SYSTEM.md | Rules, not facts. It goes in front of every job: who the agent serves, how it should write, what it must never do. The engine adds its own baseline after yours, so the safety rules you cannot switch off stay on. |
| jobs/*.md | A few settings, then a page of plain-language instructions — below. |
| systems/*.md | The register. One file per system, naming the skill that reaches it, the variables holding its credential, what access is allowed, and the cheapest read that proves it works. A system with no file here is a system the agent does not have — the credential can be perfectly valid and still no job will ever use it. |
| worlds/ | One world per business, product or brand — each either a single file (worlds/acme.md) or a folder of them. Facts that outlive any one job, and that travel with the agent when a job runs inside one of your other repos. |
| skills/ | A procedure of your own. A folder named after one of the built-in skills replaces it — the folder name is what decides, so renaming one quietly stops it overriding anything. |
| On push | Prompts, run settings, SYSTEM.md, systems/, worlds/, skills/. Live on the next run — there is nothing to build and nothing to deploy. |
| On deploy | Whether a job exists, and when it fires: a new job file, a renamed one, or a changed cron:, webhook: or inbox:. These are read once when the service starts, so it has to restart to see them. |
Renaming a job file makes a new job. Its memory is kept under its name, so the old name keeps the history and the new one starts blank — worth doing deliberately rather than while tidying up.
A page of plain-language instructions. Teaching it something new is editing that page.
jobs/morning-brief.md
---
cron: "0 7 * * 1-5"
---
Say good morning and list today's date. Send it by email to <person> using the
send-email skill. If email is unavailable, return the text as your result.
Settings a job can carry: cron, webhook, inbox,
repo, model, effort,
allowedTools, timeoutSeconds, disabled. A job
with no name: takes its filename.
deploy, which restarts the service so the new cron: is registered.disabled: true is the kill switch, effective on push.Every prompt, result and full transcript lands in the audit bucket. Add jobs one at a time and read the first few runs of each.
Describe what you want in a sentence. The agent writes the page, because it is the one that has to read it:
npx @meffecta/agent create-job "a marketing report every Wednesday at 2pm"
It thinks about this on the deployment rather than on your laptop, which is the point — from there it can see the jobs you already have, the systems you have connected, and what it is actually able to do. So it writes something that fits, names the right systems, and tells you if you have asked for something it cannot reach yet.
You get a file — jobs/<name>.md — and nothing else happens. It is not
committed, not deployed, not scheduled. Read it before you keep it: this is the page your
agent will act on, and it is meant to be edited. Change the wording, tighten what it should
produce, fix the day.
npx @meffecta/agent check-jobs — it re-checks every job page here, so a schedule you edited by hand cannot quietly stop working.Add --dry-run to see it without saving anything.
Ongoing work rather than set-up — one credential each, whenever you want the ability.
npx @meffecta/agent connect # what can be connected npx @meffecta/agent connect gmail # what to do, in order, with the commands
Each walkthrough names what the agent gains, what you have to create on the other side,
and the exact set-secret / set-env to finish it:
gmail, outlook, ga4 (with Search Console and Google Ads),
grafana, posthog, serper, hubspot, kleer,
mongodb, cloudflare, and inbox — triggering a job by emailing it.
Google Analytics, Search Console and Google Ads need no token at all: they authenticate as the agent's own login instead, so you invite that as a viewer on each property of minting anything. Nothing breaks while you wait — a job that cannot reach a system says so and carries on.
Run from the content repo. Every command takes --help.
| doctor | The one to run when something feels wrong — it judges rather than describes, and every finding carries the command that fixes it |
| status | Revision, runtime shape, triggers, and what is in the queue right now |
| jobs | What the service has registered, and what triggers each |
| env / secrets | Every setting, and what is in Secret Manager — values are never printed |
| triggers | The Cloud Scheduler jobs and the task queue that drive it |
| logs --limit 100 | Recent service logs |
| run <job> | Trigger one job now, or --in <seconds> |
| ask "…" | Ask it something as a one-off run, with the same skills and context a job gets |
| create-job "…" | Have it write a new job page from a description, for you to read and edit |
| check-jobs | Re-check the job pages here — worth running after you edit one |
| resources | Everything the set-up built in Google Cloud, what each part is for, and a link to each |
| deploy | Move to a new engine — run it as npx @meffecta/agent@latest, since the tool and the engine travel together |
doctor exists for these, and fails with an error when something is genuinely wrong, so you can run it on a schedule: