Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions nodejs/claude/sample-agent/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# TeamsFx files
env/.env.*.user
env/.env.local
env/.env.sandbox
.localConfigs
.localConfigs.playground
.localConfigs
.notification.localstore.json
.notification.playgroundstore.json
appPackage/build

# dependencies
node_modules/

# misc
.env
.deployment
.DS_Store

# build
dist/

# Dev tool directories
/devTools/
5 changes: 5 additions & 0 deletions nodejs/claude/sample-agent/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"recommendations": [
"TeamsDevApp.ms-teams-vscode-extension"
]
}
31 changes: 31 additions & 0 deletions nodejs/claude/sample-agent/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Local Service",
"type": "node",
"request": "attach",
"port": 9239,
"restart": true,
"presentation": {
"group": "all",
"hidden": true
},
"internalConsoleOptions": "neverOpen"
}
],
"compounds": [
{
"name": "Debug in Microsoft 365 Agents Playground",
"configurations": [
"Attach to Local Service"
],
"preLaunchTask": "Start App in Microsoft 365 Agents Playground",
"presentation": {
"group": "1-playground",
"order": 1
},
"stopAll": true
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
$ErrorActionPreference = 'Stop'

$workspace = Get-Location
$playgroundEnvPath = Join-Path $workspace 'env/.env.playground'
$playgroundUserEnvPath = Join-Path $workspace 'env/.env.playground.user'

$a365Command = Get-Command a365 -ErrorAction SilentlyContinue
if (-not $a365Command) {
throw "a365 CLI is not installed or not on PATH. Install with: dotnet tool install --global Microsoft.Agents.A365.DevTools.Cli"
}

if (-not (Test-Path $playgroundEnvPath)) {
throw "Missing env file: $playgroundEnvPath"
}

$appIdLine = Get-Content $playgroundEnvPath | Where-Object { $_ -match '^\s*CLIENT_APP_ID\s*=\s*.+$' } | Select-Object -First 1
if (-not $appIdLine) {
throw "CLIENT_APP_ID is required in env/.env.playground"
}

$appId = ($appIdLine -split '=', 2)[1].Trim()
if ([string]::IsNullOrWhiteSpace($appId)) {
throw "CLIENT_APP_ID in env/.env.playground is empty"
}

Write-Host "Running a365 develop add-permissions for app id $appId"
& a365 develop add-permissions --app-id $appId
if ($LASTEXITCODE -ne 0) {
throw "a365 develop add-permissions failed"
}

Write-Host "Getting bearer token via a365..."
Write-Host "This may complete silently using cached credentials, or it may require interactive Windows sign-in (WAM)."
Write-Host "If interactive sign-in is required and no prompt appears, check the taskbar for a hidden sign-in window and bring it to front."
Write-Host "Running a365 develop get-token for app id $appId"
$tokenOutput = & a365 develop get-token --app-id $appId --output raw
if ($LASTEXITCODE -ne 0) {
throw "a365 develop get-token failed"
}

$rawOutput = [string]::Join("`n", $tokenOutput)
$rawOutput = $rawOutput -replace "`r", ''

$bearerToken = $null
$jwtRegex = '(?<token>[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)'
$jwtMatches = [regex]::Matches($rawOutput, $jwtRegex)
if ($jwtMatches.Count -gt 0) {
$bearerToken = ($jwtMatches | ForEach-Object { $_.Groups['token'].Value } | Sort-Object Length -Descending | Select-Object -First 1).Trim()
}

if ([string]::IsNullOrWhiteSpace($bearerToken)) {
throw "Unable to extract a bearer token from a365 develop get-token output"
}

$userEnvLines = @()
if (Test-Path $playgroundUserEnvPath) {
$userEnvLines = Get-Content $playgroundUserEnvPath
}

$updated = $false
for ($i = 0; $i -lt $userEnvLines.Count; $i++) {
if ($userEnvLines[$i] -match '^\s*SECRET_BEARER_TOKEN\s*=') {
$userEnvLines[$i] = "SECRET_BEARER_TOKEN=$bearerToken"
$updated = $true
break
}
}

if (-not $updated) {
if ($userEnvLines.Count -gt 0 -and -not [string]::IsNullOrWhiteSpace($userEnvLines[$userEnvLines.Count - 1])) {
$userEnvLines += ''
}
$userEnvLines += "SECRET_BEARER_TOKEN=$bearerToken"
}

Set-Content -Path $playgroundUserEnvPath -Value $userEnvLines -Encoding UTF8
Write-Host 'SECRET_BEARER_TOKEN has been updated in env/.env.playground.user'
121 changes: 121 additions & 0 deletions nodejs/claude/sample-agent/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Start App in Microsoft 365 Agents Playground",
"dependsOn": [
"Validate prerequisites (Microsoft 365 Agents Playground)",
"Refresh bearer token (Microsoft 365 Agents Playground)",
"Deploy (Microsoft 365 Agents Playground)",
"Start application (Microsoft 365 Agents Playground)",
"Start Microsoft 365 Agents Playground"
],
"dependsOrder": "sequence"
},
{
// Check all required prerequisites.
// See https://aka.ms/teamsfx-tasks/check-prerequisites to know the details and how to customize the args.
"label": "Validate prerequisites (Microsoft 365 Agents Playground)",
"type": "teamsfx",
"command": "debug-check-prerequisites",
"args": {
"prerequisites": [
"nodejs", // Validate if Node.js is installed.
"portOccupancy" // Validate available ports to ensure those debug ones are not occupied.
],
"portOccupancy": [
3978, // app service port
9239, // app inspector port for Node.js debugger
56150 // Microsoft 365 Agents Playground port
]
}
},
{
"label": "Refresh bearer token (Microsoft 365 Agents Playground)",
"type": "shell",
"command": "powershell",
"args": [
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-File",
"${workspaceFolder}/.vscode/scripts/refresh-bearer-token.ps1"
],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
// Build project.
// See https://aka.ms/teamsfx-tasks/deploy to know the details and how to customize the args.
"label": "Deploy (Microsoft 365 Agents Playground)",
"dependsOrder": "sequence",
"type": "teamsfx",
"command": "deploy",
"args": {
"env": "playground"
}
},
{
"label": "Start application (Microsoft 365 Agents Playground)",
"type": "shell",
"command": "npm run dev:teamsfx:playground",
"isBackground": true,
"options": {
"cwd": "${workspaceFolder}",
},
"problemMatcher": {
"pattern": [
{
"regexp": "^.*$",
"file": 0,
"location": 1,
"message": 2
}
],
"background": {
"activeOnStart": true,
"beginsPattern": "[nodemon] starting",
"endsPattern": "Server listening on|Bot/ME service listening at|[nodemon] app crashed"
}
}
},
{
"label": "Start Microsoft 365 Agents Playground",
"type": "shell",
"command": "npm run dev:teamsfx:launch-playground",
"isBackground": true,
"options": {
"env": {
"PATH": "${workspaceFolder}/devTools/playground/node_modules/.bin;${env:PATH}"
}
},
"windows": {
"options": {
"env": {
"PATH": "${workspaceFolder}/devTools/playground/node_modules/.bin;${env:PATH}"
}
}
},
"problemMatcher": {
"pattern": [
{
"regexp": "^.*$",
"file": 0,
"location": 1,
"message": 2
}
],
"background": {
"activeOnStart": true,
"beginsPattern": ".*",
"endsPattern": "Listening on"
}
},
"presentation": {
"panel": "dedicated",
"reveal": "silent"
}
}
]
}
26 changes: 21 additions & 5 deletions nodejs/claude/sample-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,17 @@ This sample uses the [Microsoft Agent 365 SDK for Node.js](https://github.com/mi
For comprehensive documentation and guidance on building agents with the Microsoft Agent 365 SDK, including how to add tooling, observability, and notifications, visit the [Microsoft Agent 365 Developer Documentation](https://learn.microsoft.com/en-us/microsoft-agent-365/developer/).

## Prerequisites

- Node.js 18.x or higher
- Microsoft Agent 365 SDK
- Claude Agent SDK 0.1.1 or higher
- Claude API credentials
>
> To run the template in your local dev machine, you will need:
>
> - [Node.js](https://nodejs.org/), supported versions: 18.x or higher
> - [Microsoft 365 Agents Toolkit Visual Studio Code Extension](https://aka.ms/teams-toolkit) latest version
> - Prepare your own Anthropic API credentials
> - Azure CLI signed in with `az login`

> - Microsoft Agent 365 SDK
> - Claude Agent SDK 0.1.1 or higher
> - A365 CLI: Required for agent deployment and management.

## Working with User Identity

Expand All @@ -32,6 +38,16 @@ information — always available with no API calls or token acquisition:
The sample logs these fields at the start of every message turn and injects the display name
into the LLM system instructions for personalized responses.

## Running the Agent in Microsoft 365 Agents Playground

1. First, select the Microsoft 365 Agents Toolkit icon on the left in the VS Code toolbar.
1. In file *env/.env.playground.user*, fill in your Anthropic API key `SECRET_ANTHROPIC_API_KEY=<your-key>`.
1. In file *env/.env.playground*, fill in your custom app registration client id `CLIENT_APP_ID`.
1. Press F5 to start debugging which launches your agent in Microsoft 365 Agents Playground using a web browser. Select `Debug in Microsoft 365 Agents Playground`.
1. You can send any message to get a response from the agent.

**Congratulations**! You are running an agent that can now interact with users in Microsoft 365 Agents Playground.

## Running the Agent

To set up and test this agent, refer to the [Configure Agent Testing](https://learn.microsoft.com/en-us/microsoft-agent-365/developer/testing?tabs=nodejs) guide for complete instructions.
Expand Down
23 changes: 23 additions & 0 deletions nodejs/claude/sample-agent/env/.env.playground
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This file includes environment variables that can be committed to git. It's gitignored by default because it represents your local development environment.

# Built-in environment variables
TEAMSFX_ENV=playground

# Environment variables used by Microsoft 365 Agents Playground
TEAMSAPPTESTER_PORT=56150
TEAMSFX_NOTIFICATION_STORE_FILENAME=.notification.testtoolstore.json

# Custom app registration needed for bearer token
CLIENT_APP_ID=

# Use Agentic Authentication rather than OBO
USE_AGENTIC_AUTH=false

# Set service connection as default
connectionsMap__0__serviceUrl=*
connectionsMap__0__connection=service_connection

# AgenticAuthentication Options
agentic_type=agentic
agentic_altBlueprintConnectionName=service_connection
agentic_scopes=ea9ffc3e-8a23-4a7d-836d-234d7c7565c1/.default # Prod Agentic scope
4 changes: 4 additions & 0 deletions nodejs/claude/sample-agent/env/.env.playground.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Anthropic Configuration
SECRET_ANTHROPIC_API_KEY=

SECRET_BEARER_TOKEN=
38 changes: 38 additions & 0 deletions nodejs/claude/sample-agent/m365agents.playground.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# yaml-language-server: $schema=https://aka.ms/m365-agents-toolkits/v1.11/yaml.schema.json
# Visit https://aka.ms/teamsfx-v5.0-guide for details on this file
# Visit https://aka.ms/teamsfx-actions for details on actions
version: v1.11

environmentFolderPath: ./env

deploy:
# Install development tool(s)
- uses: devTool/install
with:
testTool:
version: ~0.2.7
symlinkDir: ./devTools/playground

# Run npm command
- uses: cli/runNpmCommand
with:
args: install

- uses: file/createOrUpdateEnvironmentFile
with:
target: ./.localConfigs.playground
envs:
NODE_ENV: local
PORT: 3978
TEAMSFX_NOTIFICATION_STORE_FILENAME: ${{TEAMSFX_NOTIFICATION_STORE_FILENAME}}
ANTHROPIC_API_KEY: ${{SECRET_ANTHROPIC_API_KEY}}
BEARER_TOKEN: ${{SECRET_BEARER_TOKEN}}
USE_AGENTIC_AUTH: ${{USE_AGENTIC_AUTH}}
connectionsMap__0__serviceUrl: ${{connectionsMap__0__serviceUrl}}
connectionsMap__0__connection: ${{connectionsMap__0__connection}}
agentic_type: ${{agentic_type}}
agentic_altBlueprintConnectionName: ${{agentic_altBlueprintConnectionName}}
agentic_scopes: ${{agentic_scopes}}
connections__service_connection__settings__clientId: ""
connections__service_connection__settings__clientSecret: ""
connections__service_connection__settings__tenantId: ""
4 changes: 4 additions & 0 deletions nodejs/claude/sample-agent/m365agents.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# yaml-language-server: $schema=https://aka.ms/m365-agents-toolkits/v1.11/yaml.schema.json
# Visit https://aka.ms/teamsfx-v5.0-guide for details on this file
# Visit https://aka.ms/teamsfx-actions for details on actions
version: v1.11
7 changes: 5 additions & 2 deletions nodejs/claude/sample-agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
"type": "commonjs",
"scripts": {
"start": "node dist/index.js",
"dev": "nodemon --watch src --exec ts-node src/index.ts",
"dev": "nodemon --exec node --inspect=9239 --signal SIGINT -r ts-node/register src/index.ts",
"test-tool": "agentsplayground",
"install:clean": "npm run clean && npm install",
"clean": "rimraf dist node_modules package-lock.json",
"build": "tsc"
"build": "tsc",
"dev:teamsfx:playground": "env-cmd --silent -f .localConfigs.playground npm run dev",
"dev:teamsfx:launch-playground": "env-cmd --silent -f env/.env.playground agentsplayground start"
},
"keywords": [],
"author": "Microsoft",
Expand All @@ -32,6 +34,7 @@
"@microsoft/m365agentsplayground": "^0.2.18",
"@types/express": "^4.17.21",
"@types/node": "^20.14.9",
"env-cmd": "^11.0.0",
"nodemon": "^3.1.10",
"rimraf": "^5.0.0",
"ts-node": "^10.9.2",
Expand Down
Loading