feat(ai): add agent mode to emit instructions and skills#270
Closed
dmcilvaney wants to merge 1 commit into
Closed
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a new azldev docs agent command surface and an embedded agentskill registry so azldev can emit (and serve via MCP) version-matched AI agent skills and instruction wrappers, while tightening MCP server behavior to better support agent usage.
Changes:
- Add
docs agent install/showcommands to emit agent scaffolding and expose a read-only MCP tool (docs-agent-show) for loading skill content on demand. - Introduce
internal/app/azldev/agentskill/as the single embedded source of truth for skills/instructions (templates + registries + rendering). - Update MCP server implementation to serialize tool calls and propagate a read-only hint from Cobra annotations into MCP tool schemas.
Reviewed changes
Copilot reviewed 31 out of 31 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| scenario/snapshots/TestMCPServerMode_1.snap.json | Updates MCP snapshot for the new read-only docs-agent-show tool and schema changes. |
| internal/app/azldev/core/mcp/mcpserver.go | Serializes MCP tool execution and propagates read-only tool hints from command annotations. |
| internal/app/azldev/command.go | Adds read-only MCP annotation support via ExportAsReadOnlyMCPTool. |
| internal/app/azldev/cmds/docs/markdown.go | Stops exporting docs-markdown as an MCP tool. |
| internal/app/azldev/cmds/docs/docs.go | Registers the new docs agent command subtree. |
| internal/app/azldev/cmds/docs/agent.go | Implements azldev docs agent install/show and .vscode/mcp.json upsert logic. |
| internal/app/azldev/cmds/docs/agent_test.go | Adds unit tests for agent file emission and .vscode/mcp.json behavior. |
| internal/app/azldev/cmds/docs/agent_internal_test.go | Adds internal tests for binding resolution and skill drift guards. |
| internal/app/azldev/agentskill/doc.go | Documents the agentskill architecture and rendering model. |
| internal/app/azldev/agentskill/content/update-component.md.tmpl | Adds embedded skill template content for updating component locks. |
| internal/app/azldev/agentskill/content/skill-wrapper.md.tmpl | Adds wrapper template that redirects to the read-only MCP tool for full skill text. |
| internal/app/azldev/agentskill/content/remove-component.md.tmpl | Adds embedded skill template content for removing components. |
| internal/app/azldev/agentskill/content/overlays.md.tmpl | Adds embedded skill template content for overlays workflows and rules. |
| internal/app/azldev/agentskill/content/mock.md.tmpl | Adds embedded skill template content for mock chroot usage. |
| internal/app/azldev/agentskill/content/instruction-wrapper.md.tmpl | Adds instruction wrapper template for path-scoped guidance. |
| internal/app/azldev/agentskill/content/image.md.tmpl | Adds embedded skill template content for image workflows. |
| internal/app/azldev/agentskill/content/comp-toml.md.tmpl | Adds embedded skill template content for component TOML authoring. |
| internal/app/azldev/agentskill/content/build-component.md.tmpl | Adds embedded skill template content for build/debug workflows. |
| internal/app/azldev/agentskill/content/azldev.md.tmpl | Adds primary azldev overview skill template content. |
| internal/app/azldev/agentskill/content/add-component.md.tmpl | Adds embedded skill template content for adding components. |
| internal/app/azldev/agentskill/agentskill.go | Implements registries, rendering, layouts, and file emission for skills/instructions. |
| internal/app/azldev/agentskill/agentskill_test.go | Adds tests validating skill/instruction rendering and invariants. |
| docs/user/reference/cli/azldev_docs.md | Regenerates CLI docs to include azldev docs agent. |
| docs/user/reference/cli/azldev_docs_agent.md | Adds generated CLI docs for azldev docs agent. |
| docs/user/reference/cli/azldev_docs_agent_show.md | Adds generated CLI docs for azldev docs agent show. |
| docs/user/reference/cli/azldev_docs_agent_install.md | Adds generated CLI docs for azldev docs agent install. |
| docs/user/README.md | Adds user-docs index entry for AI agent setup. |
| docs/user/how-to/set-up-ai-agents.md | Adds user guide for emitting agent files and wiring MCP. |
| docs/developer/prds/agent-scaffolding.prd.md | Adds PRD describing broader agent scaffolding roadmap. |
| .github/instructions/agent-skills.instructions.md | Adds maintainer instructions for editing/validating embedded skills. |
| .github/copilot-instructions.md | Updates contributor guidance to note the new agentskill single source of truth. |
Comment on lines
+191
to
+195
| // LIMITATION: the [azldev.Env] here is created once when the MCP server starts and is reused | ||
| // for every tool call. Per-call global flags parsed by this Execute (e.g. '--dry-run', | ||
| // '--project') update the App's flag fields but are NOT re-threaded into this reused Env, so | ||
| // env.DryRun() and the loaded config still reflect the server's startup values. As a result a | ||
| // mutating tool invoked with '--dry-run=true' would still write. Until the MCP rework gives |
Comment on lines
+191
to
+195
| // LIMITATION: the [azldev.Env] here is created once when the MCP server starts and is reused | ||
| // for every tool call. Per-call global flags parsed by this Execute (e.g. '--dry-run', | ||
| // '--project') update the App's flag fields but are NOT re-threaded into this reused Env, so | ||
| // env.DryRun() and the loaded config still reflect the server's startup values. As a result a | ||
| // mutating tool invoked with '--dry-run=true' would still write. Until the MCP rework gives |
Comment on lines
+149
to
+152
| // Skills returns the registered skills in emission order. | ||
| func Skills() []Skill { | ||
| return skills | ||
| } |
Comment on lines
+258
to
+261
| // Instructions returns the registered instruction files in emission order. | ||
| func Instructions() []Instruction { | ||
| return instructions | ||
| } |
Comment on lines
+191
to
+195
| // LIMITATION: the [azldev.Env] here is created once when the MCP server starts and is reused | ||
| // for every tool call. Per-call global flags parsed by this Execute (e.g. '--dry-run', | ||
| // '--project') update the App's flag fields but are NOT re-threaded into this reused Env, so | ||
| // env.DryRun() and the loaded config still reflect the server's startup values. As a result a | ||
| // mutating tool invoked with '--dry-run=true' would still write. Until the MCP rework gives |
Comment on lines
+191
to
+197
| // LIMITATION: the [azldev.Env] here is created once when the MCP server starts and is reused | ||
| // for every tool call. Per-call global flags parsed by this Execute (e.g. '--dry-run', | ||
| // '--project') update the App's flag fields but are NOT re-threaded into this reused Env, so | ||
| // env.DryRun() and the loaded config still reflect the server's startup values. As a result a | ||
| // mutating tool invoked with '--dry-run=true' would still write. Until the MCP rework gives | ||
| // each call its own Env, only expose read-only commands as MCP tools (do not add | ||
| // [azldev.ExportAsMCPTool] to commands that mutate state). |
Comment on lines
+149
to
+152
| // Skills returns the registered skills in emission order. | ||
| func Skills() []Skill { | ||
| return skills | ||
| } |
Comment on lines
+258
to
+261
| // Instructions returns the registered instruction files in emission order. | ||
| func Instructions() []Instruction { | ||
| return instructions | ||
| } |
Comment on lines
+53
to
+55
| 3. A little hand-written prose here is fine, if it helps direct agents to the right skill. Keep it short, and avoid | ||
| repeating the skill content. | ||
| 3. Keep the count-based tests happy: `Files()` emits one file per skill plus one per instruction. |
Comment on lines
+191
to
+195
| // LIMITATION: the [azldev.Env] here is created once when the MCP server starts and is reused | ||
| // for every tool call. Per-call global flags parsed by this Execute (e.g. '--dry-run', | ||
| // '--project') update the App's flag fields but are NOT re-threaded into this reused Env, so | ||
| // env.DryRun() and the loaded config still reflect the server's startup values. As a result a | ||
| // mutating tool invoked with '--dry-run=true' would still write. Until the MCP rework gives |
Comment on lines
+252
to
+253
| // ponytail: plain JSON only. A JSONC file with comments fails here with a clear error | ||
| // rather than being silently rewritten and losing the comments. |
Comment on lines
+191
to
+195
| // LIMITATION: the [azldev.Env] here is created once when the MCP server starts and is reused | ||
| // for every tool call. Per-call global flags parsed by this Execute (e.g. '--dry-run', | ||
| // '--project') update the App's flag fields but are NOT re-threaded into this reused Env, so | ||
| // env.DryRun() and the loaded config still reflect the server's startup values. As a result a | ||
| // mutating tool invoked with '--dry-run=true' would still write. Until the MCP rework gives |
Comment on lines
+269
to
+270
| // ponytail: plain JSON only. A JSONC file with comments fails here with a clear error | ||
| // rather than being silently rewritten and losing the comments. |
Comment on lines
+405
to
+406
| return "", nil, fmt.Errorf("unknown skill %q; choose one of: %s", | ||
| requested, strings.Join(names, ", ")) |
Comment on lines
+53
to
+55
| 3. A little hand-written prose here is fine, if it helps direct agents to the right skill. Keep it short, and avoid | ||
| repeating the skill content. | ||
| 3. Keep the count-based tests happy: `Files()` emits one file per skill plus one per instruction. |
Comment on lines
+191
to
+195
| // LIMITATION: the [azldev.Env] here is created once when the MCP server starts and is reused | ||
| // for every tool call. Per-call global flags parsed by this Execute (e.g. '--dry-run', | ||
| // '--project') update the App's flag fields but are NOT re-threaded into this reused Env, so | ||
| // env.DryRun() and the loaded config still reflect the server's startup values. As a result a | ||
| // mutating tool invoked with '--dry-run=true' would still write. Until the MCP rework gives |
Comment on lines
+277
to
+280
| servers, _ := config[serversKey].(map[string]any) | ||
| if servers == nil { | ||
| servers = map[string]any{} | ||
| } |
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| package docs |
Comment on lines
+404
to
+406
| if _, findErr := agentskill.FindSkill(requested); findErr != nil { | ||
| return "", nil, fmt.Errorf("unknown skill %q; choose one of: %s", | ||
| requested, strings.Join(names, ", ")) |
Comment on lines
+69
to
+71
| azldevInstruction := agentskill.Instructions()[0] | ||
| instructionsPath := filepath.Join(outputDir, | ||
| filepath.FromSlash(agentskill.InstructionFile(azldevInstruction))) |
Comment on lines
+191
to
+195
| // LIMITATION: the [azldev.Env] here is created once when the MCP server starts and is reused | ||
| // for every tool call. Per-call global flags parsed by this Execute (e.g. '--dry-run', | ||
| // '--project') update the App's flag fields but are NOT re-threaded into this reused Env, so | ||
| // env.DryRun() and the loaded config still reflect the server's startup values. As a result a | ||
| // mutating tool invoked with '--dry-run=true' would still write. Until the MCP rework gives |
dmcilvaney
force-pushed
the
damcilva/new_skills_v2
branch
from
July 13, 2026 22:02
4d4b537 to
aadf36f
Compare
dmcilvaney
force-pushed
the
damcilva/new_skills_v2
branch
from
July 13, 2026 22:26
aadf36f to
7cdd1bf
Compare
Comment on lines
+282
to
+289
| azldevServer := map[string]any{ | ||
| "type": "stdio", | ||
| "command": "azldev", | ||
| "args": []any{"advanced", "mcp"}, | ||
| } | ||
| if exposeAllTools { | ||
| azldevServer["tools"] = []any{"*"} | ||
| } |
Comment on lines
+149
to
+152
| // Skills returns the registered skills in emission order. | ||
| func Skills() []Skill { | ||
| return skills | ||
| } |
Comment on lines
+261
to
+264
| // Instructions returns the registered instruction files in emission order. | ||
| func Instructions() []Instruction { | ||
| return instructions | ||
| } |
Comment on lines
+404
to
+407
| if _, findErr := agentskill.FindSkill(requested); findErr != nil { | ||
| return "", nil, fmt.Errorf("unknown skill %q; choose one of: %s", | ||
| requested, strings.Join(names, ", ")) | ||
| } |
Contributor
Author
|
superseded by #287 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.