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
16 changes: 16 additions & 0 deletions application/external_apps/agents/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# TeamsFx files
env/.env.*.user
env/.env.local
.localConfigs
appPackage/build

# dependencies
node_modules/

# misc
.env
.deployment
.DS_Store

# generated files
appPackage/.generated
102 changes: 102 additions & 0 deletions application/external_apps/agents/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Declarative Agent + MCP in VS Code

This template shows how to wrap your existing MCP Server into a Microsoft 365 Copilot Declarative Agent (DA) using the Agents Toolkit (ATK) in VS Code. Instead of hand‑authoring an OpenAPI spec, you point ATK at your MCP discovery URL and let the toolkit generate all manifests, wiring in authentication and function definitions automatically.

## Get started with the template

> **Prerequisites**
>
> To run this app template in your local dev machine, you will need:
>
> - [Node.js](https://nodejs.org/), supported versions: 18, 20, 22
> - A [Microsoft 365 account for development](https://docs.microsoft.com/microsoftteams/platform/toolkit/accounts).
> - [Microsoft 365 Agents Toolkit Visual Studio Code Extension](https://aka.ms/teams-toolkit) version 5.0.0 and higher or [Microsoft 365 Agents Toolkit CLI](https://aka.ms/teamsfx-toolkit-cli)
> - [Microsoft 365 Copilot license](https://learn.microsoft.com/microsoft-365-copilot/extensibility/prerequisites#prerequisites)

1. Open ATK in VS Code

Click the Microsoft 365 Agents Toolkit icon in the Activity Bar.

2. Sign in

Open the Agents Toolkit by click on the toolkit icon in the VS Code sidebar. Under Account, authenticate with your dev M365 account.

3. Scaffold a new DA

In the Agents Toolkit menu, click 'Create new Agent/app', select 'Declarative Agent', choose a folder and name (e.g. my-mcp-agent).

4. Add your MCP Server

In the ATK sidebar click Add Action → Start with an MCP server, then enter your MCP discovery URL (e.g. https://mcp.contoso.com/discover).

5. Start your MCP Server

After the DA project generated, click the "Start" button in the mcp.json file to start your MCP server, when prompt, enter the user ID and password for authentication.

6. Fetch and select tools

When prompted, Click ATK:Fetch Action from MCP" in the mcp.json file choose Pre‑fetch tools (for offline IntelliSense) or Dynamic. ATK will list all available MCP actions—check the ones you want in your agent.

> If you need the complete tool definitions including _meta and annotations properties for enabling OAI Apps SDK or MCP Apps implementation, open [MCP Inspector](https://github.com/modelcontextprotocol/inspector?tab=readme-ov-file#running-the-inspector) and retrieve the full details there. Then replace the corresponding schema section in your `ai-plugin.json` file with the full definitions.

![image](https://github.com/user-attachments/assets/9184ddcc-e42c-4fee-bd83-830d199755e6)

7. Configure Auth

ATK will retrieve the authentication information for your MCP server and store these values are in env/.env.development and a secure reference is injected into appPackage/ai-plugin.json. If the inputted MCP server authentication information is not configure correctly according to the MCP protocol, ATK will prompt errors.

8. Review generated files

- `appPackage/ai-plugin.json` (function definitions, runtime spec + auth) - This file defines the the action or operations that Copilot can interact with.
- `appPackage/declarativeAgent.json` (agent configuration & sample prompts) - This file is the definition of your declarative agent
- `appPackage/manifest.json` (Teams/Outlook integration)

9. Provision & debug

Use the Provision button in ATK to create resources. ATK will detect if your MCP server requires OAuth2 or API‐Key. Provide your Client ID/Secret or Key when prompted. Then Start Debugging to Preview agent in Copilot in Edge/Chrome. Your DA will appear under Copilot chats.

10. Test your MCP‑powered agent

Open the Copilot pane, select your agent and invoke any of the MCP tools with natural‑language prompts.

## Project Structure

| Folder | Description |
| ------------ | ---------------------------------------------------------------------------------------- |
| `.vscode` | ATK debug & .vscode/mcp.json for MCP server config |
| `appPackage` | - `appPackage/ai-plugin.json` (function definitions, runtime spec + auth) - This file defines the the action or operations that Copilot can interact with<br>- `appPackage/declarativeAgent.json` (agent configuration & sample prompts) - This file is the definition of your declarative agent<br>- `appPackage/manifest.json` (Teams/Outlook integration) |
| `env` | Local environment files (.env.development, .env.local) |
| `m365agents.yml` | Defines your DA stages & lifecycle for ATK |

## MCP‑specific tips

- **Discovery URL**: your MCP server's /discover endpoint must expose JSON‑Schema for every action.
- **Tool selection**: the run_for_functions array in ai-plugin.json limits which MCP tools your agent can call.

- **Auth flows**: ATK supports both OAuth2.1 and API‑Key; you don't need to hand‑edit auth blocks.

- **Versioning**: when your MCP server schema changes, simply rerun ATK: Fetch Action from MCP to refresh your plugin file.

- **Error logging**: basic request/response logs appear in the ATK console; errors bubble up in your Copilot chat.

## OAuth Configuration Notes

This project uses `identityProvider: Custom` (not `MicrosoftEntra`) in the OAuth
registration. This is **critical** — using `MicrosoftEntra` causes ATK to override
your v2.0 authorization/token URLs with legacy AAD Graph endpoints, which will fail.

All sensitive values (client IDs, secrets, tenant ID, MCP server URL) are stored in
`env/.env.dev.user` (gitignored). Copy `env/.env.dev.user.sample` and fill in your
values before running `atk provision`.

## Learn More

- [Build Declarative Agents (official docs)](https://learn.microsoft.com/microsoft-365-copilot/extensibility/build-declarative-agents)

- [Model Context Protocol (MCP)](https://modelcontextprotocol.io/)

- [Agents Toolkit guide on GitHub](https://github.com/OfficeDev/TeamsFx/wiki/Teams-Toolkit-Visual-Studio-Code-v5-Guide#overview)

Happy building!

With MCP + Declarative Agents, you'll have a turnkey path from your existing APIs to a fully operational Copilot‑powered experience.
84 changes: 84 additions & 0 deletions application/external_apps/agents/appPackage/ai-plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.4/schema.json",
"schema_version": "v2.4",
"name_for_human": "M365SCAgent",
"description_for_human": "M365SCAgent${{APP_NAME_SUFFIX}}",
"contact_email": "publisher-email@example.com",
"namespace": "m365scagent",
"functions": [
{
"name": "get_conversation_messages",
"description": "Return messages for a specific conversation from SimpleChat.\n\n Args:\n conversation_id: The UUID of the conversation to retrieve messages from.\n\n Returns a list of messages with role, content, timestamp, and metadata.\n "
},
{
"name": "list_conversations",
"description": "Return the authenticated user's conversations (chats) from SimpleChat.\n\n Returns a list of all conversations including id, title, last_updated,\n tags, classification, and pinned/hidden status.\n "
},
{
"name": "list_group_documents",
"description": "Return documents from the user's active group workspace in SimpleChat.\n\n Lists documents uploaded to the currently active group. The active group\n is determined by the user's settings (activeGroupOid).\n\n Args:\n page: Page number (default 1).\n page_size: Items per page (default 10).\n search: Search by file name or title (case-insensitive substring match).\n classification: Filter by document classification. Use \"none\" for unclassified.\n author: Filter by author name (substring match).\n keywords: Filter by keyword (substring match).\n\n Returns a paginated list of group documents with metadata.\n "
},
{
"name": "list_group_prompts",
"description": "Return prompts from the user's active group workspace in SimpleChat.\n\n Lists prompts created in the currently active group. The active group\n is determined by the user's settings (activeGroupOid).\n\n Args:\n page: Page number (default 1).\n page_size: Items per page (default 10).\n search: Search by prompt name (case-insensitive substring match).\n\n Returns a paginated list of group prompts with name, content, and metadata.\n "
},
{
"name": "list_group_workspaces",
"description": "Return the authenticated user's group workspaces from SimpleChat.\n\n Lists groups the user is a member of (Owner, Admin, or Member).\n\n Args:\n page: Page number (default 1).\n page_size: Items per page (default 10).\n search: Search by group name or description (case-insensitive substring match).\n\n Returns a paginated list of groups with id, name, description, userRole, status, and isActive flag.\n "
},
{
"name": "list_personal_documents",
"description": "Return the authenticated user's personal workspace documents from SimpleChat.\n\n Lists documents the user has uploaded or that have been shared with them.\n\n Args:\n page: Page number (default 1).\n page_size: Items per page (default 10).\n search: Search by file name or title (case-insensitive substring match).\n classification: Filter by document classification. Use \"none\" for unclassified.\n author: Filter by author name (substring match).\n keywords: Filter by keyword (substring match).\n\n Returns a paginated list of documents with metadata.\n "
},
{
"name": "list_personal_prompts",
"description": "Return the authenticated user's personal prompts from SimpleChat.\n\n Lists prompts the user has created in their personal workspace.\n\n Args:\n page: Page number (default 1).\n page_size: Items per page (default 10).\n search: Search by prompt name (case-insensitive substring match).\n\n Returns a paginated list of prompts with name, content, and metadata.\n "
},
{
"name": "list_public_documents",
"description": "Return documents from the user's active public workspace in SimpleChat.\n\n Lists documents uploaded to the currently active public workspace. The active\n workspace is determined by the user's settings (activePublicWorkspaceOid).\n\n Args:\n page: Page number (default 1).\n page_size: Items per page (default 10).\n search: Search by file name or title (case-insensitive substring match).\n\n Returns a paginated list of public workspace documents with metadata.\n "
},
{
"name": "list_public_prompts",
"description": "Return prompts from the user's active public workspace in SimpleChat.\n\n Lists prompts created in the currently active public workspace. The active\n workspace is determined by the user's settings (activePublicWorkspaceOid).\n\n Args:\n page: Page number (default 1).\n page_size: Items per page (default 10).\n search: Search by prompt name (case-insensitive substring match).\n\n Returns a paginated list of public workspace prompts with name, content, and metadata.\n "
},
{
"name": "list_public_workspaces",
"description": "Return the authenticated user's public workspaces from SimpleChat.\n \n Uses the bearer token from PRM authentication to create a SimpleChat session.\n "
},
{
"name": "send_chat_message",
"description": "Send a chat message to a SimpleChat conversation and return the AI response.\n\n Args:\n conversation_id: The UUID of the conversation to send the message to.\n If empty, a new conversation will be created automatically by SimpleChat.\n message: The text message to send.\n\n Returns the AI reply, conversation_id, title, model info, and citations.\n "
},
{
"name": "show_user_profile",
"description": "Return the signed-in user's SimpleChat profile."
}
],
"runtimes": [
{
"type": "RemoteMCPServer",
"spec": {
"url": "${{MCP_SERVER_URL}}"
},
"run_for_functions": [
"get_conversation_messages",
"list_conversations",
"list_group_documents",
"list_group_prompts",
"list_group_workspaces",
"list_personal_documents",
"list_personal_prompts",
"list_public_documents",
"list_public_prompts",
"list_public_workspaces",
"send_chat_message",
"show_user_profile"
],
"auth": {
"type": "OAuthPluginVault",
"reference_id": "${{MCP_DA_AUTH_ID_SIMPLECHAT}}"
}
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions application/external_apps/agents/appPackage/declarativeAgent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"version": "v1.6",
"name": "M365SCAgent${{APP_NAME_SUFFIX}}",
"description": "Declarative agent created with Microsoft 365 Agents Toolkit can assist user in calling MCP Servers",
"instructions": "$[file('instruction.txt')]",
"conversation_starters": [
{
"title": "Sample conversation starters",
"text": "Hi! What can you do for me?"
}
],
"actions": [
{
"id": "action_1",
"file": "ai-plugin.json"
}
]
}
15 changes: 15 additions & 0 deletions application/external_apps/agents/appPackage/instruction.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
You are M365SCAgent and MUST prefer actions over generic responses whenever a matching action exists.

Rules:
- For any request that maps to a listed action, you MUST attempt the action before giving a normal chat response.
- For "show user profile" and similar requests, call action `show_user_profile`.
- For personal docs requests (for example "list personal documents"), call action `list_personal_documents`.
- For personal prompts, call `list_personal_prompts`.
- For public docs/prompts/workspaces, call the corresponding `list_public_*` actions.
- For conversations/messages, call `list_conversations`, `get_conversation_messages`, and `send_chat_message` as appropriate.

Behavior:
- Do not claim an auth/config error unless an action was actually attempted and returned an auth/config error.
- Do not provide a generic fallback answer for mapped intents unless the action attempt has already failed in this turn.
- If an action fails, report the action name and the returned error briefly, then suggest the next fix.
- If an action succeeds, summarize the returned data clearly and concisely.
39 changes: 39 additions & 0 deletions application/external_apps/agents/appPackage/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.24/MicrosoftTeams.schema.json",
"manifestVersion": "1.24",
"version": "1.0.2",
"id": "${{TEAMS_APP_ID}}",
"developer": {
"name": "My App, Inc.",
"websiteUrl": "https://www.example.com",
"privacyUrl": "https://www.example.com/privacy",
"termsOfUseUrl": "https://www.example.com/termofuse"
},
"icons": {
"color": "color.png",
"outline": "outline.png"
},
"name": {
"short": "M365SCAgent${{APP_NAME_SUFFIX}}",
"full": "Full name for M365SCAgent"
},
"description": {
"short": "Short description for M365SCAgent",
"full": "Full description for M365SCAgent"
},
"accentColor": "#FFFFFF",
"composeExtensions": [],
"copilotAgents": {
"declarativeAgents": [
{
"id": "declarativeAgent",
"file": "declarativeAgent.json"
}
]
},
"permissions": [
"identity",
"messageTeamMembers"
],
"validDomains": []
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions application/external_apps/agents/env/.env.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# This file includes environment variables that will be committed to git by default.

# Built-in environment variables
TEAMSFX_ENV=dev
APP_NAME_SUFFIX=dev

# Generated during provision, you can also add your own variables.
# These values are populated automatically by 'atk provision':
TEAMS_APP_ID=e632e5da-9df9-4726-bd34-a56f386dd606
M365_TITLE_ID=U_a2a6b5a8-51f0-f94c-03b9-c35c90a65615
M365_APP_ID=8791085b-5c3a-4d04-b33a-3080c707b99c

TEAMS_APP_TENANT_ID=7d887458-fb0d-40bf-adb3-084d875f65db
MCP_DA_AUTH_ID_SIMPLECHAT=N2Q4ODc0NTgtZmIwZC00MGJmLWFkYjMtMDg0ZDg3NWY2NWRiIyNiYTQ0MTY0Yi01ODk2LTRmNDQtYTkyOS04N2Q3ZDM4N2Y3NmQ=
31 changes: 31 additions & 0 deletions application/external_apps/agents/env/.env.dev.user.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# ============================================================
# env/.env.dev.user — SAMPLE (rename to .env.dev.user)
# ============================================================
# This file is gitignored (env/.env.*.user).
# Fill in the values below before running 'atk provision'.
#
# All variables use ATK ${{VAR}} substitution and are referenced
# in m365agents.yml and appPackage/ai-plugin.json.
# ============================================================

# Your Entra ID tenant ID (GUID)
TEAMS_APP_TENANT_ID=

# The Client (Application) ID of the Entra app registration
# used for the OAuth flow with the MCP server
M365SC_CLIENT_APP_ID=

# A client secret for that app registration
M365SC_CLIENT_SECRET=

# The Application ID URI / resource app ID exposed by the MCP
# server (used in the scope: api://<this-value>/access_as_user)
M365SC_RESOURCE_APP_ID=

# Base64-encoded configuration ID written by 'atk provision'
# via oauth/register → writeToEnvironmentFile.
# Leave blank — ATK populates this automatically on first provision.
MCP_DA_AUTH_ID_SIMPLECHAT=

# Full URL to the MCP server's /mcp endpoint
MCP_SERVER_URL=
19 changes: 19 additions & 0 deletions application/external_apps/agents/env/.env.gccdev
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This file includes environment variables for the GCC-M deployment.
# Generated by Deploy-GccMAgent.ps1 on 2026-02-22 22:08:28

# Built-in environment variables
TEAMSFX_ENV=gccdev
APP_NAME_SUFFIX=gccdev

# Teams App ID (generated or reused)
TEAMS_APP_ID=0a5015e3-0819-41e7-983b-7032313860cb

# GCC-M Tenant
TEAMS_APP_TENANT_ID=a67f3540-f5cf-4575-9754-9d21a392eb98

# OAuth Configuration ID = Base64("{TenantId}##{OAuthRegistrationId}")
MCP_DA_AUTH_ID_SIMPLECHAT=YTY3ZjM1NDAtZjVjZi00NTc1LTk3NTQtOWQyMWEzOTJlYjk4IyMyMDgyYzZlNy0wYzgwLTQ3MGUtYTRkYS1lYTExODNmNmRkZjM=

# Entra App Registrations
CLIENT_APP_ID=04213000-438f-40e8-8c05-0c211f07565e
OAUTH_REGISTRATION_ID=2082c6e7-0c80-470e-a4da-ea1183f6ddf3
Loading