# Make Ai Teammate

> >

- **Type:** Skill
- **Install:** `agentstack add skill-microsoft-agent365-skills-make-ai-teammate`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [microsoft](https://agentstack.voostack.com/s/microsoft)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [microsoft](https://github.com/microsoft)
- **Source:** https://github.com/microsoft/agent365-skills/tree/main/plugins/agent365/skills/make-ai-teammate

## Install

```sh
agentstack add skill-microsoft-agent365-skills-make-ai-teammate
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Make AI Teammate

> **Trigger phrases** — any of these will activate this skill:
> - "make this agent an ai teammate"
> - "transform this agent into an ai teammate"
> - "publish this agent to teams"
> - "make this agent available in microsoft teams"
> - "publish this agent to microsoft copilot"
> - "add teams support to this agent"
> - "set up ai teammate hosting for this agent"
> - "convert this agent to a teams agent"
> - "make this agent work with microsoft 365"

> **What this skill does:** It wraps your existing LLM logic with the Microsoft Agent 365
> AI Teammate layer — hosting, routing, and notifications. Your existing LLM code (models,
> prompts, tools, business logic) is preserved and integrated into the new structure. Nothing is deleted.
>
> **Prerequisite:** Run `a365-setup` first — it registers the agent with Agent 365 and writes
> the detection cache that this skill reads.
>
> **Supported languages:** Node.js (LangChain, OpenAI Agents SDK, Claude SDK, Semantic Kernel, Google ADK) · .NET (AgentFramework, Semantic Kernel) · Python (AgentFramework, LangChain, OpenAI, Claude, Semantic Kernel, Google ADK)

---

## Phase 0A — Workspace Triage and Detection Cache

### Step 1 — Triage the workspace

Run in parallel and combine results:

- **Glob** `**/*.csproj`, `package.json`, `requirements.txt`, `pyproject.toml`, `src/**/*.ts`, `**/*.cs`, `**/*.py` → does any agent code or project file exist? Call this `hasProjectFiles`.
- **Read** `.a365-workspace-detection.local.json` → does the cache exist, and is `detectedAt` within 60 minutes? Call this `cacheState` (`fresh`, `stale`, or `missing`).
- **Parse `$ARGUMENTS`** for an explicit framework hint (e.g. `dotnet`, `dotnet-sk`, `langchain`, `openai`, `claude`, `semantickernel`, `googleadk`, `python`) and the word `create`. Store as `argFramework` and `argCreateIntent`.

Decide what to do next from this table — do not fall through to Step 2 until one of these branches has run:

| `cacheState` | `hasProjectFiles` | Action |
|--------------|-------------------|--------|
| `fresh`      | —                 | Continue to Step 2 below (load cache). |
| `missing`    | false             | **Empty workspace, new-agent path.** Tell the user: *"This is a fresh workspace — I'll scaffold a starter agent from Agent365-Samples first, then run `a365-setup` to register it."* Jump directly to **Phase 0A.5**. If `argFramework` is set, pre-select the matching sample (e.g. `dotnet` → option 1, `dotnet-sk` → option 2, `langchain` → option 3, etc.) and skip the menu. After scaffolding completes, **Read** `${CLAUDE_PLUGIN_ROOT}/skills/a365-setup/SKILL.md` and follow it to register the new agent — then return to Step 2 below. |
| `missing`    | true              | Tell the user: *"I found existing agent code but no Agent 365 registration. I'll run `a365-setup` now to register it and detect its framework, then continue here automatically."* **Read** `${CLAUDE_PLUGIN_ROOT}/skills/a365-setup/SKILL.md` and follow it to completion, then return to Step 2 below. |
| `stale`      | —                 | Tell the user the detection cache is stale (>60 min) and re-run `a365-setup` the same way as the `missing + true` row, then return to Step 2. |

> **Why this triage exists:** Phase 0A.5 was designed for the empty-workspace new-agent path, but is only reachable after Step 2 succeeds. Without this triage, a user running `/make-ai-teammate create a dotnet agentframework agent` in an empty directory gets a "run a365-setup first" wall instead of the scaffold flow they asked for.

### Step 2 — Load Detection Cache

**🛑 STOP — `.a365-workspace-detection.local.json` MUST exist before this step.** Read the file path `.a365-workspace-detection.local.json` in the working directory. If it does not exist, you arrived at Step 2 by skipping Step 1's triage routing. Do NOT proceed. Do NOT invent default cache values. Do NOT run any further phase (no package install, no file edits, no `a365` CLI commands). Instead:

1. Tell the user verbatim: *"I skipped the Step 1 triage and the detection cache wasn't written. Running `a365-setup` now to fix that, then I'll return here."*
2. **Read** `${CLAUDE_PLUGIN_ROOT}/skills/a365-setup/SKILL.md` and follow it to completion — `a365-setup` is what writes `.a365-workspace-detection.local.json`.
3. Re-verify the file now exists, then continue with "Load from cache" below.

This guard exists because earlier sessions have rationalised past Step 1's triage and run all of Phase 1 onward without the cache, producing partially-wired agents with no detection metadata. The stop hook (`validate-make-ai-teammate.js`) will fail the session at end if the cache file is still missing.

(Only reached once the cache is fresh — either it already was, or `a365-setup` just wrote it.)

Load from cache:
- `programmingLanguage` → use as `language`
- `agentStack`

**Find existing LLM entry point** (not stored by a365-setup — still required):

*NodeJS:* **Glob** `src/**/*.ts` and **Grep** for LLM instantiation (`ChatOpenAI`, `AzureChatOpenAI`, `OpenAI`, `Anthropic`, `Kernel`, `@google/generative-ai`, `@google/adk`), chain/agent creation, or existing HTTP server.

*DotNet:* **Glob** `**/*.cs` and **Grep** for `AddAgent "**git is not installed.** Please install it from https://git-scm.com/downloads and restart your terminal, then try again."

Stop until the user confirms git is installed.

### Step 2 — Verify GitHub CLI is installed

```bash
gh --version
```

If the command fails:
> "**GitHub CLI (gh) is not installed.** Install it from https://cli.github.com/ and restart
> your terminal. The CLI is used to authenticate with GitHub before cloning the sample."

Stop until the user confirms `gh` is installed.

### Step 3 — Verify GitHub authentication

```bash
gh auth status
```

Check the output:
- If output contains `Logged in to github.com` → authenticated, proceed.
- If output contains `not logged in` or exits non-zero:

> "You are not logged in to GitHub. Run the following command to authenticate:
>
> ```
> gh auth login
> ```
>
> Choose **GitHub.com**, then **HTTPS**, then **Login with a web browser**.
> Follow the prompts, then come back here."

Stop until `gh auth status` succeeds.

### Step 4 — Verify language-specific toolchain (pre-clone)

Run the relevant check for the chosen sample:

| Sample | Check command | Install URL if missing |
|--------|--------------|------------------------|
| .NET (1, 2) | `dotnet --version` | https://dotnet.microsoft.com/download (requires .NET 8+) |
| Node.js (3, 4) | `node --version && npm --version` | https://nodejs.org (requires Node.js 18+) |
| Python (5, 6, 7) | `python --version` or `python3 --version` | https://www.python.org/downloads (requires 3.11+) |

If the check fails:
> "**{tool} is not installed or is below the minimum version.** Please install it from
> {install URL} and restart your terminal."

Stop until the check passes.

### Step 5 — Clone the sample

Once all checks pass, clone and copy the chosen sample into the current directory:

```bash
# Pattern — replace {path} with the framework subfolder
git clone --depth 1 https://github.com/microsoft/Agent365-Samples.git _tmp_a365samples
```

Then copy only the chosen sample subfolder:

| Option | Source path inside clone |
|--------|--------------------------|
| 1 — .NET Agent Framework | `dotnet/agent-framework/sample-agent` |
| 2 — .NET Semantic Kernel | `dotnet/semantic-kernel/sample-agent` |
| 3 — Node.js LangChain | `nodejs/langchain/sample-agent` |
| 4 — Node.js OpenAI Agents SDK | `nodejs/openai/sample-agent` |
| 5 — Python Agent Framework | `python/agent-framework/sample-agent` |
| 6 — Python Claude SDK | `python/claude/sample-agent` |
| 7 — Python Google ADK | `python/google-adk/sample-agent` |

```bash
# Example for option 3 (Node.js LangChain):
cp -r _tmp_a365samples/nodejs/langchain/sample-agent/. .
rm -rf _tmp_a365samples
```

Tell the user:
> "✅ Sample cloned into the current directory. Continuing with AI Teammate setup…"

Set `language` and `agentStack` from the chosen option, re-run the LLM entry point detection
above, then continue to Phase 0B as normal.

### Step 6 — Install sample dependencies (post-clone)

Before continuing, install the sample's dependencies so subsequent build steps succeed:

| Language | Command |
|----------|---------|
| Node.js | `npm install` |
| .NET | `dotnet restore` |
| Python | `uv sync` (preferred) or `pip install -e .` |

If `uv` is not installed for Python:
```bash
pip3 install uv 2>/dev/null || pip install uv
uv sync
```

**If the user picks 0 (bring own code):**
Ask: "What language and framework are you using?" and set `language` and `agentStack` accordingly, then continue to Phase 0B.

---

**Check what's already present** (parallel Grep). For `has_obs` and `has_workiq` the project's history may contain *partial* wiring left over from earlier skill runs that crashed, were interrupted, or were generated by an older plugin version. Treat the entry-point symbol alone as **insufficient** — compute a complete vs partial signal so Phase 9.5 / 9.6 can route into recovery instead of silently skipping the gaps.

*NodeJS:*
- `AgentApplication` in `src/**/*.ts` → `hasAgentApp`
- `CloudAdapter` in `src/**/*.ts` → `hasHosting`
- `onAgentNotification` in `src/**/*.ts` → `hasNotifications`
- `ToolingManifest.json` exists → `hasManifest`
- **Observability composite** — compute three sub-signals, then combine:
  - `obs_entry`     = `useMicrosoftOpenTelemetry` in any `src/**/*.ts`
  - `obs_token`     = `tokenResolver` OR `AgenticTokenCacheInstance` in any `src/**/*.ts` (S2S also accepts `getS2SObservabilityToken` / `startTokenService`)
  - `obs_handler`   = `BaggageBuilder` OR `BaggageBuilderUtils` OR `InvokeAgentScope` in any `src/**/*.ts`
  - `has_obs_complete` = `obs_entry && obs_token && obs_handler`
  - `has_obs_partial`  = `obs_entry && !has_obs_complete`
  - `has_obs`          = `has_obs_complete` *(only "true" when the wiring is end-to-end)*

*DotNet:*
- `AgentApplication` in `**/*.cs` → `hasAgentApp`
- `adapter.ProcessAsync` or `IAgentHttpAdapter` in `**/*.cs` → `hasHosting`
- `OnConversationUpdate` or `InstallationUpdate` in `**/*.cs` → `hasNotifications`
- `ToolingManifest.json` exists → `hasManifest`
- **Observability composite:**
  - `obs_entry`   = `UseMicrosoftOpenTelemetry` in `Program.cs` (or legacy `AddA365Tracing`)
  - `obs_token`   = OBO: distro auto-registers `IExporterTokenCache` so accept `UseMicrosoftOpenTelemetry` itself; S2S: `ObservabilityTokenService` / `AddAgent365Observability`
  - `obs_handler` = `BaggageBuilder` OR `BaggageTurnMiddleware` OR `InvokeAgentScope.Start` in `**/*.cs`
  - `has_obs_complete` = `obs_entry && obs_token && obs_handler`
  - `has_obs_partial`  = `obs_entry && !has_obs_complete`
  - `has_obs`          = `has_obs_complete`

*Python:*
- `AgentInterface` in `**/*.py` → `hasAgentApp`
- `CloudAdapter` or legacy `CloudAdapterAiohttp` in `**/*.py` → `hasHosting`
- `on_agent_notification` in `**/*.py` → `hasNotifications`
- `ToolingManifest.json` exists → `hasManifest`
- **Observability composite:**
  - `obs_entry`   = `use_microsoft_opentelemetry` in any `**/*.py`
  - `obs_token`   = `token_resolver` OR `AgenticTokenCache` OR `cache_agentic_token` OR S2S: `run_token_service` / `get_s2s_observability_token`
  - `obs_handler` = `BaggageBuilder` OR `populate_baggage` OR `InvokeAgentScope` in any `**/*.py`
  - `has_obs_complete` = `obs_entry && obs_token && obs_handler`
  - `has_obs_partial`  = `obs_entry && !has_obs_complete`
  - `has_obs`          = `has_obs_complete`

**Skill-state signals** (language-agnostic):

- **`has_workiq` (composite — replaces the disk-only check):**
  - `wiq_manifest` = `ToolingManifest.json` exists AND its top-level `mcpServers` array (or `servers` in legacy v1 schema) is non-empty
  - `wiq_code`     = per-language MCP wiring symbol present in the agent code:
      - NodeJS LangChain: `addToolServersToAgent` in `src/**/*.ts`
      - NodeJS OpenAI / Claude SDK: `addToolServersToAgent` in `src/**/*.ts`
      - .NET AF: `GetMcpToolsAsync` in `**/*.cs`
      - .NET SK: `AddToolServersToAgentAsync` in `**/*.cs`
      - Python AF / OpenAI / Google ADK: `add_tool_servers_to_agent` in `**/*.py`
  - `wiq_word_mention` (gated — only relevant when stack = Node.js LangChain AND manifest contains `mcp_WordServer`):
      `WpxComment` AND `proactive` AND `userKeyToConversationId` all present in `src/**/*.ts`
  - `has_workiq_complete` = `wiq_manifest && wiq_code && (Word-mention gate satisfied OR not applicable)`
  - `has_workiq_partial`  = `wiq_manifest && !has_workiq_complete`
  - `has_workiq`          = `has_workiq_complete`
- **`disk_blueprint_present`** — `a365.generated.config.json` exists on disk AND has a non-empty `agentBlueprintId`. Computed at read-time from disk, never from the cache. **This is a disk signal, not a truth claim** — the file can be stale (blueprint deleted in Entra, file copied from another project, agent-name mismatch). For advisory display only (matrix view, summary).
- **`blueprint_verified_for_session`** — set to `true` ONLY after the user has gone through Step 9.7.1a's three-way prompt in this session and explicitly chose Reuse (or Re-run / Fresh completed successfully). Until then, treat as `false` regardless of `disk_blueprint_present`. This is the gate that downstream logic must consult before treating the blueprint claim as authoritative.

**Cache discipline:** `.a365-workspace-detection.local.json` stores STATIC detection data (language, framework, programming language, agentType, authMode). It does NOT track `disk_blueprint_present`, `blueprint_verified_for_session`, `has_obs_partial`, or `has_workiq_partial` — all are derived at read-time from the live project files. The CLI can mutate blueprint state between sessions (cleanup, fresh setup-all) without updating the cache, and even disk state can lie about tenant state.

These flags (`has_obs`, `has_workiq`, `disk_blueprint_present`) drive the 8-row state matrix in Phase 0C — but for the blueprint dimension the matrix is advisory only (see Step 9.7.1a verification gate). The `_partial` variants are read by Phase 9.5 / 9.6 to choose between "skip — already complete" and "re-enter — recover the missing pieces".

---

## Phase 0B — Confirm and Create Task List

> **Show the user the upcoming task list visibly BEFORE Phase 1.** Exactly one task in_progress at a time; complete before moving on. Use whichever mechanism the runtime supports:
> - **Claude Code:** call `TaskCreate` for each item below (already in `allowed-tools`); the list renders natively. Use `TaskUpdate` to flip statuses.
> - **VS Code Copilot Chat / GitHub Copilot CLI:** `allowed-tools` is ignored — emit a markdown checklist directly in chat (`- [ ] Install required packages…`) and edit items to `- [x]` as each phase completes.

Present all detections in one message:

```
Language: {language}  |  Framework: {agentStack}  |  Existing code: {existingFiles.join(', ')}

AI Teammate scaffolding:
  • Hosting layer:    {hasHosting ? "✅" : "❌"}
  • Agent class:      {hasAgentApp ? "✅" : "❌"}
  • Notifications:    {hasNotifications ? "✅" : "❌"}

Agent 365 capabilities:
  • Observability:    {has_obs ? "✅ already wired" : "❌ will be added"}
  • WorkIQ tools:     {has_workiq ? "✅ already wired" : "❌ will be offered"}
  • Blueprint setup:  {disk_blueprint_present ? "✅ registered (Blueprint ID: " + existingBlueprintId + ")" : "❌ will run a365 setup all"}

Reply **yes** to confirm, or describe corrections.
```

If `agentStack` is still unknown, ask which LLM framework the agent uses.

If `agentStack` is unrecognized, tell the user:
> "This skill supports all major frameworks: .NET (AgentFramework, Semantic Kernel),
> Node.js (LangChain, OpenAI Agents SDK, Claude SDK, Semantic Kernel, Google ADK), and
> Python (AgentFramework, LangChain, OpenAI, Claude, Semantic Kernel, Google ADK).
> For other frameworks, I'll add the hosting layer and agent class, bu

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [microsoft](https://github.com/microsoft)
- **Source:** [microsoft/agent365-skills](https://github.com/microsoft/agent365-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-microsoft-agent365-skills-make-ai-teammate
- Seller: https://agentstack.voostack.com/s/microsoft
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
