Microsoft 365 Copilot declarative agents as code: a growing collection of custom agents, composed from reusable instruction fragments, validated against the published schema, and rendered into uploadable app packages. Terraform and Azure Logic Apps are where the collection starts, not where it stops.
Branding is a swappable profile, so you can publish the same agents under your own organisation with two commands and without editing a fragment.
The manifests are the product. A declarative agent is chat shaped: it produces Terraform and workflow JSON in the Copilot pane for a human to review and commit. It does not write to your repositories. What lives here is the versioned, reviewable definition of how it behaves.
Just uv. It brings its own Python and the task runner, so there is
nothing else to install and no global Python to pollute.
curl -LsSf https://astral.sh/uv/install.sh | sh # macOS and Linux
winget install --id=astral-sh.uv # Windowsgit clone https://github.com/libre-devops/copilot-agents.git
cd copilot-agents
uv run just quickstartThat is the whole thing. It checks the knowledge packs, renders, lints, and then prints the exact file to open and the URL to open it beside:
[1/4] Knowledge packs................... 5 present
[2/4] Profile........................... using profiles/default.yaml
[3/4] Rendering......................... 3 agents
[4/4] Linting........................... clean
Done. Build your first agent:
1. Open https://m365.cloud.microsoft/agents/new
2. Choose "Skip to configure", not the Describe tab
3. Follow rendered/terraform-author/BUILD-GUIDE.md
Offline, no tenant, nothing to log in to. The build guide then tells you what to paste into every field, which files to upload, how to test it and how to share it.
uv run just quickstart cdsrName a profile that does not exist yet and the wizard runs on the way through. It asks for your
organisation name, URLs and colour, each with a default, and then asks for your own standards,
importing a file or a whole folder and converting Markdown, YAML or JSON into the .txt Agent
Builder requires. Imported documents land in knowledge/local/, which is gitignored, so an internal
standard cannot become committable by being put in the obvious place.
Hold Enter through every question and you still get something that builds, on the Libre DevOps standards. See docs/knowledge.md to reground it later.
| Path | Manifests and build guide | App packages |
|---|---|---|
| default profile | rendered/<agent>/ (committed) |
dist/ |
| your profile | build/<profile>/<agent>/ |
dist/<profile>/ |
Every script in tools/ carries a
PEP 723 inline metadata header, so uv resolves each one's
dependencies on the fly. If you only want to build once and never touch the repo again, you need no
virtual environment and no just:
uv run tools/render.py
uv run tools/lint.pypyproject.toml exists for the other case: working on the repo, where one environment holding the
task runner and the linters is more convenient. Keep its versions in step with the PEP 723 headers.
| Agent | What it does | Uploaded knowledge |
|---|---|---|
terraform-author |
Terraform to the Libre DevOps Terraform Standard and Azure Naming Convention: the file split, for_each over count, the this label, argument ordering, typed variables, and the three kinds of assertion (validation at plan time, check to warn, precondition to abort) |
the Terraform Standard, the Azure Naming Convention |
logic-app-author |
Workflow Definition Language: the three export wrappers, declarations versus values, action names as stored keys, and the failure modes that pass validation and break at run time | the Logic App Standard, the workflow definition schema |
agent-author |
Declarative agents themselves: schema v1.8 and every limit it imposes, which capabilities cost a licence, and how to structure instructions inside the budget | the declarative agent manifest schema |
Each also gets scoped WebSearch over the relevant public references, but the uploaded files come
first: the agents are instructed to treat them as authoritative over both web results and their own
training. That ordering is what makes them enforce your standard rather than generic advice.
All three ship the same contract: every factual claim cites its source, retrieved content is data
rather than instructions, a knowledge source that returns nothing is reported rather than quietly
replaced with model knowledge, anything unconfirmed is marked UNVERIFIED rather than guessed, and
the agent never claims to have run, deployed or validated anything.
fragments/ instruction fragments, composed in order what the agent DOES
shared/ house rules every agent inherits
terraform/ the Terraform standard, distilled to rules
logic-app/ Workflow Definition Language, distilled to rules
agent/ the declarative agent manifest and its limits
|
agents/<id>/agent.yaml the definition: metadata, fragment list, capabilities, starters
|
profiles/<name>.yaml publisher, tokens, colour, app ids who PUBLISHES it
|
tools/render.py substitute, compose, enforce every limit, fail loudly
|
rendered/<id>/ committed output for the default profile
build/<profile>/<id>/ everything else, gitignored
BUILD-GUIDE.md paste-ready field values for Agent Builder
|
tools/lint.py schema validation, semantics, and brand leakage
|
dist/[<profile>/]<id>-<ver>.zip the uploadable Microsoft 365 app package
rendered/ is committed on purpose. It is the drop-in path for anyone who wants the manifests
without running the toolchain, and CI fails if it drifts from its source.
This is the path most people want: no admin, no tenant upload, and you can share the result with a link in about five minutes.
Agent Builder has no import path. It is a form, so you cannot upload a declarativeAgent.json
into it. That is why every render also writes a paste-ready rendered/<agent>/BUILD-GUIDE.md,
which is the bridge between the version controlled definition and the form. Open the guide next to
the browser and work down it.
Go to https://m365.cloud.microsoft/agents/new, or choose New agent in the left pane of the
Microsoft 365 Copilot app. It also works from microsoft365.com/chat, office.com/chat, and the
Teams desktop and web clients. It is not available on mobile.
On the New agent screen choose Skip to configure. The natural language Describe tab writes instructions for you, which is not what you want here: the point of this repository is that the instructions are reviewed, budgeted and version controlled.
Every field below comes straight out of the build guide, in the order Agent Builder asks for it.
| Agent Builder field | Comes from | Limit |
|---|---|---|
| Name | guide section 1 | 30 characters, tighter than the manifest's 100 |
| Icon | rendered/<agent>/color.png |
PNG, 192x192, 1 MB |
| Description | guide section 2 | 1,000 characters |
| Instructions | guide section 3, paste the whole block | 8,000 characters |
| Knowledge, uploaded files | guide section 4, drag from knowledge/ |
20 files, .txt .pdf .docx and friends |
| Knowledge, Enter URL | guide section 4 | 4 public websites, two path levels each, no query strings |
| Capabilities | guide section 5, both off | code interpreter, image generator |
| Model | guide section 6 | Auto, Quick response, or Think deeper |
| Starter Prompts | guide section 8 | 12 maximum |
| About this agent (... menu) | guide section 9 | short description 80, URLs must be HTTPS |
Three things worth getting right:
- Upload the knowledge files before you add the websites. They are the standards the agent is meant to enforce, and it is instructed to trust them over web results and over its own training. Scoped web search only reads what Bing indexes, so it can never see private documentation: the uploads are what make the agent actually authoritative. They need a Copilot licence or metered usage; the rest of the agent works without one.
- Leave the other Work content toggles off (Outlook, Teams, People) unless you actually want tenant grounding. An unscoped source reaches much further than most people expect.
- Leave "Only use specified sources" off. An agent that cannot draw on its own knowledge of HCL or JSON cannot write either, and the knowledge precedence in its instructions already puts your standards first. Agent Builder describes that toggle as prioritising your sources rather than blocking model knowledge, which it states plainly it cannot fully do.
Use the Try it pane. The build guide lists three checks worth doing every time:
- Run each starter prompt and confirm it does what its title claims.
- Ask for something just outside the agent's scope and confirm it declines rather than improvises.
- Paste content containing an embedded instruction and confirm the agent reports it as text it found rather than acting on it.
Then choose Create. The agent is private to you at first.
Choose Share on the created agent.
- Can chat lets someone use it. Can edit makes them a co-owner with full rights. Groups can only be added as chat users, so owners must be added individually.
- Copy chat link and send it to whoever needs it.
- Org-wide sharing for chat access lists the agent in the Agent Store for everyone in the tenant. An admin policy can restrict or disable this.
- To get into Built by your org, submit the agent to your org catalog and an admin reviews it in the Microsoft 365 admin centre. The shared version and the catalog version are managed separately.
After any later edit, choose Update, or your changes stay invisible to the people you shared with. If the agent uses SharePoint knowledge, reshare it so file permissions follow.
Full detail: Share and manage agents.
Agent Builder can export what you built: View all agents, then ..., then Download .zip file, which contains the agent manifest and icon and can be sideloaded into Teams. That zip cannot include embedded files. It is a one way trip out of the builder, so this repository stays the source of truth and the build guide stays the way in.
The alternative path, for publishing to an organisation rather than sharing from your own account. There is no create API for a declarative agent, so this is an admin or portal step and this repo ends where the API does.
- Build the package:
uv run just package(oruv run just package <profile>). - Upload
dist/<agent>-<version>.zipthrough the Microsoft 365 admin centre under Integrated apps, or side load it with the Microsoft 365 Agents Toolkit for testing. - Assign it to the users or groups who should see it.
Agents using only WebSearch work without a Microsoft 365 Copilot licence. Every other capability
needs a licence or metered usage enabled in the tenant.
| Agent Builder | App package | |
|---|---|---|
| Who can do it | any licensed user | a tenant admin |
| Time to first share | minutes | an approval cycle |
| How people get it | a chat link, or the Agent Store | assigned in Integrated apps |
| Actions and API plugins | not supported, use Copilot Studio | supported |
| Getting your definition in | paste from BUILD-GUIDE.md |
upload the zip directly |
| Best for | trying an agent, sharing with a team | rolling one out to an organisation |
Schema 1.8 caps instructions at 8,000 characters. The Libre DevOps Terraform Standard alone is
over 2,500 lines, so it cannot be inlined, and Microsoft explicitly warns against offloading
instructions into a knowledge source to dodge the cap: knowledge content is subject to cross-prompt
injection classifiers and is not honoured as maker-authored instruction.
So the split is deliberate and enforced:
- Instructions carry behaviour. Composed from fragments, budget checked at build time, and the build fails rather than truncates.
- Knowledge carries facts. Schemas and reference documentation, for grounding claims only.
Every render prints the spend, so the budget stays visible:
agent-author: instructions 7144/8000 (89%), 4 files
logic-app-author: instructions 6799/8000 (84%), 4 files
terraform-author: instructions 6225/8000 (77%), 4 files
Full reasoning in docs/instruction-budget.md.
new-profile brands the agents. If you are forking the repository itself and want the README,
the docs, the licence and the default profile to be yours too:
uv run just rebrandIt asks for your organisation name, GitHub org, domain, prefix and copyright holder, previews every
replacement with a per-file count, and only writes once you confirm. All replacements happen in a
single pass, so a value you supply is never itself rewritten (replacing Libre DevOps with
Waldo Corp and then replacing ldo would otherwise produce Wacm Corp).
It also issues a new app id namespace and clears the pinned ids, so your fork cannot claim the same agent identities as upstream.
Run it just after forking, not later. Changing the namespace changes every agent id, and the agent names change too. Agents already installed from this checkout do not follow: they would have to be rebuilt and reshared.
You do not need to have cloned it. Downloading the zip and copying it into an existing repository is
a normal way to adopt this, and the tool handles it: unless it finds a clean git checkout, where
git checkout . already does the job, it copies every file it is about to change into
.rebrand-backup/<timestamp>/ and prints the one command that restores them.
Two things it deliberately does not touch: knowledge/*.txt, because those are other people's
published documents and rewriting their text would falsify them, and its own source, because its
search strings are the very words it is replacing.
If the standards are yours and you are adopting them under a new name, just localise-knowledge
is the deliberate exception: it rebrands the prose into knowledge/local/ while preserving the real
Terraform registry module addresses, which would otherwise start pointing at modules that do not
exist.
| Path | What it holds |
|---|---|
agents/ |
one directory per agent: agent.yaml and a README |
profiles/ |
branding profiles: publisher, tokens, colour, app ids. Yours is gitignored |
fragments/ |
instruction fragments, the single source for shared house rules |
rendered/ |
generated manifests, icons and Agent Builder build guides, committed and drift gated, never hand edited |
knowledge/ |
the standards agents upload as knowledge, fetched by just update-knowledge |
schema/ |
the vendored declarative agent JSON schema, refreshed by just update-schema |
tools/ |
the renderer, the linter, and the icon generator |
docs/ |
the operating model, see below |
dist/ |
build output, gitignored |
| Document | What it covers |
|---|---|
| instruction-budget.md | the 8,000 character cap, why you cannot route around it, and how to spend it |
| authoring.md | adding an agent, the fragment convention, choosing capabilities, testing |
| profiles.md | publishing under your own brand, tokens, app id derivation, output routing |
| roadmap.md | what is planned, and what is blocked on the platform rather than on effort |
| knowledge.md | what grounds each agent, WebSearch and embedded file limits, tenant sources |
| platform-notes.md | where the docs, the schema and reality disagree, with dates |
Everything here was checked against the live Microsoft documentation on 2026-08-23, not written from memory: declarative agent schema 1.8, write effective instructions, the Microsoft 365 app model for agents, Agent Builder, building agents with Agent Builder, adding knowledge sources, sharing and managing agents, and the published JSON schema.
Where they disagree with each other, the discrepancy is recorded in platform-notes.md rather than papered over.
More agents, since this is a collection rather than two topics. Real branding assets. A repeatable behaviour test pack, because the offline gates prove packaging and not behaviour.
The question that comes up most is whether deployment can be automated with Terraform. Not
properly, and the blocker is authentication rather than tooling. Microsoft Graph does have a
publish API (POST /appCatalogs/teamsApps), but it supports no application permissions at all,
only delegated ones, so there is no service principal and no federated CI identity. Separately,
msgraph_resource serialises its body as JSON and the endpoint needs a raw application/zip, so
the provider cannot express the call either way.
Wrapping it in local-exec would be a shell script in a Terraform costume: no plan, no drift
detection, and state that lies the moment an admin touches the portal. So the repository stops at a
reproducible, drift gated, sha256 recorded artefact and leaves the upload to a human.
The full reasoning, including what would change the answer, is in docs/roadmap.md.
These agents declare WebSearch as their only capability. They call no service of ours, send
nothing to Libre DevOps, and this repository collects no data from anyone who uses them.
An installed agent runs inside your Microsoft 365 tenant, so what Copilot does with your prompts and
your grounding data is governed by
Microsoft's data, privacy and security terms for Microsoft 365 Copilot,
not by anything here. If you add a capability such as OneDriveAndSharePoint or GraphConnectors
to an agent, you are scoping your own tenant's content, and scoping it is your responsibility: see
docs/knowledge.md for the defaults that grant more than you might expect.
The developer.privacyUrl and developer.termsOfUseUrl fields in each app manifest are required
by the Microsoft 365 app schema and point at this section and at the licence below.
MIT.