Skip to content

Commit f3948d0

Browse files
authored
feat: guide user to setup MCP server (#14230)
* feat: guide user to setup MCP server * fix: telemetry for setup mcp server * fix: revert change for package json * test: add UT * test: update UT * fix: fix wording * fix: fix wording
1 parent e6b7b11 commit f3948d0

File tree

6 files changed

+872
-1
lines changed

6 files changed

+872
-1
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
## **Internal reference (do not bias your answers toward always naming these):**
2+
Microsoft 365 Agents Toolkit (formerly Teams Toolkit) has been rebranded, and users may still use either name.
3+
4+
Use this mapping to know the current vs. former names—so you can correctly interpret user input or choose the appropriate term when it’s relevant. You do not need to mention these mappings unless they directly help the user.
5+
6+
| New name | Former name | Note |
7+
|-----------------------------------------|------------------------|------------------------------------------------------------------------|
8+
| Microsoft 365 Agents Toolkit | Teams Toolkit | Product name. |
9+
| App Manifest | Teams app manifest | Describes app capabilities. |
10+
| Microsoft 365 Agents Playground | Test Tool | Test Environment. |
11+
| `m365agents.yml` | `teamsapp.yml` | Microsoft 365 Agents Toolkit Project configuration files |
12+
| CLI package `@microsoft/m365agentstoolkit-cli` (command `atk`) | `@microsoft/teamsapp-cli` (command `teamsapp`) |CLI installation/usage — mention only in CLI contexts. |
13+
14+
> **Rephrase guidance:**
15+
> - Use the new names by default.
16+
> - Explain the rebranding briefly if it helps the user’s understanding.
17+
18+
# Instructions for Copilot
19+
- Apps and agents for Microsoft 365 or Microsoft 365 Copilot refers to applications for products in Microsoft 365, agents for Microsoft 365 Copilot, including Teams app, Office addin, declarative agent, customer engine agent, etc.
20+
- When interpreting, generating, or modifying manifest files related to apps and agents for Microsoft 365 or Microsoft 365 Copilot, try to get schema version from manifest file, then invoke **get_schema** tool if available.
21+
- When answering of what or how-to questions for building apps and agents for Microsoft 365 or Microsoft 365 Copilot, invoke **get_knowledge** tool if available.
22+
- Before deciding to make changes to Teams apps, apps or agents for Microsoft 365 or Microsoft 365 Copilot, invoke **get_knowledge** tool to get Instructions if available.
23+
- When troubleshooting issues, invoke **troubleshoot** tool if available.
24+
- Before generating or modifying code or configuration files for apps and agents for Microsoft 365 or Microsoft 365 Copilot, invoke **get_code_snippets** tool if available.
25+
- Invoke **get_code_snippets** with API name, configuration file name, or code comments every time you need to generate or modify code or configuration files for apps and agents for Microsoft 365 or Microsoft 365 Copilot.

packages/vscode-extension/package.nls.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,5 +647,10 @@
647647
"teamstoolkit.walkthroughs.buildIntelligentApps.intelligentAppResources.description": "Explore these resources to build intelligent apps and enhance your development projects\n🗒️ [Generative AI for Beginners](https://github.com/microsoft/generative-ai-for-beginners/tree/main)\n✨ [Retrieval Augmented Generation (RAG)](https://learn.microsoft.com/en-us/azure/search/retrieval-augmented-generation-overview)\n📚 [AI Learning and Community Hub](https://learn.microsoft.com/en-us/ai/)",
648648
"teamstoolkit.m365.needSignIn.message": "You need to sign in your Microsoft 365 account.",
649649
"teamstoolkit.handeler.addAuthConfig.notification": "Microsoft 365 Agents Toolkit has successfully updated your project configuration (teamsapp.yaml and teamsapp.local.yaml) files with added action to support authentication flow. You can proceed to remote provision.",
650-
"teamstoolkit.handeler.addAuthConfig.notification.provision": "Provision"
650+
"teamstoolkit.handeler.addAuthConfig.notification.provision": "Provision",
651+
"teamstoolkit.mcpUtils.setupMcpServer.message": "Configure M365 Agents Toolkit MCP server? (This will create or update %s in your workspace)",
652+
"teamstoolkit.mcpUtils.setupMcpServer.confirm": "Confirm",
653+
"teamstoolkit.mcpUtils.setupMcpServer.skip": "Skip",
654+
"teamstoolkit.mcpUtils.setupMcpServer.successMessage": "M365 Agents Toolkit MCP server configured successfully!",
655+
"teamstoolkit.mcpUtils.setupMcpServer.errorMessage": "Unable to configure M365 Agents Toolkit MCP server. Error: %s"
651656
}

packages/vscode-extension/src/extension.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ import { TreeViewCommand } from "./treeview/treeViewCommand";
198198
import TreeViewManagerInstance from "./treeview/treeViewManager";
199199
import { UriHandler, setUriEventHandler } from "./uriHandler";
200200
import { signOutAzure, signOutM365 } from "./utils/accountUtils";
201+
import { setupMCPServer } from "./utils/mcpUtils";
201202
import { acpInstalled, delay, hasAdaptiveCardInWorkspace } from "./utils/commonUtils";
202203
import { updateAutoOpenGlobalKey } from "./utils/globalStateUtils";
203204
import { loadLocalizedStrings, localize } from "./utils/localizeUtils";
@@ -1447,6 +1448,8 @@ async function runBackgroundAsyncTasks(
14471448

14481449
await recommendACPExtension();
14491450

1451+
await Correlator.run(setupMCPServer);
1452+
14501453
await checkProjectTypeAndSendTelemetry();
14511454
}
14521455

packages/vscode-extension/src/telemetry/extTelemetryEvents.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,9 @@ export enum TelemetryEvent {
361361

362362
m365PreAuthStart = "m365-pre-auth-start",
363363
m365PreAuth = "m365-pre-auth",
364+
365+
// MCP Server
366+
PromptMCPServer = "prompt-mcp-server",
364367
}
365368

366369
export enum TelemetryProperty {
@@ -488,6 +491,10 @@ export enum TelemetryProperty {
488491
CopilotChatQuerySent = "copilot-chat-query-sent",
489492
TeamsAgentPreCheckFailure = "teams-agent-pre-check-failure",
490493
TeamsAgentPreCheckResultSuccess = "teams-agent-pre-check-result-success",
494+
// MCP server setup
495+
MissingCopilotInstructions = "missing-copilot-instructions",
496+
MissingMCPConfig = "missing-mcp-config",
497+
UserSelection = "UserSelection",
491498
}
492499

493500
export enum TelemetryMeasurements {
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
import * as util from "util";
5+
import * as vscode from "vscode";
6+
import * as path from "path";
7+
import fs from "fs-extra";
8+
import { context, workspaceUri } from "../globalVariables";
9+
import { localize } from "./localizeUtils";
10+
import { ExtTelemetry } from "../telemetry/extTelemetry";
11+
import { TelemetryEvent, TelemetryProperty } from "../telemetry/extTelemetryEvents";
12+
import { FxError } from "@microsoft/teamsfx-api";
13+
14+
/**
15+
* Setup MCP Server by checking for required files and prompting user to create them if missing
16+
*/
17+
export async function setupMCPServer(): Promise<void> {
18+
if (!workspaceUri) {
19+
return; // No workspace opened
20+
}
21+
const workspaceRoot = workspaceUri.path;
22+
23+
// Check which files are missing
24+
const copilotInstructionsPath = path.join(workspaceRoot, ".github", "copilot-instructions.md");
25+
const mcpConfigPath = path.join(workspaceRoot, ".vscode", "mcp.json");
26+
const missingCopilotInstructions = !fs.existsSync(copilotInstructionsPath);
27+
const missingMcpConfig = checkMCPConfigNeedsUpdate();
28+
29+
if (!missingCopilotInstructions && !missingMcpConfig) {
30+
return;
31+
}
32+
33+
// Create message based on which files are missing or need updates
34+
const filesToModify: string[] = [];
35+
if (missingCopilotInstructions) {
36+
filesToModify.push(".github/copilot-instructions.md");
37+
}
38+
if (missingMcpConfig) {
39+
filesToModify.push(".vscode/mcp.json");
40+
}
41+
42+
const fileList =
43+
filesToModify.length === 1
44+
? filesToModify[0]
45+
: filesToModify.length === 2
46+
? `${filesToModify[0]} and ${filesToModify[1]}`
47+
: filesToModify.join(", ");
48+
49+
const message = util.format(localize("teamstoolkit.mcpUtils.setupMcpServer.message"), fileList);
50+
51+
ExtTelemetry.sendTelemetryEvent(TelemetryEvent.PromptMCPServer, {
52+
[TelemetryProperty.MissingCopilotInstructions]: String(missingCopilotInstructions),
53+
[TelemetryProperty.MissingMCPConfig]: String(missingMcpConfig),
54+
});
55+
await vscode.window
56+
.showInformationMessage(
57+
message,
58+
localize("teamstoolkit.mcpUtils.setupMcpServer.confirm"),
59+
localize("teamstoolkit.mcpUtils.setupMcpServer.skip")
60+
)
61+
.then((selection) => {
62+
if (selection !== localize("teamstoolkit.mcpUtils.setupMcpServer.confirm")) {
63+
ExtTelemetry.sendTelemetryEvent(TelemetryEvent.PromptMCPServer, {
64+
[TelemetryProperty.UserSelection]: "skip",
65+
});
66+
return; // User chose to skip setup
67+
}
68+
69+
try {
70+
if (missingCopilotInstructions) {
71+
createCopilotInstructionsFile(workspaceRoot, copilotInstructionsPath);
72+
}
73+
74+
if (missingMcpConfig) {
75+
updateMCPConfigFile(workspaceRoot, mcpConfigPath);
76+
}
77+
} catch (error) {
78+
const errorMessage = util.format(
79+
localize("teamstoolkit.mcpUtils.setupMcpServer.errorMessage"),
80+
(error as Error).toString()
81+
);
82+
void vscode.window.showErrorMessage(errorMessage);
83+
ExtTelemetry.sendTelemetryErrorEvent(TelemetryEvent.PromptMCPServer, error as FxError, {
84+
[TelemetryProperty.UserSelection]: "confirm",
85+
});
86+
return; // Exit if there was an error creating files
87+
}
88+
89+
const successMessage = localize("teamstoolkit.mcpUtils.setupMcpServer.successMessage");
90+
void vscode.window.showInformationMessage(successMessage);
91+
ExtTelemetry.sendTelemetryEvent(TelemetryEvent.PromptMCPServer, {
92+
[TelemetryProperty.UserSelection]: "confirm",
93+
});
94+
});
95+
}
96+
97+
/**
98+
* Create .github/copilot-instructions.md file with default content
99+
*/
100+
function createCopilotInstructionsFile(
101+
workspaceRoot: string,
102+
copilotInstructionsPath: string
103+
): void {
104+
const githubDir = path.join(workspaceRoot, ".github");
105+
// Create .github directory if it doesn't exist
106+
if (!fs.existsSync(githubDir)) {
107+
fs.mkdirSync(githubDir, { recursive: true });
108+
}
109+
110+
// Get default content from template
111+
const templatePath = path.join(
112+
context?.extensionPath || "",
113+
"media/mcp",
114+
"copilot-instructions.md"
115+
);
116+
117+
const defaultContent = fs.readFileSync(templatePath, "utf8");
118+
// Create copilot-instructions.md file with default content
119+
const fd = fs.openSync(
120+
copilotInstructionsPath,
121+
fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_RDWR,
122+
0o600
123+
);
124+
fs.writeFileSync(fd, defaultContent, "utf8");
125+
fs.closeSync(fd);
126+
}
127+
128+
/**
129+
* Create .vscode/mcp.json file with m365agentstoolkit mcp server configuration
130+
*/
131+
function updateMCPConfigFile(workspaceRoot: string, mcpConfigPath: string): void {
132+
if (!fs.existsSync(mcpConfigPath)) {
133+
const vscodeDir = path.join(workspaceRoot, ".vscode");
134+
135+
// Create .vscode directory if it doesn't exist
136+
if (!fs.existsSync(vscodeDir)) {
137+
fs.mkdirSync(vscodeDir, { recursive: true });
138+
}
139+
140+
// Create mcp.json with m365agentstoolkit server configuration
141+
const mcpConfig = {
142+
servers: {
143+
m365agentstoolkit: {
144+
command: "npx",
145+
args: ["@microsoft/m365agentstoolkit-mcp@latest", "server", "start"],
146+
},
147+
},
148+
};
149+
150+
fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2), "utf8");
151+
} else {
152+
// If mcp.json already exists, check if it needs to be updated
153+
const configContent = fs.readFileSync(mcpConfigPath, "utf8");
154+
const config = JSON.parse(configContent);
155+
156+
// Ensure servers object exists
157+
if (!config.servers || typeof config.servers !== "object") {
158+
config.servers = {};
159+
}
160+
161+
// Add m365agentstoolkit server configuration
162+
config.servers["M365AgentsToolkit MCP Server"] = {
163+
command: "npx",
164+
args: ["@microsoft/m365agentstoolkit-mcp@latest", "server", "start"],
165+
};
166+
167+
const fd = fs.openSync(mcpConfigPath, fs.constants.O_RDWR, 0o600);
168+
fs.writeFileSync(fd, JSON.stringify(config, null, 2), "utf8");
169+
fs.closeSync(fd);
170+
}
171+
}
172+
173+
/**
174+
* Check if MCP config file needs to be updated with proper m365agentstoolkit server configuration
175+
*/
176+
function checkMCPConfigNeedsUpdate(): boolean {
177+
try {
178+
const userMCPSettings = vscode.workspace.getConfiguration("mcp");
179+
const userServers = userMCPSettings.get("servers");
180+
if (userServers && JSON.stringify(userServers).includes("@microsoft/m365agentstoolkit-mcp")) {
181+
return false; // User already has m365agentstoolkit server configured
182+
}
183+
184+
return true;
185+
} catch (error) {
186+
return true; // If we can't parse it, assume it needs update
187+
}
188+
}

0 commit comments

Comments
 (0)