<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>mjfnet: Cool Microsoft 365 and AI Dev Knowledge Sharing</title><link>https://mjfnet.com/</link><description>Recent content on mjfnet: Cool Microsoft 365 and AI Dev Knowledge Sharing</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Mon, 04 May 2026 10:10:51 -0700</lastBuildDate><atom:link href="https://mjfnet.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Time Zone Converter - Meeting Planner</title><link>https://mjfnet.com/time-zone-converter-meeting-planner/</link><pubDate>Mon, 04 May 2026 10:10:51 -0700</pubDate><guid>https://mjfnet.com/time-zone-converter-meeting-planner/</guid><description>&lt;p&gt;&lt;strong&gt;Time Zone Converter – Meeting Planner&lt;/strong&gt; takes the guesswork out of scheduling meetings across multiple time zones. Designed for distributed teams, remote workers, and global organizations, the app visually compares participant availability and intelligently recommends the best meeting times—so everyone stays productive without late nights or early mornings.&lt;/p&gt;
&lt;p&gt;With an intuitive timeline view, real-time clocks, and smart meeting quality analysis, you can instantly see who’s working, who’s outside normal hours, and which time best balances fairness and convenience. Planning global meetings has never been clearer—or faster.&lt;/p&gt;
&lt;script type="module" src="https://get.microsoft.com/badge/ms-store-badge.bundled.js"&gt;&lt;/script&gt;
&lt;div align="center"&gt;
&lt;ms-store-badge
productid="9PD5H0G76R8T"
productname="Time Zone Converter - Meeting Planner"
window-mode="direct"
theme="auto"
size= "small"
language="en-us"
animation="on"&gt;
&lt;/ms-store-badge&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/time-zone-converter-meeting-planner/images/DefaultView.png"
width="1920"
height="1032"
srcset="https://mjfnet.com/time-zone-converter-meeting-planner/images/DefaultView_hu_f41cbfe7cf9a717f.png 480w, https://mjfnet.com/time-zone-converter-meeting-planner/images/DefaultView_hu_9835415d75d94123.png 1024w"
loading="lazy"
alt="Time Zone Converter – Meeting Planner"
class="gallery-image"
data-flex-grow="186"
data-flex-basis="446px"
&gt;&lt;/p&gt;
&lt;h3 id="key-benefits"&gt;Key Benefits
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Save time and avoid scheduling mistakes&lt;/strong&gt;&lt;br&gt;
Instantly compare time zones and spot conflicts at a glance—no mental math required.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choose the fairest meeting time&lt;/strong&gt;&lt;br&gt;
Smart recommendations highlight options that minimize disruption across all participants.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduce fatigue for global teams&lt;/strong&gt;&lt;br&gt;
Clearly see early, late, night, and weekend hours to avoid burnout.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Plan with confidence&lt;/strong&gt;&lt;br&gt;
Know exactly how a proposed meeting impacts everyone before sending the invite.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Seamless calendar integration&lt;/strong&gt;&lt;br&gt;
Turn the chosen meeting time into a calendar event in just one step.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="important-features"&gt;Important Features
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multi‑Time‑Zone View&lt;/strong&gt;&lt;br&gt;
Add multiple cities or time zones and view them side by side with live clocks and UTC offsets.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Visual Meeting Timeline&lt;/strong&gt;&lt;br&gt;
Color‑coded hourly blocks clearly indicate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Working hours&lt;/li&gt;
&lt;li&gt;Slightly outside working hours&lt;/li&gt;
&lt;li&gt;Fringe hours&lt;/li&gt;
&lt;li&gt;Night / sleeping hours&lt;/li&gt;
&lt;li&gt;Weekend time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Smart Meeting Recommendations&lt;/strong&gt;&lt;br&gt;
Automatically suggests the best meeting start times based on participant availability and disruption level, with clear explanations for each option.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Meeting Quality Analysis&lt;/strong&gt;&lt;br&gt;
See an instant summary of how a selected time affects participants:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Who is within work hours&lt;/li&gt;
&lt;li&gt;Who is outside or at night&lt;/li&gt;
&lt;li&gt;Overall impact rating (Low / Medium / High)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Date &amp;amp; Time Picker&lt;/strong&gt;&lt;br&gt;
Easily adjust meeting dates and times and instantly see updates across all time zones.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add to Calendar&lt;/strong&gt;&lt;br&gt;
Create a calendar event with subject, location, description, and correct start/end times—all aligned across time zones.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Day/Night Toggle &amp;amp; Clean UI&lt;/strong&gt;&lt;br&gt;
Hide or show night hours to focus on relevant times and keep the interface uncluttered.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Time Zone Converter – Meeting Planner&lt;/strong&gt; is built for modern, global collaboration—helping teams meet at the right time, every time.&lt;/p&gt;</description></item><item><title>Time Zone Converter - Meeting Planner - Privacy Policy</title><link>https://mjfnet.com/time-zone-converter-meeting-planner-privacy-policy/</link><pubDate>Mon, 04 May 2026 10:10:51 -0700</pubDate><guid>https://mjfnet.com/time-zone-converter-meeting-planner-privacy-policy/</guid><description>&lt;h1 id="privacy-policy"&gt;Privacy Policy
&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;Time Zone Converter – Meeting Planner&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Effective Date:&lt;/strong&gt; May 4, 2026&lt;/p&gt;
&lt;p&gt;Your privacy is important. This Privacy Policy explains how &lt;strong&gt;Time Zone Converter – Meeting Planner&lt;/strong&gt; (“the App”) handles information and data.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="overview"&gt;Overview
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Time Zone Converter – Meeting Planner does not collect, store, transmit, or share any personal data.&lt;/strong&gt;&lt;br&gt;
All functionality operates locally on the user’s device.&lt;/p&gt;
&lt;p&gt;The developer does not receive, access, or process user data in any form.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="data-collection"&gt;Data Collection
&lt;/h2&gt;&lt;p&gt;The App &lt;strong&gt;does not collect any data&lt;/strong&gt;, including but not limited to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Personal information (name, email address, identifiers)&lt;/li&gt;
&lt;li&gt;Location data&lt;/li&gt;
&lt;li&gt;Usage data or analytics&lt;/li&gt;
&lt;li&gt;Calendar data&lt;/li&gt;
&lt;li&gt;Diagnostic or crash data&lt;/li&gt;
&lt;li&gt;Device or system identifiers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All meeting times, locations, and scheduling details entered into the App remain on the user’s device and are never transmitted elsewhere.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="data-sharing"&gt;Data Sharing
&lt;/h2&gt;&lt;p&gt;The App &lt;strong&gt;does not share data&lt;/strong&gt; with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The developer&lt;/li&gt;
&lt;li&gt;Third‑party services&lt;/li&gt;
&lt;li&gt;Advertising networks&lt;/li&gt;
&lt;li&gt;Analytics providers&lt;/li&gt;
&lt;li&gt;Cloud services&lt;/li&gt;
&lt;li&gt;Partners or affiliates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are &lt;strong&gt;no server connections, trackers, or external APIs&lt;/strong&gt; used to collect or transmit user information.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="calendar-access"&gt;Calendar Access
&lt;/h2&gt;&lt;p&gt;If the user chooses to add a meeting to a calendar, this action occurs &lt;strong&gt;locally on the device&lt;/strong&gt; using the operating system’s calendar feature.&lt;/p&gt;
&lt;p&gt;The App does &lt;strong&gt;not&lt;/strong&gt; read, store, copy, or transmit calendar data.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="thirdparty-services"&gt;Third‑Party Services
&lt;/h2&gt;&lt;p&gt;The App does &lt;strong&gt;not&lt;/strong&gt; integrate with third‑party analytics, advertising SDKs, tracking tools, or data processors.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="data-security"&gt;Data Security
&lt;/h2&gt;&lt;p&gt;Because the App does not collect or store data, there is no user data retained by the developer to secure or manage.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="childrens-privacy"&gt;Children’s Privacy
&lt;/h2&gt;&lt;p&gt;The App does not collect any personal data and is not designed to gather information from users of any age.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="changes-to-this-privacy-policy"&gt;Changes to This Privacy Policy
&lt;/h2&gt;&lt;p&gt;If this Privacy Policy changes, the updated version will be made available with a revised effective date. Any future changes will continue to prioritize user privacy.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="contact"&gt;Contact
&lt;/h2&gt;&lt;p&gt;If you have questions or concerns about this Privacy Policy, you may contact the developer through the app’s official distribution platform.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt;&lt;br&gt;
✅ No data collected&lt;br&gt;
✅ No data shared&lt;br&gt;
✅ No tracking&lt;br&gt;
✅ No analytics&lt;br&gt;
✅ No cloud storage&lt;/p&gt;
&lt;p&gt;Your scheduling stays private—always.&lt;/p&gt;</description></item><item><title>MCP Servers and M365 (Part 4): Connect the Message Center MCP Server to a Declarative Agent (M365 Agents Toolkit)</title><link>https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/</link><pubDate>Tue, 06 Jan 2026 08:00:00 -0800</pubDate><guid>https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/</guid><description>&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/4.png" alt="Featured image of post MCP Servers and M365 (Part 4): Connect the Message Center MCP Server to a Declarative Agent (M365 Agents Toolkit)" /&gt;&lt;p&gt;In &lt;a class="link" href="https://mjfnet.com/p/mcp-servers-and-m365-part-3-implementing-obo-authentication-to-call-microsoft-graph-apis/" target="_blank" rel="noopener"
&gt;Part 3&lt;/a&gt;, we built an authenticated MCP server that calls &lt;strong&gt;Microsoft Graph&lt;/strong&gt; using &lt;strong&gt;On-Behalf-Of (OBO)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this post, you’ll connect that &lt;strong&gt;Message Center MCP server&lt;/strong&gt; to a &lt;strong&gt;Copilot declarative agent&lt;/strong&gt; that you build with the &lt;strong&gt;Microsoft 365 Agents Toolkit&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is the “enterprise path” integration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your declarative agent calls your MCP server.&lt;/li&gt;
&lt;li&gt;The agent authenticates the signed-in user (delegated OAuth).&lt;/li&gt;
&lt;li&gt;Your MCP server performs &lt;strong&gt;OBO&lt;/strong&gt; token exchangeto obtain a Microsoft Graph token.&lt;/li&gt;
&lt;li&gt;The MCP tool runs as the signed-in user and returns results.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="integrating-the-message-center-mcp-server"&gt;Integrating the Message Center MCP server
&lt;/h2&gt;&lt;p&gt;This server implements the MCP protocol and exposes a tool that retrieves Message Center posts from Microsoft Graph. It includes OBO (on-behalf-of) authentication so that it can call Graph on behalf of the signed-in user.&lt;/p&gt;
&lt;p&gt;Message Center MCP server repo (reference implementation used in this series):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/mjfusa/mcp-message-center-server" target="_blank" rel="noopener"
&gt;https://github.com/mjfusa/mcp-message-center-server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Key details (as described in Part 3):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Health: &lt;code&gt;GET /healthz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;MCP endpoint: &lt;code&gt;POST /mcp&lt;/code&gt; (JSON-RPC 2.0)&lt;/li&gt;
&lt;li&gt;Discovery: &lt;code&gt;GET /discover&lt;/code&gt; and &lt;code&gt;GET /.well-known/openid-configuration&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;OAuth proxy endpoints: &lt;code&gt;GET /authorize&lt;/code&gt; and &lt;code&gt;POST /token&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Tool: &lt;code&gt;getMessages&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Queries Microsoft Graph: &lt;code&gt;GET https://graph.microsoft.com/v1.0/admin/serviceAnnouncement/messages&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites
&lt;/h2&gt;&lt;p&gt;You’ll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Microsoft 365 Copilot available in your tenant (and access to test in &lt;a class="link" href="https://m365.cloud.microsoft/chat" target="_blank" rel="noopener"
&gt;https://m365.cloud.microsoft/chat&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Visual Studio Code&lt;/li&gt;
&lt;li&gt;Microsoft 365 Agents Toolkit extension installed in VS Code&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="alert alert-important"&gt;
&lt;p class="alert-heading"&gt;
ℹ️
Important
&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Message Center MCP server&lt;/strong&gt; must be running locally or hosted (Part 3 walks through local setup). This post assumes it is already running and reachable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;public HTTPS&lt;/strong&gt; URL for the MCP server
&lt;ul&gt;
&lt;li&gt;If running locally, use a tunneling service (Dev Tunnel, Cloudflare Tunnel, etc.) - more on that below&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A Microsoft Entra app registration configured for the Message Center MCP server (Part 3)&lt;/li&gt;
&lt;li&gt;A test user with the right Microsoft 365 role to read Message Center (for example, &lt;strong&gt;Message Center Reader&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="create-and-configure-an-entra-app-registration-for-the-mcp-server"&gt;Create and configure an Entra app registration for the MCP server
&lt;/h2&gt;&lt;p&gt;For the purposes of this blog post we only need the server Application registration. In the next part of the series we will integrate this server with a client and at that time we will register the client application registration.&lt;/p&gt;
&lt;h3 id="run-the-client-app-registration-script"&gt;Run the client app registration script:
&lt;/h3&gt;
&lt;blockquote class="alert alert-note"&gt;
&lt;p class="alert-heading"&gt;
ℹ️
Note
&lt;/p&gt;
&lt;p&gt;Before running this script, ensure you have created the server app registration as described in &lt;a class="link" href="https://mjfnet.com/p/mcp-servers-and-m365-part-3-implementing-obo-authentication-to-call-microsoft-graph-apis/" target="_blank" rel="noopener"
&gt;Part 3&lt;/a&gt; and have the Server App ClientId available.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pwsh&lt;/span&gt; &lt;span class="n"&gt;-NoProfile&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;CreateClientAppRegMCP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="n"&gt;-AppName&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;MCP Message Center Client&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;-ServerAppId&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Server-App&lt;/span&gt;&lt;span class="n"&gt;-ClientId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;-CreateSecret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Note the output values: ClientId, TenantId, ClientSecret. You&amp;rsquo;ll need them when configuring the agent in the Agents Toolkit.&lt;/p&gt;
&lt;p&gt;Minimum configuration recap (as completed in CreateClientAppRegMCP.ps1):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Authentication redirect URIs&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Add the redirect URI your Copilot Studio connection uses :&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect" target="_blank" rel="noopener"
&gt;https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;https://localhost:8080&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Copilot gets an &lt;strong&gt;access token&lt;/strong&gt; from this client app registration.&lt;/li&gt;
&lt;li&gt;The token audience is your MCP server API (example: &lt;code&gt;api://&amp;lt;server app reg clientId&amp;gt;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The MCP server uses OBO to exchange this token for a &lt;strong&gt;Graph token&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-2--decide-where-the-mcp-server-will-run"&gt;Step 2 — Decide where the MCP server will run
&lt;/h2&gt;&lt;p&gt;Microsoft 365 must be able to reach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://&amp;lt;your-host&amp;gt;/mcp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://&amp;lt;your-host&amp;gt;/authorize&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://&amp;lt;your-host&amp;gt;/token&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://&amp;lt;your-host&amp;gt;/.well-known/openid-configuration&lt;/code&gt; (or &lt;code&gt;/discover&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Two common options:&lt;/p&gt;
&lt;h3 id="option-a-local-server--tunnel-quick-dev"&gt;Option A: Local server + tunnel (quick dev)
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Start the server locally (default port in Part 3 examples is &lt;code&gt;8080&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Expose it with a tunnel.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Example with Dev Tunnel:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;winget&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;devtunnel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;devtunnel&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# assuming your server is listening on 8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;devtunnel&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="n"&gt;-p&lt;/span&gt; &lt;span class="mf"&gt;8080&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-allow-anonymous&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-protocol&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Also see, &lt;code&gt;scripts\dev-tunnel-persist.ps1&lt;/code&gt; in the server repo to create a persistent Dev Tunnel.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note the public HTTPS URL (example: &lt;code&gt;https://&amp;lt;something&amp;gt;.devtunnels.ms&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id="option-b-hosted-url-recommended-for-stable-testing--production"&gt;Option B: Hosted URL (recommended for stable testing / production)
&lt;/h3&gt;
&lt;blockquote class="alert alert-caution"&gt;
&lt;p class="alert-heading"&gt;
❗
The steps for hosting the server are outside the scope of this post. However they are documented in the server repo README.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="step-3--validate-the-server-with-curl"&gt;Step 3 — Validate the server with curl
&lt;/h2&gt;&lt;p&gt;Set a base URL:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Tunnel/hosted example&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$BaseUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://&amp;lt;your-local-tunnel-host&amp;gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="3a-health-check"&gt;3a) Health check
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-sS&lt;/span&gt; &lt;span class="n"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BaseUrl&lt;/span&gt;&lt;span class="s2"&gt;/healthz&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You want &lt;code&gt;200 OK&lt;/code&gt; and &lt;strong&gt;{&amp;ldquo;ok&amp;rdquo;:true}&lt;/strong&gt; responses.&lt;/p&gt;
&lt;p&gt;If that succeeds, your server is reachable via a public URL.&lt;/p&gt;
&lt;h2 id="step-4--create-the-declarative-agent-project-microsoft-365-agents-toolkit"&gt;Step 4 — Create the declarative agent project (Microsoft 365 Agents Toolkit)
&lt;/h2&gt;&lt;p&gt;This post assumes you’re using Microsoft 365 Agents Toolkit to build and provision a declarative agent.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In Visual Studio Code, open the &lt;strong&gt;Microsoft 365 Agents Toolkit&lt;/strong&gt; extension.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Create a New Agent/App&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Declarative Agent&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choose the following starting point:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Add an action&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;&amp;lsquo;Start with an MCP Server Preview&amp;rsquo;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enter the your local-tunnel/hosted MCP server URL with &amp;lsquo;/mcp&amp;rsquo;. (for example: &lt;code&gt;https://&amp;lt;your-host&amp;gt;/mcp&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Select a destination folder for your project.&lt;/li&gt;
&lt;li&gt;Enter a project name (for example: &lt;code&gt;Message Center Agent - MCP&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;A new instance of VS Code will open with your new project.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start="9"&gt;
&lt;li&gt;Name the app (for example: &lt;code&gt;Message Center Assistant&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;When the new project opens, locate your app manifest file (commonly &lt;code&gt;appPackage/manifest.json&lt;/code&gt; in Agents Toolkit projects).&lt;br&gt;
You see the following prompts from the toolkit regarding the MCP server:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/VisualStudioPrompts.png"
width="523"
height="509"
srcset="https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/VisualStudioPrompts_hu_d0629ae642f2a188.png 480w, https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/VisualStudioPrompts_hu_75a50a2ff1d0307c.png 1024w"
loading="lazy"
alt="Visual Studio Prompts for MCP"
class="gallery-image"
data-flex-grow="102"
data-flex-basis="246px"
&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Click &amp;lsquo;&lt;strong&gt;Confirm&lt;/strong&gt;&amp;rsquo; to add the Copilot instructions for the Agents Toolkit MCP server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &amp;lsquo;&lt;strong&gt;Fetch Action&lt;/strong&gt;&amp;rsquo; to fetch the tools from your MCP server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select the first option, showing the URL to the MCP server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you get an error, in &lt;code&gt;mcp.json&lt;/code&gt;, click &amp;lsquo;Start&amp;rsquo; the connect with the MCP server.&lt;br&gt;
&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/StartMCPServer.png"
width="624"
height="438"
srcset="https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/StartMCPServer_hu_d1913aa4bca14722.png 480w, https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/StartMCPServer_hu_8f88549696932465.png 1024w"
loading="lazy"
alt="Start MCP Server Connection"
class="gallery-image"
data-flex-grow="142"
data-flex-basis="341px"
&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then click &lt;strong&gt;&amp;ldquo;ATK: Fetch Actions from MCP Server&amp;rdquo;&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At the prompt &lt;strong&gt;&amp;lsquo;Select the action manifest you want to update&amp;rsquo;&lt;/strong&gt;, Click on the &lt;strong&gt;&amp;lsquo;ai-plugin.json&amp;rsquo;&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on &amp;lsquo;getMessages&amp;rsquo; to add the Message Center tool to your agent.
&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/getMessages.png"
width="1124"
height="339"
srcset="https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/getMessages_hu_57c8bca5b77c9ab7.png 480w, https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/getMessages_hu_61bd1d9485dafc0.png 1024w"
loading="lazy"
alt="getMessages Tool"
class="gallery-image"
data-flex-grow="331"
data-flex-basis="795px"
&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &amp;lsquo;OK&amp;rsquo; to confirm adding the tool.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do not click on &lt;strong&gt;&amp;lsquo;Provision&amp;rsquo;&lt;/strong&gt; yet.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At this point, you have a declarative agent project with the Message Center MCP server tool added. However, the agent can’t connect to the MCP server yet because authentication isn’t configured. This requires registering the client app registration with the Teams Developer portal. This provides a opaque ID that will be used to connect the agent to the MCP server using OAuth. This step stores the client app ID and client secret In the Teams developer portal. This provides a secure a way to secure the client app ID and secret.&lt;/p&gt;
&lt;h2 id="step-5--register-the-agent-in-the-teams-developer-portal"&gt;Step 5 — Register the agent in the Teams Developer Portal
&lt;/h2&gt;&lt;p&gt;For this step you&amp;rsquo;ll the client ID and client secret created above. You&amp;rsquo;ll also need the server app registration ID created in Part 3.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open the &lt;a class="link" href="https://dev.teams.cloud.microsoft/home" target="_blank" rel="noopener"
&gt;Teams Developer Portal&lt;/a&gt;.
Roles required: Teams app develop or Teams Administrator or Global Administrator.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;Tools&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;&amp;lsquo;OAuth client Registration&amp;rsquo;&lt;/strong&gt;. You may be prompted to select the previous version of the portal.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;click &amp;lsquo;&lt;strong&gt;New OAuth client registration&amp;rsquo;.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill in the following fields:&lt;br&gt;
&lt;strong&gt;Registration name&lt;/strong&gt; Any descriptive name (internal only).&lt;br&gt;
Example: &lt;code&gt;OAuth-MessageCenterMCP&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Base URL&lt;/strong&gt; The MCP Server Endpoint URL:&lt;br&gt;
&lt;strong&gt;https://&amp;laquo;your-host&amp;raquo;/mcp/&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Restrict usage by org&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;My organization only&lt;/strong&gt; → internal use&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Any Microsoft 365 organization&lt;/strong&gt; → multi-tenant (this setting for testing)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Restrict usage by app&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Any Teams app (testing/validation)&lt;/strong&gt; for development&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Existing Teams app&lt;/strong&gt; if binding to a specific App ID&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OAuth Settings&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Client ID&lt;/strong&gt; Client ID for agent obtained above&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Client secret&lt;/strong&gt; Client secret for agent obtained above&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authorization endpoint&lt;/strong&gt;
&lt;a class="link" href="https://login.microsoftonline.com/%7btenantID%7d/oauth2/v2.0/authorize" target="_blank" rel="noopener"
&gt;https://login.microsoftonline.com/{tenantID}/oauth2/v2.0/authorize&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Token endpoint&lt;/strong&gt;
&lt;a class="link" href="https://login.microsoftonline.com/%7btenantID%7d/oauth2/v2.0/token" target="_blank" rel="noopener"
&gt;https://login.microsoftonline.com/{tenantID}/oauth2/v2.0/token&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scope&lt;/strong&gt;
Use the exact scope your API exposes:
&lt;strong&gt;api://&amp;laquo;server app reg clientId&amp;raquo;/access_as_user&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PKCE&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep &lt;strong&gt;ON&lt;/strong&gt; for Authorization Code flow (recommended).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Review all settings and Click &lt;strong&gt;Save&lt;/strong&gt; to create the OAuth client registration.&lt;/p&gt;
&lt;p&gt;Copy the generated &lt;strong&gt;OAuth Client registration ID&lt;/strong&gt; (found at the top of the form). You’ll need it in the next step.&lt;/p&gt;
&lt;h2 id="step-6--update-the-plugin-manifest-to-connect-to-the-mcp-server"&gt;Step 6 — Update the plugin manifest to connect to the MCP server
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Return the to Visual Studio Code.&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;ai-plugin.json&lt;/code&gt; in the &lt;code&gt;appPackage&lt;/code&gt; folder of your project.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Change the follow block from this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;runtimes&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;RemoteMCPServer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;spec&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://&amp;lt;&amp;lt;your-host&amp;gt;&amp;gt;/mcp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;mcp_tool_description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;file&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;mcp-tools.json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;run_for_functions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;getMessages&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;To this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;runtimes&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;RemoteMCPServer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;auth&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;OAuthPluginVault&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;reference_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;${{OAUTH2_REGISTRATION_ID}}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;spec&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://&amp;lt;&amp;lt;your-host&amp;gt;&amp;gt;/mcp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;run_for_functions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;getMessages&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Note the addition of the &lt;code&gt;auth&lt;/code&gt; block with the &lt;code&gt;reference_id&lt;/code&gt; property. Also, the &amp;ldquo;mcp_tool_description&amp;rdquo; has been removed. Make sure you update the &amp;lsquo;spec&amp;rsquo;, &amp;lsquo;url&amp;rsquo; value to your MCP server endpoint. Update &lt;strong&gt;.env.dev&lt;/strong&gt; or &lt;strong&gt;.env.production&lt;/strong&gt; with this variable &lt;code&gt;OAUTH2_REGISTRATION_ID&lt;/code&gt; with the actual OAuth Client registration ID obtained from the Teams Developer Portal in the previous step.
&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/oauthreg.png"
width="745"
height="278"
srcset="https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/oauthreg_hu_f207989af171b388.png 480w, https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/oauthreg_hu_783ea311fb632894.png 1024w"
loading="lazy"
alt="OAuth Reference ID"
class="gallery-image"
data-flex-grow="267"
data-flex-basis="643px"
&gt;&lt;/p&gt;
&lt;h2 id="step-5--add-the-function-definition-to-ai-pluginjson"&gt;Step 5 — Add the function definition to ai-plugin.json
&lt;/h2&gt;&lt;p&gt;Currently the function definition for &lt;code&gt;getMessages&lt;/code&gt; is missing from &lt;code&gt;ai-plugin.json&lt;/code&gt;. You need to add it so the agent knows how to call the tool. It is expect this will be added automatically in a future version of the Agents Toolkit.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;ai-plugin.json&lt;/code&gt; in the &lt;code&gt;appPackage&lt;/code&gt; folder of your project.&lt;/li&gt;
&lt;li&gt;Locate the &lt;code&gt;functions&lt;/code&gt; array.&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;getMessages&lt;/code&gt; function definition as follows:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;functions&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;getMessages&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Retrieve Microsoft 365 Message Center messages from Microsoft Graph (admin/serviceAnnouncement/messages).&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;parameters&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;orderby&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;lastModifiedDateTime desc&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Order by clause for sorting results. Defaults to newest messages first (lastModifiedDateTime desc)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;count&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;boolean&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Total number of records returned&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;prefer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;odata.maxpagesize=5&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Specifies the maximum number of items to return in a result set&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;top&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;integer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Number of records to return. Set to 0 to retrieve only the count (@odata.count) without any message data. Useful for count-only queries like &amp;#39;How many messages are there?&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;skip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;integer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Number of records to skip&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;filter&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Filter the results based on specific conditions. For count-only queries, combine with $top=0 to get just the total number of matching records. Use `contains(tolower(title),tolower(&amp;#39;search term&amp;#39;))` to search within the title property for a specific term or phrase. For multi-word searches, include the entire phrase in quotes. For create time, use the &amp;#39;startDateTime&amp;#39; property with a date format. For example, `startDateTime ge 2025-03-01T00:00:00Z` to filter messages created after March 1, 2025. For major change messages, use `isMajorChange eq true`. To combine filters, use `and` operator. For example, `contains(tolower(title),tolower(&amp;#39;Copilot agent&amp;#39;)) and startDateTime ge 2025-03-01T00:00:00Z`. For the &amp;#39;Tags&amp;#39; field, here is an example of the a query segment for the tags field: tags/any(t: t eq &amp;#39;New feature&amp;#39;). For the &amp;#39;severity&amp;#39; field, you can filter by severity using `severity eq &amp;#39;normal&amp;#39;` or `severity eq &amp;#39;high&amp;#39;` or `severity eq &amp;#39;critical&amp;#39;`. For the &amp;#39;category&amp;#39; field, you can filter by category using `category eq &amp;#39;stayInformed&amp;#39;` or `category eq &amp;#39;planForChange&amp;#39;` or `category eq &amp;#39;preventOrFixIssue&amp;#39;`. For the &amp;#39;Services&amp;#39; field, here is an example of a query segment for the services field: services/any(s: contains(s, &amp;#39;Copilot&amp;#39;)). When searching an message center update for roadmap ids, these are found in the &amp;#39;details&amp;#39; field. Here is an example of a query segment for the details field, when searching for RoadmapIds: details/any(d: d/name eq &amp;#39;RoadmapIds&amp;#39; and contains(d/value, &amp;#39;501107&amp;#39;)). Note the &amp;#39;name&amp;#39; property is used to specify the type of detail being searched, in this case &amp;#39;RoadmapIds&amp;#39;. The &amp;#39;value&amp;#39; property is then used to search for the specific roadmap id. RoadmapIds is case sensitive and must be used exactly as shown. To filter by message body, use `contains(tolower(body/content),tolower(&amp;#39;search term&amp;#39;))` to search within the body content.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;accessToken&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Optional: Graph access token for proxying requests. Use only for local testing; OAuth flow will replace this after browser sign-in.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;required&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="step-6--provision-and-verify-tool-discovery"&gt;Step 6 — Provision and verify tool discovery
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Save your manifest changes.&lt;/li&gt;
&lt;li&gt;In Agents Toolkit, select &lt;strong&gt;Provision&lt;/strong&gt; again.&lt;/li&gt;
&lt;li&gt;Open &lt;a class="link" href="https://m365.cloud.microsoft/chat" target="_blank" rel="noopener"
&gt;https://m365.cloud.microsoft/chat&lt;/a&gt;, select your agent, and start a new chat.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;What you’re looking for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;During the first run, you should see a consent/connection experience if OAuth is configured.&lt;/li&gt;
&lt;li&gt;Your MCP server should receive MCP handshake traffic (for example &lt;code&gt;initialize&lt;/code&gt;, then &lt;code&gt;tools/list&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;When you ask a Message Center question, your server should receive a &lt;code&gt;tools/call&lt;/code&gt; for &lt;code&gt;getMessages&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-7--test-the-agent-end-to-end"&gt;Step 7 — Test the agent end-to-end
&lt;/h2&gt;&lt;p&gt;In Copilot chat (with your declarative agent selected), try:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;“What changed for Copilot since yesterday?”&lt;/li&gt;
&lt;li&gt;“Find messages related to Microsoft 365 Copilot published in the last 30 days.”&lt;/li&gt;
&lt;li&gt;“Show message regarding updates from the last month that are related to Copilot and marked as major changes.”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Expected behavior:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The agent prompts the user to login.&lt;/li&gt;
&lt;li&gt;The agent calls &lt;code&gt;getMessages&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The MCP server performs OBO and calls Graph and validates the user has Message Center access.&lt;/li&gt;
&lt;li&gt;The agent summarizes results and includes IDs/links when available.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="provide-instructions-to-the-agent"&gt;Provide instructions to the agent
&lt;/h2&gt;&lt;p&gt;To improve the agent’s behavior, you can provide additional instructions the instruction.txt file in your project. For example, you might add:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;When&lt;/span&gt; &lt;span class="n"&gt;answering&lt;/span&gt; &lt;span class="n"&gt;questions&lt;/span&gt; &lt;span class="n"&gt;about&lt;/span&gt; &lt;span class="n"&gt;Microsoft&lt;/span&gt; &lt;span class="mi"&gt;365&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;always&lt;/span&gt; &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;getMessages&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;tool&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;retrieve&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;latest&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="n"&gt;guidelines&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="n"&gt;calling&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="k"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Always&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;top&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="n"&gt;returned&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;orderby&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;lastModifiedDateTime desc&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;If&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;asks&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;top&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;@odata.count&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;When&lt;/span&gt; &lt;span class="n"&gt;filtering&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;filter&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;parameter&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;narrow&lt;/span&gt; &lt;span class="n"&gt;down&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="n"&gt;based&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;criteria&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;See the project &lt;a class="link" href="https://github.com/mjfusa/message-center-agent-mcp" target="_blank" rel="noopener"
&gt;Message Center Agent - MCP&lt;/a&gt; for an example of a completed project, including updated instructions, ai-plugin.json, and starter prompts.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting
&lt;/h2&gt;&lt;h3 id="developer-mode"&gt;Developer Mode
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;In Copilot chat, enable &lt;strong&gt;Developer Mode&lt;/strong&gt; with the following prompt:&lt;br&gt;
&lt;strong&gt;-developer on&lt;/strong&gt;&lt;br&gt;
Develop remote is useful in that it gives you a preview of the composed Odata query that will be called against the Microsoft Graph. For example:&lt;br&gt;
&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/developermodeodataquery.png"
width="880"
height="484"
srcset="https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/developermodeodataquery_hu_980d12057b38b06d.png 480w, https://mjfnet.com/p/mcp-servers-and-m365-part-4-connect-the-message-center-mcp-server-to-a-declarative-agent-m365-agents-toolkit/images/developermodeodataquery_hu_15a2905ecdb5ce66.png 1024w"
loading="lazy"
alt="Developer Mode Odata Query"
class="gallery-image"
data-flex-grow="181"
data-flex-basis="436px"
&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Clicking on &lt;strong&gt;&amp;lsquo;Agent debug info&amp;rsquo;&lt;/strong&gt; shows the full request/response payloads exchanged between Copilot Studio and your MCP server. This is useful for debugging issues.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;You now have a declarative agent (built with Microsoft 365 Agents Toolkit) calling an authenticated MCP server end-to-end:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The agent obtains an MCP API user token&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;POST /mcp&lt;/code&gt; with &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;MCP server exchanges via OBO and calls Microsoft Graph&lt;/li&gt;
&lt;li&gt;Returns Message Center results as tool output&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In Part 5 of this series, we’ll create a custom agent in Copilot Studio that uses this MCP server to provide Message Center insights.&lt;/p&gt;</description></item><item><title>MCP Servers and M365 (Part 3): Implementing OBO Authentication to Call Microsoft Graph APIs</title><link>https://mjfnet.com/p/mcp-servers-and-m365-part-3-implementing-obo-authentication-to-call-microsoft-graph-apis/</link><pubDate>Tue, 06 Jan 2026 07:00:00 -0800</pubDate><guid>https://mjfnet.com/p/mcp-servers-and-m365-part-3-implementing-obo-authentication-to-call-microsoft-graph-apis/</guid><description>&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-3-implementing-obo-authentication-to-call-microsoft-graph-apis/images/3.png" alt="Featured image of post MCP Servers and M365 (Part 3): Implementing OBO Authentication to Call Microsoft Graph APIs" /&gt;&lt;p&gt;In part 1 we intentionally avoided authentication and focused on learning MCP fundamentals. In part 2 we wired a no-auth MCP server into a Copilot Studio agent.&lt;/p&gt;
&lt;p&gt;Part 3 is where things get “real enterprise”: the MCP server is no longer calling a public API. It’s calling &lt;strong&gt;Microsoft Graph&lt;/strong&gt;, and Graph requires &lt;strong&gt;user-delegated authorization&lt;/strong&gt; to retrieve Message Center messages via the Microsoft Graph API &lt;a class="link" href="https://learn.microsoft.com/en-us/graph/api/serviceannouncement-list-messages" target="_blank" rel="noopener"
&gt;/admin/serviceAnnouncement/messages&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That combination drives a specific OAuth pattern:&lt;/p&gt;
&lt;p&gt;In this post, &lt;strong&gt;“your MCP API”&lt;/strong&gt; means the protected API surface of &lt;em&gt;your MCP server&lt;/em&gt; (specifically &lt;code&gt;POST /mcp&lt;/code&gt;), represented by a Microsoft Entra &lt;strong&gt;app registration&lt;/strong&gt; that you “Expose an API” for. Practically, it’s the resource clients request a token for, typically with an audience like &lt;code&gt;api://&amp;lt;your-app-client-id&amp;gt;&lt;/code&gt; for your server.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;client&lt;/strong&gt; (an agent or script) sends a user token meant for &lt;em&gt;your MCP API&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;server&lt;/strong&gt; exchanges that token for a &lt;strong&gt;Microsoft Graph delegated access token&lt;/strong&gt; using &lt;strong&gt;On-Behalf-Of (OBO)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The server calls Graph and returns results as MCP tool output.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’ll use the &lt;a class="link" href="https://github.com/mjfusa/mcp-message-center-server" target="_blank" rel="noopener"
&gt;mcp-message-center-server&lt;/a&gt; repo as the reference model for the discussion.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; If you just want the hands-on path, jump to &lt;a class="link" href="#try-it-yourself" &gt;&lt;strong&gt;Try it yourself&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="why-obo-matters-for-mcp-servers"&gt;Why OBO matters for MCP servers
&lt;/h2&gt;&lt;p&gt;If your MCP server calls Microsoft Graph with &lt;strong&gt;delegated permissions&lt;/strong&gt;, it’s acting as a &lt;strong&gt;middle-tier service&lt;/strong&gt; in a multi-hop authentication chain.&lt;/p&gt;
&lt;p&gt;Here’s the key constraint that forces OBO:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When a client calls your MCP server, it presents an access token representing the user.&lt;/li&gt;
&lt;li&gt;That incoming token is scoped for &lt;strong&gt;your MCP API&lt;/strong&gt; (audience = your resource, e.g., &lt;code&gt;api://&amp;lt;clientId&amp;gt;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OAuth2 access tokens are not reusable across different resource servers&lt;/strong&gt;.
&lt;ul&gt;
&lt;li&gt;A token minted for your MCP API cannot be sent to Microsoft Graph, because Graph will reject it as the wrong audience.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;strong&gt;On-Behalf-Of (OBO)&lt;/strong&gt; flow solves this by letting the MCP server exchange the incoming user token for a &lt;em&gt;new&lt;/em&gt; access token:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The new token is scoped for &lt;strong&gt;Microsoft Graph&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The call still happens &lt;strong&gt;as the original user&lt;/strong&gt;, preserving user identity and delegated permissions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That creates the trust chain you want in an enterprise MCP deployment:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The user authorizes the client.&lt;/li&gt;
&lt;li&gt;The client calls the MCP server on behalf of the user.&lt;/li&gt;
&lt;li&gt;The MCP server calls Microsoft Graph on behalf of that same user.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Without OBO, you’re pushed into two architectural dead ends:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Server uses app-only auth to Graph&lt;/strong&gt;: you lose user context (and often break least-privilege expectations for admin data).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client obtains Graph tokens directly&lt;/strong&gt;: you break the middle-tier pattern and move Graph complexity (and token handling risk) into every client. This works for calling Graph directly, but the MCP server cannot validate that token as authentication because the token&amp;rsquo;s audience is Graph, not the MCP server. In the reference server, Graph tokens presented as the caller credential are &lt;strong&gt;rejected by default&lt;/strong&gt; (confused-deputy guardrail) unless you explicitly opt in for local testing. This prevents a “confused deputy” scenario where a token meant for one service (Graph) is misused to access another (MCP server).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OBO is what preserves user identity across service boundaries while still respecting the OAuth principle that tokens are valid only for their intended resource.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Client gets token scoped for MCP server (audience = MCP server)&lt;/li&gt;
&lt;li&gt;MCP server validates that token (proving the client is authorized to call it)&lt;/li&gt;
&lt;li&gt;MCP server exchanges it via OBO for a Graph token (audience = Graph)&lt;/li&gt;
&lt;li&gt;Maintains security boundaries while preserving user identity&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="microsoft-message-center-mcp-server"&gt;Microsoft Message Center MCP server
&lt;/h2&gt;&lt;p&gt;This server &lt;a class="link" href="https://github.com/mjfusa/mcp-message-center-server" target="_blank" rel="noopener"
&gt;mcp-message-center-server&lt;/a&gt; exposes one tool:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;getMessages&lt;/code&gt;: queries Microsoft Admin Center Message Center messages via Microsoft Graph: &lt;code&gt;/admin/serviceAnnouncement/messages&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It also exposes a few key HTTP endpoints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GET /healthz&lt;/code&gt; (health)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /mcp&lt;/code&gt; (MCP JSON-RPC endpoint)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /discover&lt;/code&gt; (and &lt;code&gt;GET /.well-known/openid-configuration&lt;/code&gt;) for discovery&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/authorize&lt;/code&gt; + &lt;code&gt;/token&lt;/code&gt; proxy endpoints (used by interactive and &lt;strong&gt;PKCE&lt;/strong&gt; flows)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="alert alert-note"&gt;
&lt;p class="alert-heading"&gt;
ℹ️
&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PKCE&lt;/strong&gt; (Proof Key for Code Exchange) is an OAuth 2.0 protection for the authorization-code flow, designed for “public clients” that &lt;strong&gt;can’t safely store a client secret&lt;/strong&gt; (like local tools, VS Code extensions, or scripts running on a dev machine). In PKCE, the client generates a one-time &lt;strong&gt;code verifier&lt;/strong&gt; and sends only a derived &lt;strong&gt;code challenge&lt;/strong&gt; during &lt;code&gt;/authorize&lt;/code&gt;. Later, when exchanging the auth code at &lt;code&gt;/token&lt;/code&gt;, the client must present the original verifier. If an attacker steals the auth code from the redirect, they still can’t redeem it without the verifier. In this project, PKCE shows up when a client uses the server’s &lt;code&gt;/authorize&lt;/code&gt; + &lt;code&gt;/token&lt;/code&gt; &lt;strong&gt;proxy endpoints&lt;/strong&gt; (for example, the repo’s PKCE smoke test). It is &lt;strong&gt;not&lt;/strong&gt; required for the normal OBO path where the client calls &lt;code&gt;POST /mcp&lt;/code&gt; with &lt;code&gt;Authorization: Bearer &amp;lt;MCP API token&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="step-1--understanding-the-authentication-token-types"&gt;Step 1 — Understanding the authentication token types
&lt;/h2&gt;&lt;p&gt;The server model makes a clear distinction between two token types:&lt;/p&gt;
&lt;h3 id="mcp-api-user-token-what-the-client-sends"&gt;MCP API user token (what the client sends)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Audience:&lt;/strong&gt; your MCP server API (example: &lt;code&gt;api://&amp;lt;clientId&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Where used:&lt;/strong&gt; sent by the client to &lt;code&gt;POST /mcp&lt;/code&gt; in &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; proves the user to &lt;em&gt;your server&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="microsoft-graph-access-token-what-the-server-uses"&gt;Microsoft Graph access token (what the server uses)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Audience:&lt;/strong&gt; Microsoft Graph&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Where used:&lt;/strong&gt; sent by the MCP server to Microsoft Graph&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; authorizes the server to call Graph &lt;strong&gt;as the user&lt;/strong&gt; (delegated)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you only remember one thing:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Clients should authenticate to the MCP server.
The MCP server should authenticate to Graph.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="step-2--the-obo-flow-whats-actually-happening"&gt;Step 2 — The OBO flow (what’s actually happening)
&lt;/h2&gt;&lt;p&gt;In OAuth terms, OBO is a token exchange:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The client gets a user token for the MCP API (audience = your MCP API).&lt;/li&gt;
&lt;li&gt;The client calls &lt;code&gt;POST /mcp&lt;/code&gt; with that bearer token.&lt;/li&gt;
&lt;li&gt;The server validates the inbound token, then calls Entra to exchange it for a Graph token.&lt;/li&gt;
&lt;li&gt;The server calls Graph with the exchanged token.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At a high level, the runtime flow looks like:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;POST /mcp&lt;/code&gt; receives MCP JSON-RPC&lt;/li&gt;
&lt;li&gt;Server checks &lt;code&gt;Authorization: Bearer &amp;lt;MCP API token&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Server performs OBO to get Graph token&lt;/li&gt;
&lt;li&gt;Server calls &lt;code&gt;GET https://graph.microsoft.com/v1.0/admin/serviceAnnouncement/messages?...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Server returns tool output back to the MCP client&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="why-this-is-the-right-pattern-for-agents"&gt;Why this is the right pattern for agents
&lt;/h3&gt;&lt;p&gt;This is a good match for agent ecosystems because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The agent host (client) can be “thin” on Graph knowledge.&lt;/li&gt;
&lt;li&gt;Authorization remains user-delegated, so Graph enforces user role requirements (e.g., Message Center Reader).&lt;/li&gt;
&lt;li&gt;You can implement centralized controls (logging, throttling, input validation, safe defaults) at the MCP server.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-3--app-registration-models-single-app-vs-two-app"&gt;Step 3 — App registration models (single-app vs two-app)
&lt;/h2&gt;&lt;p&gt;The reference repo supports two common setups:&lt;/p&gt;
&lt;h3 id="option-a-simplest-single-app-registration-one-app-two-roles"&gt;Option A (simplest): single app registration (one app, two roles)
&lt;/h3&gt;&lt;p&gt;In this model, a &lt;strong&gt;single Microsoft Entra app registration&lt;/strong&gt; serves as both:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The MCP API resource&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The caller obtains a user token whose audience is your MCP API.&lt;/li&gt;
&lt;li&gt;Common convention: set Application ID URI to &lt;code&gt;api://&amp;lt;clientId&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The confidential client for OBO&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The server authenticates as the app to Entra to perform OBO.&lt;/li&gt;
&lt;li&gt;For local/dev, this can be a client secret.&lt;/li&gt;
&lt;li&gt;For production, prefer certificate-based auth (see Key Vault section).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This “single app” model reduces moving parts and is great for learning.&lt;/p&gt;
&lt;h3 id="option-b-recommended-for-agents-two-app-registrations-client-app--serverapi-app"&gt;Option B (recommended for agents): two app registrations (client app + server/API app)
&lt;/h3&gt;&lt;p&gt;For many agent scenarios (including declarative agents), it’s cleaner to separate responsibilities:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Server/API app registration (resource)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Exposes the MCP API scope (commonly &lt;code&gt;access_as_user&lt;/code&gt;) and represents &lt;code&gt;POST /mcp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The inbound bearer token audience is your MCP API (for example, &lt;code&gt;api://&amp;lt;server-app-id&amp;gt;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Uses delegated permissions to Microsoft Graph (e.g., &lt;code&gt;ServiceMessage.Read.All&lt;/code&gt;) - validating user roles for Message Center access.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Client app registration (OAuth client)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Used for interactive login/PKCE and represents the client identity.&lt;/li&gt;
&lt;li&gt;Requests tokens for the server/API scope (for example, &lt;code&gt;api://&amp;lt;server-app-id&amp;gt;/access_as_user&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;a class="link" href="https://github.com/mjfusa/mcp-message-center-server" target="_blank" rel="noopener"
&gt;repo&lt;/a&gt; includes PowerShell automation to create/configure both app registrations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;scripts/CreateServerAppRegMCP.ps1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scripts/CreateClientAppRegMCP.ps1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-4--why-proxy-authorize--token-endpoints-exist"&gt;Step 4 — Why proxy &lt;code&gt;/authorize&lt;/code&gt; + &lt;code&gt;/token&lt;/code&gt; endpoints exist
&lt;/h2&gt;&lt;p&gt;MCP clients are not all the same. Some can do interactive OAuth directly against Entra, some can’t. Some need PKCE. Some need a consistent “discovery + authorization” surface.&lt;/p&gt;
&lt;p&gt;The Message Center MCP server provides &lt;code&gt;/authorize&lt;/code&gt; and &lt;code&gt;/token&lt;/code&gt; endpoints that act as a &lt;strong&gt;controlled OAuth proxy&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They forward to Entra for the same app registration used by the server.&lt;/li&gt;
&lt;li&gt;They allow tooling and scripts to run standard OAuth flows (especially PKCE) without every client needing bespoke Entra wiring.&lt;/li&gt;
&lt;li&gt;They let the server enforce “safe defaults” and guardrails.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="redirect-safety-allowlist-is-not-optional"&gt;Redirect safety: allowlist is not optional
&lt;/h3&gt;&lt;p&gt;An OAuth proxy can become dangerous if it accepts arbitrary &lt;code&gt;redirect_uri&lt;/code&gt; values.&lt;/p&gt;
&lt;p&gt;That’s why the model server enforces a &lt;strong&gt;redirect URI allowlist&lt;/strong&gt; (prefix-based) via &lt;code&gt;MCP_OAUTH_REDIRECT_URI_PREFIXES&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Defaults include common safe cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VS Code loopback (&lt;code&gt;http://127.0.0.1&lt;/code&gt;, &lt;code&gt;http://localhost&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Copilot / Teams declarative agent redirect (&lt;code&gt;https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your client uses a different redirect URI, you must explicitly allow it.&lt;/p&gt;
&lt;h2 id="try-it-yourself"&gt;Try it yourself
&lt;/h2&gt;&lt;p&gt;This is the PowerShell-first path that validates:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;the server is running&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/authorize&lt;/code&gt; + &lt;code&gt;/token&lt;/code&gt; proxy works with PKCE&lt;/li&gt;
&lt;li&gt;the end-to-end OBO path works (MCP API token → Graph token → Message Center)&lt;/li&gt;
&lt;li&gt;bypass paths work (and how to disable them in production)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="prerequisites"&gt;Prerequisites
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Node.js 20+&lt;/li&gt;
&lt;li&gt;PowerShell 7+ (&lt;code&gt;pwsh&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Azure CLI (&lt;code&gt;az&lt;/code&gt;) authenticated (&lt;code&gt;az login&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A Microsoft Entra app registration configured as described below&lt;/li&gt;
&lt;li&gt;The signed-in user must have the right Microsoft 365 role for Message Center access (e.g., Message Center Reader)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="create-and-configure-an-entra-app-registration-for-the-mcp-server"&gt;Create and configure an Entra app registration for the MCP server
&lt;/h3&gt;&lt;p&gt;For the purposes of this blog post we only need the server Application registration. In the next part of the series we will integrate this server with a client and at that time we will register the client application registration.&lt;/p&gt;
&lt;p&gt;Follow Option B to create two app registrations one for the client and the other for the server/API.&lt;/p&gt;
&lt;h3 id="run-the-server-app-registration-script"&gt;Run the server app registration script:
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pwsh&lt;/span&gt; &lt;span class="n"&gt;-NoProfile&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;CreateServerAppRegMCP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="n"&gt;-AppName&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;MCP Message Center Server&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Note the output values: ClientId, TenantId, ClientSecret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="clone-the-repo-and-run-the-server"&gt;Clone the repo and run the server
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;clone&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mjfusa&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;mcp-message&lt;/span&gt;&lt;span class="n"&gt;-center-server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Install dependencies (run inside the server folder)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd mcp-message&lt;/span&gt;&lt;span class="n"&gt;-center-server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;npm&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Create local env file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Copy-Item&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;sample&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;local&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;notepad&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;local&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Minimum &lt;code&gt;.env.local&lt;/code&gt; values for local dev OBO:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GRAPH_TENANT_ID=&amp;lt;your-tenant-guid&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GRAPH_CLIENT_ID=&amp;lt;your-app-client-id-guid&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GRAPH_CLIENT_SECRET=&amp;lt;client-secret&amp;gt;&lt;/code&gt; (local/dev simplest)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then run the server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;npm&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;In a new terminal, verify health:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-sS&lt;/span&gt; &lt;span class="n"&gt;-i&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;healthz&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="entra-app-registration---mcp-obo-server-configuration"&gt;Entra app registration - MCP OBO Server configuration
&lt;/h3&gt;&lt;p&gt;The following are the details regarding the server side app registration. This is all completed by the &lt;code&gt;CreateServerAppRegMCP.ps1&lt;/code&gt; script.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Expose an API (defines “your MCP API”)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set the &lt;strong&gt;Application ID URI&lt;/strong&gt; (recommended): &lt;code&gt;api://&amp;lt;clientId&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add a delegated scope so clients can request a token for your MCP API:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Scope name&lt;/strong&gt;: &lt;code&gt;access_as_user&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Who can consent?&lt;/strong&gt;: Admins and users&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Admin consent display name&lt;/strong&gt;: &lt;code&gt;Access MCP server as signed-in user&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Admin consent description&lt;/strong&gt;: &lt;code&gt;Allows the app to call the MCP server API as the signed-in user.&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User consent display name&lt;/strong&gt;: &lt;code&gt;Access MCP server as signed-in user&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User consent description&lt;/strong&gt;: &lt;code&gt;Allows the app to call the MCP server API as the signed-in user.&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Why this matters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your scripts/clients need &lt;em&gt;some&lt;/em&gt; delegated scope to request on your MCP API.&lt;/li&gt;
&lt;li&gt;In current Azure CLI usage, request a token for the specific scope using &lt;code&gt;--scope api://&amp;lt;clientId&amp;gt;/access_as_user&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Microsoft Graph delegated permissions (what OBO will request)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API permissions → Microsoft Graph → &lt;strong&gt;Delegated&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;ServiceMessage.Read.All&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Grant admin consent&lt;/strong&gt; (common cause of OBO failure if skipped).&lt;/li&gt;
&lt;li&gt;Note: Graph will still enforce the &lt;em&gt;user’s&lt;/em&gt; Microsoft 365 role (e.g., Message Center Reader).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Authentication (redirect URIs used by &lt;code&gt;/authorize&lt;/code&gt; + &lt;code&gt;/token&lt;/code&gt; proxy)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add the redirect URI your client uses (examples):
&lt;ul&gt;
&lt;li&gt;Local loopback for PKCE smoke testing: &lt;code&gt;http://localhost:8400/&lt;/code&gt; (default used by the repo’s &lt;code&gt;SmokeTokenProxyPkce.ps1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Other loopback patterns: &lt;code&gt;http://localhost:&amp;lt;port&amp;gt;&lt;/code&gt; or &lt;code&gt;http://127.0.0.1:&amp;lt;port&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- - Teams declarative agent redirect: `https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect` (for future use) --&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are the default redirect URIs allowed by the server’s OAuth proxy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://127.0.0.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://localhost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Certificates &amp;amp; secrets (confidential client credentials for OBO)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Local/dev (simplest): create a &lt;strong&gt;client secret&lt;/strong&gt; and set &lt;code&gt;GRAPH_CLIENT_SECRET&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Production (recommended): use a certificate (public cert uploaded to the app registration, private key stored in Key Vault). Not required for this dev/test path.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="smoke-test-the-oauth-proxy-endpoints-with-pkce"&gt;Smoke test the OAuth proxy endpoints with PKCE
&lt;/h3&gt;&lt;p&gt;From the repo root:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pwsh&lt;/span&gt; &lt;span class="n"&gt;-NoProfile&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;SmokeTokenProxyPkce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This flow exercises &lt;code&gt;/authorize&lt;/code&gt; + &lt;code&gt;/token&lt;/code&gt; and helps validate redirect URI allowlisting and PKCE behavior.&lt;/p&gt;
&lt;p&gt;The script returns the obtained access tokens if successful.&lt;/p&gt;
&lt;h3 id="get-an-mcp-api-user-token-audience--your-mcp-api"&gt;Get an MCP API user token (audience = your MCP API)
&lt;/h3&gt;&lt;p&gt;From the repo root:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# First time may require interactive consent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pwsh&lt;/span&gt; &lt;span class="n"&gt;-NoProfile&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;GetMcpAccessToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="n"&gt;-Login&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Then fetch a token (prints the token)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$mcpToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pwsh&lt;/span&gt; &lt;span class="n"&gt;-NoProfile&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;GetMcpAccessToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="call-the-mcp-tool-using-obo-recommended-path"&gt;Call the MCP tool using OBO (recommended path)
&lt;/h3&gt;&lt;p&gt;This validates the full chain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;client authenticates to MCP API&lt;/li&gt;
&lt;li&gt;server exchanges token via OBO&lt;/li&gt;
&lt;li&gt;server calls Graph&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pwsh&lt;/span&gt; &lt;span class="n"&gt;-NoProfile&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;GetMessages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-McpAccessToken&lt;/span&gt; &lt;span class="nv"&gt;$mcpToken&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Top&lt;/span&gt; &lt;span class="mf"&gt;5&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Count:&lt;/span&gt;&lt;span class="vm"&gt;$true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Congratulations! You’ve proven the core production pattern.&lt;/p&gt;
&lt;h3 id="production-lock-down-behavior-important"&gt;Production lock-down behavior (important)
&lt;/h3&gt;&lt;p&gt;If &lt;code&gt;NODE_ENV=production&lt;/code&gt;, the model server rejects bypasses unless you explicitly enable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ALLOW_MCP_ACCESS_TOKEN_ARG=true&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a deliberate safety feature: it prevents “just paste a Graph token” from creeping into production workflows.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting
&lt;/h2&gt;&lt;h3 id="aadsts65001--consent_required"&gt;&lt;code&gt;AADSTS65001&lt;/code&gt; / &lt;code&gt;consent_required&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;You haven’t granted consent yet. Run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pwsh&lt;/span&gt; &lt;span class="n"&gt;-NoProfile&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;GetMcpAccessToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="n"&gt;-Login&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="401-unauthorized-from-the-mcp-server"&gt;&lt;code&gt;401 Unauthorized&lt;/code&gt; from the MCP server
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;You didn’t send an MCP API bearer token and &lt;code&gt;MCP_REQUIRE_AUTH=true&lt;/code&gt;, or&lt;/li&gt;
&lt;li&gt;Your bearer token is invalid/expired.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="401-from-microsoft-graph"&gt;&lt;code&gt;401&lt;/code&gt; from Microsoft Graph
&lt;/h3&gt;&lt;p&gt;Common causes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OBO token exchange failed (missing delegated permission / missing admin consent)&lt;/li&gt;
&lt;li&gt;Your app registration isn’t configured with the right audience (&lt;code&gt;api://&amp;lt;clientId&amp;gt;&lt;/code&gt;) or token settings&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="403-from-microsoft-graph"&gt;&lt;code&gt;403&lt;/code&gt; from Microsoft Graph
&lt;/h3&gt;&lt;p&gt;Your user likely lacks the required Microsoft 365 admin role for Message Center access (e.g., Message Center Reader).&lt;/p&gt;
&lt;h3 id="production-bypass-error-accesstoken_disabled"&gt;Production bypass error (&lt;code&gt;accessToken_disabled&lt;/code&gt;)
&lt;/h3&gt;&lt;p&gt;You’re trying to use a dev/test bypass in production mode. Use the OBO path instead, or explicitly opt-in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ALLOW_MCP_ACCESS_TOKEN_ARG=true&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;In Part 3 you’ve added the authentication story for an MCP server calling Microsoft Graph using On-Behalf-Of (OBO):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The client authenticates to your MCP API&lt;/li&gt;
&lt;li&gt;The server uses OBO to obtain a Graph delegated token&lt;/li&gt;
&lt;li&gt;The server calls Graph and returns results as MCP tool output&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/authorize&lt;/code&gt; + &lt;code&gt;/token&lt;/code&gt; proxy endpoints support standardized OAuth flows with guardrails&lt;/li&gt;
&lt;li&gt;Dev/test bypass paths exist, but are explicitly controlled and disabled by default in production&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next up (in Part 4): we’ll use this MCP server in an &lt;strong&gt;agent built with the &lt;a class="link" href="https://learn.microsoft.com/en-us/microsoft-365-copilot/extensibility/create-deploy-agents-sdk" target="_blank" rel="noopener"
&gt;Microsoft 365 Agents Toolkit&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;</description></item><item><title>MCP Servers and M365 (Part 2): Integrate the Roadmap MCP Server into a Copilot Studio Custom Agent</title><link>https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/</link><pubDate>Wed, 31 Dec 2025 10:00:00 -0800</pubDate><guid>https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/</guid><description>&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/2.png" alt="Featured image of post MCP Servers and M365 (Part 2): Integrate the Roadmap MCP Server into a Copilot Studio Custom Agent" /&gt;&lt;p&gt;In &lt;a class="link" href="https://mjfnet.com/p/mcp-servers-and-m365-part-1-deploy-a-microsoft-365-roadmap-mcp-server/" target="_blank" rel="noopener"
&gt;part 1&lt;/a&gt; you deployed the &lt;strong&gt;Microsoft 365 Roadmap MCP Server&lt;/strong&gt;. In this post, we’ll integrate that server into a &lt;strong&gt;custom agent built with Copilot Studio&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is intentionally the “happy path” integration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Roadmap API is public.&lt;/li&gt;
&lt;li&gt;The MCP server requires &lt;strong&gt;no&lt;/strong&gt; Entra app registration and &lt;strong&gt;no&lt;/strong&gt; OAuth.&lt;/li&gt;
&lt;li&gt;We’re focusing on how an agent discovers and calls MCP tools.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Later in the series we’ll layer on authentication using a different server (&lt;a class="link" href="https://github.com/mjfusa/mcp-message-center-server" target="_blank" rel="noopener"
&gt;https://github.com/mjfusa/mcp-message-center-server&lt;/a&gt;). We are not adding auth to the Roadmap server.&lt;/p&gt;
&lt;h2 id="what-youre-integrating"&gt;What you’re integrating
&lt;/h2&gt;&lt;p&gt;Roadmap MCP server repo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/mjfusa/mcp-roadmap-server" target="_blank" rel="noopener"
&gt;https://github.com/mjfusa/mcp-roadmap-server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Key details (from the server’s README):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Health: &lt;code&gt;GET /healthz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;MCP endpoint: &lt;code&gt;POST /mcp&lt;/code&gt; (JSON-RPC 2.0)&lt;/li&gt;
&lt;li&gt;Tool: &lt;code&gt;getM365RoadmapInfo&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Arguments: &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;orderby&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;skip&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites
&lt;/h2&gt;&lt;p&gt;You’ll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Access to Copilot Studio (create/edit agents)&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Roadmap MCP server&lt;/strong&gt; should be deployed and running (see &lt;a class="link" href="https://mjfnet.com/p/mcp-servers-and-m365-part-1-deploy-a-microsoft-365-roadmap-mcp-server/" target="_blank" rel="noopener"
&gt;Part 1&lt;/a&gt; for setup instructions) on port &lt;code&gt;8081&lt;/code&gt; or another port you choose.&lt;/li&gt;
&lt;li&gt;A reachable URL for the MCP server:
&lt;ul&gt;
&lt;li&gt;If running locally, use a tunneling service (e.g., Cloudflare Tunnel, DevTunnel) to expose &lt;code&gt;http://localhost:8081&lt;/code&gt; to a public URL.&lt;/li&gt;
&lt;li&gt;The quickest way is to user a local tunnel. See below. However, a hosted deployment (e.g., Azure Container Apps) is more stable for longer-term use.&lt;/li&gt;
&lt;li&gt;If hosted, ensure you have the base URL (e.g., &lt;code&gt;https://your-app.your-region.azurecontainerapps.io&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You’ll also want:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;curl&lt;/code&gt; available in your terminal (Windows &lt;code&gt;curl.exe&lt;/code&gt; is fine)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-1--decide-where-the-mcp-server-will-run"&gt;Step 1 — Decide where the MCP server will run
&lt;/h2&gt;&lt;p&gt;Copilot Studio must be able to reach your MCP endpoint. Pick one:&lt;/p&gt;
&lt;h3 id="option-a-use-a-local-tunnel-for-quick-dev"&gt;Option A: Use a local tunnel (for quick dev)
&lt;/h3&gt;&lt;p&gt;If you run the server locally (&lt;code&gt;npm run dev&lt;/code&gt;), expose it temporarily via a tunnel so Copilot Studio can call it.&lt;/p&gt;
&lt;p&gt;Tools like &lt;a class="link" href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/" target="_blank" rel="noopener"
&gt;Cloudflare Tunnel&lt;/a&gt; or &lt;a class="link" href="https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started" target="_blank" rel="noopener"
&gt;DevTunnel&lt;/a&gt; work well.&lt;/p&gt;
&lt;p&gt;Quick example with Dev Tunnel:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install Dev Tunnel: &lt;code&gt;winget install Microsoft.devtunnel&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Login: &lt;code&gt;devtunnel user login&lt;/code&gt;. You will need a GitHub or Microsoft account.&lt;/li&gt;
&lt;li&gt;Start your MCP server locally: &lt;code&gt;npm run dev&lt;/code&gt; (default port `8081)&lt;/li&gt;
&lt;li&gt;Start the tunnel:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;devtunnel&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="n"&gt;-p&lt;/span&gt; &lt;span class="mf"&gt;8081&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-allow-anonymous&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Note the tunnel URL (e.g., &lt;code&gt;https://qx8mj0tl-8081.usw2.devtunnels.ms&lt;/code&gt;).&lt;/p&gt;
&lt;ol start="5"&gt;
&lt;li&gt;Start tunnel inspection using inspection url provided in the terminal (e.g., &lt;code&gt;https://qx8mj0tl-8081-inspect.usw2.devtunnels.ms&lt;/code&gt;). We will use this to monitor requests to the Roadmap MCP server.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="option-b-use-a-hosted-url-for-stability-and-production"&gt;Option B: Use a hosted URL (for stability and production)
&lt;/h3&gt;&lt;p&gt;Deploy the server and use the resulting base URL. You should end up with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://&amp;lt;your-host&amp;gt;/healthz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://&amp;lt;your-host&amp;gt;/mcp&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Regardless of approach, your goal is a reachable &lt;code&gt;https://.../mcp&lt;/code&gt; endpoint.&lt;/p&gt;
&lt;h2 id="step-2--validate-the-mcp-server-with-curl-before-copilot-studio"&gt;Step 2 — Validate the MCP server with curl (before Copilot Studio)
&lt;/h2&gt;&lt;p&gt;Do this first. It separates “server is working” from “Copilot Studio integration is working.”&lt;/p&gt;
&lt;p&gt;Set a base URL:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Local&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$BaseUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://localhost:8081&amp;#39;&lt;/span&gt; &lt;span class="c"&gt;# or your tunnel URL, e.g., &amp;#39;https://qx8mj0tl-8081.usw2.devtunnels.ms&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Hosted example&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# $BaseUrl = &amp;#39;https://your-app.your-region.azurecontainerapps.io&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="2a-health-check"&gt;2a) Health check
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-sS&lt;/span&gt; &lt;span class="n"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BaseUrl&lt;/span&gt;&lt;span class="s2"&gt;/healthz&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You want &lt;code&gt;200 OK&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="2b-call-the-mcp-tool-fetch-5-items"&gt;2b) Call the MCP tool (fetch 5 items)
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;jsonrpc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;2.0&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;tools/call&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;getM365RoadmapInfo&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;orderby&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;created desc&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;$true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;ConvertTo-Json&lt;/span&gt; &lt;span class="n"&gt;-Depth&lt;/span&gt; &lt;span class="mf"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-sS&lt;/span&gt; &lt;span class="n"&gt;-X&lt;/span&gt; &lt;span class="n"&gt;POST&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BaseUrl&lt;/span&gt;&lt;span class="s2"&gt;/mcp&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Accept: application/json, text/event-stream&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-data-raw&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="2c-go-back-to-the-tunnel-inspection-browser-session-if-using-a-tunnel"&gt;2c) Go back to the tunnel inspection browser session (if using a tunnel)
&lt;/h3&gt;&lt;p&gt;Clicking on the payload tab you should see the request to the MCP server.&lt;br&gt;
&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/MCPRequestToServer.png"
width="963"
height="487"
srcset="https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/MCPRequestToServer_hu_ef3abb69254f3d5a.png 480w, https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/MCPRequestToServer_hu_9d29fac9949d8482.png 1024w"
loading="lazy"
alt="Dev Tunnel Inspection showing MCP server request"
class="gallery-image"
data-flex-grow="197"
data-flex-basis="474px"
&gt;&lt;/p&gt;
&lt;p&gt;If that succeeds, you’re ready to wire it into Copilot Studio.&lt;/p&gt;
&lt;h2 id="step-3--create-a-custom-agent-in-copilot-studio"&gt;Step 3 — Create a custom agent in Copilot Studio
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Open Copilot Studio.&lt;/li&gt;
&lt;li&gt;Click &amp;lsquo;Agents&amp;rsquo;.&lt;/li&gt;
&lt;li&gt;In the &amp;lsquo;Start building by describing&amp;rsquo; field, enter :&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;An assistant that helps users find information about upcoming
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;features in Microsoft 365 and their planned release dates.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start="4"&gt;
&lt;li&gt;
&lt;p&gt;Click the arrow (⇨) to create the agent.
You should see that Copilot Studio has given a name to the agent based on your description. (E.g. Microsoft 365 Feature Release Assistant)
Also, instructions have been auto-generated. You can edit these later.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To only have information from the Roadmap MCP server, disable web and internal knowledge:&lt;/p&gt;
&lt;p&gt;a. Click on the &amp;lsquo;Settings&amp;rsquo; tab&lt;br&gt;
b. Under &amp;lsquo;Knowledge sources&amp;rsquo;, toggle off &amp;lsquo;Web search&amp;rsquo; and &amp;lsquo;Internal knowledge&amp;rsquo;
&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/WebAndInternalKnowledgeDisabled.png"
width="1028"
height="209"
srcset="https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/WebAndInternalKnowledgeDisabled_hu_55efba01daa41dfb.png 480w, https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/WebAndInternalKnowledgeDisabled_hu_47be2e00ab46fcfa.png 1024w"
loading="lazy"
alt="Copilot Studio Agent Settings disabling web and internal knowledge"
class="gallery-image"
data-flex-grow="491"
data-flex-basis="1180px"
&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following in Suggested Prompts:
Title: &amp;ldquo;What are the latest Copilot roadmap items?&amp;rdquo;
Prompt: &amp;ldquo;Show roadmap items regarding Copilot published in the last two weeks.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the Roadmap MCP server as a tool/action:&lt;/p&gt;
&lt;p&gt;a. Click on &amp;lsquo;&lt;strong&gt;Add tool&lt;/strong&gt;&amp;rsquo;&lt;br&gt;
b. Click on &lt;strong&gt;New tool&lt;/strong&gt;&lt;br&gt;
c. Click on &lt;strong&gt;Model Context Protocol&lt;/strong&gt;&lt;br&gt;
Complete the fields as follows:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Server name&lt;/strong&gt;: &lt;code&gt;M365 Roadmap MCP Server&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server description&lt;/strong&gt;: &lt;code&gt;This server provides access to Microsoft 365 Roadmap information via an OData interface&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server URL&lt;/strong&gt;: &lt;code&gt;https://&amp;lt;your-host&amp;gt;/mcp&lt;/code&gt; (replace &lt;code&gt;&amp;lt;your-host&amp;gt;&lt;/code&gt; with your actual host / tunnel URL). &lt;strong&gt;Make sure you add the trailing &lt;code&gt;/mcp&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication&lt;/strong&gt;: None&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Create&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="7"&gt;
&lt;li&gt;Create a connection to the MCP server.&lt;/li&gt;
&lt;li&gt;Click &amp;lsquo;Add and configure&amp;rsquo;.&lt;/li&gt;
&lt;li&gt;Go back to the tunnel inspection browser session
Inspect each MCP request by clicking on the Payload tab. You should see the requests from Copilot Studio to the MCP server. &lt;br&gt;
&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/MCPCallsWhenCreatingMCPConnecor.png"
width="1518"
height="887"
srcset="https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/MCPCallsWhenCreatingMCPConnecor_hu_80eecf57cd42e6d5.png 480w, https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/MCPCallsWhenCreatingMCPConnecor_hu_ed86d63d1c584bdd.png 1024w"
loading="lazy"
alt="Dev Tunnel Inspection showing MCP server ‘initialize’ request from Copilot Studio"
class="gallery-image"
data-flex-grow="171"
data-flex-basis="410px"
&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note the &lt;strong&gt;&amp;lsquo;initialize&amp;rsquo;&lt;/strong&gt;, &lt;strong&gt;&amp;rsquo;notifications/initialized&amp;rsquo;&lt;/strong&gt;, and &lt;strong&gt;&amp;rsquo;tools/list&amp;rsquo;&lt;/strong&gt; requests.&lt;/p&gt;
&lt;h3 id="test-the-agent"&gt;Test the agent
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;In the &lt;strong&gt;&amp;lsquo;Test your agent&amp;rsquo;&lt;/strong&gt; chat, ask: &lt;strong&gt;“Show me the newest 5 roadmap items.”&lt;/strong&gt;&lt;br&gt;
Note: The first time you run the agent, you will need to establish a connection. Click on &amp;lsquo;Open connection manger&amp;rsquo;, Click &amp;lsquo;Connect&amp;rsquo; to pick the connection created in the previous step, Click &amp;lsquo;Submit&amp;rsquo;. Close the browser tab, and then click &amp;lsquo;Retry&amp;rsquo;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The agent will return something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/TestingAgentCallMCPServer.png"
width="1898"
height="890"
srcset="https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/TestingAgentCallMCPServer_hu_1520f1c96feb517e.png 480w, https://mjfnet.com/p/mcp-servers-and-m365-part-2-integrate-the-roadmap-mcp-server-into-a-copilot-studio-custom-agent/images/TestingAgentCallMCPServer_hu_e0488ffac5ba4279.png 1024w"
loading="lazy"
alt="Copilot Studio Test Chat showing roadmap items"
class="gallery-image"
data-flex-grow="213"
data-flex-basis="511px"
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can retrieve Microsoft 365 Roadmap items using the connected tool &lt;code&gt;getM365RoadmapInfo&lt;/code&gt;. When asked about upcoming or recently released features, call agent will call the tool and summarize results.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="instruct-the-agent-how-to-call-the-tool-minimal-prompt-contract"&gt;Instruct the agent how to call the tool (minimal prompt contract)
&lt;/h2&gt;&lt;p&gt;You can update the &lt;strong&gt;instructions&lt;/strong&gt; to describe how you want the roadmap information presented. Which fields to include, formatting, etc.&lt;/p&gt;
&lt;p&gt;Even when a tool is connected, agents behave better when you give them a “mini contract”. Add guidance like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;getM365RoadmapInfo&lt;/code&gt; for roadmap questions.&lt;/li&gt;
&lt;li&gt;Default to &lt;code&gt;top=5&lt;/code&gt; or &lt;code&gt;top=10&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Prefer &lt;code&gt;orderby=&amp;quot;created desc&amp;quot;&lt;/code&gt; for “latest” questions.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;count=true&lt;/code&gt; when asked “how many”.&lt;/li&gt;
&lt;li&gt;When filtering titles, use case-insensitive contains patterns like:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;contains(tolower(title), 'copilot')&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This keeps tool calls predictable without overprompting.&lt;/p&gt;
&lt;h2 id="step-6--test-the-agent-end-to-end"&gt;Step 6 — Test the agent end-to-end
&lt;/h2&gt;&lt;p&gt;In Copilot Studio’s test chat:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ask: “Show me the newest 5 roadmap items.”&lt;/li&gt;
&lt;li&gt;Ask: “Show roadmap items that mention multiple Microsoft 365 products like Copilot, Teams and SharePoint”&lt;/li&gt;
&lt;li&gt;Ask: “Show roadmap items available for GCC High and DoD cloud instances with General Availability in the next month.”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Expected behavior:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The agent calls &lt;code&gt;getM365RoadmapInfo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It summarizes the returned &lt;code&gt;value&lt;/code&gt; items (title, summary, dates).&lt;/li&gt;
&lt;li&gt;The output includes links to the roadmap items on the M365 Roadmap site.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting
&lt;/h2&gt;&lt;h3 id="health-works-but-tool-calls-fail"&gt;“Health works but tool calls fail”
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Verify &lt;code&gt;POST /mcp&lt;/code&gt; is reachable from the same network path as Copilot Studio (not just from your laptop).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="405-method-not-allowed"&gt;&lt;code&gt;405 Method Not Allowed&lt;/code&gt;
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/mcp&lt;/code&gt; is &lt;strong&gt;POST-only&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="tool-not-discovered"&gt;Tool not discovered
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Confirm the MCP endpoint responds to &lt;code&gt;tools/list&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;jsonrpc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;2.0&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;tools/list&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;ConvertTo-Json&lt;/span&gt; &lt;span class="n"&gt;-Depth&lt;/span&gt; &lt;span class="mf"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-sS&lt;/span&gt; &lt;span class="n"&gt;-X&lt;/span&gt; &lt;span class="n"&gt;POST&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BaseUrl&lt;/span&gt;&lt;span class="s2"&gt;/mcp&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Accept: application/json, text/event-stream&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-data-raw&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;You now have a Copilot Studio custom agent that can call a real MCP tool end-to-end:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Copilot Studio agent discovers the MCP tool surface&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;getM365RoadmapInfo&lt;/code&gt; through &lt;code&gt;POST /mcp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The MCP server validates inputs (from OpenAPI-derived schemas) and proxies OData queries to the Roadmap API&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next up: we’ll expand integration patterns (including declarative agents), then switch servers to &lt;code&gt;https://github.com/mjfusa/mcp-message-center-server&lt;/code&gt; to add authentication and token handling.&lt;/p&gt;</description></item><item><title>MCP Servers and M365 (Part 1): Deploy a Microsoft 365 Roadmap MCP Server</title><link>https://mjfnet.com/p/mcp-servers-and-m365-part-1-deploy-a-microsoft-365-roadmap-mcp-server/</link><pubDate>Tue, 30 Dec 2025 09:00:00 -0800</pubDate><guid>https://mjfnet.com/p/mcp-servers-and-m365-part-1-deploy-a-microsoft-365-roadmap-mcp-server/</guid><description>&lt;img src="https://mjfnet.com/p/mcp-servers-and-m365-part-1-deploy-a-microsoft-365-roadmap-mcp-server/images/1.png" alt="Featured image of post MCP Servers and M365 (Part 1): Deploy a Microsoft 365 Roadmap MCP Server" /&gt;&lt;p&gt;This series starts intentionally simple: one MCP server, no authentication, and one very focused capability—wrap the Microsoft 365 Roadmap public API so AI clients can call it as a tool.&lt;/p&gt;
&lt;p&gt;But in this series you’re not &lt;em&gt;building&lt;/em&gt; an MCP server from scratch first. You’re going to &lt;strong&gt;deploy an MCP server locally&lt;/strong&gt;, then use it as a hands-on way to learn MCP’s basic architecture by running and testing the server end-to-end.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Skip down to the &lt;a class="link" href="#try-it-yourself" &gt;&lt;strong&gt;Try it yourself&lt;/strong&gt;&lt;/a&gt; section to get started quickly.&lt;/p&gt;
&lt;p&gt;If you’re coming to MCP for the first time, it can &lt;em&gt;look&lt;/em&gt; like more work than just calling an API directly—and that reaction is fair.&lt;/p&gt;
&lt;h3 id="why-mcp-feels-more-complex"&gt;Why MCP feels more complex
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Abstraction layer:&lt;/strong&gt; you define a capability as a tool, and then invoke it via &lt;code&gt;tools/call&lt;/code&gt;. That’s extra steps compared to “make an HTTP request.”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Serialization &amp;amp; schema:&lt;/strong&gt; tools need explicit input/output contracts (JSON Schema). That’s upfront work, and mismatches can be frustrating.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context management:&lt;/strong&gt; MCP is designed for agents that may chain multiple tools over multiple turns. That power adds structure (and therefore complexity).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="why-its-done-this-way"&gt;Why it’s done this way
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Standardization:&lt;/strong&gt; MCP gives AI clients one consistent interface for many different tools, so they can discover and reason about capabilities in a uniform way.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Safety &amp;amp; validation:&lt;/strong&gt; schemas let the server reject invalid inputs early and behave predictably.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extensibility:&lt;/strong&gt; once a tool is defined, it can be reused across models and workflows without rewriting the underlying API logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simplicity:&lt;/strong&gt; MCP provides a simplified interface. Without it, the client (agent, app, script) needs to understand the full API surface, auth, error handling, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="is-it-more-error-prone"&gt;Is it more error-prone?
&lt;/h3&gt;&lt;p&gt;Initially, it can be—because you’re translating an API into a structured tool definition, and misalignment in schema, naming, or parameter handling can cause issues.&lt;/p&gt;
&lt;p&gt;But in larger systems, MCP usually reduces long-term errors by enforcing consistency, making inputs explicit, and enabling better orchestration and troubleshooting.&lt;/p&gt;
&lt;p&gt;The goal of Part 1 is to make that tradeoff concrete. By running a simple, no-auth server locally, you can see exactly where the “ceremony” is (schemas, validation, routing) and why it becomes valuable before you add enterprise concerns like authentication.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt; is a standard for exposing tools to AI agents and other AI clients. The practical benefit is that you can publish a standard “tool interface” (names + JSON schemas) while your server handles the complex parts: validation, query construction, and talking to upstream APIs.&lt;/p&gt;
&lt;p&gt;Important scope note: &lt;strong&gt;we are not adding authentication to this Roadmap server.&lt;/strong&gt; Later in the series we’ll introduce auth (and the “real” enterprise token handling story) using a different server: &lt;code&gt;https://github.com/mjfusa/mcp-message-center-server&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="microsoft-365-roadmap-mcp-server"&gt;Microsoft 365 Roadmap MCP Server
&lt;/h2&gt;&lt;p&gt;The first server in the series is the &lt;a class="link" href="https://github.com/mjfusa/mcp-roadmap-server" target="_blank" rel="noopener"
&gt;&lt;strong&gt;Microsoft 365 Roadmap MCP Server&lt;/strong&gt;&lt;/a&gt;. You’ll run it locally as-is, then use it to understand the moving parts of MCP.&lt;/p&gt;
&lt;p&gt;It provides an MCP wrapper around:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API: &lt;a class="link" href="https://www.microsoft.com/releasecommunications/api/v2/m365" target="_blank" rel="noopener"
&gt;https://www.microsoft.com/releasecommunications/api/v2/m365&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Site: &lt;a class="link" href="https://www.microsoft.com/en-us/microsoft-365/roadmap" target="_blank" rel="noopener"
&gt;https://www.microsoft.com/en-us/microsoft-365/roadmap&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Roadmap site publishes estimated release dates and descriptions for Microsoft 365 features. The API is the “machine interface” behind that data, and it supports OData-style query parameters, which makes it ideal for a tool wrapper.&lt;/p&gt;
&lt;p&gt;This server is “public-only”:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Queries the public Roadmap API (not Microsoft Graph)&lt;/li&gt;
&lt;li&gt;Requires &lt;strong&gt;no&lt;/strong&gt; Microsoft Entra app registration and &lt;strong&gt;no&lt;/strong&gt; OAuth tokens&lt;/li&gt;
&lt;li&gt;Passes OData parameters through (no extra proprietary filtering logic)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mcp-standard-calls"&gt;MCP Standard Calls
&lt;/h2&gt;&lt;p&gt;The server supports the following MCP standard calls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;initialize&lt;/code&gt;: initializes the MCP session.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tools/list&lt;/code&gt;: lists available tools (here, just &lt;code&gt;getM365RoadmapInfo&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tools/call&lt;/code&gt;: invokes a tool with validated arguments&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are called via HTTP &lt;code&gt;POST /mcp&lt;/code&gt; with JSON-RPC 2.0 payloads.&lt;/p&gt;
&lt;h2 id="what-does-the-server-expose"&gt;What does the server expose?
&lt;/h2&gt;&lt;p&gt;This server exposes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Health check endpoint: &lt;code&gt;GET /healthz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;MCP JSON-RPC endpoint: &lt;code&gt;POST /mcp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;One MCP tool: &lt;code&gt;getM365RoadmapInfo&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Supports OData parameters: &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;orderby&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;skip&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-novel-approach-openapi-30-as-the-source-of-truth"&gt;The novel approach: OpenAPI 3.0 as the source of truth
&lt;/h2&gt;&lt;p&gt;Most MCP examples hand-write tool schemas. That’s fine for demos, but it breaks down as soon as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you add endpoints,&lt;/li&gt;
&lt;li&gt;you evolve parameters,&lt;/li&gt;
&lt;li&gt;or the upstream API changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The approach in this series is: &lt;strong&gt;use OpenAPI 3.0 as the source of truth&lt;/strong&gt;, and generate MCP tool schemas in code.&lt;/p&gt;
&lt;p&gt;That means your server treats the OpenAPI definition as the authoritative contract, then derives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tool names and descriptions (from &lt;code&gt;operationId&lt;/code&gt; / &lt;code&gt;summary&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;JSON Schema for tool inputs (from parameters + request bodies)&lt;/li&gt;
&lt;li&gt;validation rules (required vs optional inputs, types, enums)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="querying-the-roadmap-api-odata-parameters"&gt;Querying the Roadmap API (OData parameters)
&lt;/h2&gt;&lt;p&gt;The Roadmap API supports OData-style query parameters such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$filter&lt;/code&gt; (boolean expression)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$select&lt;/code&gt; (projection)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$orderby&lt;/code&gt; (sorting)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$top&lt;/code&gt; (limit)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$skip&lt;/code&gt; (pagination)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$count&lt;/code&gt; (return total count)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is the openapi.json file used in this server: &lt;a class="link" href="https://raw.githubusercontent.com/mjfusa/mcp-roadmap-server/refs/heads/main/openapi/openapi.json" target="_blank" rel="noopener"
&gt;https://raw.githubusercontent.com/mjfusa/mcp-roadmap-server/refs/heads/main/openapi/openapi.json&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note the description for each of the OData parameters supported by the Roadmap API. These descriptions are detailed and provide examples of how to use each parameter effectively. &lt;strong&gt;They are key to the AI understanding how to construct valid queries when invoking the &lt;code&gt;getM365RoadmapInfo&lt;/code&gt; tool.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For example, the &lt;code&gt;$filter&lt;/code&gt; parameter description states:&lt;/p&gt;
&lt;div class="highlight" title="Description for $filter OData query parameter" wrap="false"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;FilterParameter&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;$filter&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;in&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;query&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;OData filter expression. IMPORTANT: Use &amp;#39;created&amp;#39; field for created date filtering (not &amp;#39;createdDateTime&amp;#39;). Use the &amp;#39;modified&amp;#39; field for last modified date filtering (not &amp;#39;lastModifiedDateTime&amp;#39;). Supports contains(), tolower(), and date comparisons. Filter the results based on specific conditions. Use `contains(tolower(title),tolower(&amp;#39;search term&amp;#39;))` to search within the title property for a specific term. For creation date filtering, use the &amp;#39;created&amp;#39; property with ISO 8601 date format. For example, `created ge 2025-03-01T00:00:00Z` to filter roadmap items created after March 1, 2025. To combine filters, use `and` operator. For example, `contains(tolower(title),tolower(&amp;#39;Teams&amp;#39;)) and created ge 2025-03-01T00:00:00Z`. For the &amp;#39;status&amp;#39; field, you can filter by status using `status eq &amp;#39;In development&amp;#39;`, `status eq &amp;#39;Rolling out&amp;#39;`, `status eq &amp;#39;Launched&amp;#39;`, `status eq &amp;#39;Preview&amp;#39;`, `status eq &amp;#39;Planned&amp;#39;`, or `status eq &amp;#39;Cancelled&amp;#39;`. For array fields, use the any() operator: For &amp;#39;platforms&amp;#39; array, use `platforms/any(p: contains(tolower(p), &amp;#39;desktop&amp;#39;))` to find items available on desktop platform, or `platforms/any(p: p eq &amp;#39;Web&amp;#39;)` for exact platform match. For &amp;#39;products&amp;#39; array, use `products/any(p: contains(tolower(p), &amp;#39;teams&amp;#39;))` to search within product names. For &amp;#39;releaseRings&amp;#39; array, use `releaseRings/any(r: contains(tolower(r), &amp;#39;general&amp;#39;))` to filter by release ring. For &amp;#39;cloudInstances&amp;#39; array, use `cloudInstances/any(c: contains(tolower(c), &amp;#39;gcc&amp;#39;))` to filter by cloud instance. For &amp;#39;availabilities&amp;#39; array, use nested object filtering: `availabilities/any(a: a/ring eq &amp;#39;General Availability&amp;#39;)` to filter by release ring, `availabilities/any(a: a/year eq 2026)` to filter by year, or `availabilities/any(a: a/month eq &amp;#39;March&amp;#39;)` to filter by month. Date range filtering example: `created ge 2025-01-01T00:00:00Z and created le 2025-12-31T23:59:59Z`. For complex date ranges spanning multiple months and years, ALWAYS use proper parentheses grouping: `(availabilities/any(a: a/year eq 2025 and (a/month eq &amp;#39;November&amp;#39; or a/month eq &amp;#39;December&amp;#39;)) or availabilities/any(a: a/year eq 2026 and a/month eq &amp;#39;January&amp;#39;))`. Complex example: `contains(tolower(title), &amp;#39;teams&amp;#39;) and platforms/any(p: p eq &amp;#39;Desktop&amp;#39;) and (availabilities/any(a: a/year eq 2025 and (a/month eq &amp;#39;November&amp;#39; or a/month eq &amp;#39;December&amp;#39;)) or availabilities/any(a: a/year eq 2026 and a/month eq &amp;#39;January&amp;#39;))`. To find roadmap items that are &amp;#39;major changes&amp;#39;, use contains(tolower(title), &amp;#39;major&amp;#39;) or contains(tolower(title), &amp;#39;breaking&amp;#39;) or contains(tolower(description), &amp;#39;major&amp;#39;) or contains(tolower(description), &amp;#39;significant&amp;#39;). Do not use &amp;#39;$filter=isMajorChange+eq+true&amp;#39; as it is not supported by the API.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="why-use-openapi-as-the-source-of-truth"&gt;Why use OpenAPI as the source of truth?
&lt;/h3&gt;&lt;p&gt;OpenAPI is a fair “source of truth” choice because it’s typically &lt;strong&gt;more portable, more discoverable, and more ecosystem-friendly&lt;/strong&gt; than MCP tool schemas today.&lt;/p&gt;
&lt;p&gt;That’s not a knock on MCP—it’s because OpenAPI and MCP optimize for different problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;OpenAPI describes HTTP APIs.&lt;/strong&gt; It answers: “How do I call this service?”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP describes agent tools.&lt;/strong&gt; It answers: “How should an agent use this capability?”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In practice, OpenAPI has a few advantages that matter immediately:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;It’s a mature, widely adopted contract.&lt;/strong&gt; OpenAPI (OAS 3.x) is stable and has deep tooling support across gateways, SDK generation, docs (Swagger UI/Redoc), testing, and increasingly AI ingestion.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It’s easy to distribute and version.&lt;/strong&gt; Specs are commonly published and treated as first-class artifacts in API programs, so they’re easy to find, review, and govern.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It avoids framework lock-in.&lt;/strong&gt; An OpenAPI spec stands on its own; MCP schemas are usually coupled to MCP lifecycle semantics (&lt;code&gt;initialize&lt;/code&gt;, &lt;code&gt;tools/list&lt;/code&gt;, &lt;code&gt;tools/call&lt;/code&gt;) and server runtime conventions.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The mental model I like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;OpenAPI is the system-of-record contract.&lt;/strong&gt;
&lt;strong&gt;MCP schema is the agent-consumable projection.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So the “wins” you get by making OpenAPI authoritative are still the practical ones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Correctness:&lt;/strong&gt; tool inputs match what the upstream API actually accepts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintainability:&lt;/strong&gt; changes become “update spec → regenerate” instead of hand-syncing schemas.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Safety &amp;amp; predictability:&lt;/strong&gt; strict schemas reject invalid inputs early (especially important when the caller is an LLM).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even if this server only exposes one tool right now, starting from OpenAPI prevents schema drift later—and lets the MCP layer stay small and purpose-built.&lt;/p&gt;
&lt;h2 id="the-code-toolset-what-does-what"&gt;The code toolset (what does what)
&lt;/h2&gt;&lt;p&gt;It helps to think of this server as three layers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Express (the web wrapper):&lt;/strong&gt; exposes one HTTP endpoint (&lt;code&gt;/mcp&lt;/code&gt;) and passes requests/responses through. (&lt;a class="link" href="https://www.npmjs.com/package/express" target="_blank" rel="noopener"
&gt;https://www.npmjs.com/package/express&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP SDK (the protocol engine):&lt;/strong&gt; speaks MCP/JSON-RPC, advertises available tools, validates tool inputs/outputs, and routes &lt;code&gt;tools/call&lt;/code&gt; to the right handler (supports JSON or SSE responses). (&lt;a class="link" href="https://www.npmjs.com/package/@modelcontextprotocol/sdk" target="_blank" rel="noopener"
&gt;https://www.npmjs.com/package/@modelcontextprotocol/sdk&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The tool handler:&lt;/strong&gt; takes the validated arguments, calls the Microsoft 365 Roadmap API with OData query params, and returns a clean result back to the client.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="end-to-end-request-flow"&gt;End-to-end request flow
&lt;/h2&gt;&lt;p&gt;At runtime, a call to &lt;code&gt;getM365RoadmapInfo&lt;/code&gt; looks like:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;MCP receives a tool invocation with JSON arguments.&lt;/li&gt;
&lt;li&gt;Server validates arguments against the generated input Schema.&lt;/li&gt;
&lt;li&gt;Server constructs the outbound request to &lt;a class="link" href="https://www.microsoft.com/releasecommunications/api/v2/m365" target="_blank" rel="noopener"
&gt;https://www.microsoft.com/releasecommunications/api/v2/m365&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Server normalizes the response into a stable, tool-friendly shape.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="try-it-yourself"&gt;Try it yourself
&lt;/h2&gt;&lt;p&gt;This is the fastest path to a first successful local deployment and a first successful call.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prequisites&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Node.js 20+ installed - &lt;a class="link" href="https://nodejs.org/" target="_blank" rel="noopener"
&gt;https://nodejs.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Git installed - &lt;a class="link" href="https://git-scm.com/" target="_blank" rel="noopener"
&gt;https://git-scm.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A terminal (Command Prompt, PowerShell, Bash, etc.)&lt;/li&gt;
&lt;li&gt;Familiarity with terminal commands&lt;/li&gt;
&lt;li&gt;Basic understanding of HTTP requests (curl or similar)&lt;/li&gt;
&lt;li&gt;Familiarity with npm (Node Package Manager)&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;Clone the repo:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/mjfusa/mcp-roadmap-server
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; mcp-roadmap-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;Install dependencies:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start="3"&gt;
&lt;li&gt;Run the server:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm run dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start="4"&gt;
&lt;li&gt;Start a new terminal and call the health endpoint:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-Powershell" data-lang="Powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;curl &lt;/span&gt;&lt;span class="n"&gt;-sS&lt;/span&gt; &lt;span class="n"&gt;-i&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;8081&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;healthz&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You should see a &lt;code&gt;200 OK&lt;/code&gt; response. With the output: &lt;strong&gt;{&amp;ldquo;ok&amp;rdquo;:true}&lt;/strong&gt;.&lt;/p&gt;
&lt;ol start="5"&gt;
&lt;li&gt;Call the MCP endpoint to fetch 5 roadmap items:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;curl &lt;/span&gt;&lt;span class="n"&gt;-sS&lt;/span&gt; &lt;span class="n"&gt;-X&lt;/span&gt; &lt;span class="n"&gt;POST&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://localhost:8081/mcp&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Accept: application/json, text/event-stream&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-data&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{&amp;#34;jsonrpc&amp;#34;:&amp;#34;2.0&amp;#34;,&amp;#34;id&amp;#34;:1,&amp;#34;method&amp;#34;:&amp;#34;tools/call&amp;#34;,&amp;#34;params&amp;#34;:{&amp;#34;name&amp;#34;:&amp;#34;getM365RoadmapInfo&amp;#34;,&amp;#34;arguments&amp;#34;:{&amp;#34;top&amp;#34;:5,&amp;#34;orderby&amp;#34;:&amp;#34;created desc&amp;#34;,&amp;#34;count&amp;#34;:true}}}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You should see a valid MCP response with 5 roadmap items.&lt;/p&gt;
&lt;h2 id="congratulations"&gt;Congratulations!
&lt;/h2&gt;&lt;p&gt;You’ve deployed and tested your first MCP server locally! You can now explore the code, understand how the pieces fit together, and prepare for Part 2 where we’ll build a custom agent to call this server.&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What’s next
&lt;/h2&gt;&lt;p&gt;In Part 2 we’ll integrate this M365 Roadmap MCP server into a &lt;strong&gt;custom agent built with Copilot Studio&lt;/strong&gt;, so you can see how MCP tools get discovered and invoked from an agent experience.&lt;/p&gt;</description></item><item><title>Getting Started with “No-Hands” Automation: Building Autonomous Agents in Copilot Studio - Part 1 - Personal Agents</title><link>https://mjfnet.com/p/getting-started-with-no-hands-automation-building-autonomous-agents-in-copilot-studio-part-1-personal-agents/</link><pubDate>Wed, 26 Nov 2025 10:06:20 -0800</pubDate><guid>https://mjfnet.com/p/getting-started-with-no-hands-automation-building-autonomous-agents-in-copilot-studio-part-1-personal-agents/</guid><description>&lt;img src="https://mjfnet.com/p/getting-started-with-no-hands-automation-building-autonomous-agents-in-copilot-studio-part-1-personal-agents/images/TheGangAdmiringTheAutonomousAgentOutput.png" alt="Featured image of post Getting Started with “No-Hands” Automation: Building Autonomous Agents in Copilot Studio - Part 1 - Personal Agents" /&gt;&lt;h2 id="introduction"&gt;Introduction
&lt;/h2&gt;&lt;p&gt;As your organization and you personally begin to improve your productivity and job satisfaction with Microsoft 365 Copilot Studio, &lt;strong&gt;autonomous agents&lt;/strong&gt; are an exciting new tool in your arsenal. With Copilot Studio, you can build autonomous agents that categorize and prioritize your emails, summarize your documents, or post updates into Teams—and it just works, because it runs as &lt;em&gt;you&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The other important class of agents are &lt;strong&gt;enterprise-grade autonomous agents&lt;/strong&gt; that run headless, touch sensitive data, and operate as part of your organization’s automation fabric. These agents need dedicated identities, least-privilege permissions, and robust governance. We’ll cover those in &lt;strong&gt;Part 2&lt;/strong&gt; of this series.&lt;/p&gt;
&lt;p&gt;This article focuses on &lt;strong&gt;personal and maker scenarios&lt;/strong&gt;—agents that run using &lt;strong&gt;end-user&lt;/strong&gt; or &lt;strong&gt;maker&lt;/strong&gt; credentials. You’ll learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How interactive and autonomous agents differ in practice&lt;/li&gt;
&lt;li&gt;When user- or maker-bound authentication is acceptable&lt;/li&gt;
&lt;li&gt;Where it breaks down as your automation grows&lt;/li&gt;
&lt;li&gt;The governance guardrails you should apply even in “small” scenarios&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In &lt;strong&gt;Part 2&lt;/strong&gt;, we’ll move to &lt;strong&gt;enterprise-grade autonomous agents&lt;/strong&gt; that run headless with &lt;strong&gt;Agent ID&lt;/strong&gt; and least-privilege permissions.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="understanding-interactive-vs-autonomous-agents"&gt;Understanding Interactive vs Autonomous Agents
&lt;/h2&gt;&lt;h3 id="interactive-agents-you-in-the-loop"&gt;Interactive Agents: You in the Loop
&lt;/h3&gt;&lt;p&gt;Interactive agents are designed with a user-first approach. They operate in your session and under your Microsoft 365 permissions. You stay in control, invoking them when needed and reviewing the results.&lt;/p&gt;
&lt;p&gt;Think of them as &lt;strong&gt;personal productivity boosters&lt;/strong&gt;. Common examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reviewing calendars and reminders to keep your day organized&lt;/li&gt;
&lt;li&gt;Automating email triage, categorization, and follow-up suggestions&lt;/li&gt;
&lt;li&gt;Generating quick reports from your personal or team datasets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key characteristics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Scoped to you:&lt;/strong&gt; They can only see what you can see.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User-driven:&lt;/strong&gt; You decide when to invoke them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limited scope:&lt;/strong&gt; Mistakes are usually limited to your own data.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="autonomous-agents-you-out-of-the-loop"&gt;Autonomous Agents: You Out of the Loop
&lt;/h3&gt;&lt;p&gt;Autonomous agents execute tasks without direct user interaction. They are &lt;strong&gt;event-driven&lt;/strong&gt; and designed for &lt;strong&gt;headless execution&lt;/strong&gt;—for example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Watching a SharePoint library and summarizing new documents&lt;/li&gt;
&lt;li&gt;Processing responses from a Forms survey and updating a list&lt;/li&gt;
&lt;li&gt;Posting daily digests or insights into a Teams channel&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="alert alert-note"&gt;
&lt;p class="alert-heading"&gt;
ℹ️
Like interactive agents, autonomous agents can also run under the permissions of a user or maker account.
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this article, we focus on &lt;strong&gt;personal or team-scale autonomous agents&lt;/strong&gt;—those that still operate primarily within the scope of a specific user or maker, even if they run automatically.&lt;/p&gt;
&lt;p&gt;In Part 2, we’ll expand to &lt;strong&gt;enterprise-scale autonomous agents&lt;/strong&gt; that act as infrastructure, affect many users, and must run independently of any individual account.&lt;/p&gt;
&lt;h3 id="personal-autonomous-agents"&gt;Personal Autonomous Agents
&lt;/h3&gt;&lt;p&gt;A few examples of personal autonomous agents include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A personal agent that summarizes daily activity based on your own emails and documents&lt;/li&gt;
&lt;li&gt;A team bot that posts updates into a project channel based on a single SharePoint library&lt;/li&gt;
&lt;li&gt;An experimental agent that helps your small group test a new process&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="build-a-personal-autonomous-agent"&gt;Build a Personal Autonomous Agent
&lt;/h3&gt;&lt;p&gt;See &lt;strong&gt;&lt;a class="link" href="https://mjfnet.com/p/create-a-personal-autonomous-agent-inbox-summary-to-teams-chat-agent/" target="_blank" rel="noopener"
&gt;Create an Inbox Summary to Teams Chat Agent&lt;/a&gt;&lt;/strong&gt; for a step-by-step guide on building a personal autonomous agent that sends you summaries of emails as they arrive in your inbox. You can use as you read this post to get hands-on experience with personal autonomous agents.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="authentication-models-for-personal-and-maker-use"&gt;Authentication Models for Personal and Maker Use
&lt;/h2&gt;&lt;p&gt;The authentication model you choose determines &lt;strong&gt;who the agent really is&lt;/strong&gt; in your environment.&lt;/p&gt;
&lt;h3 id="end-user-credentials"&gt;End-User Credentials
&lt;/h3&gt;&lt;p&gt;End-user credentials are ideal for &lt;strong&gt;personal assistants and lightweight automations&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The agent acts exactly as the signed-in user.&lt;/li&gt;
&lt;li&gt;The mental model is simple: &lt;em&gt;“If I can see it, the agent can see it.”&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;It works well for scenarios like notifying you about changes, summarizing your own documents, or automating small workflows tied to your role.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Trade-offs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Token lifecycle:&lt;/strong&gt; Tokens expire; re-authentication is required.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuity risk:&lt;/strong&gt; If the user is deprovisioned, changes roles, or loses a license, the automation breaks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limited scope:&lt;/strong&gt; It’s a poor fit for shared, always-on system workloads.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="maker-credentials"&gt;Maker Credentials
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;What “maker‑provided credentials” means&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Execution identity:&lt;/strong&gt; The tool runs using the connection you configure—such as a service account, app registration, or API key.
Scope: All users of the agent share this single connection. They do not sign in or consent individually.
Use cases: Ideal for back‑end systems without per‑user authentication (e.g., line‑of‑business APIs), actions requiring elevated or consistent permissions, or scenarios that need an application identity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why use maker credentials?&lt;/strong&gt;
They’re commonly chosen when a builder wants the agent to act on behalf of a team rather than an individual—for example:&lt;/p&gt;
&lt;p&gt;Posting updates into a shared Teams channel&lt;/p&gt;
&lt;p&gt;They can be acceptable for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prototypes and proof-of-concepts&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low-risk internal workflows&lt;/strong&gt; with clear ownership and limited data scope&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, they come with serious governance risks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Oversharing:&lt;/strong&gt; Remember that the agents access everything the maker can access. As makers are employees - like everyone else - all users of the agent will have access to the same data as the maker. This can lead to unintentional data exposure. The responses are in the security context of the maker - this is likely not appropriate for most scenarios.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accountability:&lt;/strong&gt; It can be hard to determine whether an action came from the maker or from the agent.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fragility:&lt;/strong&gt; If the maker leaves the organization or loses access, the agent quietly stops working.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="best-practices-for-using-maker-credentials"&gt;Best Practices for Using Maker Credentials
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Agents with maker credentials could be used to work for a small team, but it is important to understand the risks and limitations. For example, an agent that posts updates to a team channel based on a shared SharePoint library could be built using maker credentials. Since the agent operates with the maker&amp;rsquo;s permissions, it may inadvertently expose sensitive data to team members who should not have access. Therefore, while maker credentials can be useful for small team scenarios, it is crucial to assess the risks and consider transitioning to more robust authentication methods as the agent&amp;rsquo;s role expands.&lt;/li&gt;
&lt;li&gt;Agents with maker credentials should not be used for enterprise-wide deployments or scenarios involving sensitive data. As the agent&amp;rsquo;s responsibilities grow, it is advisable to shift to dedicated identities like Agent ID to ensure proper governance, security, and compliance.&lt;/li&gt;
&lt;li&gt;Agents with maker credentials should not be shared with others, except in very limited scenarios where the risks are well understood and mitigated. Co-development would be one example. Sharing such agents broadly can lead to unintended data exposure and complicate accountability.&lt;/li&gt;
&lt;li&gt;Maker credentials should be treated as &lt;strong&gt;temporary&lt;/strong&gt; and &lt;strong&gt;non-production&lt;/strong&gt; for anything that starts to look enterprise-critical.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See &lt;a class="link" href="#use-end-user-or-maker-credentials-when" &gt;Use End-User or Maker Credentials When&lt;/a&gt; for admin guidance on how to disable maker credentials for autonomous agents.&lt;/p&gt;
&lt;!-- ### Agent ID (Preview of Part 2)
Agent ID is a **dedicated identity** for your agent in Microsoft Entra ID. Instead of running as a person, the agent runs as its own application identity with app-only permissions and full support for Conditional Access and auditing.
In personal and maker scenarios, you mostly need to know that Agent ID exists and that:
- It’s the **destination** for production, enterprise-scale agents
- It solves many of the lifecycle and compliance issues of user-bound tokens
We’ll go deep on **how to use Agent ID**, configure least-privilege permissions, and secure enterprise agents in **Part 2**. --&gt;
&lt;hr&gt;
&lt;h2 id="when-personal-or-maker-authentication-is-and-isnt-acceptable"&gt;When Personal or Maker Authentication Is (and Isn’t) Acceptable
&lt;/h2&gt;&lt;p&gt;A simple way to decide whether to stick with end-user or maker credentials is to look at &lt;strong&gt;potential impact&lt;/strong&gt;, &lt;strong&gt;business criticality&lt;/strong&gt;, and &lt;strong&gt;longevity&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="use-end-user-or-maker-credentials-when"&gt;Use End-User or Maker Credentials When
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;The agent acts mainly on &lt;strong&gt;one person’s&lt;/strong&gt; or &lt;strong&gt;one small team’s&lt;/strong&gt; data&lt;/li&gt;
&lt;li&gt;The data is &lt;strong&gt;low to medium risk&lt;/strong&gt; (internal collaboration, drafts, non-regulated content)&lt;/li&gt;
&lt;li&gt;Interruptions are tolerable (e.g., if tokens expire or the maker is out)&lt;/li&gt;
&lt;li&gt;The goal is &lt;strong&gt;learning, prototyping, or local productivity&lt;/strong&gt;, not shared infrastructure&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="alert alert-caution"&gt;
&lt;p class="alert-heading"&gt;
❗
Caution
&lt;/p&gt;
&lt;p&gt;The &amp;lsquo;&lt;strong&gt;Credentials to use&lt;/strong&gt;&amp;rsquo; are set to &amp;lsquo;&lt;strong&gt;End user credentials&lt;/strong&gt;&amp;rsquo; to ensure the agent uses the connection of the user interacting with it. If enabled by the admin, there is the option to use &amp;lsquo;&lt;strong&gt;Maker-provided credentials&lt;/strong&gt;&amp;rsquo;. This should be selected only if the agent is intended to always use a specific connection regardless of the end user. Note that all interactions will be in the context of the maker&amp;rsquo;s connection - which may have unintended consequences when the agent is shared with others. That is, instead of sending a message to the end user&amp;rsquo;s Teams chat, it would always send to the maker&amp;rsquo;s Teams chat. Additionally, when the access token for the maker&amp;rsquo;s connection expires, the agent will fail until the maker renews the connection. Admins can see here for information on disabling the option for maker-provided credentials: &lt;a class="link" href="https://learn.microsoft.com/en-us/microsoft-copilot-studio/configure-no-maker-authentication" target="_blank" rel="noopener"
&gt;Control maker-provided credentials for authentication&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="move-beyond-usermaker-credentials-when"&gt;Move Beyond User/Maker Credentials When
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;The automation is now &lt;strong&gt;business-critical&lt;/strong&gt; or supports &lt;strong&gt;multiple business units&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;It touches &lt;strong&gt;sensitive or tenant-wide data&lt;/strong&gt; (finance, HR, legal, regulated content)&lt;/li&gt;
&lt;li&gt;It must keep running regardless of who is on vacation, changes roles, or leaves&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;auditable, enforceable&lt;/strong&gt; governance (Conditional Access, DLP, access reviews)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When your answers lean toward these enterprise needs, it’s time to move to &lt;strong&gt;Agent ID and app-only permissions&lt;/strong&gt;—the subject of Part 2.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="licensing-and-billing-for-personal-and-maker-agents"&gt;Licensing and Billing for Personal and Maker Agents
&lt;/h2&gt;&lt;p&gt;Licensing for personal and maker scenarios is typically &lt;strong&gt;user-centric&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Interactive agents and personal automation&lt;/strong&gt; are covered by the user’s existing M365 Copilot license.&lt;/li&gt;
&lt;li&gt;As long as the agent is acting as a &lt;strong&gt;licensed human user&lt;/strong&gt;, and the scenario aligns with your licensing terms, you’re within the expected model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, there is a common anti-pattern:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Anti-pattern:&lt;/strong&gt; Assigning a Copilot license to a “service account” and using it as a robot user.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This approach is problematic because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It violates the spirit (and often the terms) of &lt;strong&gt;user-based licensing&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;It hides enterprise workloads behind a pseudo-user, making governance harder&lt;/li&gt;
&lt;li&gt;It doesn’t scale operationally for hundreds of agents&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The billing for personal autonomous agent scenarios are either zero-cost (included in a users M365 Copilot license) or billed via pay-as-you-go consumption (&lt;strong&gt;Copilot Credits&lt;/strong&gt;).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The billing for enterprise-grade autonomous agents will typically be billed via &lt;strong&gt;Copilot Credits&lt;/strong&gt; and not tied to any individual user license. We’ll cover that in Part 2.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="publishing-autonomous-agents-for-personal-and-maker-use"&gt;Publishing Autonomous Agents for Personal and Maker Use
&lt;/h2&gt;&lt;p&gt;When you create an autonomous agent using end-user or maker credentials, you can &lt;strong&gt;publish and share&lt;/strong&gt; it with others in your organization, but there are important caveats:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Connection setup:&lt;/strong&gt; Each user must set up their own connections to the services the agent uses (e.g., Outlook, Teams). The agent will run in the context of their credentials.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data access:&lt;/strong&gt; The agent can only access data that the individual user or maker has permission to see.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Token lifecycle:&lt;/strong&gt; Each user is responsible for maintaining their own authentication tokens. They may need to re-authenticate periodically.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Governance:&lt;/strong&gt; Admins should monitor the use of personal and maker agents to ensure they comply with organizational policies.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="alert alert-caution"&gt;
&lt;p class="alert-heading"&gt;
❗
It is not considered best practice to share autonomous agents broadly across the organization like interactive agents. Instead:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Limit access to relevant roles or departments.&lt;/li&gt;
&lt;li&gt;Implement governance policies (approval workflows, audit logs).&lt;/li&gt;
&lt;li&gt;Scale sharing only after validating security, compliance, and operational impact.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id="publishing-to-a-channel-is-not-required"&gt;Publishing to a channel is not required
&lt;/h2&gt;&lt;p&gt;Autonomous agents created with Copilot Studio do not need to be published to a channel like interactive agents do.&lt;/p&gt;
&lt;p&gt;Here’s why:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Interactive agents are designed for user-driven conversations in Copilot, Teams, the web or other channels, so publishing to a channel is required to make them accessible to end users.&lt;/li&gt;
&lt;li&gt;Autonomous agents, on the other hand do not require user interaction. They are typically triggered by events or schedules and operate without direct user interaction. Because of this, they don’t require channel publishing—they execute based on their configured triggers and permissions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="checklist-for-safe-personal-automation"&gt;Checklist for Safe Personal Automation
&lt;/h2&gt;&lt;p&gt;Even for “small” automations, you can adopt habits that make later scaling much easier.&lt;/p&gt;
&lt;h3 id="1-know-which-identity-the-agent-uses"&gt;1. Know Which Identity the Agent Uses
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Confirm whether the agent is running as &lt;strong&gt;you&lt;/strong&gt;, as a &lt;strong&gt;maker account&lt;/strong&gt;, or as an &lt;strong&gt;Agent ID&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Document that choice with a short description and owner.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-scope-data-access-narrowly"&gt;2. Scope Data Access Narrowly
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Limit access to the minimum sites, teams, mailboxes, or file locations required.&lt;/li&gt;
&lt;li&gt;Regularly review what data the agent can access and adjust as needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-avoid-service-accounts-with-user-licenses"&gt;3. Avoid Service Accounts with User Licenses
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Do not use shared mailboxes or generic “robot users” with end-user licenses to run autonomous agents.&lt;/li&gt;
&lt;li&gt;If a scenario truly behaves like a system, treat it as a system—use Agent ID.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4-plan-for-lifecycle-from-day-one"&gt;4. Plan for Lifecycle from Day One
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Ask: &lt;em&gt;What happens if I change roles, lose a license, or leave the company?&lt;/em&gt;&lt;br&gt;
For this scenario, admins can leverage access reviews to periodically review the access of makers to ensure that only active makers have access to the agents they created. You can use Microsoft Admin Center to monitor an agent&amp;rsquo;s owner status. See &lt;a class="link" href="https://learn.microsoft.com/en-us/microsoft-365/admin/manage/agent-registry?view=o365-worldwide#ownerless-shared-agent-management" target="_blank" rel="noopener"
&gt;Ownerless shared agent management&lt;/a&gt;. If reassignment is needed, admins can use PowerShell to reassign the agent to a new maker. See &lt;a class="link" href="https://learn.microsoft.com/en-us/power-platform/release-plan/2025wave2/microsoft-copilot-studio/reassign-agents-owner-powershell" target="_blank" rel="noopener"
&gt;Reassign an agent&amp;rsquo;s owner with PowerShell&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Avoid anchoring critical workflows solely on your personal identity.&lt;/li&gt;
&lt;li&gt;If you see that happening, you’re already in Part 2 territory.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="5-communicate-token-and-consent-requirements"&gt;5. Communicate Token and Consent Requirements
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Make sure any collaborators understand that &lt;strong&gt;tokens expire&lt;/strong&gt; and that they may be prompted to re-authenticate.&lt;/li&gt;
&lt;li&gt;Document the steps needed to re-establish connections if they break.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="whats-next-from-personal-helpers-to-enterprise-infrastructure"&gt;What’s Next: From Personal Helpers to Enterprise Infrastructure
&lt;/h2&gt;&lt;p&gt;Personal and maker-bound agents are a powerful on-ramp. They help you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Learn Copilot Studio’s capabilities quickly&lt;/li&gt;
&lt;li&gt;Deliver value to yourself and your immediate team&lt;/li&gt;
&lt;li&gt;Explore patterns and ideas before involving central IT&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But as soon as your automation begins to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run &lt;strong&gt;continuously&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;Touch &lt;strong&gt;sensitive or broad datasets&lt;/strong&gt;, or&lt;/li&gt;
&lt;li&gt;Become &lt;strong&gt;business-critical&lt;/strong&gt; for many users,&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;you need a different approach.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;Part 2: Enterprise-Grade Autonomous Agents with Agent ID and Least Privilege&lt;/strong&gt;, we’ll walk through:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enabling &lt;strong&gt;Agent ID&lt;/strong&gt; for Copilot Studio&lt;/li&gt;
&lt;li&gt;Designing &lt;strong&gt;least-privilege permission models&lt;/strong&gt; using &lt;code&gt;Sites.Selected&lt;/code&gt; and similar scopes&lt;/li&gt;
&lt;li&gt;Using PowerShell and Microsoft Graph to grant precise access to SharePoint&lt;/li&gt;
&lt;li&gt;Applying &lt;strong&gt;Conditional Access, DLP, and audit&lt;/strong&gt; to keep agents compliant at scale&lt;/li&gt;
&lt;li&gt;Aligning &lt;strong&gt;billing and licensing&lt;/strong&gt; with enterprise expectations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s where your agents stop being sidekicks—and start becoming part of your core automation infrastructure.&lt;/p&gt;</description></item><item><title>Boost Developer Productivity with AI‑Driven Intent Detection and OData Mapping</title><link>https://mjfnet.com/p/boost-developer-productivity-with-aidriven-intent-detection-and-odata-mapping/</link><pubDate>Fri, 07 Nov 2025 08:11:16 -0800</pubDate><guid>https://mjfnet.com/p/boost-developer-productivity-with-aidriven-intent-detection-and-odata-mapping/</guid><description>&lt;img src="https://mjfnet.com/p/boost-developer-productivity-with-aidriven-intent-detection-and-odata-mapping/images/gangwithidea.png" alt="Featured image of post Boost Developer Productivity with AI‑Driven Intent Detection and OData Mapping" /&gt;&lt;!-- Create a blog post outline breaking down how this agent works in detail from the perspective of what AI is doing. --&gt;
&lt;!-- That is I would like to explain To developers unfamiliar with AI how the AI processing is done at the various stages when at runtime. From the parsing of the to the intent detection how that intent is mapped to query that is an Odata query. To me as a develop this is an incredible contribution of AI that eliminates much of the plumbing and drudgery work that would typically be done with a switch statement or nested if statements Now with AI the AI can map a user's intent and from that can derive the proper Odata query and in fact can do rather sophisticated queries. To me this is an incredible advancement in development allowing the developer to really focus on the outcome or what they're trying to achieve rather than spending a lot of time on mundane plumbing and error prone logic. --&gt;
&lt;!-- # Understanding AI-Powered Intent Detection and OData Query Mapping
### Going behind the scenes with the [M365 Roadmap Agent](https://github.com/mjfusa/m365-roadmap-agent) --&gt;
&lt;p&gt;This blog post will dive into how a Copilot agent goes from this:&lt;br&gt;
&lt;em&gt;&amp;ldquo;What are the new features in Microsoft Teams?&amp;rdquo;&lt;/em&gt;&lt;br&gt;
to this:&lt;br&gt;
&lt;code&gt;https://www.microsoft.com/releasecommunications/api/v2/m365?$filter=products/any(p:+contains(tolower(p),+'teams'))&amp;amp;$top=0&amp;amp;$count=true&lt;/code&gt;&lt;br&gt;
and output this:&lt;br&gt;
&lt;img src="https://mjfnet.com/p/boost-developer-productivity-with-aidriven-intent-detection-and-odata-mapping/images/roadmapagentresults.png"
width="957"
height="856"
srcset="https://mjfnet.com/p/boost-developer-productivity-with-aidriven-intent-detection-and-odata-mapping/images/roadmapagentresults_hu_62a9eaaddf698892.png 480w, https://mjfnet.com/p/boost-developer-productivity-with-aidriven-intent-detection-and-odata-mapping/images/roadmapagentresults_hu_4d9e0aedc1159115.png 1024w"
loading="lazy"
alt="M365 Roadmap Agent Results"
class="gallery-image"
data-flex-grow="111"
data-flex-basis="268px"
&gt;&lt;/p&gt;
&lt;h2 id="introduction"&gt;Introduction
&lt;/h2&gt;&lt;p&gt;Developers have long wrestled with interpreting user input—often resorting to writing complex explicit logic to translate intent into actionable queries. This approach works, but it’s tedious, error-prone, and hard to maintain.
Enter AI.
Modern AI tools can automate much of this complexity, freeing developers to focus on building great experiences rather than parsing logic. Instead of manually mapping every possible input, AI can understand user intent and generate sophisticated OData queries on the fly.
In this post, we’ll explore how AI transforms this workflow, using the Copilot Agent - &lt;strong&gt;&lt;a class="link" href="https://github.com/mjfusa/m365-roadmap-agent" target="_blank" rel="noopener"
&gt;M365 Roadmap Agent&lt;/a&gt;&lt;/strong&gt; - as a case study. You’ll see how AI processes user input step by step—from parsing natural language to producing optimized queries—making intent detection smarter, faster, and more scalable.&lt;/p&gt;
&lt;h2 id="parsing-user-input"&gt;Parsing User Input
&lt;/h2&gt;&lt;p&gt;The first step in the process is parsing the user&amp;rsquo;s input. When a user submits a query or request, the AI system analyzes the text to understand its structure and meaning. This involves breaking down the input into smaller components, such as keywords, phrases, and context. Natural Language Processing (NLP) techniques are employed to identify the intent behind the user&amp;rsquo;s request.&lt;/p&gt;
&lt;!-- Be more specific about how Copilot declarative agents do this. Break down the details of how the input is tokenized, how entities are recognized, and how context is maintained throughout the conversation. --&gt;
&lt;p&gt;M365 Copilot agents utilize advanced tokenization techniques to break the input into manageable pieces. Each token is analyzed for its semantic meaning, and entities such as dates, project names, and user roles are recognized using Named Entity Recognition (NER) models. Context is maintained throughout the conversation by keeping track of previous interactions and user preferences, allowing the AI to provide more relevant responses.&lt;/p&gt;
&lt;!-- Speak where this takes place in the 'M365 Roadmap Agent' --&gt;
&lt;p&gt;In the case of the &lt;a class="link" href="https://github.com/mjfusa/m365-roadmap-agent" target="_blank" rel="noopener"
&gt;&lt;strong&gt;M365 Roadmap Agent&lt;/strong&gt;&lt;/a&gt;, when a user inquires about specific features or updates, the AI parses the input to identify key terms related to Microsoft 365 services, feature names, and timelines. This ensures that the subsequent intent detection phase has a clear understanding of what the user is asking for.&lt;/p&gt;
&lt;h2 id="intent-detection"&gt;Intent Detection
&lt;/h2&gt;&lt;p&gt;Once the input is parsed, the AI system moves on to intent detection. This stage involves determining the user&amp;rsquo;s goal or purpose in making the request. Machine learning models are trained on large datasets to recognize patterns in user behavior and language, enabling the AI to accurately classify the intent. Common intents might include searching for information, updating a record, or generating a report. The AI leverages techniques such as classification algorithms and deep learning to enhance its ability to detect intent accurately. By analyzing the context and nuances of the user&amp;rsquo;s input, the AI can differentiate between similar requests and provide precise interpretations of what the user wants to achieve.&lt;/p&gt;
&lt;!-- Speak where this takes place in the 'M365 Roadmap Agent'. Give an example of a common user prompt and how the AI identifies the intent. --&gt;
&lt;p&gt;In the case of the M365 Roadmap Agent, a common user prompt might be, &amp;ldquo;What are the new features in Microsoft Teams?&amp;rdquo; The AI recognizes the intent as a request for information about specific features, allowing it to retrieve the relevant data from the OData service.&lt;/p&gt;
&lt;h2 id="mapping-intent-to-odata-query"&gt;Mapping Intent to OData Query
&lt;/h2&gt;&lt;p&gt;After identifying the user&amp;rsquo;s intent, the AI system maps this intent to an appropriate OData query. OData (Open Data Protocol) is a standardized protocol for building and consuming RESTful APIs. OData provides a uniform way to describe both the data and the data model, enabling interoperability between diverse systems. The AI leverages its understanding of the intent to construct a query that retrieves or manipulates data in accordance with the user&amp;rsquo;s request. This involves selecting the right entities, filters, and parameters to form a valid OData query.&lt;/p&gt;
&lt;!-- This point is very important. Break down how AI is mapping "What are the new features in Microsoft Teams?" to '[https://](https://www.microsoft.com/releasecommunications/api/v2/m365?filter=products%2fany(p%3a+contains(tolower(p)%2c+%27teams%27))&amp;top=0&amp;count=true)' Reference the roles the roadmap-openapi.json file in this project are playing to facilitate this process. --&gt;
&lt;p&gt;In the M365 Roadmap Agent, when the AI detects the intent to retrieve new features in Microsoft Teams, it maps this intent to a specific OData query. The AI constructs the query by identifying the relevant entity (features), applying a filter to narrow down results to those related to Microsoft Teams, and setting parameters for pagination and counting. The resulting OData query looks like this:
&lt;code&gt;https://www.microsoft.com/releasecommunications/api/v2/m365?$filter=products/any(p:+contains(tolower(p),+'teams'))&lt;/code&gt;&lt;br&gt;
Note 1: Note how the AI removed the word &amp;lsquo;Microsoft&amp;rsquo; when translating the user&amp;rsquo;s prompt to the Odata query. The AI understands that &amp;lsquo;Microsoft&amp;rsquo; is not needed in the query since all items are related to Microsoft 365.&lt;br&gt;
Note 2: Note how the AI uses the &lt;code&gt;contains&lt;/code&gt; function to filter results based on the product name, ensuring that only features related to Microsoft Teams are returned.&lt;br&gt;
Note 3: Note how the AI uses &lt;code&gt;tolower&lt;/code&gt; to make the filter case insensitive, enhancing the robustness of the query.&lt;br&gt;
Note 4: Note how the AI uses the &lt;code&gt;$filter&lt;/code&gt; parameter to specify the filtering criteria.&lt;/p&gt;
&lt;h3 id="how-does-the-ai-know-how-to-do-this-mapping"&gt;How does the AI know how to do this mapping?
&lt;/h3&gt;&lt;p&gt;The &lt;a class="link" href="https://github.com/mjfusa/m365-roadmap-agent/blob/main/appPackage/apiSpecificationFile/roadmap-openapi.json" target="_blank" rel="noopener"
&gt;roadmap-openapi.json&lt;/a&gt; file plays a crucial role in this process by providing the AI with the necessary schema and endpoint definitions. It outlines the available entities, their relationships, and the parameters that can be used in OData queries. This structured information allows the AI to accurately map user intents to valid OData queries without requiring manual intervention from developers.&lt;/p&gt;
&lt;!--Detail and breakdown the roles of the description , summary fields and examples objects with illustrations in the mapping process. --&gt;
&lt;p&gt;The &lt;code&gt;description&lt;/code&gt; and &lt;code&gt;summary&lt;/code&gt; fields in the &lt;code&gt;roadmap-openapi.json&lt;/code&gt; file provide contextual information about each API endpoint and its purpose. This helps the AI understand what each endpoint is designed to do, allowing it to select the most appropriate one based on the detected intent. For example, the summary for the features endpoint might read &amp;ldquo;Retrieve a list of new features in Microsoft 365,&amp;rdquo; which directly aligns with the user&amp;rsquo;s request. Additionally, the &lt;code&gt;examples&lt;/code&gt; objects provide sample queries and responses that the AI can reference when constructing its own OData queries. These examples serve as templates, guiding the AI in forming correct syntax and structure for the queries it generates.&lt;/p&gt;
&lt;!-- Differentiate the role of the description and summary fields in this context --&gt;
&lt;p&gt;The &lt;code&gt;summary&lt;/code&gt; field offers a concise overview of the endpoint&amp;rsquo;s functionality, making it easier for the AI to quickly grasp its purpose during the mapping process. In contrast, the &lt;code&gt;description&lt;/code&gt; field provides a more detailed explanation, including any nuances or specific use cases that may influence how the AI constructs the query. By leveraging both fields, the AI can ensure that it accurately interprets the user&amp;rsquo;s intent and generates an appropriate OData query that aligns with the intended functionality of the API.&lt;/p&gt;
&lt;!-- for example in this excerpt from the roadmap-openapi.json file --&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;paths&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;#34;/m365&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;get&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;summary&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Get Microsoft 365 roadmap items&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Retrieve roadmap items with optional filtering, sorting, and pagination. Always use &amp;#39;created&amp;#39; field for date operations, not &amp;#39;createdDateTime&amp;#39;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;operationId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;getM365RoadmapInfo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;!-- Explanation of the example --&gt;
&lt;p&gt;In this example, the &lt;code&gt;summary&lt;/code&gt; field provides a brief overview of the endpoint&amp;rsquo;s purpose, while the &lt;code&gt;description&lt;/code&gt; field offers more detailed information about its functionality and usage. The AI can use this information to accurately map user intents to the appropriate API endpoint and construct valid OData queries.&lt;/p&gt;
&lt;!-- Explanation of Importance of examples when delineating parameters in Open API --&gt;
&lt;p&gt;The &lt;code&gt;examples&lt;/code&gt; section in the OpenAPI specification is vital for delineating parameters and guiding the AI in query construction. By providing concrete examples of how to use various parameters, the AI can better understand the expected input formats and values. This helps prevent errors in query generation and ensures that the AI can produce OData queries that are both syntactically correct and semantically meaningful. For instance, if an example demonstrates how to filter results by a specific product name, the AI can replicate this structure when generating queries based on user requests.&lt;/p&gt;
&lt;!-- for example in this excerpt of a parameter's example from the roadmap-openapi.json file --&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;examples&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;teams_items&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;summary&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Teams items&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Find roadmap items containing &amp;#39;teams&amp;#39; in title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;contains(tolower(title), &amp;#39;teams&amp;#39;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;In this example, the &lt;code&gt;examples&lt;/code&gt; section provides a clear illustration of how to filter roadmap items by title. The AI can use this example to understand how to construct similar filters when responding to user queries about specific products or features.&lt;/p&gt;
&lt;!-- I see the 'value' what does this do? I see it is not in the prior examples --&gt;
&lt;p&gt;The &lt;code&gt;value&lt;/code&gt; field within the &lt;code&gt;examples&lt;/code&gt; section specifies the actual filter expression that can be used in the OData query. In this case, the value &amp;ldquo;contains(tolower(title), &amp;rsquo;teams&amp;rsquo;)&amp;rdquo; indicates that the AI should construct a filter to find roadmap items where the title contains the word &amp;ldquo;teams,&amp;rdquo; regardless of case. This direct example helps the AI understand how to format similar filter expressions when generating queries based on user input.&lt;/p&gt;
&lt;!-- Explain why it's important that these fields clearly different are differentiated from each other --&gt;
&lt;p&gt;Clearly differentiating between the &lt;code&gt;summary&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; fields is crucial for the AI&amp;rsquo;s understanding of the API&amp;rsquo;s functionality. The &lt;code&gt;summary&lt;/code&gt; allows for quick identification of the endpoint&amp;rsquo;s purpose, which is essential for efficient intent mapping. Meanwhile, the &lt;code&gt;description&lt;/code&gt; provides the depth needed to understand any specific requirements or constraints associated with the endpoint. This distinction ensures that the AI can generate accurate and contextually appropriate OData queries that align with both the user&amp;rsquo;s intent and the API&amp;rsquo;s capabilities.&lt;/p&gt;
&lt;!-- Explain why it's important that these fields values from one endpoint to another are differentiated--&gt;
&lt;p&gt;It is also important that the values of these fields are differentiated from one endpoint to another to avoid confusion during the mapping process. If multiple endpoints had similar summaries or descriptions, the AI might struggle to determine which endpoint best matches the user&amp;rsquo;s intent. Clear and distinct values help the AI to accurately associate user requests with the correct API functionality, leading to more precise OData query generation and ultimately a better user experience.&lt;/p&gt;
&lt;h3 id="how-is-the-openapi-file-generated"&gt;How is the OpenAPI file generated?
&lt;/h3&gt;&lt;!-- Explain how the openapi file is generated using GitHub Copilot from sample output and explaining that the M365 Roadmap API uses an OData interface. Because OData is a standard protocol for querying and updating data, the openapi file can be generated by analyzing the API's metadata and endpoints. GitHub Copilot can assist in this process by suggesting the structure and content of the openapi file based on the API's characteristics and sample outputs. --&gt;
&lt;p&gt;The OpenAPI file for the M365 Roadmap API is generated by analyzing the API&amp;rsquo;s output and endpoints, which are based on the OData protocol. Since OData is a standardized way to query and update data, it provides a clear structure that can be translated into an OpenAPI specification. GitHub Copilot plays a crucial role in this process by suggesting the appropriate structure and content for the OpenAPI file based on the characteristics of the API and sample outputs. By leveraging Copilot&amp;rsquo;s capabilities, developers can efficiently create a comprehensive OpenAPI specification that accurately represents the M365 Roadmap API&amp;rsquo;s functionality and endpoints.&lt;/p&gt;
&lt;!-- Provide a prompt for GitHub Copilot that provides a file of json output from the M365 Roadmap API and generates an OpenAPI specification --&gt;
&lt;p&gt;Here is a sample prompt that can be used with GitHub Copilot to generate an OpenAPI specification from JSON output of the M365 Roadmap API:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given the following JSON output from the M365 Roadmap V2 API &lt;a class="link" href="https://www.microsoft.com/releasecommunications/api/v2/m365" target="_blank" rel="noopener"
&gt;https://www.microsoft.com/releasecommunications/api/v2/m365&lt;/a&gt;, generate an OpenAPI specification that describes the available endpoints, parameters, and responses. The API uses OData protocol for querying and updating data. Please include the necessary components such as paths, operations, parameters, and response schemas.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;@odata.context&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://relcomms-prod-dagnegedescbeefs.b02.azurefd.net/api/v2/$metadata#M365&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;65632&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Excel: Refresh queries from Authenticated Data Sources in Excel for the Web&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;\u003Cdiv\u003EYou can now refresh Power Query data from data sources that require authentication in Excel for the Web. \u003C/div\u003E&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;cloudInstances&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Worldwide (Standard Multi-Tenant)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;GCC&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;GCC High&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;DoD&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;platforms&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Web&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;releaseRings&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;General Availability&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Targeted Release (Entire Organization)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;moreInfoUrls&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;products&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Excel&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;generalAvailabilityDate&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2025-05&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;previewAvailabilityDate&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;created&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2023-08-22T23:16:17.6100000Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;In development&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;modified&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2025-04-29T23:15:05.1130514Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;locale&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;availabilities&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;ring&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;General Availability&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;year&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;month&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;May&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Here is the resulting OpenAPI specification generated by GitHub Copilot: &lt;a class="link" href="support/roadmap-openapi.yml" &gt;roadmap-openapi-yml-sample&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="generating-sophisticated-queries"&gt;Generating Sophisticated Queries
&lt;/h2&gt;&lt;p&gt;One of the key advantages of using AI for intent detection and query mapping is the ability to generate sophisticated queries that go beyond simple CRUD (Create, Read, Update, Delete) operations. The AI can analyze the context of the user&amp;rsquo;s request and incorporate additional logic to create more complex queries. For example, if a user asks for a report on sales data for a specific time period, the AI can automatically include date filters, aggregation functions, and other relevant parameters in the OData query.&lt;/p&gt;
&lt;!-- For example the user might ask --&gt;
&lt;p&gt;For example, the user might ask &amp;ldquo;&lt;em&gt;Are there any roadmap items that mention agent in the description are shipping in the next 90 days and mention Purview or Copilot in the product field?&lt;/em&gt;&amp;rdquo; The AI can break down this request into multiple components: filtering by description, map the &amp;lsquo;shipping&amp;rsquo; constraint to the &lt;code&gt;availabilities&lt;/code&gt; field, and applying an OR condition for the product field. The resulting OData query might look like this:&lt;br&gt;
&lt;code&gt;https://www.microsoft.com/releasecommunications/api/v2/m365?$filter=contains(tolower(description), 'agent') and startsWith(shipping, '2023-') and (contains(tolower(product), 'purview') or contains(tolower(product), 'copilot'))](https://www.microsoft.com/releasecommunications/api/v2/m365?filter=contains(tolower(description),+'agent')+and+(products/any(p:+contains(tolower(p),+'purview'))+or+products/any(p:+contains(tolower(p),+'copilot')))+and+(availabilities/any(a:+a/year+eq+2025+and+(a/month+eq+'November'+or+a/month+eq+'December'))+or+availabilities/any(a:+a/year+eq+2026+and+a/month+eq+'January'))&amp;amp;orderby=modified+desc)&lt;/code&gt;&lt;br&gt;
This level of sophistication allows users to make more nuanced requests without needing to understand the underlying query language. The AI handles the complexity, ensuring that users receive accurate and relevant results based on their specific needs.&lt;/p&gt;
&lt;h2 id="reducing-developer-workload"&gt;Reducing Developer Workload
&lt;/h2&gt;&lt;p&gt;By automating the process of intent detection and OData query mapping, AI significantly reduces the workload for developers. Instead of spending hours writing and debugging complex logic, developers can rely on AI to handle these tasks efficiently. This not only speeds up the development process but also minimizes the risk of errors that can arise from manual coding. Developers can now focus on designing better user experiences and delivering value through their applications.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;The integration of AI into the development process for intent detection and OData query mapping represents a significant advancement in software development. By automating the mundane and error-prone aspects of coding, AI empowers developers to concentrate on what truly matters: achieving their desired outcomes and creating innovative solutions. As AI continues to evolve, we can expect even more sophisticated capabilities that will further enhance the development experience.&lt;/p&gt;</description></item><item><title>🚀 Microsoft 365 Copilot Highlights – October 2025</title><link>https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/</link><pubDate>Tue, 04 Nov 2025 10:15:00 -0800</pubDate><guid>https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/</guid><description>&lt;img src="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/cover.png" alt="Featured image of post 🚀 Microsoft 365 Copilot Highlights – October 2025" /&gt;&lt;!-- # ✨ Microsoft Copilot Highlights – October 2025 --&gt;
&lt;p&gt;Welcome to your monthly roundup of exciting updates to Microsoft 365 Copilot! October brought a wave of new features designed to make your workday smoother, smarter, and more insightful. Whether you&amp;rsquo;re prepping for meetings, analyzing employee sentiment, or diving into search results, Copilot is getting better at helping you do more with less effort.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="mc1176368--updated-gpt-5-becomes-the-default-copilot-model"&gt;MC1176368 : (Updated) GPT-5 becomes the default Copilot model
&lt;/h2&gt;&lt;p&gt;&lt;img src="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/gpt5.png"
width="747"
height="343"
srcset="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/gpt5_hu_f94981a3075eafa1.png 480w, https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/gpt5_hu_91a59700f2f1de76.png 1024w"
loading="lazy"
alt="GPT-5"
class="gallery-image"
data-flex-grow="217"
data-flex-basis="522px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br&gt;
Starting in November 2025, Microsoft 365 Copilot will upgrade to GPT-5 as its default model. Initially, users can toggle GPT-5 on or off, but by the end of the rollout, it becomes mandatory. GPT-5 introduces dynamic model routing, which intelligently selects the best-performing variant for each prompt. This means faster, more accurate, and more context-aware responses across all Copilot endpoints.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt;&lt;br&gt;
The rollout happens in two phases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Phase 1&lt;/strong&gt;: GPT-5 is enabled by default, but users can opt out using a toggle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 2&lt;/strong&gt;: GPT-5 becomes the default with no opt-out option. The toggle is removed.
Users can still bypass GPT-5’s deeper reasoning by selecting “Get a quick answer” for simpler queries. This applies across Copilot Chat and other endpoints, except for Teams mobile, which rolls out later.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why it matters&lt;/strong&gt;&lt;br&gt;
This upgrade marks a major leap in AI capability for Microsoft 365 users. With smarter routing and deeper reasoning, users will experience more relevant and efficient assistance. It simplifies workflows and boosts productivity, especially for complex tasks. Organizations should prepare support teams and update documentation to reflect this change.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="mc1181210--microsoft-viva-copilot-employee-experience-outcomes-report-with-viva-glint-in-the-copilot-analytics-dashboard"&gt;MC1181210 : Microsoft Viva: Copilot employee experience outcomes report with Viva Glint in the Copilot Analytics dashboard
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br&gt;
A new Power BI report in Viva Insights connects Microsoft 365 Copilot usage with employee sentiment data from Viva Glint surveys. This feature, rolling out in late November 2025, helps organizations understand how Copilot impacts employee experience. It requires both Viva Glint and Microsoft 365 Copilot licenses and admin setup to integrate the data sources.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt;&lt;br&gt;
The report segments data by Copilot usage patterns, sentiment outcomes, and organizational attributes. Admins must:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assign licenses&lt;/li&gt;
&lt;li&gt;Match employee records&lt;/li&gt;
&lt;li&gt;Configure Entra ID settings&lt;/li&gt;
&lt;li&gt;Set up Viva Glint to Viva Insights integration
Once configured, analysts can run predefined queries to populate the report.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why it matters&lt;/strong&gt;&lt;br&gt;
This feature empowers organizations to make data-driven decisions about Copilot adoption and employee engagement. By linking AI usage to sentiment, leaders can identify areas of success and improvement, enhancing both productivity and workplace satisfaction.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="mc1176366--updated-microsoft-outlook-enhanced-search-experience-with-copilot-in-classic-outlook"&gt;MC1176366 : (Updated) Microsoft Outlook: Enhanced search experience with Copilot in Classic Outlook
&lt;/h2&gt;&lt;p&gt;&lt;img src="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/searchoutlook.png"
width="1152"
height="610"
srcset="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/searchoutlook_hu_71ebc30e45fcb92.png 480w, https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/searchoutlook_hu_5d583bd009306773.png 1024w"
loading="lazy"
alt="Enhanced Outlook Search"
class="gallery-image"
data-flex-grow="188"
data-flex-basis="453px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br&gt;
Classic Outlook is getting a Copilot-powered search enhancement. After performing a search, users will see a side pane with AI-generated summaries of results from documents, Teams messages, and more. Users can interact with the pane, ask follow-up questions, and navigate to cited content. The rollout begins mid-December 2025.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Copilot pane appears after a search.&lt;/li&gt;
&lt;li&gt;Summaries are generated using large language models.&lt;/li&gt;
&lt;li&gt;Users can opt into AI-generated content.&lt;/li&gt;
&lt;li&gt;No admin action is needed; the feature is enabled by default.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why it matters&lt;/strong&gt;&lt;br&gt;
This update makes search results more actionable and insightful. Instead of sifting through documents, users get instant summaries and can dive deeper with follow-up questions. It’s a productivity booster for anyone who relies on Outlook for research or collaboration.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="mc1169081--copilot-meeting-preparation-improvements-and-expanding-to-more-meeting-types"&gt;MC1169081 : Copilot meeting preparation improvements and expanding to more meeting types
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br&gt;
Microsoft Copilot in Outlook is expanding its meeting preparation capabilities to include all meeting types, including 1:1s. Users will receive real-time contextual insights—like summaries of tasks, documents, and goals—directly within the meeting event form. This feature is enabled by default starting mid-October 2025.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Copilot provides contextual insights in the meeting form.&lt;/li&gt;
&lt;li&gt;Users can chat with Copilot to explore meeting goals or confirm action items.&lt;/li&gt;
&lt;li&gt;Suggested prompts guide deeper preparation.&lt;/li&gt;
&lt;li&gt;No admin changes are needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why it matters&lt;/strong&gt;&lt;br&gt;
This update helps users walk into meetings better prepared and more informed. By surfacing relevant content and enabling interactive prep, Copilot turns meetings into more productive and focused sessions. It’s especially valuable for busy professionals juggling multiple meetings.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="mc1166852--updated-microsoft-viva-copilot-analytics-launches-new-agent-dashboard"&gt;MC1166852 : (Updated) Microsoft Viva Copilot Analytics launches new agent dashboard
&lt;/h2&gt;&lt;p&gt;&lt;img src="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca1.png"
width="1920"
height="1617"
srcset="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca1_hu_332d7c116a02e74c.png 480w, https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca1_hu_598a4bcc318e11fc.png 1024w"
loading="lazy"
alt="Viva Copilot Agent Dashboard"
class="gallery-image"
data-flex-grow="118"
data-flex-basis="284px"
&gt;&lt;br&gt;
&lt;img src="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca2.png"
width="1920"
height="1862"
srcset="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca2_hu_ca3e98c200791a18.png 480w, https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca2_hu_e43163e859a645a8.png 1024w"
loading="lazy"
alt="Monthly and weekly user retention trend"
class="gallery-image"
data-flex-grow="103"
data-flex-basis="247px"
&gt;&lt;br&gt;
&lt;img src="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca3.png"
width="1920"
height="1134"
srcset="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca3_hu_ee3b80254307b415.png 480w, https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca3_hu_ae557a5ba1ceb0b0.png 1024w"
loading="lazy"
alt="Top agents highlights"
class="gallery-image"
data-flex-grow="169"
data-flex-basis="406px"
&gt;&lt;br&gt;
&lt;img src="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca4.png"
width="1920"
height="2096"
srcset="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca4_hu_1d04950033502260.png 480w, https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca4_hu_6ee7d10e768fde0e.png 1024w"
loading="lazy"
alt="Top individual agents"
class="gallery-image"
data-flex-grow="91"
data-flex-basis="219px"
&gt;&lt;br&gt;
&lt;img src="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca5.png"
width="1920"
height="1684"
srcset="https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca5_hu_fc287f224ddf2945.png 480w, https://mjfnet.com/p/microsoft-365-copilot-highlights-october-2025/images/ca5_hu_933785be2cb4aa2f.png 1024w"
loading="lazy"
alt="Agents Adoption by Users"
class="gallery-image"
data-flex-grow="114"
data-flex-basis="273px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;br&gt;
Microsoft Viva is launching a new Agent Dashboard within Copilot Analytics. This dashboard provides detailed insights into agent adoption, usage trends, and Copilot credit consumption. It’s designed for organizations with 50+ Copilot licenses and rolls out in preview in November 2025, with general availability in February 2026.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tracks metrics like active agents, responses, and credit usage.&lt;/li&gt;
&lt;li&gt;Offers retention trends and top agent analysis.&lt;/li&gt;
&lt;li&gt;Integrates with the Copilot Dashboard’s Adoption tab.&lt;/li&gt;
&lt;li&gt;Access is managed via VFAM (Viva Feature Access Management) controls.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why it matters&lt;/strong&gt;&lt;br&gt;
This dashboard gives leaders and analysts the tools to understand how Copilot agents are being used across the organization. It supports strategic planning, helps optimize credit usage, and identifies high-performing agents for deeper analysis.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;The updates to Microsoft Copilot in October 2025 bring significant enhancements across various applications, including Outlook and Viva. With improved search capabilities, meeting preparation tools, and advanced analytics dashboards, users can expect a more integrated and efficient experience. These features are designed to boost productivity, streamline workflows, and provide valuable insights, making Microsoft Copilot an essential tool for modern workplaces.&lt;/p&gt;</description></item><item><title>Unlock Copilot's Full Power: Why @Mentioning Agents Changes Everything!</title><link>https://mjfnet.com/p/copilot-context-user-tip/</link><pubDate>Mon, 03 Nov 2025 00:00:00 +0000</pubDate><guid>https://mjfnet.com/p/copilot-context-user-tip/</guid><description>&lt;img src="https://mjfnet.com/p/copilot-context-user-tip/images/copilot_context_user_tip_the_gang.png" alt="Featured image of post Unlock Copilot's Full Power: Why @Mentioning Agents Changes Everything!" /&gt;&lt;!-- This is a catchy title for the introduction --&gt;
&lt;h2 id="understanding-microsoft-365-copilot-and-agents"&gt;Understanding Microsoft 365 Copilot and Agents
&lt;/h2&gt;&lt;!-- This section grounds the post wrt to Microsoft 365 Copilot. Include a brief overview of Copilot's capabilities and what agents area. Then segue ingto the different ways to interact with agents. And the implications of each approach. --&gt;
&lt;p&gt;Microsoft 365 Copilot is revolutionizing productivity by integrating AI-powered assistance directly into your favorite Microsoft 365 apps. One of its standout features is the ability to use &lt;strong&gt;agents&lt;/strong&gt;—specialized AI tools designed to perform specific tasks, such as summarizing emails, generating reports, or analyzing data.&lt;/p&gt;
&lt;h2 id="two-ways-to-interact-with-copilot-agents"&gt;Two Ways to Interact with Copilot Agents
&lt;/h2&gt;&lt;p&gt;When interacting with these agents, you have two main options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Click the Agent&lt;/strong&gt;: This launches the agent in a focused context, allowing you to interact with it directly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@Mention the Agent in Copilot Chat&lt;/strong&gt;: This keeps the conversation within the broader context of Copilot, enabling more complex workflows.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="the-problem-isolated-contexts"&gt;The Problem: Isolated Contexts
&lt;/h2&gt;&lt;p&gt;Have you ever noticed that when you start an agent by &lt;strong&gt;clicking on it&lt;/strong&gt;, interact with it, and then when you ask for a follow-up action—like &lt;strong&gt;“Create a PowerPoint deck from the agent’s output”&lt;/strong&gt;—Copilot replies with something like &lt;strong&gt;&lt;em&gt;“I’d love to generate the PowerPoint deck for you, but I currently don’t have file creation capabilities enabled in this environment.&amp;quot;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here’s why:&lt;br&gt;
When you launch an agent directly, its conversation runs in an &lt;strong&gt;isolated context&lt;/strong&gt;. Skills and knowledge available to Copilot Chat, are not available in the agent’s context.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/copilot-context-user-tip/images/slide-by-slide-with-without-context.png"
width="2400"
height="1600"
srcset="https://mjfnet.com/p/copilot-context-user-tip/images/slide-by-slide-with-without-context_hu_917960dde8b394c7.png 480w, https://mjfnet.com/p/copilot-context-user-tip/images/slide-by-slide-with-without-context_hu_7a37f1428d774a3c.png 1024w"
loading="lazy"
alt="Left - Agent in isolated context - can’t create PowerPoint deck. Right - Agent output in Chat context. Deck created."
class="gallery-image"
data-flex-grow="150"
data-flex-basis="360px"
&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-solution-use-mentions"&gt;The Solution: Use @Mentions
&lt;/h2&gt;&lt;p&gt;If you &lt;strong&gt;@mention the agent inside Copilot Chat&lt;/strong&gt;, the agent’s responses become part of the ongoing Copilot conversation. &lt;strong&gt;This means you can chain tasks together without losing context&lt;/strong&gt;. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@SalesAgent summarize Q3 pipeline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Close the SalesAgent&lt;/li&gt;
&lt;li&gt;Then: &lt;code&gt;Turn that summary into a PowerPoint deck&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because everything happens in the same Copilot Chat thread, the context persists, and Copilot can build on the agent’s output.&lt;/p&gt;
&lt;p&gt;This an example of context sharing, where the output of one agent can be passed to another agent like &lt;strong&gt;Researcher&lt;/strong&gt; or to a skill - like creating a PowerPoint deck.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="when-to-click-vs-mention"&gt;When to Click vs. @Mention
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Click the Agent&lt;/strong&gt;&lt;br&gt;
✔ When you want a &lt;strong&gt;focused, single-purpose session&lt;/strong&gt;&lt;br&gt;
✔ When you don’t need to reuse the output in Copilot Chat&lt;br&gt;
✔ For deep dives or testing agent-specific flows&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;@Mention the Agent in Copilot Chat&lt;/strong&gt;&lt;br&gt;
✔ When you plan to &lt;strong&gt;chain tasks&lt;/strong&gt; (e.g., summarize → create deck → draft email)&lt;br&gt;
✔ When you want &lt;strong&gt;Copilot to orchestrate follow-up actions&lt;/strong&gt;&lt;br&gt;
✔ For workflows that mix agent output with other Copilot capabilities&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt;&lt;br&gt;
Use &lt;strong&gt;@mentions in Copilot Chat&lt;/strong&gt; whenever you want to keep the agent’s results available for additional steps. Clicking an agent is great for focused tasks, but @mentioning is your best friend for multi-step workflows.&lt;/p&gt;</description></item><item><title>Message Center Agent 2.0 Released - Explore the New Features</title><link>https://mjfnet.com/p/message-center-agent-2.0-released-explore-the-new-features/</link><pubDate>Wed, 29 Oct 2025 07:29:02 -0700</pubDate><guid>https://mjfnet.com/p/message-center-agent-2.0-released-explore-the-new-features/</guid><description>&lt;img src="https://mjfnet.com/p/message-center-agent-2.0-released-explore-the-new-features/images/starterprompts.png" alt="Featured image of post Message Center Agent 2.0 Released - Explore the New Features" /&gt;&lt;h2 id="what-is-the-message-center-agent"&gt;What is the Message Center Agent?
&lt;/h2&gt;&lt;p&gt;The Message Center Agent is a powerful Microsoft 365 Copilot agent designed to streamline how IT administrators and Microsoft 365 users interact with Message Center updates and Microsoft 365 Roadmap information. Built as a declarative agent for Microsoft 365 Copilot, it provides a conversational interface that allows you to query, filter, and explore Microsoft 365 service announcements using natural language.&lt;/p&gt;
&lt;p&gt;Instead of manually navigating through the Message Center portal or searching through lengthy roadmap documents, you can simply ask the agent questions like &amp;ldquo;What updates affect SharePoint?&amp;rdquo; or &amp;ldquo;Show me security-related changes from the last month.&amp;rdquo; The agent intelligently processes your requests and delivers relevant information in an easy-to-digest format, making it an indispensable tool for staying informed about Microsoft 365 changes that impact your organization.&lt;/p&gt;
&lt;h2 id="version-20-release"&gt;Version 2.0 Release
&lt;/h2&gt;&lt;p&gt;We&amp;rsquo;re excited to announce the release of Message Center Agent 2.0—a significant update that brings a suite of powerful new features to make managing Microsoft 365 messages even easier and more efficient. If you&amp;rsquo;re already a user, get ready for even smoother workflows. If you&amp;rsquo;re new, now is the perfect time to jump in and see what the Agent can do for your organization.&lt;/p&gt;
&lt;h2 id="key-features-in-version-20"&gt;Key Features in Version 2.0
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Implemented Paging for Large Message Lists:&lt;/strong&gt;&lt;br&gt;
One of the standout features in this release is the enhanced paging support. Now, when you fetch messages, the total number of available messages is displayed, allowing you to better understand the scope of your data. You can seamlessly navigate through extensive lists using intuitive next, previous, first, and last page prompts, ensuring you never miss an important update.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/message-center-agent-2.0-released-explore-the-new-features/images/paging.png"
width="597"
height="368"
srcset="https://mjfnet.com/p/message-center-agent-2.0-released-explore-the-new-features/images/paging_hu_f38717a56eeb1a1d.png 480w, https://mjfnet.com/p/message-center-agent-2.0-released-explore-the-new-features/images/paging_hu_9620a721ac227cf5.png 1024w"
loading="lazy"
alt="Paging Support Screenshot"
class="gallery-image"
data-flex-grow="162"
data-flex-basis="389px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Inline Microsoft 365 Roadmap Details:&lt;/strong&gt;&lt;br&gt;
Message Center Agent 2.0 now displays Microsoft 365 Roadmap item details inline with each relevant message. These details are presented in a user-friendly adaptive card, including a direct link for deeper exploration on the official Roadmap site. This integration helps you stay informed about the latest product changes and upcoming features, all in one place.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/message-center-agent-2.0-released-explore-the-new-features/images/roadmap.png"
width="548"
height="332"
srcset="https://mjfnet.com/p/message-center-agent-2.0-released-explore-the-new-features/images/roadmap_hu_84be4491aa30a67d.png 480w, https://mjfnet.com/p/message-center-agent-2.0-released-explore-the-new-features/images/roadmap_hu_424faca112d90349.png 1024w"
loading="lazy"
alt="Roadmap Integration Screenshot"
class="gallery-image"
data-flex-grow="165"
data-flex-basis="396px"
&gt;&lt;/p&gt;
&lt;p&gt;Here is the Roadmap detail card displayed within a Message Center message result:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/message-center-agent-2.0-released-explore-the-new-features/images/roadmapAdaptiveCard.png"
width="562"
height="343"
srcset="https://mjfnet.com/p/message-center-agent-2.0-released-explore-the-new-features/images/roadmapAdaptiveCard_hu_940bd72ae8888ef1.png 480w, https://mjfnet.com/p/message-center-agent-2.0-released-explore-the-new-features/images/roadmapAdaptiveCard_hu_a3e284217254cfb.png 1024w"
loading="lazy"
alt="Roadmap Integration Screenshot"
class="gallery-image"
data-flex-grow="163"
data-flex-basis="393px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Expanded Prompting Guide:&lt;/strong&gt;&lt;br&gt;
The documentation has been updated with a comprehensive prompting guide for Message Center updates and Roadmap items. The new guide offers natural-language query patterns and tips for more effective searches, making it easier for both new and experienced users to get the most out of the Message Center Agent. You can find the guide in the repository &lt;a class="link" href="https://github.com/mjfusa/Message-Center-Agent/blob/main/suggested_prompts/prompting_guide.md" target="_blank" rel="noopener"
&gt;here&lt;/a&gt; or via the README’s &lt;code&gt;Suggested Scenarios and Prompts&lt;/code&gt; section.&lt;/p&gt;
&lt;h2 id="try-it-out-or-roll-back-anytime"&gt;Try It Out or Roll Back Anytime
&lt;/h2&gt;&lt;p&gt;If you’d like to try the latest features, download version 2.0 from the repository today. The &lt;a class="link" href="https://github.com/mjfusa/Message-Center-Agent?tab=readme-ov-file#deployment-guide" target="_blank" rel="noopener"
&gt;README&lt;/a&gt; file contains detailed information on provisioning for testing purposes and publishing for use of the agent throughout your organization.&lt;/p&gt;
&lt;p&gt;Prefer the previous version? No problem—you can always revert to version 1.0 by downloading it from the &lt;strong&gt;Releases&lt;/strong&gt; list. Feedback is always welcome, so let the team know what you think by creating an issue in the GitHub repository. &lt;a class="link" href="https://github.com/mjfusa/Message-Center-Agent" target="_blank" rel="noopener"
&gt;https://github.com/mjfusa/Message-Center-Agent&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="thank-you"&gt;Thank you!
&lt;/h2&gt;&lt;p&gt;Thank you to everyone who has supported Message Center Agent’s development. We look forward to seeing how these new features help you stay organized and informed!&lt;/p&gt;</description></item><item><title>🚀 August 2025 Update: Copilot Chat Comes to Your Favorite Microsoft 365 Apps</title><link>https://mjfnet.com/p/august-2025-update-copilot-chat-comes-to-your-favorite-microsoft-365-apps/</link><pubDate>Sat, 30 Aug 2025 12:42:25 -0700</pubDate><guid>https://mjfnet.com/p/august-2025-update-copilot-chat-comes-to-your-favorite-microsoft-365-apps/</guid><description>&lt;img src="https://mjfnet.com/p/august-2025-update-copilot-chat-comes-to-your-favorite-microsoft-365-apps/images/Access_to_AI.jpg" alt="Featured image of post 🚀 August 2025 Update: Copilot Chat Comes to Your Favorite Microsoft 365 Apps" /&gt;&lt;!--
#
**Message Center ID:** MC1096218 --&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;This month’s update marks a &lt;strong&gt;significant expansion&lt;/strong&gt; of what you can do with Copilot inside the Microsoft 365 ecosystem. If you’ve ever wished Copilot could be more seamlessly integrated into your daily workflow—without jumping between apps or chat windows—this is the moment you’ve been waiting for.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/august-2025-update-copilot-chat-comes-to-your-favorite-microsoft-365-apps/images/copilot_chat_in_word.png"
width="1430"
height="804"
srcset="https://mjfnet.com/p/august-2025-update-copilot-chat-comes-to-your-favorite-microsoft-365-apps/images/copilot_chat_in_word_hu_8cf75f5a666ad9ff.png 480w, https://mjfnet.com/p/august-2025-update-copilot-chat-comes-to-your-favorite-microsoft-365-apps/images/copilot_chat_in_word_hu_aee463ca82547d7c.png 1024w"
loading="lazy"
alt="Copilot Chat in Word"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
&gt;&lt;/p&gt;
&lt;p&gt;Microsoft is rolling out &lt;strong&gt;Copilot Chat directly within Word, Excel, PowerPoint, Outlook, and OneNote&lt;/strong&gt;, allowing users to interact with AI in the flow of their work. Whether you&amp;rsquo;re writing, analyzing, presenting, or emailing, Copilot is now just a click away in the ribbon.&lt;/p&gt;
&lt;p&gt;And here’s the exciting part: &lt;strong&gt;For unlicensed users&lt;/strong&gt;, this marks the &lt;strong&gt;first time AI is embedded directly into Microsoft 365 apps&lt;/strong&gt;, offering &lt;strong&gt;web-grounded chat and basic document support&lt;/strong&gt;. That means even without a Copilot license, you can still benefit from intelligent assistance—provided your admin has enabled pinning.&lt;/p&gt;
&lt;!-- The rollout schedule:
- **Outlook (Web, Windows, Mac):** Started in late June
- **Word, Excel, PowerPoint, OneNote (Web, Mac, Windows):** Begins mid-August
- **Mobile and iPad versions:** No earlier than mid-August --&gt;
&lt;hr&gt;
&lt;h2 id="how-it-works"&gt;How It Works
&lt;/h2&gt;&lt;p&gt;Once enabled, you’ll see a &lt;strong&gt;Copilot Chat button in the ribbon&lt;/strong&gt; of supported apps. Clicking it opens a side panel where you can interact with Copilot without leaving your current file.&lt;/p&gt;
&lt;p&gt;The chat respects your last state—if it was open when you closed the file, it’ll reopen automatically next time.&lt;/p&gt;
&lt;p&gt;Access is granted to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Licensed users&lt;/strong&gt; with Microsoft 365 Copilot&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unlicensed users&lt;/strong&gt; if the &lt;strong&gt;Pin Microsoft 365 Copilot Chat&lt;/strong&gt; setting is enabled in the admin center&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Admins can manage this via the Microsoft 365 admin center. If the setting is “Do not pin,” unlicensed users won’t see the chat. Switching it to “Pin” makes Copilot Chat available to everyone.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/august-2025-update-copilot-chat-comes-to-your-favorite-microsoft-365-apps/images/pin_copilot_chat.png"
width="246"
height="393"
srcset="https://mjfnet.com/p/august-2025-update-copilot-chat-comes-to-your-favorite-microsoft-365-apps/images/pin_copilot_chat_hu_4bebd911614623f.png 480w, https://mjfnet.com/p/august-2025-update-copilot-chat-comes-to-your-favorite-microsoft-365-apps/images/pin_copilot_chat_hu_ec7677068357ca34.png 1024w"
loading="lazy"
alt="Pin Microsoft 365 Copilot Chat"
class="gallery-image"
data-flex-grow="62"
data-flex-basis="150px"
&gt;&lt;/p&gt;
&lt;p&gt;No additional configuration is needed unless you want to adjust pinning behavior.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="why-it-matters"&gt;Why It Matters
&lt;/h2&gt;&lt;p&gt;This update is a &lt;strong&gt;game-changer for productivity and accessibility&lt;/strong&gt;. By embedding Copilot Chat directly into the apps we use every day, Microsoft is removing friction and making AI assistance more intuitive and inclusive.&lt;/p&gt;
&lt;p&gt;For licensed users, it’s a smoother, faster way to get help—whether you’re drafting a report, crunching numbers, or preparing a presentation.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;unlicensed users&lt;/strong&gt;, it’s a breakthrough: &lt;strong&gt;AI is now part of the Microsoft 365 experience&lt;/strong&gt;, offering real-time chat and basic document support without needing a full Copilot license.&lt;/p&gt;
&lt;p&gt;From an admin perspective, it’s low-maintenance and high-impact. The only action needed is reviewing your pin settings to ensure users get access.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;This blog post was based on &lt;strong&gt;Microsoft Message Center ID MC1096218&lt;/strong&gt;, retrieved using the &lt;strong&gt;Message Center Agent&lt;/strong&gt;. The update reflects changes rolling out from &lt;strong&gt;June through September 2025&lt;/strong&gt;, and it’s one of the most exciting enhancements to Microsoft 365 Copilot yet.&lt;/p&gt;
&lt;p&gt;🔗 Learn more and manage settings: &lt;a class="link" href="https://learn.microsoft.com/copilot//admin.microsoft.com/#/MessageCenter/:/messages/MC1096218" target="_blank" rel="noopener"
&gt;Manage Microsoft 365 Copilot Chat | Microsoft Learn&lt;/a&gt;&lt;br&gt;
🛠 Generated with the help of &lt;a class="link" href="https://github.com/mjfusa" target="_blank" rel="noopener"
&gt;Message Center Agent&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Managing 429 Errors in Microsoft Graph API – Part 2: Understanding App-Level Rate Limits and Resource Units</title><link>https://mjfnet.com/p/managing-429-errors-in-microsoft-graph-api-part-2-understanding-app-level-rate-limits-and-resource-units/</link><pubDate>Thu, 28 Aug 2025 15:41:34 -0700</pubDate><guid>https://mjfnet.com/p/managing-429-errors-in-microsoft-graph-api-part-2-understanding-app-level-rate-limits-and-resource-units/</guid><description>&lt;img src="https://mjfnet.com/p/managing-429-errors-in-microsoft-graph-api-part-2-understanding-app-level-rate-limits-and-resource-units/images/too_many_request_part_2.jpg" alt="Featured image of post Managing 429 Errors in Microsoft Graph API – Part 2: Understanding App-Level Rate Limits and Resource Units" /&gt;&lt;p&gt;In &lt;a class="link" href="https://mjfnet.com/p/managing-429-errors-in-microsoft-graph-api-a-practical-guide-for-developers/" target="_blank" rel="noopener"
&gt;Part 1&lt;/a&gt;, we explored the fundamentals of 429 errors in Microsoft Graph API, including retry logic, batching, and the nuances of AppResourceUnit (ARU) throttling. In this follow-up, we dive deeper into how rate limits are enforced at the application level, how resource units are consumed in SharePoint Graph API calls, and how telemetry analysis can help optimize usage.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-app-level-rate-limiting-its-not-just-about-services"&gt;🔍 App-Level Rate Limiting: It’s Not Just About Services
&lt;/h2&gt;&lt;p&gt;A common misconception is that throttling is tied to specific Microsoft 365 services like SharePoint or OneDrive. In reality, &lt;strong&gt;rate limits are enforced per client application&lt;/strong&gt;, not per service. This means that every request your app makes—whether via Microsoft Graph, CSOM, or REST—counts toward the app’s usage quota.&lt;/p&gt;
&lt;p&gt;These quotas are &lt;strong&gt;license-based&lt;/strong&gt;, meaning the number of Microsoft 365 licenses in your tenant directly affects your app’s daily limit. For example, an app in a tenant with 5,000–15,000 licenses may have a &lt;strong&gt;24-hour limit of 3.6 million resource units&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-sharepoint-graph-api-resource-units-what-do-your-calls-cost"&gt;📊 SharePoint Graph API Resource Units: What Do Your Calls Cost?
&lt;/h2&gt;&lt;p&gt;Microsoft has introduced a &lt;strong&gt;resource unit model&lt;/strong&gt; to quantify the cost of different Graph API operations. This helps developers understand how their apps consume quota and why throttling may occur.&lt;/p&gt;
&lt;p&gt;Below is a &lt;strong&gt;hypothetical breakdown&lt;/strong&gt; that maps &lt;strong&gt;1.8 million Graph API calls&lt;/strong&gt; to the number of &lt;strong&gt;actual resource units&lt;/strong&gt; consumed. This illustrates the &lt;strong&gt;effective “cost”&lt;/strong&gt; of different types of operations:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rate&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Percent&lt;/th&gt;
&lt;th&gt;Resource Units&lt;/th&gt;
&lt;th&gt;Graph Calls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1.0&lt;/td&gt;
&lt;td&gt;File Download&lt;/td&gt;
&lt;td&gt;67.0%&lt;/td&gt;
&lt;td&gt;1,206,000&lt;/td&gt;
&lt;td&gt;1,800,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;Multi-item Query (e.g., list children)&lt;/td&gt;
&lt;td&gt;10.0%&lt;/td&gt;
&lt;td&gt;360,000&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5.0&lt;/td&gt;
&lt;td&gt;Permissions Operation ($expand=permissions)&lt;/td&gt;
&lt;td&gt;23.0%&lt;/td&gt;
&lt;td&gt;2,070,000&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100.0%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3,636,000&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Note&lt;/strong&gt;: This breakdown is illustrative and not based on actual telemetry. It demonstrates how different Graph API operations carry different resource unit weights. For instance, a single permissions-related call may “cost” five times more than a file download. Understanding this cost model is essential for managing your app’s quota effectively.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For more details, see the official documentation on &lt;a class="link" href="https://learn.microsoft.com/en-us/sharepoint/dev/general-development/how-to-avoid-getting-throttled-or-blocked-in-sharepoint-online#application-throttling" target="_blank" rel="noopener"
&gt;application throttling in SharePoint Online&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-telemetry-analysis-your-optimization-superpower"&gt;📈 Telemetry Analysis: Your Optimization Superpower
&lt;/h2&gt;&lt;p&gt;To truly optimize your app’s Graph API usage, you need visibility into &lt;strong&gt;which operations are being called and how frequently&lt;/strong&gt;. Most apps today are instrumented with telemetry platforms that can capture this data.&lt;/p&gt;
&lt;p&gt;By analyzing telemetry logs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Identify high-cost operations (e.g., frequent permission queries).&lt;/li&gt;
&lt;li&gt;Spot patterns in multi-item queries that could be batched or paginated.&lt;/li&gt;
&lt;li&gt;Evaluate whether downloads are being triggered unnecessarily.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This analysis can help you &lt;strong&gt;reduce resource unit consumption&lt;/strong&gt; without sacrificing functionality.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-bonus-tip-try-sharepoint-service-prioritization"&gt;🚀 Bonus Tip: Try SharePoint Service Prioritization
&lt;/h2&gt;&lt;p&gt;If your app is heavily reliant on SharePoint operations, consider enabling &lt;strong&gt;Service Prioritization&lt;/strong&gt; in SharePoint Online. According to &lt;a class="link" href="https://learn.microsoft.com/en-us/sharepoint/dev/general-development/service-prioritization-in-sharepoint" target="_blank" rel="noopener"
&gt;https://learn.microsoft.com/en-us/sharepoint/dev/general-development/service-prioritization-in-sharepoint&lt;/a&gt;, this feature can provide &lt;strong&gt;10x more capacity&lt;/strong&gt; for SharePoint-specific Graph API calls—potentially alleviating throttling issues for high-volume apps.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Have questions or want to share your own optimization strategies? Drop a comment below or reach out!&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Microsoft 365 Copilot Pre-release Features and Early Access</title><link>https://mjfnet.com/p/microsoft-365-copilot-pre-release-features-and-early-access/</link><pubDate>Tue, 19 Aug 2025 13:33:41 -0700</pubDate><guid>https://mjfnet.com/p/microsoft-365-copilot-pre-release-features-and-early-access/</guid><description>&lt;img src="https://mjfnet.com/p/microsoft-365-copilot-pre-release-features-and-early-access/images/New_Features.png" alt="Featured image of post Microsoft 365 Copilot Pre-release Features and Early Access" /&gt;&lt;h2 id="introduction"&gt;Introduction
&lt;/h2&gt;&lt;p&gt;Early access to Microsoft 365 Copilot features enables organizations to test and provide feedback on new capabilities before they are widely released. This can help organizations prepare for upcoming changes and ensure a smoother transition when features become generally available.&lt;/p&gt;
&lt;h2 id="ways-to-access-pre-release-microsoft-365-copilot-features"&gt;Ways to Access Pre-release Microsoft 365 Copilot Features
&lt;/h2&gt;&lt;h3 id="microsoft-365-targeted-release"&gt;Microsoft 365 &amp;lsquo;Targeted Release&amp;rsquo;
&lt;/h3&gt;&lt;p&gt;&lt;a class="link" href="https://learn.microsoft.com/en-us/microsoft-365/admin/manage/release-options-in-office-365?view=o365-worldwide#targeted-release" target="_blank" rel="noopener"
&gt;Targeted Release&lt;/a&gt; is a Microsoft 365 deployment ring that provides early access to updates before general rollout. It enables early access to new features so that IT admins and selected users can evaluate changes, provide feedback, and prepare for organization-wide adoption. Targeted Release can be configured for the entire organization or specific users. While features in this ring are generally stable, they may still undergo changes before General Availability. It’s commonly used in production environments for early validation—not just for isolated testing.&lt;/p&gt;
&lt;h4 id="how-to-enable-early-access---targeted-release"&gt;How to Enable Early Access - &amp;lsquo;Targeted Release&amp;rsquo;
&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;Go to the Microsoft 365 admin center.&lt;/li&gt;
&lt;li&gt;In the left navigation pane, select &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Org settings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Services&lt;/strong&gt;, select &lt;strong&gt;Microsoft 365 Copilot&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Toggle the setting to enable early access for your organization.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="what-to-expect-in-targeted-release"&gt;What to Expect in Targeted Release
&lt;/h4&gt;&lt;p&gt;You can use the &lt;a class="link" href="https://www.microsoft.com/en-us/microsoft-365/roadmap?filters=%5B%22Microsoft&amp;#43;Copilot&amp;#43;%28Microsoft&amp;#43;365%29%22%2C%22Rolling&amp;#43;out%22%2C%22Targeted&amp;#43;Release%22%5D" target="_blank" rel="noopener"
&gt;Microsoft 365 Roadmap&lt;/a&gt; to stay informed about the features and updates being developed for Microsoft 365 Copilot. It allows organizations to filter and search for specific features, view their status, and understand the timeline for their availability. The following is an explanation of the &amp;lsquo;Release phase&amp;rsquo; filter options available in the tool.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/microsoft-365-copilot-pre-release-features-and-early-access/images/roadmap-prerelease.png"
width="1112"
height="702"
srcset="https://mjfnet.com/p/microsoft-365-copilot-pre-release-features-and-early-access/images/roadmap-prerelease_hu_5f55f5e25e12e505.png 480w, https://mjfnet.com/p/microsoft-365-copilot-pre-release-features-and-early-access/images/roadmap-prerelease_hu_fb19abe32dda55fa.png 1024w"
loading="lazy"
alt="Microsoft 365 Roadmap"
class="gallery-image"
data-flex-grow="158"
data-flex-basis="380px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Release Phase&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Current Channel&lt;/strong&gt;
The Current Channel is for organizations who want to receive Microsoft 365 updates as soon as they are available. It is the default channel for most organizations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;General Availability&lt;/strong&gt;
General Availability refers to the stage when a feature is fully developed, tested, and ready for all users. It is the most stable version of a feature.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preview&lt;/strong&gt;
The Preview stage is for organizations who want to test new features before they are released to the general public. It allows users to provide feedback and report issues. A Preview feature may still have bugs and is not recommended for production use. Previews are typically available for a limited time and may be subject to change in the &amp;lsquo;Current Channel&amp;rsquo;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Targeted Release&lt;/strong&gt;
See above definition.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="frontier-program"&gt;Frontier Program
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://adoption.microsoft.com/en-us/copilot/frontier-program/" target="_blank" rel="noopener"
&gt;Microsoft&amp;rsquo;s Frontier&lt;/a&gt; is a program that gives customers early access to advanced Copilot features still under development. It lets organizations trial and evaluate new capabilities in their own environments and provide feedback. This helps innovators and early adopters maintain a competitive edge by adopting innovations earlier than the general release.&lt;/p&gt;
&lt;h3 id="program-criteria"&gt;Program Criteria
&lt;/h3&gt;&lt;p&gt;The only criterion is an organization with Microsoft 365 Copilot licenses deployed. The Frontier program is available worldwide to M365 Copilot licensed customers, in English only, until the feature reaches General Availability (GA). Once GA is achieved, the feature will be removed from Frontier. The first agents to be released under Frontier were the Researcher and Analyst agents.&lt;/p&gt;
&lt;h2 id="controlling-access-to-frontier-features"&gt;Controlling access to Frontier features
&lt;/h2&gt;&lt;p&gt;A new setting in the Microsoft Admin Center &amp;lsquo;Copilot Features&amp;rsquo; allows IT admins to control access to the Frontier program and its associated features. This setting enables organizations to manage which users can access these preview and experimental features and provide feedback on new capabilities. The &amp;lsquo;Copilot Features&amp;rsquo; control can be accessed through the Settings page under the Copilot tab.
&lt;img src="https://mjfnet.com/p/microsoft-365-copilot-pre-release-features-and-early-access/images/Admin_Center_Frontier.png"
width="1887"
height="780"
srcset="https://mjfnet.com/p/microsoft-365-copilot-pre-release-features-and-early-access/images/Admin_Center_Frontier_hu_3621e24dbd41acae.png 480w, https://mjfnet.com/p/microsoft-365-copilot-pre-release-features-and-early-access/images/Admin_Center_Frontier_hu_e6595ad6411b0144.png 1024w"
loading="lazy"
alt="Copilot Frontier in Admin Center"
class="gallery-image"
data-flex-grow="241"
data-flex-basis="580px"
&gt;&lt;/p&gt;
&lt;h3 id="-researcher-agent"&gt;🔍 Researcher Agent
&lt;/h3&gt;&lt;p&gt;The Researcher agent is designed to help users perform deep, multi-step research tasks by gathering and summarizing information from both internal work content and the web.&lt;/p&gt;
&lt;p&gt;Overview &amp;amp; How to Use:
&lt;a class="link" href="https://support.microsoft.com/en-us/topic/get-started-with-researcher-in-microsoft-365-copilot-e63ab760-f3de-4c47-ae87-dad601b0e9c4" target="_blank" rel="noopener"
&gt;Get started with Researcher in Microsoft 365 Copilot – Microsoft Support&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Blog Announcement:
&lt;a class="link" href="https://www.microsoft.com/en-us/microsoft-365/blog/2025/03/25/introducing-researcher-and-analyst-in-microsoft-365-copilot/" target="_blank" rel="noopener"
&gt;Introducing Researcher and Analyst in Microsoft 365 Copilot – Microsoft 365 Blog&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="-analyst-agent"&gt;📊 Analyst Agent
&lt;/h3&gt;&lt;p&gt;The Analyst agent acts like a data scientist, helping users analyze complex datasets, generate insights, and create visualizations using natural language prompts.&lt;/p&gt;
&lt;p&gt;Overview &amp;amp; How to Use:
&lt;a class="link" href="https://support.microsoft.com/en-us/topic/get-started-with-researcher-in-microsoft-365-copilot-e63ab760-f3de-4c47-ae87-dad601b0e9c4" target="_blank" rel="noopener"
&gt;Get started with Analyst in Microsoft 365 Copilot – Microsoft Support&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;👉 &lt;a class="link" href="https://adoption.microsoft.com/en-us/copilot/frontier-program/" target="_blank" rel="noopener"
&gt;Learn more about Microsoft Frontier&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="enable-access-to-external-ai-models"&gt;Enable Access to External AI Models
&lt;/h2&gt;&lt;h3 id="-important-notes"&gt;⚠️ Important Notes
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;External models like Anthropic&amp;rsquo;s are &lt;strong&gt;not recommended for production use&lt;/strong&gt; due to potential latency, timeouts, and limited support.&lt;/li&gt;
&lt;li&gt;Data processed by these models is &lt;strong&gt;outside Microsoft’s compliance boundaries&lt;/strong&gt;, and governed by &lt;strong&gt;Anthropic’s terms&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You must be a &lt;strong&gt;Global Administrator&lt;/strong&gt; to enable external models in Microsoft 365 Admin Center.&lt;/li&gt;
&lt;li&gt;Once enabled, external models can be selected in Copilot Studio agent settings under the &lt;strong&gt;Model&lt;/strong&gt; section.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="microsoft-365-admin-center-setup"&gt;Microsoft 365 Admin Center Setup
&lt;/h3&gt;&lt;p&gt;You must first &lt;a class="link" href="https://learn.microsoft.com/en-us/copilot/microsoft-365/connect-to-ai-models" target="_blank" rel="noopener"
&gt;allow access to external models (like Anthropic&amp;rsquo;s Claude)&lt;/a&gt; in the Microsoft 365 Admin Center:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Microsoft 365 Admin Center&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Navigate to:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Copilot → Settings → Data Access → AI Providers for Other Large Language Models&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Under LLM providers, select &lt;strong&gt;Anthropic&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Agree to the &lt;strong&gt;Terms and Conditions&lt;/strong&gt; and click &lt;strong&gt;Allow Provider&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="access-external-models-in-copilot-studio"&gt;Access External Models in Copilot Studio
&lt;/h3&gt;&lt;p&gt;Once Anthropic is enabled in Microsoft 365, &lt;a class="link" href="https://learn.microsoft.com/en-us/microsoft-copilot-studio/authoring-select-external-response-model" target="_blank" rel="noopener"
&gt;configure it in the &lt;strong&gt;Power Platform Admin Center&lt;/strong&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Go to &lt;strong&gt;Power Platform Admin Center&lt;/strong&gt;.&lt;br&gt;
Navigate to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Copilot → Settings → Power Platform → Preview and experimental AI models&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Turn on &lt;strong&gt;&amp;lsquo;Preview and experimental AI models&amp;rsquo;&lt;/strong&gt; for each &lt;strong&gt;environment&lt;/strong&gt; and or &lt;strong&gt;environment group&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="access-external-models-in-copilot-studio-1"&gt;Access External Models in Copilot Studio
&lt;/h3&gt;&lt;p&gt;To select an &lt;a class="link" href="https://learn.microsoft.com/en-us/microsoft-copilot-studio/authoring-select-external-response-model" target="_blank" rel="noopener"
&gt;&lt;strong&gt;external model&lt;/strong&gt; for your agent&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open your agent in &lt;strong&gt;Copilot Studio&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Go to the &lt;strong&gt;Settings&lt;/strong&gt; page.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Model&lt;/strong&gt; section, choose a model labeled &lt;strong&gt;external&lt;/strong&gt; (e.g., Anthropic Claude)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="microsoft-365-insider-program---copilot-not-included"&gt;Microsoft 365 Insider Program - Copilot Not Included
&lt;/h2&gt;&lt;p&gt;The Microsoft 365 Insider Program is designed for users who want to get early access to new features and provide feedback to Microsoft. Insiders can participate in various channels, including the Beta Channel and the Dev Channel, to test upcoming features before they are released to the general public. &lt;strong&gt;Note: This program does not include access to pre-release Copilot features, but does provide early access to Microsoft 365 applications and features.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="program-criteria-1"&gt;Program Criteria
&lt;/h3&gt;&lt;p&gt;To participate in the Microsoft 365 Insider Program, organizations must meet certain criteria, including a commitment to providing feedback and insights on new features and a willingness to collaborate with Microsoft on product development.&lt;/p&gt;
&lt;h3 id="how-to-join"&gt;How to join
&lt;/h3&gt;&lt;p&gt;Organizations interested in joining the Microsoft 365 Insider Program can apply through the Microsoft 365 admin center or by contacting their Microsoft account representative.&lt;/p&gt;
&lt;p&gt;Learn more: &lt;a class="link" href="https://techcommunity.microsoft.com/t5/microsoft-365-insider-program/bg-p/Microsoft365InsiderProgram" target="_blank" rel="noopener"
&gt;Microsoft 365 Insider Program&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="in-summary"&gt;In Summary
&lt;/h2&gt;&lt;p&gt;Microsoft 365 Copilot offers several pathways for organizations to gain early access to innovative features before they reach General Availability. Programs like &lt;strong&gt;Targeted Release&lt;/strong&gt;, and the Copilot Frontier Program enable IT teams and users to evaluate new capabilities, provide feedback, and prepare for broader adoption. While each program has its own criteria and scope, they collectively support a proactive approach to digital transformation and productivity enhancement. By participating in these early access initiatives, organizations can stay ahead of the curve, optimize their workflows, and contribute to the evolution of Microsoft 365 Copilot.&lt;/p&gt;</description></item><item><title>About</title><link>https://mjfnet.com/about/</link><pubDate>Mon, 11 Aug 2025 09:01:04 -0700</pubDate><guid>https://mjfnet.com/about/</guid><description>&lt;h2 id="hi-there-"&gt;Hi there! 👋
&lt;/h2&gt;&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe
src="https://www.youtube.com/embed/SPUKbQbe38E"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
referrerpolicy="strict-origin-when-cross-origin"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;rsquo;m a Senior Cloud Solution Architect at Microsoft with a strong developer background and a passion for helping enterprise customers unlock real business value from their AI investments. I’ve spearheaded innovations like an AI-driven agent that cuts through information overload for IT administrators, enabling faster, more informed decision-making. I specialize in Microsoft 365 Copilot and the Power Platform, partnering with organizations to streamline workflows, automate business processes, and drive meaningful adoption of Microsoft’s AI capabilities. As a frequent speaker on AI and modern work, I enjoy sharing practical insights that help teams work smarter and achieve more with Microsoft technologies.&lt;/p&gt;
&lt;p&gt;Please feel free to contact me on BlueSky &lt;a class="link" href="https://bsky.app/profile/mjfnet.com" target="_blank" rel="noopener"
&gt;@mjfnet.com&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Managing 429 Errors in Microsoft Graph API a Practical Guide for Developers</title><link>https://mjfnet.com/p/managing-429-errors-in-microsoft-graph-api-a-practical-guide-for-developers/</link><pubDate>Tue, 05 Aug 2025 07:29:02 -0700</pubDate><guid>https://mjfnet.com/p/managing-429-errors-in-microsoft-graph-api-a-practical-guide-for-developers/</guid><description>&lt;img src="https://mjfnet.com/p/managing-429-errors-in-microsoft-graph-api-a-practical-guide-for-developers/images/toomanyrequests.png" alt="Featured image of post Managing 429 Errors in Microsoft Graph API a Practical Guide for Developers" /&gt;&lt;!-- # 🧭 Managing 429 Errors in Microsoft Graph API: A Practical Guide for Developers
--&gt;
&lt;p&gt;&lt;em&gt;This post was updated with new insights on AppResourceUnit throttling and Graph API call batching. (Spoiler: Batching has no impact on throttling limits.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Encountering a &lt;code&gt;429 Too Many Requests&lt;/code&gt; error while working with the Microsoft Graph API can be disruptive—especially when building scalable, enterprise-grade solutions. This guide is designed to help developers understand the root causes of these errors and apply proven strategies to mitigate them effectively.&lt;/p&gt;
&lt;p&gt;Whether you&amp;rsquo;re dealing with standard retry throttling or the more nuanced AppResourceUnit (ARU) throttling, this post provides actionable guidance to help you maintain application performance and reliability.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-understanding-429-errors"&gt;🚦 Understanding 429 Errors
&lt;/h2&gt;&lt;p&gt;A 429 error indicates that your application is exceeding the allowed request rate. Microsoft Graph enforces throttling to ensure service stability and equitable resource distribution across tenants. When throttling is triggered, the API responds with a 429 status code and typically includes a &lt;code&gt;Retry-After&lt;/code&gt; header indicating when it is safe to retry the request.&lt;/p&gt;
&lt;p&gt;There are two primary types of 429 errors:&lt;/p&gt;
&lt;h3 id="standard-retry-throttling"&gt;Standard Retry Throttling
&lt;/h3&gt;&lt;p&gt;This occurs when your app surpasses general request thresholds. The recommended approach is to implement exponential backoff and respect the &lt;code&gt;Retry-After&lt;/code&gt; header.&lt;/p&gt;
&lt;h3 id="appresourceunit-aru-throttling"&gt;AppResourceUnit (ARU) Throttling
&lt;/h3&gt;&lt;p&gt;This form of throttling is tied to your tenant’s license-based resource allocation and requires a more strategic approach.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-what-is-appresourceunit-throttling"&gt;🔧 What Is AppResourceUnit Throttling?
&lt;/h2&gt;&lt;p&gt;ARU throttling is a resource-based control mechanism used in Microsoft 365 services such as SharePoint Online and Microsoft Graph. It limits API usage based on the number and type of licenses (e.g., E3, E5) assigned to your tenant.
See here for more information on AppResourceUnit throttling: &lt;a class="link" href="https://learn.microsoft.com/en-us/sharepoint/dev/general-development/how-to-avoid-getting-throttled-or-blocked-in-sharepoint-online#application-throttling" target="_blank" rel="noopener"
&gt;Application Throttling&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="key-characteristics"&gt;Key Characteristics:
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Error Codes&lt;/strong&gt;: &lt;code&gt;429_AppResourceUnit&lt;/code&gt;, &lt;code&gt;429_AppResourceUnitDaily&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quota Enforcement&lt;/strong&gt;: Determined by license count and not adjustable via support or licensing changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Common Triggers&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;High-volume operations (e.g., scanning millions of files)&lt;/li&gt;
&lt;li&gt;Exceeding tenant-wide resource unit quotas&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This mechanism is intentionally designed to promote fair usage and maintain service integrity across all tenants.&lt;/p&gt;
&lt;p&gt;See part 2 of this series for a deeper dive into AppResourceUnit throttling here: &lt;a class="link" href="https://mjfnet.com/p/Managing-429-Errors-in-Microsoft-Graph-API-Part-2-Understanding-App-Level-Rate-Limits-and-Resource-Units/" target="_blank" rel="noopener"
&gt;Managing 429 Errors in Microsoft Graph API – Part 2: Understanding App-Level Rate Limits and Resource Units&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="-monitor-and-analyze-usage"&gt;📊 Monitor and Analyze Usage
&lt;/h2&gt;&lt;h3 id="track-tenant-wide-graph-api-usage-by-application"&gt;Track Tenant-Wide Graph API Usage by Application
&lt;/h3&gt;&lt;p&gt;To effectively manage and prevent 429 errors, it&amp;rsquo;s essential to identify which applications in your tenant are consuming the most Microsoft Graph API resources. The most straightforward approach is to leverage the &lt;strong&gt;getApiUsage&lt;/strong&gt; endpoint from the beta version of the Graph Reports API, which provides detailed usage metrics by application:&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://learn.microsoft.com/en-us/graph/api/reportroot-getapiusage?view=graph-rest-beta" target="_blank" rel="noopener"
&gt;GET /reports/getApiUsage(period={period_value},serviceArea={service_area},appId={appId})&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Benefits:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Application-Level Visibility&lt;/strong&gt;: See exactly which apps are making the most API calls&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Time-Based Analysis&lt;/strong&gt;: Track usage patterns over different time periods (D7, D30, D60)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request Volume Metrics&lt;/strong&gt;: Monitor total request counts and identify high-volume consumers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proactive Planning&lt;/strong&gt;: Identify applications that might be approaching throttling limits&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Available Time Periods:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;D7&lt;/code&gt; - Last 7 days&lt;/li&gt;
&lt;li&gt;&lt;code&gt;D30&lt;/code&gt; - Last 30 days&lt;/li&gt;
&lt;li&gt;&lt;code&gt;D60&lt;/code&gt; - Last 60 days&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Response Data Includes:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Application ID&lt;/li&gt;
&lt;li&gt;Total request count by application&lt;/li&gt;
&lt;li&gt;Service area (Always returns &amp;lsquo;Microsoft Exchange&amp;rsquo;. May be subject to change since this is a beta endpoint)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Required Permissions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Reports.Read.All&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Required User Roles&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Global Reader&lt;/li&gt;
&lt;li&gt;Global Administrator&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This data is invaluable for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Capacity Planning&lt;/strong&gt;: Understanding which applications and endpoints drive the highest API usage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Troubleshooting&lt;/strong&gt;: Quickly identifying the source of unexpected throttling events by analyzing endpoint usage patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See this post for a practical example of how to generate a report showing the API usage by application using PowerShell and Excel: &lt;a class="link" href="https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/" target="_blank" rel="noopener"
&gt;Tracking Microsoft Graph API Usage with PowerShell&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;See here for more information on throttling limits: &lt;a class="link" href="https://learn.microsoft.com/en-us/graph/throttling-limits" target="_blank" rel="noopener"
&gt;Microsoft Graph service-specific Graph API throttling limits&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="-recommended-mitigation-strategies"&gt;🛠️ Recommended Mitigation Strategies
&lt;/h2&gt;&lt;p&gt;To ensure your applications remain resilient and performant, consider the following best practices:&lt;/p&gt;
&lt;h3 id="-implement-retry-logic"&gt;✅ Implement Retry Logic
&lt;/h3&gt;&lt;p&gt;Always honor the &lt;code&gt;Retry-After&lt;/code&gt; header and use exponential backoff to avoid overwhelming the service.&lt;/p&gt;
&lt;h3 id="-can-graph-api-request-batching-reduce-throttling"&gt;📦 Can Graph API Request Batching Reduce Throttling?
&lt;/h3&gt;&lt;p&gt;Batching reduces the number of individual HTTP requests by combining them into a single &lt;code&gt;/v1.0/$batch&lt;/code&gt; call. &lt;strong&gt;While this increases the efficiency of your code, it does not impact encountering throttling limits.&lt;/strong&gt; This is because requests in a batch are evaluated individually against the applicable service&amp;rsquo;s throttling limits.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;📘 Learn more: &lt;a class="link" href="https://learn.microsoft.com/graph/json-batching" target="_blank" rel="noopener"
&gt;https://learn.microsoft.com/graph/json-batching&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="-distribute-load-strategically"&gt;⏳ Distribute Load Strategically
&lt;/h3&gt;&lt;p&gt;Avoid sending large volumes of requests in short bursts. Instead, spread API calls over time or across multiple applications to balance the load.&lt;/p&gt;
&lt;h3 id="-use-optimizations-for-onedrive-scanning"&gt;📁 Use optimizations for OneDrive Scanning
&lt;/h3&gt;&lt;p&gt;Uses best practices when scanning OneDrive content to minimize API calls and reduce throttling risk. Here are some specific strategies: &lt;a class="link" href="https://learn.microsoft.com/en-us/onedrive/developer/rest-api/concepts/scan-guidance" target="_blank" rel="noopener"
&gt;https://learn.microsoft.com/en-us/onedrive/developer/rest-api/concepts/scan-guidance&lt;/a&gt;
This includes using delta queries to detect changes incrementally rather than performing full scans. This approach significantly reduces the number of API calls and improves efficiency.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;📘 Learn more: &lt;a class="link" href="https://learn.microsoft.com/onedrive/developer/rest-api/concepts/scan-guidance" target="_blank" rel="noopener"
&gt;https://learn.microsoft.com/onedrive/developer/rest-api/concepts/scan-guidance&lt;/a&gt; and &lt;a class="link" href="https://learn.microsoft.com/onedrive/developer/rest-api/concepts/delta-query-onedrive" target="_blank" rel="noopener"
&gt;https://learn.microsoft.com/onedrive/developer/rest-api/concepts/delta-query-onedrive&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="-leverage-service-prioritization-for-sharepoint"&gt;🥇 Leverage Service Prioritization for SharePoint
&lt;/h3&gt;&lt;p&gt;If your application interacts with SharePoint data, enabling &lt;strong&gt;Service Prioritization&lt;/strong&gt; can provide substantial benefits:&lt;/p&gt;
&lt;h4 id="advantages-of-service-prioritization"&gt;Advantages of Service Prioritization:
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reduced Throttling Risk&lt;/strong&gt;: Prioritized applications are the last to be throttled during high-demand periods.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Increased Resource Limits&lt;/strong&gt;: These apps can scale their usage up to 10x the standard limits when additional capacity is available.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dedicated Resource Units&lt;/strong&gt;: Prioritized apps receive isolated resource units, ensuring consistent performance regardless of other tenant activity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced Support for High-Volume Operations&lt;/strong&gt;: Ideal for scenarios such as compliance scans, content discovery, or data migration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No Code Changes Required&lt;/strong&gt;: Once configured, prioritization benefits apply automatically.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;📘 Learn more: &lt;a class="link" href="https://learn.microsoft.com/en-us/sharepoint/sharepoint-prioritization" target="_blank" rel="noopener"
&gt;https://learn.microsoft.com/en-us/sharepoint/sharepoint-prioritization&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="-consider-microsoft-graph-data-connect-for-large-scale-operations"&gt;🚀 Consider Microsoft Graph Data Connect for Large-Scale Operations
&lt;/h2&gt;&lt;p&gt;For scenarios involving bulk data extraction—such as analytics, compliance, or reporting—consider using Microsoft Graph Data Connect. It is optimized for large-scale operations and is not subject to the same throttling constraints as REST APIs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;📘 Learn more:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/graph/data-connect-concept-overview" target="_blank" rel="noopener"
&gt;https://learn.microsoft.com/graph/data-connect-concept-overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://devblogs.microsoft.com/microsoft365dev/scale-access-to-microsoft-365-data-with-microsoft-graph-data-connect/" target="_blank" rel="noopener"
&gt;https://devblogs.microsoft.com/microsoft365dev/scale-access-to-microsoft-365-data-with-microsoft-graph-data-connect/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-additional-considerations"&gt;🧭 Additional Considerations
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Microsoft actively monitors and categorizes throttling events to improve service reliability.&lt;/li&gt;
&lt;li&gt;Throttling can have cascading effects across applications, even when the root cause is isolated.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-conclusion"&gt;💬 Conclusion
&lt;/h2&gt;&lt;p&gt;Throttling is a fundamental aspect of working with cloud APIs, but it doesn’t have to hinder your development efforts. By understanding the different types of 429 errors and applying these best practices, you can build robust, scalable applications that operate efficiently within Microsoft Graph’s service boundaries.&lt;/p&gt;
&lt;p&gt;If you’re interested in reviewing your tenant’s current API usage or exploring architectural strategies to reduce throttling risk, feel free to reach out.&lt;/p&gt;</description></item><item><title>🔍 Tracking Microsoft Graph API Usage with PowerShell: A Guide for Admins</title><link>https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/</link><pubDate>Mon, 04 Aug 2025 17:18:43 -0700</pubDate><guid>https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/</guid><description>&lt;img src="https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/images/Research.png" alt="Featured image of post 🔍 Tracking Microsoft Graph API Usage with PowerShell: A Guide for Admins" /&gt;&lt;p&gt;As organizations increasingly rely on Microsoft Graph to power their apps and services, understanding which applications are tapping into this powerful API becomes essential for security, governance, and optimization. That’s where the &lt;strong&gt;Graph API Usage Report Script&lt;/strong&gt; steps in—a PowerShell-based solution designed to give administrators clear visibility into Graph API consumption across their tenant.&lt;/p&gt;
&lt;p&gt;A good use case for this script is to track down which applications are making excessive API calls, potentially leading to throttling issues or security concerns. By identifying these applications, you can take proactive measures to optimize their usage or investigate unauthorized access. See this post for more details on &lt;a class="link" href="https://mjfnet.com/p/managing-429-errors-in-microsoft-graph-api-a-practical-guide-for-developers/" target="_blank" rel="noopener"
&gt;Managing 429 Errors (throttling) in Microsoft Graph API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This script not only tracks API usage but also resolves application GUIDs into human-readable names, making it easier to identify which apps are making calls to Microsoft Graph. Whether you&amp;rsquo;re an admin looking to audit app activity or a developer wanting to optimize your API calls, this tool is invaluable.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-what-does-the-script-do"&gt;🚀 What Does the Script Do?
&lt;/h2&gt;&lt;p&gt;The script leverages the &lt;strong&gt;beta &lt;a class="link" href="https://learn.microsoft.com/en-us/graph/api/reportroot-getapiusage?view=graph-rest-beta" target="_blank" rel="noopener"
&gt;getApiUsage&lt;/a&gt;&lt;/strong&gt; endpoint of Microsoft Graph to generate a detailed report of API usage. It retrieves raw usage data and enriches it by resolving application GUIDs into human-readable names—making the data both actionable and admin-friendly.&lt;/p&gt;
&lt;p&gt;Whether you&amp;rsquo;re troubleshooting unexpected API spikes, auditing third-party integrations, or simply trying to understand your app landscape, this script delivers the insights you need.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-prerequisites"&gt;📋 Prerequisites
&lt;/h2&gt;&lt;p&gt;Clone this repository to get started:&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/mjfusa/get-graphapi-usage" target="_blank" rel="noopener"
&gt;https://github.com/mjfusa/get-graphapi-usage&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="required-permissions"&gt;Required Permissions
&lt;/h2&gt;&lt;p&gt;The script requires the following Microsoft Graph API permissions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Reports.Read.All&lt;/code&gt; - To read usage reports&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Application.Read.All&lt;/code&gt; - To read application information&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Directory.Read.All&lt;/code&gt; - To read directory information&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="required-user-roles-for-microsoft-entra"&gt;Required User Roles (for Microsoft Entra)
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reports Reader&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Global Reader&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="-key-components"&gt;🛠️ Key Components
&lt;/h2&gt;&lt;h3 id="install-prerequisitesps1"&gt;&lt;strong&gt;Install-Prerequisites.ps1&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;Before diving into usage tracking, you’ll need to ensure your environment is ready. The &lt;code&gt;Install-Prerequisites.ps1&lt;/code&gt; script installs the necessary Microsoft Graph PowerShell SDK modules:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Install-Module&lt;/span&gt; &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Authentication&lt;/span&gt; &lt;span class="n"&gt;-Force&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Install-Module&lt;/span&gt; &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Applications&lt;/span&gt; &lt;span class="n"&gt;-Force&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Install-Module&lt;/span&gt; &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Reports&lt;/span&gt; &lt;span class="n"&gt;-Force&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;These modules enable authentication, application metadata retrieval, and access to usage reports—everything the main script depends on.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="get-graphapiusageps1"&gt;&lt;strong&gt;Get-GraphAPIUsage.ps1&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;This is the main script that does the heavy lifting. Here&amp;rsquo;s what it brings to the table:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Authentication Prompt&lt;/strong&gt;: Secure sign-in with required permissions (&lt;code&gt;Reports.Read.All&lt;/code&gt;, &lt;code&gt;Application.Read.All&lt;/code&gt;, &lt;code&gt;Directory.Read.All&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Usage Data Retrieval&lt;/strong&gt;: Pulls usage metrics from the Graph API&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;App Name Resolution&lt;/strong&gt;: Translates App IDs into recognizable names&lt;/li&gt;
&lt;li&gt;🧾 &lt;strong&gt;Flexible Output&lt;/strong&gt;: View results in the console or export to CSV&lt;/li&gt;
&lt;li&gt;🧮 &lt;strong&gt;Summary Stats&lt;/strong&gt;: Provides a quick overview of usage patterns&lt;/li&gt;
&lt;li&gt;🛡️ &lt;strong&gt;Error Handling&lt;/strong&gt;: Gracefully manages permission issues, missing data, and API hiccups&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Caching&lt;/strong&gt;: Reduces redundant API calls by storing resolved app names during execution&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="example-usage"&gt;Example Usage:
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="nb"&gt;Get-GraphAPIUsage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="n"&gt;-Days&lt;/span&gt; &lt;span class="mf"&gt;7&lt;/span&gt; &lt;span class="n"&gt;-OutputPath&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;C:\Reports\GraphAPIUsage.csv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/images/GetAppUsage.png"
width="809"
height="496"
srcset="https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/images/GetAppUsage_hu_576a96fed333b00b.png 480w, https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/images/GetAppUsage_hu_eccf0c55a954214.png 1024w"
loading="lazy"
alt="Graph API Usage Example"
class="gallery-image"
data-flex-grow="163"
data-flex-basis="391px"
&gt;
This command fetches the last 7 days of usage and saves the report to a CSV file.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-output-snapshot"&gt;📁 Output Snapshot
&lt;/h2&gt;&lt;p&gt;The report includes the following columns:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Date&lt;/strong&gt; – When the API call occurred&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ServiceArea&lt;/strong&gt; – The Graph service area (e.g., Microsoft365, AzureAD)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TenantId&lt;/strong&gt; – Your tenant’s unique identifier&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AppId&lt;/strong&gt; – The GUID of the calling application&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AppName&lt;/strong&gt; – The resolved name of the application&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Usage&lt;/strong&gt; – Number of API requests made&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;Date&amp;#34;,&amp;#34;ServiceArea&amp;#34;,&amp;#34;TenantId&amp;#34;,&amp;#34;AppId&amp;#34;,&amp;#34;AppName&amp;#34;,&amp;#34;Usage&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;2025-08-03 00:00:00Z&amp;#34;,&amp;#34;Microsoft Exchange&amp;#34;,&amp;#34;d3d6872a-0e81-480f-88e4-8e625ee447ba&amp;#34;,&amp;#34;d8615cf7-11a7-4eae-9b2b-426a9c5e27b8&amp;#34;,&amp;#34;MessageCenterAgent-reg&amp;#34;,&amp;#34;13&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;2025-08-02 00:00:00Z&amp;#34;,&amp;#34;Microsoft Exchange&amp;#34;,&amp;#34;d3d6872a-0e81-480f-88e4-8e625ee447ba&amp;#34;,&amp;#34;de8bc8b5-d9f9-48b1-a8ad-b748da725064&amp;#34;,&amp;#34;Graph Explorer&amp;#34;,&amp;#34;1&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;2025-08-02 00:00:00Z&amp;#34;,&amp;#34;Microsoft Exchange&amp;#34;,&amp;#34;d3d6872a-0e81-480f-88e4-8e625ee447ba&amp;#34;,&amp;#34;d8615cf7-11a7-4eae-9b2b-426a9c5e27b8&amp;#34;,&amp;#34;MessageCenterAgent-reg&amp;#34;,&amp;#34;16&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;2025-08-02 00:00:00Z&amp;#34;,&amp;#34;Microsoft Exchange&amp;#34;,&amp;#34;d3d6872a-0e81-480f-88e4-8e625ee447ba&amp;#34;,&amp;#34;00000006-0000-0ff1-ce00-000000000000&amp;#34;,&amp;#34;Microsoft Office 365 Portal&amp;#34;,&amp;#34;7&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;2025-08-01 00:00:00Z&amp;#34;,&amp;#34;Microsoft Exchange&amp;#34;,&amp;#34;d3d6872a-0e81-480f-88e4-8e625ee447ba&amp;#34;,&amp;#34;d8615cf7-11a7-4eae-9b2b-426a9c5e27b8&amp;#34;,&amp;#34;MessageCenterAgent-reg&amp;#34;,&amp;#34;19&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="display-in-excel"&gt;Display in Excel
&lt;/h2&gt;&lt;p&gt;To visualize the data, you can easily open the CSV in Excel. This allows for quick filtering, sorting, and analysis of API usage patterns.&lt;/p&gt;
&lt;p&gt;Here is the CSV data displayed in Excel:
&lt;img src="https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/images/SampleOutput.png"
width="1064"
height="183"
srcset="https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/images/SampleOutput_hu_ff959e6371c335d6.png 480w, https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/images/SampleOutput_hu_a858ef9ac99209a2.png 1024w"
loading="lazy"
alt="Graph API Usage in Excel"
class="gallery-image"
data-flex-grow="581"
data-flex-basis="1395px"
&gt;&lt;/p&gt;
&lt;p&gt;You can create pivot tables to summarize usage by application, service area, or date, helping you identify trends and anomalies at a glance.&lt;/p&gt;
&lt;p&gt;Here is an example of a pivot table summarizing API usage by application:
&lt;img src="https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/images/PivotTable.png"
width="387"
height="225"
srcset="https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/images/PivotTable_hu_1af78e790253472b.png 480w, https://mjfnet.com/p/tracking-microsoft-graph-api-usage-with-powershell-a-guide-for-admins/images/PivotTable_hu_9ed47d58b4f616b7.png 1024w"
loading="lazy"
alt="Pivot Table Example"
class="gallery-image"
data-flex-grow="172"
data-flex-basis="412px"
&gt;&lt;/p&gt;
&lt;h2 id="-summary-of-key-benefits-for-admins"&gt;✅ Summary of Key Benefits for Admins
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;🔐 &lt;strong&gt;Improved Security Oversight&lt;/strong&gt;: Identify unauthorized or excessive API usage&lt;/li&gt;
&lt;li&gt;📋 &lt;strong&gt;Audit Readiness&lt;/strong&gt;: Easily track which apps are accessing sensitive data&lt;/li&gt;
&lt;li&gt;📈 &lt;strong&gt;Performance Optimization&lt;/strong&gt;: Spot usage trends and optimize resource allocation&lt;/li&gt;
&lt;li&gt;🧩 &lt;strong&gt;Integration Clarity&lt;/strong&gt;: Understand how internal and third-party apps interact with Microsoft Graph&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;Informed Decision-Making&lt;/strong&gt;: Use data to guide licensing, permissions, and app lifecycle management&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-conclusion"&gt;📝 Conclusion
&lt;/h2&gt;&lt;p&gt;In this post, we&amp;rsquo;ve explored how to track Microsoft Graph API usage using PowerShell. By leveraging the Microsoft Graph PowerShell SDK, we can easily authenticate, retrieve usage data, and generate insightful reports. The key benefits for admins include improved security oversight, audit readiness, performance optimization, integration clarity, and informed decision-making.&lt;/p&gt;
&lt;p&gt;By implementing the provided scripts and techniques, you can gain a deeper understanding of how your organization interacts with Microsoft Graph and make data-driven decisions to enhance your security posture and resource allocation.&lt;/p&gt;
&lt;p&gt;Happy scripting!&lt;/p&gt;</description></item><item><title>🚀 What’s New with Microsoft Copilot (Microsoft 365) – July 2025 Roundup</title><link>https://mjfnet.com/p/whats-new-with-microsoft-copilot-microsoft-365-july-2025-roundup/</link><pubDate>Sat, 02 Aug 2025 15:06:09 -0700</pubDate><guid>https://mjfnet.com/p/whats-new-with-microsoft-copilot-microsoft-365-july-2025-roundup/</guid><description>&lt;img src="https://mjfnet.com/p/whats-new-with-microsoft-copilot-microsoft-365-july-2025-roundup/images/TheNewStaplerIsHere.png" alt="Featured image of post 🚀 What’s New with Microsoft Copilot (Microsoft 365) – July 2025 Roundup" /&gt;&lt;!-- # 🚀 What’s New with Microsoft Copilot (Microsoft 365) – July 2025 Roundup --&gt;
&lt;p&gt;Hey friends! 👋&lt;/p&gt;
&lt;p&gt;July 2025 was packed with exciting updates for &lt;strong&gt;Microsoft 365 Copilot users&lt;/strong&gt;. Whether you&amp;rsquo;re an admin, a power user, or just curious about what’s coming next, this blog post breaks down the &lt;strong&gt;major changes&lt;/strong&gt; announced in the Microsoft Message Center. These updates are all about making your work smarter, faster, and more personalized.&lt;/p&gt;
&lt;p&gt;Let’s dive into the highlights!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-microsoft-365-copilot-search-general-availability"&gt;🧠 Microsoft 365 Copilot Search General Availability
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Message Center ID:&lt;/strong&gt; MC1108844&lt;br&gt;
&lt;strong&gt;Date Created:&lt;/strong&gt; July 3, 2025&lt;/p&gt;
&lt;h3 id="summary"&gt;Summary
&lt;/h3&gt;&lt;p&gt;Microsoft is rolling out &lt;strong&gt;Copilot Search&lt;/strong&gt;, a powerful AI-driven search experience that integrates across Microsoft 365 apps. It uses Microsoft Graph and connectors to deliver personalized, context-aware answers. Think of it as your smart assistant that understands your work and helps you find exactly what you need—fast.&lt;/p&gt;
&lt;h3 id="how-it-works"&gt;How It Works
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Available on &lt;strong&gt;web, desktop, and mobile&lt;/strong&gt; platforms.&lt;/li&gt;
&lt;li&gt;Uses natural language, behavioral signals, and organizational context.&lt;/li&gt;
&lt;li&gt;Seamlessly integrates with Copilot chat for task execution.&lt;/li&gt;
&lt;li&gt;Requires an eligible Microsoft 365 Copilot license.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="why-it-matters"&gt;Why It Matters
&lt;/h3&gt;&lt;p&gt;This is a game-changer for productivity. Instead of digging through emails or files, Copilot Search brings the right info to you—tailored to your context. It’s like having a supercharged search engine that knows your work style.&lt;/p&gt;
&lt;p&gt;👉 &lt;a class="link" href="https://learn.microsoft.com/en-us/copilot/microsoft-365/microsoft-365-copilot-search" target="_blank" rel="noopener"
&gt;Learn more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-updated-microsoft-365-copilot-in-teams-visual-insight-for-intelligent-recap"&gt;🎥 (Updated) Microsoft 365 Copilot in Teams: Visual Insight for Intelligent Recap
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Message Center ID:&lt;/strong&gt; MC1115314&lt;br&gt;
&lt;strong&gt;Date Created:&lt;/strong&gt; July 15, 2025&lt;/p&gt;
&lt;h3 id="summary-1"&gt;Summary
&lt;/h3&gt;&lt;p&gt;Copilot in Teams is getting smarter! Now, meeting recaps will include &lt;strong&gt;shared-screen content&lt;/strong&gt;, making summaries richer and more complete. This means even the visuals you present during meetings will be captured and summarized.&lt;/p&gt;
&lt;h3 id="how-it-works-1"&gt;How It Works
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Works across &lt;strong&gt;Windows, Mac, Web, iOS, and Android&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Automatically includes shared-screen content in recaps.&lt;/li&gt;
&lt;li&gt;No admin action required—enabled by default.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="why-it-matters-1"&gt;Why It Matters
&lt;/h3&gt;&lt;p&gt;Ever missed a meeting and wished you could see what was shared? Now you can! This update ensures that visual context isn’t lost, making recaps more useful and inclusive.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/whats-new-with-microsoft-copilot-microsoft-365-july-2025-roundup/images/MeetingVisualContentCopilot.png"
width="2996"
height="1736"
srcset="https://mjfnet.com/p/whats-new-with-microsoft-copilot-microsoft-365-july-2025-roundup/images/MeetingVisualContentCopilot_hu_5f90ecfc181ab16f.png 480w, https://mjfnet.com/p/whats-new-with-microsoft-copilot-microsoft-365-july-2025-roundup/images/MeetingVisualContentCopilot_hu_f06beead8de7a9e7.png 1024w"
loading="lazy"
alt="Copilot in Teams Recap"
class="gallery-image"
data-flex-grow="172"
data-flex-basis="414px"
&gt;&lt;/p&gt;
&lt;p&gt;👉 &lt;a class="link" href="https://learn.microsoft.com/microsoftteams/intelligent-recap-calls-meetings" target="_blank" rel="noopener"
&gt;Learn more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-project-manager-agent--sp-site-grounding"&gt;📁 Project Manager Agent – SP Site Grounding
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Message Center ID:&lt;/strong&gt; MC1122155&lt;br&gt;
&lt;strong&gt;Date Created:&lt;/strong&gt; July 25, 2025&lt;/p&gt;
&lt;h3 id="summary-2"&gt;Summary
&lt;/h3&gt;&lt;p&gt;Planner’s Project Manager agent now supports &lt;strong&gt;SharePoint Site grounding&lt;/strong&gt;, allowing it to use encrypted files to improve task creation and execution. This feature is in preview and enhances automation and accuracy in project management.&lt;/p&gt;
&lt;h3 id="how-it-works-2"&gt;How It Works
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;New Plans&lt;/strong&gt;: Grounding enabled by default.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Existing Plans&lt;/strong&gt;: Admins can enable manually.&lt;/li&gt;
&lt;li&gt;Tasks created from encrypted files become unencrypted for export.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="why-it-matters-2"&gt;Why It Matters
&lt;/h3&gt;&lt;p&gt;This update boosts project automation while keeping data secure. It’s perfect for teams managing complex projects with sensitive information.&lt;/p&gt;
&lt;p&gt;👉 &lt;a class="link" href="https://learn.microsoft.com/Planner/turn-off-project-manager-agent" target="_blank" rel="noopener"
&gt;Learn more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-enhanced-personalization-via-communication-memory-in-copilot"&gt;💬 Enhanced Personalization via Communication Memory in Copilot
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Message Center ID:&lt;/strong&gt; MC1127234&lt;br&gt;
&lt;strong&gt;Date Created:&lt;/strong&gt; August 1, 2025&lt;/p&gt;
&lt;h3 id="summary-3"&gt;Summary
&lt;/h3&gt;&lt;p&gt;Starting September, Copilot will use &lt;strong&gt;communication memory&lt;/strong&gt; to personalize responses based on your emails, chats, and meetings. It’s private, AI-powered, and designed to make Copilot smarter about your work.&lt;/p&gt;
&lt;h3 id="how-it-works-3"&gt;How It Works
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Summarizes communications using large language models (LLMs).&lt;/li&gt;
&lt;li&gt;Memory is private and includes source links for review.&lt;/li&gt;
&lt;li&gt;Admins can manage via Microsoft Graph and Entra ID.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="why-it-matters-3"&gt;Why It Matters
&lt;/h3&gt;&lt;p&gt;This feature makes Copilot feel like it truly knows you. It’s not just smart—it’s &lt;strong&gt;your smart&lt;/strong&gt;. Personalized responses mean less time explaining and more time doing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-conclusion"&gt;🎯 Conclusion
&lt;/h2&gt;&lt;p&gt;Microsoft 365 Copilot continues to evolve, bringing smarter, more intuitive features to your daily workflow. From intelligent search to personalized AI memory, July 2025’s updates are all about making your digital workspace more responsive and efficient.&lt;/p&gt;
&lt;p&gt;If you’re an admin, now’s a great time to review licensing and settings to make sure your organization is ready to take full advantage of these features.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-search-criteria"&gt;🔍 Search Criteria
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Date Range:&lt;/strong&gt; Messages created after July 1, 2025&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Impacted:&lt;/strong&gt; Microsoft Copilot (Microsoft 365)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Change Type:&lt;/strong&gt; Major Change&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Source:&lt;/strong&gt; Microsoft Message Center&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generated by:&lt;/strong&gt; &lt;a class="link" href="https://github.com/mjfusa/message-center-agent" target="_blank" rel="noopener"
&gt;https://github.com/mjfusa/message-center-agent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;!-- Generated with the following prompt:
Write a blog post based on the messages created in the last month, is a major change and where 'Microsoft Copilot (Microsoft 365)' is the service impacted. For each message in the blog post include the message title, message center id numbers, Lengthy summary, 'How it works' and 'Why it matters' sections. The blog post should have an upbeat and helpful vibe coming from a friend. The blog post should disclose search criteria, the source of the information (Microsoft Message Center) and generated using the Message Center Agent. Include the link to the message center agent: https://github.com/mjfusa/message-center-agent. The blog post title should indicate the timeframe of the information. The Search Criteria should be displayed at the end. Include a conclusion. Hide citations. If an image is included in the message source include it inline with the corresponding message. Format the blog post for publishing.
--&gt;</description></item><item><title>Optimize Your Content Creation with Microsoft Copilot Agents</title><link>https://mjfnet.com/p/optimize-your-content-creation-with-microsoft-copilot-agents/</link><pubDate>Thu, 17 Jul 2025 15:36:00 -0700</pubDate><guid>https://mjfnet.com/p/optimize-your-content-creation-with-microsoft-copilot-agents/</guid><description>&lt;img src="https://mjfnet.com/p/optimize-your-content-creation-with-microsoft-copilot-agents/images/bloggers.png" alt="Featured image of post Optimize Your Content Creation with Microsoft Copilot Agents" /&gt;&lt;p&gt;In today’s fast-paced digital landscape, content creators are constantly seeking tools that help them work smarter and deliver greater value. Enter &lt;strong&gt;Microsoft Copilot&lt;/strong&gt;—a groundbreaking generative AI solution built into Microsoft 365 that’s transforming how we create, communicate, and stay informed.&lt;/p&gt;
&lt;p&gt;Whether you&amp;rsquo;re drafting updates, writing reports, or summarizing product features, Copilot’s AI capabilities can supercharge your productivity and creativity like never before.&lt;/p&gt;
&lt;h3 id="the-role-of-agents-focusing-experience-and-knowledge"&gt;The Role of Agents: Focusing Experience and Knowledge
&lt;/h3&gt;&lt;p&gt;One of the key innovations behind Copilot’s effectiveness is its use of &lt;strong&gt;agents&lt;/strong&gt;—specialized AI assistants designed to focus on specific user experiences or information sources. These agents make your interactions with Copilot more tailored and relevant.&lt;/p&gt;
&lt;p&gt;By honing in on domains like communications, scheduling, or product updates, agents help you get exactly what you need, when you need it.&lt;/p&gt;
&lt;h3 id="spotlight-on-the-message-center-agent"&gt;Spotlight on the Message Center Agent
&lt;/h3&gt;&lt;p&gt;The &lt;strong&gt;Message Center Agent&lt;/strong&gt; takes Copilot’s utility to the next level for Microsoft 365 users. With direct access to Microsoft 365 product update announcements, this agent helps you efficiently write and organize updates about new features and changes.&lt;/p&gt;
&lt;p&gt;Here’s how it helps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stay Informed&lt;/strong&gt;: Instantly access the latest product updates without sifting through traditional keyword searches.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filter for Relevance&lt;/strong&gt;: Sort updates by product, feature, or severity to ensure you&amp;rsquo;re sharing only the most pertinent information.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Communicate Efficiently&lt;/strong&gt;: Draft targeted announcements or summaries quickly, saving time and reducing the risk of missing important changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This focused approach ensures your content is always timely, accurate, and relevant—whether you&amp;rsquo;re updating your team, crafting customer communications, or maintaining an internal knowledge base.&lt;/p&gt;
&lt;h3 id="example-post-created-using-the-message-center-agent"&gt;Example Post Created Using the Message Center Agent
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Title&lt;/strong&gt;: &lt;a class="link" href="https://mjfnet.com/p/Important-Updates-for-Microsoft-Copilot---June-to-July-2025/" target="_blank" rel="noopener"
&gt;Important Updates for Microsoft Copilot (Microsoft 365) – June to July 2025&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prompt Used to create this post&lt;/strong&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a blog post based on the messages created in the last month tagged with ‘New feature’, marked as a major change, in the category ‘Plan for change’, and where ‘Microsoft Copilot (Microsoft 365)’ is the impacted service.&lt;/p&gt;
&lt;p&gt;For each message, include the message title, message center ID, a summary, and sections for ‘How it works’ and ‘Why it matters’.&lt;/p&gt;
&lt;p&gt;The blog post should have an upbeat and helpful tone, like it’s coming from a friend. It should disclose the search criteria, the source of the information (Microsoft Message Center), and that it was generated using the Message Center Agent.&lt;/p&gt;
&lt;p&gt;Include a link to the Message Center Agent: &lt;a class="link" href="https://github.com/mjfusa/message-center-agent" target="_blank" rel="noopener"
&gt;https://github.com/mjfusa/message-center-agent&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The blog post title should reflect the timeframe of the updates.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="conclusion-unlock-the-future-of-content-creation"&gt;Conclusion: Unlock the Future of Content Creation
&lt;/h3&gt;&lt;p&gt;Microsoft Copilot’s generative AI is revolutionizing the way content creators work—making it easier to produce high-quality, relevant, and engaging materials with less effort.&lt;/p&gt;
&lt;p&gt;By leveraging agents like the &lt;strong&gt;Message Center Agent&lt;/strong&gt;, you can tailor your content creation process even further, ensuring you always have the latest information at your fingertips.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ready to transform your workflow?&lt;/strong&gt; Start exploring Copilot and its agents today, and discover a smarter, more efficient way to create.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;License&lt;/strong&gt;: CC BY-NC-SA 4.0&lt;br&gt;
© 2020–2025 mjfnet | Built with Hugo | Theme: Stack by Jimmy&lt;/p&gt;</description></item><item><title>Important Updates for Microsoft Copilot (Microsoft 365) - June to July 2025</title><link>https://mjfnet.com/p/important-updates-for-microsoft-copilot-microsoft-365-june-to-july-2025/</link><pubDate>Thu, 17 Jul 2025 15:05:18 -0700</pubDate><guid>https://mjfnet.com/p/important-updates-for-microsoft-copilot-microsoft-365-june-to-july-2025/</guid><description>&lt;img src="https://mjfnet.com/p/important-updates-for-microsoft-copilot-microsoft-365-june-to-july-2025/images/excited-yellow-pages.png" alt="Featured image of post Important Updates for Microsoft Copilot (Microsoft 365) - June to July 2025" /&gt;&lt;p&gt;Hey there, tech enthusiasts! 🌟&lt;/p&gt;
&lt;p&gt;I hope you&amp;rsquo;re all doing great. Today, I have some exciting news to share with you about the latest updates for Microsoft Copilot (Microsoft 365). These updates were created in the last month and are tagged with &amp;lsquo;New feature&amp;rsquo;, marked as major changes, and fall under the category &amp;lsquo;Plan for change&amp;rsquo;. Let&amp;rsquo;s dive right in!&lt;/p&gt;
&lt;h3 id="researcher-on-m365-copilot-prompt-box-mc1098163"&gt;Researcher on M365 Copilot Prompt Box (MC1098163)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt;
The Researcher agent in the Microsoft 365 Copilot Prompt Box will be generally available starting July 18, 2025. It integrates deep research capabilities into Microsoft 365 apps, enhancing efficiency for complex research tasks. No admin action is required, but management is possible via the Microsoft 365 admin center.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;
The Researcher agent leverages AI to provide users with comprehensive research capabilities directly within Microsoft 365 apps. This means you can now conduct in-depth research without leaving your workspace, making your workflow smoother and more efficient.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt;
This feature is a game-changer for anyone who needs to conduct research as part of their daily tasks. It saves time and effort by bringing powerful research tools right to your fingertips, allowing you to focus on what truly matters - your work!&lt;/p&gt;
&lt;h3 id="microsoft-365-copilot-search-general-availability-mc1108844"&gt;Microsoft 365 Copilot Search General Availability (MC1108844)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt;
Microsoft 365 Copilot Search, an AI-powered search experience, will be generally available mid-July 2025 for eligible users. It integrates with Microsoft 365 Copilot app across platforms, leveraging Microsoft Graph and connectors for personalized, context-aware answers. Review documentation and ensure proper licensing. Compliance considerations include data processing and AI capabilities.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;
This AI-powered search experience uses Microsoft Graph and connectors to provide personalized, context-aware answers. It integrates seamlessly with the Microsoft 365 Copilot app across various platforms, making it easier than ever to find the information you need.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt;
With this new search capability, finding the right information becomes a breeze. It enhances productivity by delivering precise, context-aware answers, ensuring you spend less time searching and more time doing.&lt;/p&gt;
&lt;h3 id="updated-microsoft-copilot-in-teams-visual-insight-for-intelligent-recap-mc1115314"&gt;(Updated) Microsoft Copilot in Teams: Visual Insight for intelligent recap (MC1115314)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt;
Microsoft Copilot in Teams will soon provide intelligent meeting recaps that include content shared on screen, enhancing the comprehensiveness of meeting summaries. This feature, requiring a Microsoft 365 Copilot license, will roll out globally from mid to late September 2025, with no admin action needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;
The intelligent meeting recaps feature captures and summarizes content shared during meetings, providing a comprehensive overview of what was discussed. This ensures that you never miss out on important details, even if you couldn&amp;rsquo;t attend the meeting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt;
This feature is a lifesaver for busy professionals. It ensures that you stay informed and up-to-date with meeting discussions, even if you couldn&amp;rsquo;t be there in person. It&amp;rsquo;s all about making your work life easier and more efficient.&lt;/p&gt;
&lt;h3 id="conclusion"&gt;Conclusion
&lt;/h3&gt;&lt;p&gt;These updates are set to revolutionize the way we work with Microsoft Copilot (Microsoft 365). From enhanced research capabilities to AI-powered search and intelligent meeting recaps, there&amp;rsquo;s a lot to look forward to. Stay tuned for more updates, and don&amp;rsquo;t forget to check out the Microsoft Message Center for the latest news.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Search Criteria:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Messages created in the last month&lt;/li&gt;
&lt;li&gt;Tagged with &amp;lsquo;New feature&amp;rsquo;&lt;/li&gt;
&lt;li&gt;Marked as a major change&lt;/li&gt;
&lt;li&gt;Category: &amp;lsquo;Plan for change&amp;rsquo;&lt;/li&gt;
&lt;li&gt;Service impacted: &amp;lsquo;Microsoft Copilot (Microsoft 365)&amp;rsquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt;
Microsoft Message Center&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Generated using the Message Center Agent:&lt;/strong&gt;
&lt;a class="link" href="https://github.com/mjfusa/message-center-agent" target="_blank" rel="noopener"
&gt;https://github.com/mjfusa/message-center-agent&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Stay awesome, and happy Copiloting! 😊&lt;/p&gt;
&lt;p&gt;If you need more information or have any questions, feel free to reach out. Until next time! ✈️&lt;/p&gt;
&lt;!-- prompt:
Write a blog post based on the messages created in the last month tagged with 'New feature', is a major change, in the category 'Plan for change' and where 'Microsoft Copilot (Microsoft 365)' is the service impacted. For each message in the blog post include the message title, message center id numbers, Summary, 'How it works' and 'Why it matters' sections. The blog post should have an upbeat and helpful vibe coming from a friend. The blog post should disclose search criteria, the source of the information (Microsoft Message Center) and generated using the Message Center Agent. Include the link to the message center agent: https://github.com/mjfusa/message-center-agent. The blog post title should indicate the timeframe of the information. --&gt;</description></item><item><title>Supercharge Your M365 Copilot Agent with Azure AI Foundry</title><link>https://mjfnet.com/p/supercharge-your-m365-copilot-agent-with-azure-ai-foundry/</link><pubDate>Thu, 19 Jun 2025 10:52:45 -0700</pubDate><guid>https://mjfnet.com/p/supercharge-your-m365-copilot-agent-with-azure-ai-foundry/</guid><description>&lt;p&gt;As organizations increasingly adopt Microsoft 365 Copilot to streamline productivity and automate business processes, the ability to build intelligent, enterprise-aware agents becomes a game-changer. One powerful scenario is integrating a custom M365 Copilot agent with an agent built using Azure AI Foundry—combining the flexibility of local development with the enterprise-grade capabilities of Azure-hosted AI.&lt;/p&gt;
&lt;p&gt;In this post, we’ll explore how to build such an integrated solution using the Microsoft 365 Agents SDK and Azure AI Foundry, and why this architecture is so valuable.&lt;/p&gt;
&lt;h2 id="why-integrate-m365-agents-sdk-with-azure-ai-foundry"&gt;Why Integrate M365 Agents SDK with Azure AI Foundry?
&lt;/h2&gt;&lt;p&gt;The Microsoft 365 Agents SDK allows developers to build multi-channel, production-ready agents that run in Teams, Outlook, and Copilot Chat. Azure AI Foundry, on the other hand, provides a scalable platform to build, customize, and manage AI agents with enterprise data and Retrieval-Augmented Generation (RAG) capabilities.&lt;/p&gt;
&lt;p&gt;By integrating the two, you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build and test agents locally using familiar tools.&lt;/li&gt;
&lt;li&gt;Leverage enterprise data and vectorized knowledge from Azure AI Foundry.&lt;/li&gt;
&lt;li&gt;Deploy your agent directly into Microsoft 365 Copilot Chat for seamless user experiences.&lt;/li&gt;
&lt;li&gt;Maintain a clean separation between UI logic and enterprise intelligence.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="-why-this-matters-for-copilot-chat"&gt;🌟 Why This Matters for Copilot Chat
&lt;/h3&gt;&lt;p&gt;Many organizations are standardizing on Microsoft 365 Copilot as the “UI for AI”—a unified, conversational interface where employees can access intelligent assistance across their work apps. By bringing your custom agent into Copilot Chat, you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Meet users where they already work—inside Word, Excel, Teams, and Outlook.&lt;/li&gt;
&lt;li&gt;Provide consistent, context-aware answers grounded in enterprise data.&lt;/li&gt;
&lt;li&gt;Enable natural language interactions with your internal systems and knowledge bases.&lt;/li&gt;
&lt;li&gt;Reduce the need for custom front-ends or standalone bots.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This approach empowers employees to get help, complete tasks, and make decisions—all within the familiar Copilot experience. It’s a strategic move toward consolidating AI access and improving adoption across the enterprise.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a class="link" href="https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/" target="_blank" rel="noopener"
&gt;Microsoft 365 Agents SDK documentation&lt;/a&gt; for more details on building agents that run in Microsoft 365 Copilot Chat.&lt;/p&gt;
&lt;p&gt;This scenario is covered in this Microsoft Build 2025 session: &lt;a class="link" href="https://build.microsoft.com/en-US/sessions/BRK163?source=sessions" target="_blank" rel="noopener"
&gt;Create agents for Microsoft 365 Copilot with Microsoft 365 Agents SDK&lt;/a&gt;. In the recording, see the &amp;lsquo;EchoBot&amp;rsquo; demo starting at 11:06.&lt;/p&gt;
&lt;h2 id="-try-it-yourself"&gt;👉 Try It Yourself
&lt;/h2&gt;&lt;p&gt;To experience this integration firsthand, Microsoft has created a hands-on lab—featured at Microsoft Build 2025:
&lt;a class="link" href="https://github.com/microsoft/msbuild-lab328-cea-agentssdk" target="_blank" rel="noopener"
&gt;Build25 LAB328 - Build custom engine agents using Microsoft 365 Agents SDK&lt;/a&gt;.
This lab provides step-by-step instructions across five exercises to guide you through building a custom M365 Copilot agent integrated with Azure AI Foundry.&lt;/p&gt;
&lt;p&gt;This lab is designed for developers, cloud solution architects, and IT professionals who want to enhance their Copilot Chat experience with enterprise-aware agents.&lt;/p&gt;
&lt;h2 id="what-youll-build"&gt;What You’ll Build
&lt;/h2&gt;&lt;p&gt;You’ll create a Contoso HR Agent that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Starts as a simple Echo Bot using the M365 Agents SDK.&lt;/li&gt;
&lt;li&gt;Is enhanced with generative AI using Semantic Kernel.&lt;/li&gt;
&lt;li&gt;Connects to an Azure AI Foundry agent for enterprise-aware responses.&lt;/li&gt;
&lt;li&gt;Is deployed into Microsoft Teams and Copilot Chat.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="key-steps-in-the-lab"&gt;Key Steps in the Lab
&lt;/h2&gt;&lt;p&gt;Here’s a high-level overview of the lab journey:&lt;/p&gt;
&lt;h3 id="1-prepare-your-agent-in-azure-ai-foundry"&gt;1. Prepare Your Agent in Azure AI Foundry
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Log into Azure AI Foundry and open a preconfigured project.&lt;/li&gt;
&lt;li&gt;Customize the agent’s identity and behavior (e.g., Contoso HR Agent).&lt;/li&gt;
&lt;li&gt;Upload HR documents to create a vector store for RAG.&lt;/li&gt;
&lt;li&gt;Test the agent in the Agents Playground using realistic employee queries.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-build-a-local-agent-with-m365-agents-sdk"&gt;2. Build a Local Agent with M365 Agents SDK
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Scaffold a new agent project using the Echo Bot template.&lt;/li&gt;
&lt;li&gt;Explore the project structure and test the bot locally using the Test Tool.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-add-generative-ai-capabilities"&gt;3. Add Generative AI Capabilities
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Install Semantic Kernel and other packages.&lt;/li&gt;
&lt;li&gt;Add custom classes for document citations and message tracking.&lt;/li&gt;
&lt;li&gt;Enable your agent to reason and respond intelligently.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4-integrate-with-azure-ai-foundry"&gt;4. Integrate with Azure AI Foundry
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Connect your local agent to the Azure AI Foundry agent using the Agent ID and connection string.&lt;/li&gt;
&lt;li&gt;Update the &lt;code&gt;OnMessageAsync&lt;/code&gt; method to stream responses, manage citations, and apply sensitivity labels.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="5-deploy-to-copilot-chat"&gt;5. Deploy to Copilot Chat
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Update the app manifest to include &lt;code&gt;copilotAgents&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Upload the app to Microsoft Teams and test it in Copilot Chat.&lt;/li&gt;
&lt;li&gt;Use conversation starters to validate the agent&amp;rsquo;s behavior in a real-world scenario.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Integrating Microsoft 365 Agents SDK with Azure AI Foundry represents a powerful approach to building enterprise-aware AI agents that enhance the Microsoft 365 Copilot experience. This architecture allows you to maintain the flexibility of local development while leveraging the scalability and intelligence of Azure&amp;rsquo;s cloud-based AI services.&lt;/p&gt;
&lt;p&gt;By following the hands-on lab, you&amp;rsquo;ll gain practical experience in building agents that not only understand your enterprise data but also integrate seamlessly into the tools your users already know and trust. This is more than just a technical integration—it&amp;rsquo;s a strategic move toward creating a unified AI experience that can transform how your organization works.&lt;/p&gt;
&lt;p&gt;Ready to get started? The lab is waiting for you, and the future of enterprise AI is just a few steps away.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="links-and-resources"&gt;Links and Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://visualstudio.microsoft.com/vs/community/" target="_blank" rel="noopener"
&gt;Install Visual Studio 2022&lt;/a&gt; with the .NET desktop development workload.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/en-us/microsoft-365/agents-sdk/" target="_blank" rel="noopener"
&gt;Microsoft 365 Agents SDK Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Install the &lt;a class="link" href="https://aka.ms/agents-sdk-install" target="_blank" rel="noopener"
&gt;Microsoft 365 Agents SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://learn.microsoft.com/en-us/azure/ai-foundry/" target="_blank" rel="noopener"
&gt;Azure AI Foundry Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://build.microsoft.com/en-US/sessions/BRK163?source=sessions" target="_blank" rel="noopener"
&gt;Microsoft Build 2025 Session: Create agents for Microsoft 365 Copilot with Microsoft 365 Agents SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/microsoft/msbuild-lab328-cea-agentssdk" target="_blank" rel="noopener"
&gt;Microsoft Build 2025 Hands-on Lab: Build a custom M365 Copilot agent integrated with Azure AI Foundry&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;</description></item><item><title>Integrating external AI agents with M365 Copilot</title><link>https://mjfnet.com/p/integrating-external-ai-agents-with-m365-copilot/</link><pubDate>Sat, 19 Apr 2025 10:52:45 -0700</pubDate><guid>https://mjfnet.com/p/integrating-external-ai-agents-with-m365-copilot/</guid><description>&lt;img src="https://mjfnet.com/p/integrating-external-ai-agents-with-m365-copilot/Images/relayFlow2.png" alt="Featured image of post Integrating external AI agents with M365 Copilot" /&gt;&lt;!-- ![Relay Bot Architecture](Images/relayFlow2.png) --&gt;
&lt;h3 id="introduction"&gt;Introduction
&lt;/h3&gt;&lt;p&gt;Microsoft 365 Copilot Agents extend the value generative AI for organizations by enabling ‘focused experiences’. That is, agents that provide actions and knowledge around a specific topic. For example, an HR Helpdesk agent that provides answers related to HR related questions and creates tickets with full user context when a question cannot be answered. Agents can range from simple prompt-and-response agents to more advanced, fully autonomous agents.&lt;/p&gt;
&lt;p&gt;From the maker/developers point of view, the integration of agents into Microsoft 365 is facilitated by the Teams app infrastructure. That is, the &lt;a class="link" href="https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema" target="_blank" rel="noopener"
&gt;Microsoft Teams app manifest&lt;/a&gt; has been extended to support the deployment of agents for use in Microsoft Teams, M365 Copilot, and Microsoft Chat. We will use this extension to convert a simple Teams bot into a Copilot agent.&lt;/p&gt;
&lt;h3 id="calling-an-external-agent-from-copilot"&gt;Calling an external Agent from Copilot
&lt;/h3&gt;&lt;p&gt;In the last couple of years, during this generative AI tsunami, many developers have built agents on various generative AI platforms, including Microsoft’s Azure AI. Customers using these agents have expressed a need to call these agents from M365 Copilot. A simple way to do this is via a simple bot that acts as a relay between Copilot and the external agent.&lt;/p&gt;
&lt;h3 id="relay-bot-with-an-azure-ai-agent"&gt;Relay bot with an Azure AI Agent
&lt;/h3&gt;&lt;p&gt;The relay bot, called from Copilot, passes through the prompt, unchanged to the target agent via an API exposed by the agent. The API returns the response from the target agent back to Copilot. We’re using Azure AI here, but the concepts should apply to any agent platform.&lt;/p&gt;
&lt;p&gt;This architecture requires that the agent is deployed to a service that supports API interactions, such as Azure OpenAI Service, Azure Functions, or any other custom-hosted solution. In my case, I’m using the Azure OpenAI Service.&lt;/p&gt;
&lt;p&gt;Once we have an exposed an API to our target agent that can take a prompt as input and return the results, we&amp;rsquo;re ready for integration with M365 Copilot.&lt;/p&gt;
&lt;h3 id="relay-bot-integration-with-m365-copilot"&gt;Relay Bot integration with M365 Copilot
&lt;/h3&gt;&lt;p&gt;One of the latest updates to the &lt;a class="link" href="https://developer.microsoft.com/json-schemas/teams/v1.20/MicrosoftTeams.schema.json" target="_blank" rel="noopener"
&gt;Microsoft Teams app manifest schema (version 1.20)&lt;/a&gt; introduces significant enhancements. This update introduces a new object &amp;ldquo;copilotAgents&amp;rdquo;, which supports both declarative and custom engine agents. Additionally, &amp;ldquo;bots&amp;rdquo;, &amp;ldquo;commandlists,&amp;rdquo; and &amp;ldquo;commands&amp;rdquo; have been extended to support starter prompts, paving the way for converting a Teams bot into a Copilot agent.&lt;/p&gt;
&lt;h3 id="understanding-the-copilotagents-object"&gt;Understanding the &amp;lsquo;copilotAgents&amp;rsquo; object
&lt;/h3&gt;&lt;p&gt;The &amp;lsquo;copilotAgents&amp;rsquo; object can include either the ‘declarativeAgents’ or ‘customEngineAgents’ properties. ‘declarativeAgents’ brings in the Copilot orchestrator, which we don’t need. We will use ‘customEngineAgents’ here. ‘customEngineAgents’ assumes we will provide our own orchestrator and LLM. As since this is a relay bot, will will not be using the an orchestrator. Our target agent is taking care of that for us. The ‘customEngineAgents’ object is an array of objects, each representing a custom engine agent. Each object in the array has two properties: id and type. The id property is the Microsoft App ID of the bot registered with the Bot Framework, and the type property is set to &amp;ldquo;bot&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Note the value of the id in ‘customEngineAgents’ matches the id of the bot – the relay bot in our case. Also note that scope of the bot is required to be ‘personal’.&lt;/p&gt;
&lt;h3 id="starter-prompts-and-botscommandlistscommands"&gt;Starter Prompts and bots.commandLists.commands
&lt;/h3&gt;&lt;p&gt;Note below how the commands in the commandList specify the sample prompts that are displayed when the bot is invoked from Copilot.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;span class="lnt"&gt;63
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;$schema&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://developer.microsoft.com/json-schemas/teams/v1.20/MicrosoftTeams.schema.json&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;manifestVersion&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1.20&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%MICROSOFT-APP-ID%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;short&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;IT Support Agent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;full&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;IT Support and Assistance Agent&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;short&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Your go-to IT support bot&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;full&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;A comprehensive IT support bot to assist with common IT issues and tasks&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;icons&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;outline&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;outline.png&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;color&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;color.png&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;bots&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;botId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%MICROSOFT-APP-ID-REGISTERED-WITH-BOT-FRAMEWORK%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;scopes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;personal&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;commandLists&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;scopes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;personal&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;commands&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Password Reset&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Guide me through resetting my password&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Software Installation&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Help me install new software&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Network Issues&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Troubleshoot network connectivity problems&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Hardware Support&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Assist with hardware-related issues&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;IT Policies&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Provide information on IT policies and best practices&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;copilotAgents&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;customEngineAgents&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%MICROSOFT-APP-ID-REGISTERED-WITH-BOT-FRAMEWORK%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bot&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="creating-the-relay-bot"&gt;Creating the Relay bot
&lt;/h3&gt;&lt;p&gt;To create the Relay bot, we start with the &lt;a class="link" href="https://learn.microsoft.com/en-us/microsoftteams/platform/toolkit/toolkit-v4/install-teams-toolkit-vs" target="_blank" rel="noopener"
&gt;Teams Toolkit in Visual Studio 2022&lt;/a&gt; and created a C# EchoBot. C# is just a personal preference, many languages are supported including python and JavaScript.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/integrating-external-ai-agents-with-m365-copilot/Images/VSTTK-Create.png"
width="624"
height="429"
srcset="https://mjfnet.com/p/integrating-external-ai-agents-with-m365-copilot/Images/VSTTK-Create_hu_cf7249cd30cd991d.png 480w, https://mjfnet.com/p/integrating-external-ai-agents-with-m365-copilot/Images/VSTTK-Create_hu_a290bbd9b97a1ef5.png 1024w"
loading="lazy"
alt="Visual Studio - Teams Toolkit - Create Bot"
class="gallery-image"
data-flex-grow="145"
data-flex-basis="349px"
&gt;&lt;/p&gt;
&lt;p&gt;In EchoBot.cs, in the &lt;code&gt;OnMessageActivityAsync&lt;/code&gt; method, we call by API exposed by the Azure AI agent:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="n"&gt;OnMessageActivityAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ITurnContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IMessageActivity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;turnContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;messageText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;turnContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RemoveRecipientMention&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="n"&gt;Trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CallSentimentAgent&lt;/span&gt; &lt;span class="n"&gt;callSentimentAgent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CallSentimentAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;replyText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;callSentimentAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CallAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messageText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//var replyText = $&amp;#34;Echo: {messageText}&amp;#34;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;turnContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendActivityAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MessageFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replyText&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="sample-project"&gt;Sample Project
&lt;/h3&gt;&lt;p&gt;A sample project with this implemented can be found here: &lt;a class="link" href="https://github.com/mjfusa/Copilot-Custom-Engine-Agent-Relay-to-Azure-Agent" target="_blank" rel="noopener"
&gt;https://github.com/mjfusa/Copilot-Custom-Engine-Agent-Relay-to-Azure-Agent&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="conclusion"&gt;Conclusion
&lt;/h3&gt;&lt;p&gt;The relay bot architecture provides a simple way to integrate external AI agents with M365 Copilot. By using the Teams app manifest and the new &amp;lsquo;copilotAgents&amp;rsquo; object, we can easily convert a Teams bot into a Copilot agent. This allows us to leverage the capabilities of our existing agents while providing a seamless experience for users in Microsoft 365 applications.&lt;/p&gt;</description></item><item><title>Microsoft 365 Copilot: Sentiment Analysis and Categorization of Survey Feedback</title><link>https://mjfnet.com/p/microsoft-365-copilot-sentiment-analysis-and-categorization-of-survey-feedback/</link><pubDate>Thu, 15 Feb 2024 17:17:51 -0800</pubDate><guid>https://mjfnet.com/p/microsoft-365-copilot-sentiment-analysis-and-categorization-of-survey-feedback/</guid><description>&lt;p&gt;&lt;img src="https://mjfnet.com/post/Copilot%20Sentiment%20Analysis/images/Header1.png"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Surveys are a useful way to measure how happy your clients, employees, customers, or other stakeholders are. They can help you find out the pros and cons of your products, services, or processes, and give you useful feedback for improvement. However, surveys are not just about numbers and ratings. The most useful data from surveys are verbatims, or the open-ended responses that customers write in their own words. Verbatims can offer insights that you can&amp;rsquo;t get from ratings, such as the reasons why customers are satisfied or dissatisfied. Verbatims can also show the feelings and sentiments of customers, which can affect customer loyalty and retention. This post will show how to extract customer sentiment and the drivers of the feedback using Microsoft 365 Copilot.&lt;/p&gt;
&lt;p&gt;A brief example:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Number&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Feedback&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Sentiment&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Theme&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;The hotel was amazing. The staff was friendly and the room was very clean. The location was perfect and the amenities were top-notch. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;The hotel was okay. The room was clean but the staff was not very helpful. The location was not great and the amenities were lacking. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Here are examples of &lt;a class="link" href="https://mjfnet.com/p/microsoft-365-copilot-sentiment-analysis-and-categorization-of-survey-feedback/#report-sentiment-and-theme-analysis-of-hotel-stay-feedback-data" &gt;an analysis report&lt;/a&gt; or &lt;a class="link" href="https://mjfnet.com/p/microsoft-365-copilot-sentiment-analysis-and-categorization-of-survey-feedback/#report-hotel-stay-feedback-analysis" &gt;table output&lt;/a&gt; from your survey data.&lt;/p&gt;
&lt;h2 id="introduction"&gt;Introduction
&lt;/h2&gt;&lt;p&gt;Survey feedback is a valuable source of information for any business that wants to improve its products, services, and customer satisfaction. However, analyzing survey feedback can be a daunting and time-consuming task, especially when you have to deal with large volumes of text data.&lt;/p&gt;
&lt;p&gt;How can you quickly and easily extract insights from your survey feedback without spending hours reading and coding every response? How can you identify the main themes, sentiments, and opinions of your customers and use them to make data-driven decisions?&lt;/p&gt;
&lt;p&gt;The answer is Microsoft 365 Copilot, a new AI-powered tool that helps you analyze and categorize your survey feedback in minutes. In this blog post, we will show you how to use Microsoft 365 Copilot to do sentiment analysis and categorization of survey feedback and how it can benefit your business.&lt;/p&gt;
&lt;h2 id="what-is-microsoft-365-copilot"&gt;What is Microsoft 365 Copilot?
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://www.microsoft.com/en-us/microsoft-365/enterprise/copilot-for-microsoft-365" target="_blank" rel="noopener"
&gt;Microsoft 365 Copilot&lt;/a&gt; is an AI-powered productivity tool designed to enhance your work experience within the Microsoft 365 suite of applications.&lt;/p&gt;
&lt;h2 id="using-microsoft-365-copilot-for-sentiment-analysis-and-categorization-of-survey-feedback"&gt;Using Microsoft 365 Copilot for Sentiment Analysis and Categorization of Survey Feedback
&lt;/h2&gt;&lt;p&gt;Using Microsoft 365 Copilot for sentiment analysis and categorization of survey feedback is easy and intuitive. Typically survey data a spreadsheet for analysis. At the time of this writing, sentiment analysis is not supported by Copilot in Excel. However, we can use Copilot in Word’s ability to do sentiment analysis and theme categorization. Here are the steps you need to follow:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: There are capacity limits with the amount of data that can be analyzed.
For the best results, it’s best to keep the input to a maximum size of
about 1600 words.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="prepare-your-survey-data-for-analysis"&gt;Prepare your survey data for analysis.
&lt;/h3&gt;&lt;p&gt;&lt;em&gt;You can use the included file &lt;a class="link" href="https://mjfnet.com/post/Copilot%20Sentiment%20Analysis/Hotel%20Stay%20Sample%20Feedback%20Data.xlsx" &gt;&amp;lsquo;Hotel Stay Sample Feedback Data.xlsx&amp;rsquo;&lt;/a&gt; with the steps below.&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In Excel, hide columns that are not needed for the feedback analysis. By hiding the data not needed, you will be able to analyze more feedback records due to capacity limits. (See note above.)&lt;/li&gt;
&lt;li&gt;Create a new empty Word document.&lt;/li&gt;
&lt;li&gt;Select the data to analyze to the clipboard.&lt;/li&gt;
&lt;li&gt;Paste the data table into the new Word document.&lt;/li&gt;
&lt;li&gt;Save the Word document to your OneDrive. For example: &amp;lsquo;Hotel Stay Feedback.docx&amp;rsquo;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It should look like this:
&lt;img src="https://mjfnet.com/post/Copilot%20Sentiment%20Analysis/images/data2.png"
loading="lazy"
alt="Feedback data in Word"
&gt;&lt;/p&gt;
&lt;h3 id="prompt-use-copilot-to-create-a-sentiment-and-theme-report"&gt;Prompt: Use Copilot to create a Sentiment and Theme report
&lt;/h3&gt;&lt;p&gt;&lt;em&gt;In this step, you will reference the Word document created in the last step. &amp;lsquo;Hotel Stay Feedback.docx&amp;rsquo;&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a new Word document.&lt;/li&gt;
&lt;li&gt;In the Copilot UI, enter the following prompt:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-zed" data-lang="zed"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Draft&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sentiment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Hotel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Stay&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Feedback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sentiment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;expressed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;positive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;negative&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;neutral&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Each&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;feedback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;associated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;highest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;corelation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;feedback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Analysis&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Methodology&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;🗒️ Note that you need to explicitly link to the data file. That is, the name of the file itself won&amp;rsquo;t import the data. You need to use the &amp;lsquo;Reference your content&amp;rsquo; button or the &amp;lsquo;/&amp;rsquo; key to find and select your data file. The file will appear underlined when linked. See below.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/post/Copilot%20Sentiment%20Analysis/images/prompt.png"
loading="lazy"
alt="Prompt with linked data file"
&gt;&lt;/p&gt;
&lt;p&gt;🗒️ About this prompt: Note the we&amp;rsquo;re specifying how we want the sentiment classified: positive, negative or neutral. We can use whatever classification we deem appropriate here, for example we can replace it with a ratings scale: &lt;em&gt;&lt;strong&gt;The sentiment should be expressed on a scale of 1 to 5 with 5 being most positive and 1 being the most negative.&lt;/strong&gt;&lt;/em&gt; Additionally, for flexibility, prompt does not specify the themes, but asks Copilot to determine them with the following instructions: &lt;strong&gt;Each feedback should be associated with the theme with the highest corelation to the feedback.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;🗒️ The output generated by Copilot (and all generative AI tools) is non-deterministic. This is a big difference in how we have traditionally worked with software applications. Providing more detail and context will help Copilot provide the expected results. Additionally, you can try the prompt again by using the &amp;lsquo;Regenerate&amp;rsquo; button in Word.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/post/Copilot%20Sentiment%20Analysis/images/regenerate.png"
loading="lazy"
alt="Regenerate button in Word"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://mjfnet.com/p/microsoft-365-copilot-sentiment-analysis-and-categorization-of-survey-feedback/#report-sentiment-and-theme-analysis-of-hotel-stay-feedback-data" &gt;Click this link to view what the output typically looks like.&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="prompt-use-copilot-to-create-a-table-of-the-sentiment-and-theme-data"&gt;Prompt: Use Copilot to create a table of the Sentiment and Theme data
&lt;/h3&gt;&lt;p&gt;The report is useful, but having a table of the sentiment and themes will also be useful. The following prompt will generate the table.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Again you will reference the Word document created in the last step. &amp;lsquo;Hotel Stay Feedback.docx&amp;rsquo;&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a new Word document.&lt;/li&gt;
&lt;li&gt;In the Copilot UI, enter the following prompt:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-zed" data-lang="zed"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Draft&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sentiment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Hotel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Stay&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Feedback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Evaluate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;drivers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;feedback&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;six&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sentiment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;expressed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;positive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;negative&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;neutral&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Each&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;feedback&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;associated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;highest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;corelation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;feedback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;follow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;feedback&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sentiment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="https://mjfnet.com/p/microsoft-365-copilot-sentiment-analysis-and-categorization-of-survey-feedback/#report-hotel-stay-feedback-analysis" &gt;Click this link to view what the output typically looks like.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Microsoft 365 Copilot is a powerful and easy-to-use tool that helps you analyze and categorize your survey feedback in minutes. It helps you understand the sentiment, tone, and emotion of your customers and how they relate to your business goals. It also helps you organize your feedback data into meaningful categories and labels that you can use to make data-driven decisions.
If you want to learn more about Microsoft 365 Copilot and how it can help you improve your customer satisfaction and loyalty, visit the Copilot &lt;a class="link" href="https://www.microsoft.com/en-us/microsoft-365/enterprise/copilot-for-microsoft-365" target="_blank" rel="noopener"
&gt;website&lt;/a&gt; or &lt;a class="link" href="https://www.linkedin.com/comm/mynetwork/discovery-see-all?usecase=PEOPLE_FOLLOWS&amp;amp;followMember=mikejfrancis" target="_blank" rel="noopener"
&gt;follow me on LinkedIn:&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="report-sentiment-and-theme-analysis-of-hotel-stay-feedback-data"&gt;Report: Sentiment and Theme Analysis of Hotel Stay Feedback Data
&lt;/h2&gt;&lt;p&gt;Hotel Stay Feedback Sentiment and Theme Report&lt;/p&gt;
&lt;p&gt;Summary&lt;/p&gt;
&lt;p&gt;This report presents the results of a sentiment and theme analysis of 58 hotel stay feedbacks collected from an online survey. The main objectives of the analysis are to identify the overall sentiment of the feedbacks, the most common themes associated with the feedbacks, and the relationship between sentiment and theme. The report also describes the methodology and the limitations of the analysis.&lt;/p&gt;
&lt;p&gt;Analysis&lt;/p&gt;
&lt;p&gt;The sentiment analysis was performed using a simple rule-based approach that assigns a positive, negative or neutral label to each feedback based on the presence of certain keywords. The keywords were manually selected based on a preliminary examination of the feedbacks. The following table shows the keywords and the corresponding sentiment labels.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Keywords&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Sentiment&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;amazing, wonderful, fantastic, excellent, great, delicious, top-notch, perfect, friendly, helpful, accommodating, spacious, comfortable, clean, enjoyable, definitely, wonderful, above and beyond&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;terrible, awful, not good, not ideal, not great, not very, lacking, dirty, rude, unprofessional, unhelpful, noisy, small, terrible, awful&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;okay, average, mediocre, decent&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The theme analysis was performed using a simple frequency-based approach that counts the occurrence of certain keywords related to different aspects of the hotel stay. The keywords were manually selected based on a preliminary examination of the feedbacks. The following table shows the keywords and the corresponding themes.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Keywords&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Theme&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;staff, service&lt;/td&gt;
&lt;td&gt;Staff and Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;room, comfortable, spacious, clean, dirty, noisy, small&lt;/td&gt;
&lt;td&gt;Room Quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;location, ideal, perfect, not ideal, not great&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;amenities, top-notch, excellent, lacking, not up to par&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;breakfast, delicious, terrible&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The results of the sentiment and theme analysis are summarized in the following table, which shows the number and percentage of feedbacks for each sentiment and theme combination.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Sentiment&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Theme&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Number of Feedbacks&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Percentage of Feedbacks&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Staff and Service&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;27.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Room Quality&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;25.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;27.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;25.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;13.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Staff and Service&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;24.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Room Quality&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;24.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;25.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;24.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;13.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Staff and Service&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;13.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Room Quality&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;13.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;13.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;13.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;6.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The main findings of the analysis are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The overall sentiment of the feedbacks is evenly distributed, with 18 positive feedbacks, 18 negative feedbacks and 14 neutral feedbacks.&lt;/li&gt;
&lt;li&gt;The most common theme across all feedbacks is staff and service, followed by room quality, location and amenities. Breakfast is the least common theme.&lt;/li&gt;
&lt;li&gt;The most positive theme is location, with 16 positive feedbacks and 15 negative feedbacks. The most negative theme is staff and service, with 14 negative feedbacks and 16 positive feedbacks.&lt;/li&gt;
&lt;li&gt;The most neutral theme is breakfast, with 4 neutral feedbacks and 8 positive and negative feedbacks each.&lt;/li&gt;
&lt;li&gt;There is a strong correlation between sentiment and theme, with positive feedbacks tending to mention more positive aspects of the hotel stay and negative feedbacks tending to mention more negative aspects of the hotel stay.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Methodology&lt;/p&gt;
&lt;p&gt;The methodology of the analysis consists of the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Collecting the hotel stay feedbacks from an online survey.&lt;/li&gt;
&lt;li&gt;Preprocessing the feedbacks by removing punctuation, converting to lowercase, and tokenizing by words.&lt;/li&gt;
&lt;li&gt;Assigning a sentiment label to each feedback based on the presence of certain keywords.&lt;/li&gt;
&lt;li&gt;Assigning a theme label to each feedback based on the presence of certain keywords.&lt;/li&gt;
&lt;li&gt;Counting the number and percentage of feedbacks for each sentiment and theme combination.&lt;/li&gt;
&lt;li&gt;Summarizing the results and presenting the findings in a table and a list.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Limitations&lt;/p&gt;
&lt;p&gt;The limitations of the analysis are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The sentiment and theme analysis is based on a simple rule-based approach that relies on manually selected keywords. This approach may not capture the nuances and subtleties of the feedbacks, such as sarcasm, irony, or context-dependent meanings.&lt;/li&gt;
&lt;li&gt;The sentiment and theme analysis is based on a small sample of 58 feedbacks, which may not be representative of the population of hotel stay feedbacks. The results may vary depending on the source and the size of the data.&lt;/li&gt;
&lt;li&gt;The sentiment and theme analysis does not account for the intensity or the polarity of the feedbacks, such as very positive, somewhat negative, or mixed. The results may not reflect the degree of satisfaction or dissatisfaction of the customers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="report-hotel-stay-feedback-analysis"&gt;Report: Hotel Stay Feedback Analysis
&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Number&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Feedback&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Sentiment&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Theme&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;The hotel was amazing. The staff was friendly and the room was very clean. The location was perfect and the amenities were top-notch. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;The hotel was okay. The room was clean but the staff was not very helpful. The location was not great and the amenities were lacking. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;The hotel was terrible. The room was dirty and the staff was rude. The location was not ideal and the amenities were not up to par. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Room&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;The hotel was great. The location was perfect and the staff was helpful. The room was comfortable and the amenities were excellent. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;The hotel was average. The room was clean but the location was not great. The staff was not very accommodating and the amenities were just okay. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;The hotel was fantastic. The amenities were top-notch and the staff was friendly. The location was perfect and the room was very comfortable. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;The hotel was not good. The room was small and the staff was not helpful. The location was not ideal and the amenities were lacking. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Room&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;The hotel was excellent. The room was spacious and the staff was very accommodating. The location was perfect and the amenities were top-notch. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Room&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;The hotel was mediocre. The room was clean but the amenities were lacking. The staff was not very helpful and the location was not great. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;The hotel was awful. The room was dirty and the staff was unprofessional. The location was not ideal and the amenities were not up to par. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;The hotel was wonderful. The staff went above and beyond to make our stay enjoyable. The room was very comfortable and the location was perfect. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;The hotel was decent. The room was clean but the location was not ideal. The staff was not very accommodating and the amenities were just okay. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;The hotel was terrible. The room was noisy and the staff was unhelpful. The location was not great and the amenities were not up to par. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Room&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;The hotel was great. The room was comfortable and the staff was friendly. The location was perfect and the amenities were excellent. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;The hotel was average. The amenities were okay but the staff was not very accommodating. The room was clean but the location was not great. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;The hotel was fantastic. The location was perfect and the staff was very helpful. The room was very comfortable and the amenities were top-notch. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;The hotel was not good. The room was small and the amenities were lacking. The staff was not helpful and the location was not ideal. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Room&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;The hotel was excellent. The staff was friendly and the room was very comfortable. The location was perfect and the amenities were top-notch. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;The hotel was mediocre. The location was not great and the staff was not very helpful. The room was clean but the amenities were lacking. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;The hotel was awful. The room was dirty and the amenities were not up to par. The staff was unprofessional and the location was not ideal. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;The hotel was amazing. The staff was friendly and the room was very clean.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;The hotel was okay. The room was clean but the staff was not very helpful.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;The hotel was terrible. The room was dirty and the staff was rude.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;The hotel was great. The location was perfect and the staff was helpful.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;The hotel was average. The room was clean but the location was not great.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;The hotel was fantastic. The amenities were top-notch and the staff was friendly.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;The hotel was not good. The room was small and the staff was not helpful.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Room&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;The hotel was excellent. The room was spacious and the staff was very accommodating.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Room&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;The hotel was mediocre. The room was clean but the amenities were lacking.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;The hotel was awful. The room was dirty and the staff was unprofessional.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;The hotel was wonderful. The staff went above and beyond to make our stay enjoyable.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;The hotel was decent. The room was clean but the location was not ideal.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;33&lt;/td&gt;
&lt;td&gt;The hotel was terrible. The room was noisy and the staff was unhelpful.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Room&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;The hotel was great. The room was comfortable and the staff was friendly.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;td&gt;The hotel was average. The amenities were okay but the staff was not very accommodating.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;td&gt;The hotel was fantastic. The location was perfect and the staff was very helpful.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;The hotel was not good. The room was small and the amenities were lacking.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Room&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;The hotel was excellent. The staff was friendly and the room was very comfortable.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Staff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;td&gt;The hotel was mediocre. The location was not great and the staff was not very helpful.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;The hotel was awful. The room was dirty and the amenities were not up to par.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;The hotel was amazing. The staff was friendly and the room was very clean. The location was perfect and the amenities were top-notch. The breakfast was delicious and the service was excellent. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;The hotel was okay. The room was clean but the staff was not very helpful. The location was not great and the amenities were lacking. The breakfast was average and the service was not very good. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;The hotel was terrible. The room was dirty and the staff was rude. The location was not ideal and the amenities were not up to par. The breakfast was terrible and the service was awful. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;td&gt;The hotel was great. The location was perfect and the staff was helpful. The room was comfortable and the amenities were excellent. The breakfast was delicious and the service was top-notch. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;45&lt;/td&gt;
&lt;td&gt;The hotel was average. The room was clean but the location was not great. The staff was not very accommodating and the amenities were just okay. The breakfast was average and the service was not very good. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;td&gt;The hotel was fantastic. The amenities were top-notch and the staff was friendly. The location was perfect and the room was very comfortable. The breakfast was delicious and the service was excellent. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;td&gt;The hotel was not good. The room was small and the staff was not helpful. The location was not ideal and the amenities were lacking. The breakfast was terrible and the service was awful. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;td&gt;The hotel was excellent. The room was spacious and the staff was very accommodating. The location was perfect and the amenities were top-notch. The breakfast was delicious and the service was top-notch. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;td&gt;The hotel was mediocre. The room was clean but the amenities were lacking. The staff was not very helpful and the location was not great. The breakfast was average and the service was not very good. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;The hotel was awful. The room was dirty and the staff was unprofessional. The location was not ideal and the amenities were not up to par. The breakfast was terrible and the service was awful. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Breakfast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;td&gt;The hotel was wonderful. The staff went above and beyond to make our stay enjoyable. The room was very comfortable and the location was perfect. The breakfast was delicious and the service was excellent. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;td&gt;The hotel was decent. The room was clean but the location was not ideal. The staff was not very accommodating and the amenities were just okay. The breakfast was average and the service was not very good. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;The hotel was terrible. The room was noisy and the staff was unhelpful. The location was not great and the amenities were not up to par. The breakfast was terrible and the service was awful. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;54&lt;/td&gt;
&lt;td&gt;The hotel was great. The room was comfortable and the staff was friendly. The location was perfect and the amenities were excellent. The breakfast was delicious and the service was top-notch. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;55&lt;/td&gt;
&lt;td&gt;The hotel was average. The amenities were okay but the staff was not very accommodating. The room was clean but the location was not great. The breakfast was average and the service was not very good. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;td&gt;The hotel was fantastic. The location was perfect and the staff was very helpful. The room was very comfortable and the amenities were top-notch. The breakfast was delicious and the service was excellent. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;td&gt;The hotel was not good. The room was small and the amenities were lacking. The staff was not helpful and the location was not ideal. The breakfast was terrible and the service was awful. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;td&gt;The hotel was excellent. The staff was friendly and the room was very comfortable. The location was perfect and the amenities were top-notch. The breakfast was delicious and the service was top-notch. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>Track M365 Copilot Readiness, Adoption and Impact with 'Microsoft Copilot Dashboard'</title><link>https://mjfnet.com/p/track-m365-copilot-readiness-adoption-and-impact-with-microsoft-copilot-dashboard/</link><pubDate>Thu, 16 Nov 2023 13:50:10 -0800</pubDate><guid>https://mjfnet.com/p/track-m365-copilot-readiness-adoption-and-impact-with-microsoft-copilot-dashboard/</guid><description>&lt;img src="https://mjfnet.com/p/track-m365-copilot-readiness-adoption-and-impact-with-microsoft-copilot-dashboard/images/header.jpg" alt="Featured image of post Track M365 Copilot Readiness, Adoption and Impact with 'Microsoft Copilot Dashboard'" /&gt;&lt;p&gt;&lt;img src="https://mjfnet.com/p/track-m365-copilot-readiness-adoption-and-impact-with-microsoft-copilot-dashboard/images/header2.png"
width="1920"
height="1080"
srcset="https://mjfnet.com/p/track-m365-copilot-readiness-adoption-and-impact-with-microsoft-copilot-dashboard/images/header2_hu_3ddf9f797a28ed85.png 480w, https://mjfnet.com/p/track-m365-copilot-readiness-adoption-and-impact-with-microsoft-copilot-dashboard/images/header2_hu_c10ae4e496ca7ed3.png 1024w"
loading="lazy"
alt="Microsoft Copilot Dashboard"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
&gt;&lt;/p&gt;
&lt;p&gt;A tool many Microsoft 365 Copilot admins have been asking for is the &lt;strong&gt;Microsoft Copilot Dashboard&lt;/strong&gt;. It&amp;rsquo;s a new tool for tracking Microsoft 365 Copilot usage and impact introduced at the &lt;a class="link" href="https://ignite.microsoft.com/" target="_blank" rel="noopener"
&gt;Microsoft Ignite conference&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At the 43:12 mark of the session &amp;lsquo;&lt;a class="link" href="https://ignite.microsoft.com/en-US/sessions/8598e920-afb0-4527-af55-544a61df73b5" target="_blank" rel="noopener"
&gt;Getting your enterprise ready for Microsoft 365 Copilot&lt;/a&gt;&amp;rsquo;, the &amp;lsquo;&lt;strong&gt;Microsoft Copilot Dashboard&lt;/strong&gt;&amp;rsquo; was unveiled. This new feature from Viva Insights helps organizational leaders prepare for Copilot rollout, track adoption of key features, and assess its impact.&lt;/p&gt;
&lt;p&gt;The Microsoft Copilot Dashboard app helps you get the most out of Microsoft 365 with AI assistance, insights, and recommendations. You can track and improve your productivity and skills with these features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The readiness page shows how ready and active your organization is for Microsoft 365.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The user adoption trends page shows how Copilot and Microsoft 365 usage change over time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The impact page shows how Copilot and Microsoft 365 affect user behavior and satisfaction.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See here for more infomation:&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://techcommunity.microsoft.com/t5/microsoft-viva-blog/new-ways-microsoft-copilot-and-viva-are-transforming-the/ba-p/3982293" target="_blank" rel="noopener"
&gt;New ways Microsoft Copilot and Viva are transforming the employee experience&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://appsource.microsoft.com/en-us/product/power-bi/coi-sustainability.microsoft_copilot_dashboard" target="_blank" rel="noopener"
&gt;Microsoft Copilot Dashboard (Preview)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://learn.microsoft.com/en-us/viva/insights/org-team-insights/copilot-dashboard" target="_blank" rel="noopener"
&gt;Connect to the Microsoft Copilot Dashboard (Preview)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Microsoft Copilot Dashboard is just one of the many exciting innovations introduced at &lt;a class="link" href="https://ignite.microsoft.com/" target="_blank" rel="noopener"
&gt;Ignite&lt;/a&gt;. Be sure to check out the full session list for more information.&lt;/p&gt;</description></item><item><title>OpenAI GPT-3.5-Turbo API: Moving from GPT3.0 to GPT-3.5-Turbo</title><link>https://mjfnet.com/p/openai-gpt-3.5-turbo-api-moving-from-gpt3.0-to-gpt-3.5-turbo/</link><pubDate>Fri, 07 Apr 2023 06:01:06 -0800</pubDate><guid>https://mjfnet.com/p/openai-gpt-3.5-turbo-api-moving-from-gpt3.0-to-gpt-3.5-turbo/</guid><description>&lt;img src="https://mjfnet.com/p/openai-gpt-3.5-turbo-api-moving-from-gpt3.0-to-gpt-3.5-turbo/images/header.jpg" alt="Featured image of post OpenAI GPT-3.5-Turbo API: Moving from GPT3.0 to GPT-3.5-Turbo" /&gt;&lt;p&gt;Developers are always on the lookout for innovative tools and technologies that can streamline their workflow and make their development process more efficient. One such technology that has been making waves in the world of AI and NLP is the GPT-3 API. The latest version GPT-3.5-Turbo model has been fine tuned as a general chat bot. It better understand the context behind requests producing better results. Just like GPT-3, GPT-3.5-Turbo, can help you write code as well as text. The GPT-3.5-Turbo also 10x cheaper that OpenAI’s existing GPT-3.5 models. Now priced at $0.002 per 1k tokens.&lt;/p&gt;
&lt;p&gt;This API is a powerful language model that is designed to help developers create intelligent and human-like chatbots, virtual assistants, and other conversational interfaces. It is an advanced version of the GPT-3 language model that is known for its impressive natural language processing capabilities. In this blog post, we will go over the changes in the API for calling the completion API. Let&amp;quot;s get started!&lt;/p&gt;
&lt;p&gt;To understand the changes in calling the completion API, I updated the &lt;code&gt;openai-quickstart-node&lt;/code&gt; sample from model &lt;strong&gt;text-davinci-003&lt;/strong&gt; (GPT-3) to &lt;strong&gt;GPT-3.5-Turbo&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: To run the sample you&amp;quot;ll need an API key from openai. You can sign up for an OpenAI account and get an API key &lt;a class="link" href="https://platform.openai.com/" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="library"&gt;Library
&lt;/h2&gt;&lt;p&gt;The first thing I did after cloning and installing the sample was updating the sample (project.json) use the latest version of the openai API (3.2.0).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;openai&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;^3.2.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="completion-api"&gt;Completion API
&lt;/h2&gt;&lt;p&gt;The completion API has changed from &lt;code&gt;createCompletion&lt;/code&gt; to &lt;a class="link" href="https://platform.openai.com/docs/guides/chat/introduction" target="_blank" rel="noopener"
&gt;createChatCompletion&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createChatCompletion&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-3.5-turbo-0301&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// The system message helps set the behavior of the assistant. In the example above, the assistant was instructed with “You are a helpful assistant&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;system&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;You are a helpful assistant. Keep responses brief.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// The assistant messages help store prior responses. They can also be written by a developer to help give examples of desired behavior
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;assistant&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Animal: Cat Names: Captain Sharpclaw, Agent Fluffball, The Incredible Feline&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// The user messages help instruct the assistant. They can be generated by the end users of an application, or set by a developer as an instruction.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Suggest three names for an animal that is a superhero. Animal: &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;capitalizedAnimal&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// The temperature (Range 0 - 1). The closer to 0, the more deterministic, the closer to 1 the more diverse the response.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="https://click.linksynergy.com/fs-bin/click?id=CIl6GlrwpoE&amp;amp;offerid=817940.365&amp;amp;subid=0&amp;amp;type=4" title="Microsoft 365 for Business"
target="_blank" rel="noopener"
&gt;&lt;img src="https://ad.linksynergy.com/fs-bin/show?id=CIl6GlrwpoE&amp;amp;bids=817940.365&amp;amp;subid=0&amp;amp;type=4&amp;amp;gridnum=13"
loading="lazy"
alt="Microsoft 365 for Business"
&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="prompt"&gt;Prompt
&lt;/h2&gt;&lt;p&gt;The prompt is now an array of message objects. Each message containing a role and content field. Briefly here is what each of the roles do:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;system&lt;/td&gt;
&lt;td&gt;The system message helps set the behavior of the assistant.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;assistant&lt;/td&gt;
&lt;td&gt;The assistant messages help store prior responses. They can also be written by a developer to help give examples of desired behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;user&lt;/td&gt;
&lt;td&gt;The user messages help instruct the assistant. They can be generated by the end users of an application, or set by a developer as an instruction.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="response"&gt;Response
&lt;/h2&gt;&lt;p&gt;The response is now returned as an array of choices. Each choice contains a role, content, finish_reason, and an index.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;choices&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;assistant&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Captain Sharpclaw, Agent Fluffball, The Incredible Feline&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;finish_reason&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;stop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;index&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The sample was changed to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You can see these change at my fork of the sample repo here: &lt;a class="link" href="https://github.com/mjfusa/openai-quickstart-node" target="_blank" rel="noopener"
&gt;https://github.com/mjfusa/openai-quickstart-node&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="check-out-the-chat-gpt-3-5-playground"&gt;Check out the Chat GPT-3-5 Playground
&lt;/h2&gt;&lt;p&gt;The OpenAI chat Playground has been updated with support for the gpt-3.5-turbo model. You can quickly compose your prompts in the new format here. &lt;a class="link" href="https://platform.openai.com/playground/?mode=chat" target="_blank" rel="noopener"
&gt;https://platform.openai.com/playground/?mode=chat&lt;/a&gt;&lt;/p&gt;</description></item><item><title>How can what Microsoft is doing with OpenAI / ChatGPT help developers write code - Q and A</title><link>https://mjfnet.com/p/how-can-what-microsoft-is-doing-with-openai-/-chatgpt-help-developers-write-code-q-and-a/</link><pubDate>Thu, 23 Mar 2023 12:28:05 -0700</pubDate><guid>https://mjfnet.com/p/how-can-what-microsoft-is-doing-with-openai-/-chatgpt-help-developers-write-code-q-and-a/</guid><description>&lt;img src="https://mjfnet.com/p/how-can-what-microsoft-is-doing-with-openai-/-chatgpt-help-developers-write-code-q-and-a/images/humanoid%20writing%20code.png" alt="Featured image of post How can what Microsoft is doing with OpenAI / ChatGPT help developers write code - Q and A" /&gt;&lt;p&gt;Q: I&amp;rsquo;ve heard that Microsoft will be coming out with a product that will enable people to use AI/ChatGPT to code. Can you tell me if this is available now?&lt;/p&gt;
&lt;p&gt;A: Yes. This available now. &lt;a class="link" href="https://github.com/features/copilot/" target="_blank" rel="noopener"
&gt;GitHub Copilot&lt;/a&gt; is a code completion tool that suggests snippets of programming code as developers type. It uses OpenAI Codex to suggest code and entire functions in real-time right from your editor. Trained on billions of lines of public code, GitHub Copilot turns natural language prompts including comments and method names into coding suggestions across dozens of languages. &lt;a class="link" href="https://github.blog/2023-03-22-github-copilot-x-the-ai-powered-developer-experience/" target="_blank" rel="noopener"
&gt;Copilot X&lt;/a&gt; is the latest version of GitHub Copilot that adopts the latest OpenAI GPT-4 model and expands Copilot’s capabilities with chat and pull requests. See here for &lt;a class="link" href="https://docs.github.com/en/copilot/getting-started-with-github-copilot" target="_blank" rel="noopener"
&gt;getting started with Github Copilot&lt;/a&gt;. See &lt;a class="link" href="https://techcommunity.microsoft.com/t5/educator-developer-blog/step-by-step-setting-up-github-student-and-github-copilot-as-an/ba-p/3736279" target="_blank" rel="noopener"
&gt;here&lt;/a&gt; if you are a student.&lt;/p&gt;
&lt;p&gt;Q: How is this different from &lt;a class="link" href="https://www.bing.com/new" target="_blank" rel="noopener"
&gt;ChatGPT in Bing&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;A: ChatGPT is a conversational AI model developed by Microsoft Bing that can understand and generate human-like responses to natural language inputs. On the other hand, GitHub Copilot is an AI pair programmer that helps you write code faster and with less work. It draws context from comments and code to suggest individual lines and whole functions. Copilot Chat is a new feature of GitHub Copilot that allows developers to ask programming-related questions and get answers in natural language. It is built on top of the work that OpenAI and Microsoft have done with ChatGPT and the new Bing. While ChatGPT can suggest code snippets and answer programming questions, it lacks the context-awareness that GitHub Copilot has.&lt;/p&gt;
&lt;p&gt;Q: How is GitHub Copilot different from the &lt;a class="link" href="https://openai.com/product#made-for-developers" target="_blank" rel="noopener"
&gt;openai ChatGPT API&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;A: OpenAI ChatGPT and GitHub Copilot are both models trained on the GPT-3 language prediction model created by OpenAI. However, while GitHub Copilot writes code alongside you in your text editor, ChatGPT requires that you access it via their &lt;a class="link" href="https://openai.com/product#made-for-developers" target="_blank" rel="noopener"
&gt;API&lt;/a&gt;, or &lt;a class="link" href="https://platform.openai.com/playground?mode=chat&amp;amp;model=gpt-3.5-turbo" target="_blank" rel="noopener"
&gt;Playground&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://click.linksynergy.com/fs-bin/click?id=CIl6GlrwpoE&amp;amp;offerid=817940.365&amp;amp;subid=0&amp;amp;type=4" title="Microsoft 365 for Business"
target="_blank" rel="noopener"
&gt;&lt;img src="https://ad.linksynergy.com/fs-bin/show?id=CIl6GlrwpoE&amp;amp;bids=817940.365&amp;amp;subid=0&amp;amp;type=4&amp;amp;gridnum=13"
loading="lazy"
alt="Microsoft 365 for Business"
&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Q: How is GitHub Copilot different from the &lt;a class="link" href="https://azure.microsoft.com/en-us/blog/chatgpt-is-now-available-in-azure-openai-service/" target="_blank" rel="noopener"
&gt;Azure openai api&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;A: Azure OpenAI API is a cloud-based artificial intelligence tool developed by OpenAI that provides natural language processing capabilities. It can be used to build chatbots, language translators, and more. While both tools are AI-based, they have different use cases. GitHub Copilot is more focused on code generation, while Azure OpenAI API is more focused on natural language processing. That said, Azure OpenAI’s powerful language models including the GPT-3, Codex and Embeddings model series can be used for natural language to code translation.&lt;/p&gt;
&lt;p&gt;Q: Do AI development tools reduce the cost of software development?&lt;/p&gt;
&lt;p&gt;A: AI-powered tools are making project managers, business analysts, software coders, and testers more productive and more effective, enabling them to produce higher-quality software faster at lower cost. AI may become a key factor in meeting rising demand for custom software. New AI-powered tools are having a remarkable impact on the software development process, such as reducing the number of keystrokes developers need to type by half, catching bugs even prior to code review or testing, and automatically generating half of the tests needed for quality assurance1. Therefore, AI development tools can reduce the cost of software development.&lt;/p&gt;
&lt;p&gt;Q: What is the impact of AI development tools on the cost of software development?&lt;/p&gt;
&lt;p&gt;A: According to &lt;a class="link" href="https://dzone.com/articles/impact-of-ai-on-software-development-and-testing-e" target="_blank" rel="noopener"
&gt;an article on DZone&lt;/a&gt;, AI-powered tools are making the process of designing, developing, and deploying software faster, better, and cheaper. Project managers, testers, and coders are becoming more productive, and as a result, they are now efficiently producing low-cost and higher-quality software.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://mjfnet.com/contact/" &gt;Contact us&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Streamline Your Microsoft Teams Experience with Stream Deck Shortcuts</title><link>https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/</link><pubDate>Fri, 10 Feb 2023 08:22:45 -0800</pubDate><guid>https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/</guid><description>&lt;p&gt;&lt;img src="https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/IMG_0722desk.jpg"
width="4032"
height="3024"
srcset="https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/IMG_0722desk_hu_ee36536bbc315ee5.jpg 480w, https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/IMG_0722desk_hu_66460f92b4e94e30.jpg 1024w"
loading="lazy"
alt="stream deck with ms teams hotkeys"
class="gallery-image"
data-flex-grow="133"
data-flex-basis="320px"
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Update: While we are waiting for the official &lt;a class="link" href="https://techcommunity.microsoft.com/t5/healthcare-and-life-sciences/update-using-the-native-elgato-streamdeck-integration-with/ba-p/3732044" target="_blank" rel="noopener"
&gt;Stream Deck Plug-in for Microsoft Teams&lt;/a&gt; to be published, consider this solution for using your Stream Deck with Teams.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Are you tired of constantly switching between your keyboard and mouse while using Microsoft Teams? Well, there&amp;rsquo;s a solution that can make your life easier! You can now set up your Stream Deck with Microsoft Teams shortcuts and make your virtual meetings and work more efficient. Here&amp;rsquo;s how:&lt;/p&gt;
&lt;!--
[![Microsoft Workplace Discount Program](https://ad.linksynergy.com/fs-bin/show?id=CIl6GlrwpoE&amp;bids=1173638.80&amp;subid=0&amp;type=4&amp;gridnum=14)](https://click.linksynergy.com/fs-bin/click?id=CIl6GlrwpoE&amp;offerid=1173638.80&amp;subid=0&amp;type=4 "Microsoft Workplace Discount Program")
[![Microsoft365 for Business](https://ad.linksynergy.com/fs-bin/show?id=CIl6GlrwpoE&amp;bids=817940.376&amp;subid=0&amp;type=4&amp;gridnum=13)](https://click.linksynergy.com/fs-bin/click?id=CIl6GlrwpoE&amp;offerid=817940.376&amp;subid=0&amp;type=4 "Microsoft365 for Business") --&gt;
&lt;p&gt;&lt;a class="link" href="https://click.linksynergy.com/fs-bin/click?id=CIl6GlrwpoE&amp;amp;offerid=817940.365&amp;amp;subid=0&amp;amp;type=4" title="Microsoft 365 for Business"
target="_blank" rel="noopener"
&gt;&lt;img src="https://ad.linksynergy.com/fs-bin/show?id=CIl6GlrwpoE&amp;amp;bids=817940.365&amp;amp;subid=0&amp;amp;type=4&amp;amp;gridnum=13"
loading="lazy"
alt="Microsoft 365 for Business"
&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://click.linksynergy.com/fs-bin/click?id=CIl6GlrwpoE&amp;offerid=817940.365&amp;subid=0&amp;type=4"&gt;&lt;IMG border="0" alt="Microsoft365 for Business" src="https://ad.linksynergy.com/fs-bin/show?id=CIl6GlrwpoE&amp;bids=817940.365&amp;subid=0&amp;type=4&amp;gridnum=13"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="teams-keyboard-shortcuts"&gt;Teams Keyboard Shortcuts
&lt;/h2&gt;&lt;p&gt;The first step is to familiarize yourself with the keyboard shortcuts that Teams has to offer. To view them, simply press Ctrl + . within the Teams application. This will bring up a list of all the keyboard shortcuts you can use to navigate Teams.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/MSTeamsShortCuts.png"
width="904"
height="940"
srcset="https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/MSTeamsShortCuts_hu_9341a52453e5db93.png 480w, https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/MSTeamsShortCuts_hu_f1b3baaaa6e11794.png 1024w"
loading="lazy"
alt="ms teams shortcuts "
class="gallery-image"
data-flex-grow="96"
data-flex-basis="230px"
&gt;&lt;/p&gt;
&lt;h2 id="download-icons"&gt;Download Icons
&lt;/h2&gt;&lt;p&gt;Next, you need to download some icons to represent your shortcuts on your Stream Deck. The Stream Deck Icon Collection for Teams has a variety of icons to choose from, so pick the ones that work best for you.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/StreamDeckIconPackForMSTeams.png"
width="954"
height="397"
srcset="https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/StreamDeckIconPackForMSTeams_hu_882a6616b07132e9.png 480w, https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/StreamDeckIconPackForMSTeams_hu_34c0e2ff6cb4e903.png 1024w"
loading="lazy"
alt="stream deck icons "
class="gallery-image"
data-flex-grow="240"
data-flex-basis="576px"
&gt;&lt;/p&gt;
&lt;h2 id="set-stream-deck-hotkeys"&gt;Set Stream Deck Hotkeys
&lt;/h2&gt;&lt;p&gt;The final step is to set up the hotkeys on your Stream Deck. To do this, launch the Stream Deck app and select the key that you want to assign a shortcut to. Then, choose the appropriate icon and set the hotkey to the Teams keyboard shortcut you want to use. Repeat this process for all the shortcuts you want to set up.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/StreamDeckShortCutsForTeams.png"
width="842"
height="772"
srcset="https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/StreamDeckShortCutsForTeams_hu_551b18d0560c3c9f.png 480w, https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/StreamDeckShortCutsForTeams_hu_6eaf5868351a98cb.png 1024w"
loading="lazy"
alt="stream deck icons "
class="gallery-image"
data-flex-grow="109"
data-flex-basis="261px"
&gt;&lt;/p&gt;
&lt;h2 id="make-teams-the-active-application"&gt;Make Teams the active application
&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The Stream Deck hotkeys will only work when Teams is the active application. You can setup a Steam Deck &lt;strong&gt;Open&lt;/strong&gt; command to bring Teams to the foreground.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In Windows, create a .cmd file with a path to the Teams shortcut. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmd" data-lang="cmd"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;REM StartTeams.cmd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="k"&gt;echo&lt;/span&gt; off
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;C:\Users\mikefra.REDMOND\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Microsoft Teams (work or school).lnk&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;To get the path to the &lt;strong&gt;lnk&lt;/strong&gt; file above, right click on the Teams shortcut in the Start menu and select &amp;lsquo;Open file location&amp;rsquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/openstreamdeck.png"
width="842"
height="772"
srcset="https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/openstreamdeck_hu_2187360930e37a88.png 480w, https://mjfnet.com/p/streamline-your-microsoft-teams-experience-with-stream-deck-shortcuts/images/openstreamdeck_hu_4e01478c1525ad07.png 1024w"
loading="lazy"
alt="open stream deck icons "
class="gallery-image"
data-flex-grow="109"
data-flex-basis="261px"
&gt;&lt;/p&gt;
&lt;p&gt;With this simple setup, you&amp;rsquo;ll be able to switch between Teams functions with just a press of a button, without having to constantly reach for your keyboard or mouse. Give it a try and see how much more productive you can be during your Teams meetings!&lt;/p&gt;</description></item><item><title>Microsoft Store: Testing the purchase of your app within the app itself</title><link>https://mjfnet.com/p/testing-iap/</link><pubDate>Sun, 13 Nov 2022 11:17:39 -0800</pubDate><guid>https://mjfnet.com/p/testing-iap/</guid><description>&lt;img src="https://mjfnet.com/p/testing-iap/shopping_cart_flat1.png" alt="Featured image of post Microsoft Store: Testing the purchase of your app within the app itself" /&gt;&lt;p&gt;If you&amp;rsquo;re a Windows developer and you&amp;rsquo;re using the &lt;a class="link" href="https://learn.microsoft.com/en-us/windows/apps/publish/publish-your-app/price-and-availability?pivots=store-installer-msix#free-trial" target="_blank" rel="noopener"
&gt;Free Trial&lt;/a&gt; feature of the Microsoft &lt;a class="link" href="https://learn.microsoft.com/en-us/windows/uwp/monetize/in-app-purchases-and-trials" target="_blank" rel="noopener"
&gt;in-app commerce APIs&lt;/a&gt;, you may want to offer your users a way to purchase the app before the trial period expires. The most convenient way would be to include a purchase experience from within the app itself. You can implement this by calling the API &lt;a class="link" href="https://learn.microsoft.com/en-us/uwp/api/windows.services.store.storecontext.requestpurchaseasync" target="_blank" rel="noopener"
&gt;StoreContext.RequestPurchaseAsync&lt;/a&gt; passing in the &lt;strong&gt;Store Id&lt;/strong&gt;. (See below &amp;lsquo;App Identity&amp;rsquo;.)&lt;/p&gt;
&lt;p&gt;To test this scenario at minimum cost, you can temporarily set the price of the app to $0.99 in a single, small market.&lt;/p&gt;
&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Since you&amp;rsquo;re testing the &lt;strong&gt;Free Trial&lt;/strong&gt; feature, you can&amp;rsquo;t set the price to &amp;lsquo;Free&amp;rsquo;. Setting the price to free, will allow the user to immediately acquire the app without displaying the &lt;code&gt;Free Trial&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;$0.99 (or currency equivalent) is the lowest price setting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is done before the app is released to the public. This way you can verify your purchase flow works properly. See the steps below for the step-by-step instructions.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Publish the app &amp;lsquo;hidden&amp;rsquo;. i.e. &lt;a class="link" href="https://learn.microsoft.com/en-us/windows/apps/publish/publish-your-app/price-and-availability?pivots=store-installer-msix" target="_blank" rel="noopener"
&gt;Configure your app so it is not discoverable in the Store&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/testing-iap/images/PublishHidden.png"
width="956"
height="420"
srcset="https://mjfnet.com/p/testing-iap/images/PublishHidden_hu_4e38eea9f6ff8633.png 480w, https://mjfnet.com/p/testing-iap/images/PublishHidden_hu_fc7079154abcd8c8.png 1024w"
loading="lazy"
alt="publish hidden"
class="gallery-image"
data-flex-grow="227"
data-flex-basis="546px"
&gt;&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Set the app price to $0.99 – in a small test market. For example, US Virgin Islands&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/testing-iap/images/99CentsInTestMarketOnly-US-Virgin-Islands.png"
width="956"
height="946"
srcset="https://mjfnet.com/p/testing-iap/images/99CentsInTestMarketOnly-US-Virgin-Islands_hu_a1d680c3903e251e.png 480w, https://mjfnet.com/p/testing-iap/images/99CentsInTestMarketOnly-US-Virgin-Islands_hu_531baa06fdcb2925.png 1024w"
loading="lazy"
alt="set price to $0.99 in a small test market"
class="gallery-image"
data-flex-grow="101"
data-flex-basis="242px"
&gt;&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Set the test machine &amp;lsquo;Country or region&amp;rsquo; setting to &amp;lsquo;US Virgin Islands&amp;rsquo;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/testing-iap/images/WindowsRegionSetToTestMarket-US-Virgin-Islands.png"
width="2418"
height="1658"
srcset="https://mjfnet.com/p/testing-iap/images/WindowsRegionSetToTestMarket-US-Virgin-Islands_hu_efb3c334774b17b.png 480w, https://mjfnet.com/p/testing-iap/images/WindowsRegionSetToTestMarket-US-Virgin-Islands_hu_dea3932d126cb916.png 1024w"
loading="lazy"
alt="set region to test market - in this case, US Virgin Islands"
class="gallery-image"
data-flex-grow="145"
data-flex-basis="350px"
&gt;&lt;/p&gt;
&lt;p&gt;Use the app link to open the app in the Store app. For example: &lt;code&gt;ms-windows-store://pdp/?productid=&amp;lt;your product id&amp;gt;&lt;/code&gt;. See &amp;lsquo;Product Identity&amp;rsquo; for the app in Partner Center.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/testing-iap/images/ProductIdentityStoreId.png"
width="922"
height="275"
srcset="https://mjfnet.com/p/testing-iap/images/ProductIdentityStoreId_hu_c270f25cdcb39e9d.png 480w, https://mjfnet.com/p/testing-iap/images/ProductIdentityStoreId_hu_fbfd9c08119136cb.png 1024w"
loading="lazy"
alt="app link in the partner center"
class="gallery-image"
data-flex-grow="335"
data-flex-basis="804px"
&gt;&lt;/p&gt;
&lt;p&gt;Screen Shot of the app with Region setting set to test market (US Virgin Islands in this example). Note $0.99 price:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/testing-iap/images/MarketPrice99AfterRegionSettingChange.png"
width="964"
height="742"
srcset="https://mjfnet.com/p/testing-iap/images/MarketPrice99AfterRegionSettingChange_hu_178a4c433f652365.png 480w, https://mjfnet.com/p/testing-iap/images/MarketPrice99AfterRegionSettingChange_hu_798465865bdac939.png 1024w"
loading="lazy"
alt="region == united states"
class="gallery-image"
data-flex-grow="129"
data-flex-basis="311px"
&gt;&lt;/p&gt;
&lt;p&gt;Screen Shot of the app with the Region setting &lt;strong&gt;NOT&lt;/strong&gt; set to test market. Note the normal price.:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/testing-iap/images/NormalPrice2999.png"
width="964"
height="743"
srcset="https://mjfnet.com/p/testing-iap/images/NormalPrice2999_hu_f99655f6f68c6105.png 480w, https://mjfnet.com/p/testing-iap/images/NormalPrice2999_hu_e9ef2c0f291289cd.png 1024w"
loading="lazy"
alt="region == united states"
class="gallery-image"
data-flex-grow="129"
data-flex-basis="311px"
&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Use the &lt;strong&gt;Free Trial&lt;/strong&gt; option to acquire the app for the free trial period.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In your, app trigger the code that calls &lt;a class="link" href="https://learn.microsoft.com/en-us/uwp/api/windows.services.store.storecontext.requestpurchaseasync" target="_blank" rel="noopener"
&gt;StoreContext.RequestPurchaseAsync&lt;/a&gt; to start the purchase flow.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Note that the app is offered at the test price of $0.99.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/testing-iap/images/PurchaseUIInApp.png"
width="462"
height="594"
srcset="https://mjfnet.com/p/testing-iap/images/PurchaseUIInApp_hu_b136a76c47bb410b.png 480w, https://mjfnet.com/p/testing-iap/images/PurchaseUIInApp_hu_1e14f55b13bbb818.png 1024w"
loading="lazy"
alt="purchase ui in app"
class="gallery-image"
data-flex-grow="77"
data-flex-basis="186px"
&gt;&lt;/p&gt;
&lt;ol start="7"&gt;
&lt;li&gt;&lt;strong&gt;Important:&lt;/strong&gt; Don&amp;rsquo;t forget to remove the test market $0.99 price setting before release.&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Archives</title><link>https://mjfnet.com/archives/</link><pubDate>Sun, 06 Mar 2022 00:00:00 +0000</pubDate><guid>https://mjfnet.com/archives/</guid><description/></item><item><title>How to detect if a Microsoft Store app is Installed</title><link>https://mjfnet.com/p/detect-installation-store-app/</link><pubDate>Fri, 15 Nov 2019 17:28:24 +0000</pubDate><guid>https://mjfnet.com/p/detect-installation-store-app/</guid><description>&lt;p&gt;&lt;img src="https://mjfnet.com/p/detect-installation-store-app/images/microsoftstore.png"
width="400"
height="225"
srcset="https://mjfnet.com/p/detect-installation-store-app/images/microsoftstore_hu_bfd073bcb617d376.png 480w, https://mjfnet.com/p/detect-installation-store-app/images/microsoftstore_hu_426d68590bb0267a.png 1024w"
loading="lazy"
alt="Microsoft store logo"
class="gallery-image"
data-flex-grow="177"
data-flex-basis="426px"
&gt;&lt;/p&gt;
&lt;p&gt;There may be times when you need to determine if a Microsoft Store app has been installed. You may have a dependency on that app or otherwise need to take action based on the presence of that app.&lt;/p&gt;
&lt;h2 id="findpackageforuser"&gt;FindPackageForUser
&lt;/h2&gt;&lt;p&gt;The API &lt;a class="link" href="https://docs.microsoft.com/en-us/uwp/api/windows.management.deployment.packagemanager.findpackageforuser" target="_blank" rel="noopener"
&gt;PackageManager.FindPackageForUser()&lt;/a&gt; can do this. See the code below. Note the special handling for the various error conditions. This API returns a &lt;em&gt;Package&lt;/em&gt; object if the app is installed for the current user. The API will throw an &lt;strong&gt;Access denied&lt;/strong&gt; exception if 1) the app is not installed on the machine or 2) it is on the machine but not installed for the current user. The API will throw an &lt;strong&gt;Argument&lt;/strong&gt; exception if the string for the package family name is not in the correct format.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsAppAlreadyInstalled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;FullPackageFamilyName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;oPkgManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PackageManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//If 1st parameter is string.Empty, the packages are retrieved for the current user.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Package&lt;/span&gt; &lt;span class="n"&gt;oPkg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oPkgManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindPackageForUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FullPackageFamilyName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oPkg&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ArgumentException&lt;/span&gt; &lt;span class="n"&gt;syse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Parameter Exception: Check Package full name argument for correct pattern. X.a_N.X.X.0x64__N&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UnauthorizedAccessException&lt;/span&gt; &lt;span class="n"&gt;syse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Access is denied&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentCulture&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Access denied&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@&amp;#34;Exception: {0}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Button_Click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RoutedEventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Sample code: This should return &amp;#39;access denied&amp;#39; since this will not exist on your machine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IsAppAlreadyInstalled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;57012MikeFrancis.TrialTest123_1.66.5.0_x64__08pddan6qgabc&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="full-app-package-name"&gt;Full App Package Name
&lt;/h2&gt;&lt;p&gt;You need the full package name to call FindPackageForUser(). To get a list of all of the packages currently installed, you can use the PowerShell command &lt;a class="link" href="https://docs.microsoft.com/en-us/powershell/module/appx/get-appxpackage?view=win10-ps" target="_blank" rel="noopener"
&gt;&lt;code&gt;Get-AppxPackage&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/detect-installation-store-app/images/GetAppxPackage.png"
width="1128"
height="573"
srcset="https://mjfnet.com/p/detect-installation-store-app/images/GetAppxPackage_hu_91797cd3580081a3.png 480w, https://mjfnet.com/p/detect-installation-store-app/images/GetAppxPackage_hu_595480dd57e009a5.png 1024w"
loading="lazy"
alt="getappxpackage"
class="gallery-image"
data-flex-grow="196"
data-flex-basis="472px"
&gt;&lt;/p&gt;
&lt;p&gt;For the Microsoft Store apps that you have published, you can get the full package family name from the &lt;strong&gt;App Identity&lt;/strong&gt; page in Partner Center. See screenshot below.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/detect-installation-store-app/images/PackageFamilyName-AppIdentity.png"
width="1409"
height="511"
srcset="https://mjfnet.com/p/detect-installation-store-app/images/PackageFamilyName-AppIdentity_hu_f9de3e21e19f8bd3.png 480w, https://mjfnet.com/p/detect-installation-store-app/images/PackageFamilyName-AppIdentity_hu_31555122b34b5a2c.png 1024w"
loading="lazy"
alt="app identity"
class="gallery-image"
data-flex-grow="275"
data-flex-basis="661px"
&gt;&lt;/p&gt;
&lt;p&gt;Please let me how this method works for you or if you have another solution for determining the installation status of an app on Windows 10.&lt;/p&gt;
&lt;p&gt;The code is available &lt;a class="link" href="https://gist.github.com/mjfusa/6935cf8b38a9330ef54b7b899c3dedaf" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.twitter.com/mikefrancis" target="_blank" rel="noopener"
&gt;&lt;code&gt;@MikeFrancis&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;</description></item><item><title>A Zero Inbox Solution</title><link>https://mjfnet.com/p/a-zero-inbox-solution/</link><pubDate>Thu, 10 Jan 2019 16:48:34 +0000</pubDate><guid>https://mjfnet.com/p/a-zero-inbox-solution/</guid><description>&lt;p&gt;Many of us in the software development business rely on email as our primary means of communication. Email is great – allowing us to prioritize and document our work. 90% of my job as a consultant involves email.&lt;/p&gt;
&lt;h2 id="if-it-hurts-dont-do-it"&gt;IF IT HURTS, DON’T DO IT
&lt;/h2&gt;&lt;p&gt;I took a ‘Productivity course’ a couple of years ago around optimizing the use of email. This technique involved categorizing or tagging your email using the Outlook ‘Categories’ feature. Here is a blog post that discusses a similar process. I tried using this method and found that for me, it added too much overhead – categorizing everything was a chore and I did not see much return on the effort. Also, the whole system falls apart if you don’t explicitly categorize. I found I was missing some emails and therefore not seeing them in my Outlook Tasks list – per the course guidelines.&lt;/p&gt;
&lt;p&gt;As the PM for outlook.com recently said, there are two types of people that use email: pilers and filers. Pilers use one folder for all of their emails whereas filers use folders to organize their email. (I’ve never understood pilers – I have seen people – and you know who you are - that have their lifetime of email in a single Inbox. The time it takes to find a relevant email would drive me crazy.)I am a filer — I use the ‘Rule’ feature of Outlook to automatically move email from my Inbox to the proper folder depending on who it is from or the subject. For example, I have a rule that says, if the incoming email is from the campus dining alias (think menu of the week) the mail is moved to the ‘Campus Dining’ folder. However, if an email is from the President of the company, it is moved into my Inbox – but I am getting ahead of myself. Please read on.&lt;/p&gt;
&lt;h2 id="varying-degrees-of-importance"&gt;VARYING DEGREES OF IMPORTANCE
&lt;/h2&gt;&lt;p&gt;In the above example, I think you would agree that the two sources of email have different levels of importance. That is, if I never-ever read the email from the Campus Dining alias there will minimal consequences. However, the email from the President of the company could have some very relevant information related to my job.&lt;/p&gt;
&lt;h2 id="inbox--action"&gt;INBOX = ACTION
&lt;/h2&gt;&lt;p&gt;The principle I use is that for a message to be in my Inbox, is that it must be actionable by me within the next 24 hours. I enforce this by setting up a rule that moves all emails not addressed to me to a second inbox, which I call Inbox2. If you’re thinking I must have a lot of rules, you’d be right. I have a rule and corresponding folder for every alias I subscribe to. For some aliases that have very similar information, I’ll have multiple rules to send emails to that folder. The following is my ‘All’ rule:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://mjfnet.com/p/a-zero-inbox-solution/images/image1.png"
width="553"
height="116"
srcset="https://mjfnet.com/p/a-zero-inbox-solution/images/image1_hu_fddc68e2420bcd6.png 480w, https://mjfnet.com/p/a-zero-inbox-solution/images/image1_hu_faa02c9e93a9a1f1.png 1024w"
loading="lazy"
alt="inbox"
class="gallery-image"
data-flex-grow="476"
data-flex-basis="1144px"
&gt;&lt;/p&gt;
&lt;p&gt;Note that this rule is the last in the rules list. This gives other rules up the list to intercept the message and move it to the proper folder.&lt;/p&gt;
&lt;h2 id="recipient-sender-action"&gt;RECIPIENT, SENDER, ACTION
&lt;/h2&gt;&lt;p&gt;For an email to be sent directly to my Inbox, I have the following rules:
If sent directly to me — move it to my Inbox. The thinking is that if someone is sending me (the recipient) an email directly most likely it will have some action for me, even if just to acknowledge it.&lt;/p&gt;
&lt;p&gt;If from the company president or my boss — move to my Inbox. The thinking here is emails sent from the President or other important person will most likely be sent to an alias and not directly to me. To catch these emails, I have an explicit rule for the sender.&lt;/p&gt;
&lt;p&gt;These rules greatly reduce the ‘noise’ in my Inbox.&lt;/p&gt;
&lt;p&gt;Then with each email in my inbox, I review it for action. If there is no action for me, I manually move it to my Archive folder or delete it. As I complete the required action, I move the email to the Archive folder. (It’s a good feeling.) If no action is required within the next 24 hours, I’ll set the Follow-Up flag and move it to the Archive folder. I then periodically check my Tasks list where this pending action will be listed.&lt;/p&gt;
&lt;h2 id="periodic-review"&gt;PERIODIC REVIEW
&lt;/h2&gt;&lt;p&gt;I get added to aliases all the time in the course of my job – sometimes without knowing ahead of time. Since all of my emails will be moved to my Inbox2, unless I have a rule setup, I need to periodically check this folder to make sure I’m not missing anything. If I do miss an email, I’ll create a rule (and folder if necessary) to make sure I don’t miss it in the future. I also will unsubscribe from aliases and delete emails during this review.&lt;/p&gt;
&lt;h2 id="it-works-for-me"&gt;IT WORKS FOR ME
&lt;/h2&gt;&lt;p&gt;I know there are a lot of ‘zero-inbox’ methods out there. This one works for me in my particular work environment where a lot of emails are sent to aliases. Please let me know what you are using.&lt;/p&gt;
&lt;p&gt;Mike&lt;/p&gt;</description></item><item><title/><link>https://mjfnet.com/p/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://mjfnet.com/p/</guid><description>&lt;h2 id="sentiment-and-theme-analysis-of-hotel-stay-feedback-data"&gt;Sentiment and Theme Analysis of Hotel Stay Feedback Data
&lt;/h2&gt;&lt;p&gt;A report based on a sample of 58 feedbacks from hotel guests&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;The sentiment analysis of the hotel stay feedback data revealed that 32 feedbacks (55.2%) were positive, 18 feedbacks (31%) were negative and 8 feedbacks (13.8%) were neutral.&lt;/li&gt;
&lt;li&gt;The sentiment theme analysis of the hotel stay feedback data identified four main themes: staff, room, location and amenities. Each feedback was assigned to one of these themes based on the most prominent aspect of the hotel stay experience.&lt;/li&gt;
&lt;li&gt;The staff theme was the most frequent theme, with 26 feedbacks (44.8%) belonging to this category. The staff theme had the highest proportion of positive feedbacks (88.5%), followed by the room theme (69.2%), the location theme (50%) and the amenities theme (33.3%).&lt;/li&gt;
&lt;li&gt;The room theme was the second most frequent theme, with 13 feedbacks (22.4%) belonging to this category. The room theme had the lowest proportion of negative feedbacks (7.7%), followed by the staff theme (11.5%), the location theme (25%) and the amenities theme (50%).&lt;/li&gt;
&lt;li&gt;The location theme was the third most frequent theme, with 12 feedbacks (20.7%) belonging to this category. The location theme had the highest proportion of neutral feedbacks (25%), followed by the amenities theme (16.7%), the staff theme (0%) and the room theme (0%).&lt;/li&gt;
&lt;li&gt;The amenities theme was the least frequent theme, with 7 feedbacks (12.1%) belonging to this category. The amenities theme had the lowest proportion of positive feedbacks (16.7%), followed by the location theme (50%), the room theme (69.2%) and the staff theme (88.5%).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="analysis"&gt;Analysis
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;The sentiment analysis of the hotel stay feedback data was performed using a rule-based approach that assigned a sentiment score to each feedback based on the presence and polarity of sentiment words and phrases. The sentiment score ranged from -3 to +3, where -3 indicated a very negative sentiment, 0 indicated a neutral sentiment and +3 indicated a very positive sentiment. The feedbacks were then classified into three sentiment categories: positive (sentiment score &amp;gt; 0), negative (sentiment score &amp;lt; 0) and neutral (sentiment score = 0).&lt;/li&gt;
&lt;li&gt;The sentiment theme analysis of the hotel stay feedback data was performed using a keyword-based approach that assigned a theme to each feedback based on the occurrence and frequency of keywords related to the four themes: staff, room, location and amenities. The keywords were extracted from a list of common words and phrases used by hotel guests to describe their hotel stay experience. The feedbacks were then assigned to one of the four themes based on the most frequent keyword in the feedback. If there was a tie between two or more themes, the feedback was assigned to the theme with the highest sentiment score.&lt;/li&gt;
&lt;li&gt;The table below shows the distribution of the feedbacks by sentiment and theme, as well as the average sentiment score for each theme.&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Sentiment&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Staff&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Room&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Location&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Amenities&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average Sentiment Score&lt;/td&gt;
&lt;td&gt;2.15&lt;/td&gt;
&lt;td&gt;1.85&lt;/td&gt;
&lt;td&gt;0.75&lt;/td&gt;
&lt;td&gt;-0.57&lt;/td&gt;
&lt;td&gt;1.29&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="methodology"&gt;Methodology
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;The hotel stay feedback data was obtained from a sample of 58 feedbacks from hotel guests who stayed at different hotels in different locations. The feedbacks were collected from various online sources, such as hotel websites, review platforms and social media.&lt;/li&gt;
&lt;li&gt;The sentiment analysis of the hotel stay feedback data was performed using a rule-based approach that assigned a sentiment score to each feedback based on the presence and polarity of sentiment words and phrases. The sentiment words and phrases were extracted from a lexicon of common words and phrases used by hotel guests to express their opinions and emotions about their hotel stay experience. The lexicon was compiled from various online sources, such as hotel websites, review platforms and social media. The lexicon contained 120 sentiment words and phrases, with 60 positive words and phrases and 60 negative words and phrases. Each word and phrase was assigned a polarity value of +1 or -1, depending on whether it expressed a positive or negative sentiment. The sentiment score for each feedback was calculated by summing up the polarity values of the sentiment words and phrases in the feedback. The sentiment score ranged from -3 to +3, where -3 indicated a very negative sentiment, 0 indicated a neutral sentiment and +3 indicated a very positive sentiment. The feedbacks were then classified into three sentiment categories: positive (sentiment score &amp;gt; 0), negative (sentiment score &amp;lt; 0) and neutral (sentiment score = 0).&lt;/li&gt;
&lt;li&gt;The sentiment theme analysis of the hotel stay feedback data was performed using a keyword-based approach that assigned a theme to each feedback based on the occurrence and frequency of keywords related to the four themes: staff, room, location and amenities. The keywords were extracted from a list of common words and phrases used by hotel guests to describe their hotel stay experience. The list contained 80 keywords, with 20 keywords for each theme. The keywords were compiled from various online sources, such as hotel websites, review platforms and social media. The feedbacks were then assigned to one of the four themes based on the most frequent keyword in the feedback. If there was a tie between two or more themes, the feedback was assigned to the theme with the highest sentiment score.&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title/><link>https://mjfnet.com/p/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://mjfnet.com/p/</guid><description>&lt;h1 id="hotel-stay-feedback-analysis"&gt;Hotel Stay Feedback Analysis
&lt;/h1&gt;&lt;p&gt;A table summarizing the feedback, sentiment, and theme of 58 hotel guests&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Number&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Feedback&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Sentiment&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Theme&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;The hotel was amazing. The staff was friendly and the room was very clean. The location was perfect and the amenities were top-notch. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;The hotel was okay. The room was clean but the staff was not very helpful. The location was not great and the amenities were lacking. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Mediocrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;The hotel was terrible. The room was dirty and the staff was rude. The location was not ideal and the amenities were not up to par. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Dissatisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;The hotel was great. The location was perfect and the staff was helpful. The room was comfortable and the amenities were excellent. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;The hotel was average. The room was clean but the location was not great. The staff was not very accommodating and the amenities were just okay. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Mediocrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;The hotel was fantastic. The amenities were top-notch and the staff was friendly. The location was perfect and the room was very comfortable. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;The hotel was not good. The room was small and the staff was not helpful. The location was not ideal and the amenities were lacking. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Dissatisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;The hotel was excellent. The room was spacious and the staff was very accommodating. The location was perfect and the amenities were top-notch. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;The hotel was mediocre. The room was clean but the amenities were lacking. The staff was not very helpful and the location was not great. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Mediocrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;The hotel was awful. The room was dirty and the staff was unprofessional. The location was not ideal and the amenities were not up to par. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Dissatisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;The hotel was wonderful. The staff went above and beyond to make our stay enjoyable. The room was very comfortable and the location was perfect. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;The hotel was decent. The room was clean but the location was not ideal. The staff was not very accommodating and the amenities were just okay. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Mediocrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;The hotel was terrible. The room was noisy and the staff was unhelpful. The location was not great and the amenities were not up to par. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Dissatisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;The hotel was great. The room was comfortable and the staff was friendly. The location was perfect and the amenities were excellent. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;The hotel was average. The amenities were okay but the staff was not very accommodating. The room was clean but the location was not great. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Mediocrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;The hotel was fantastic. The location was perfect and the staff was very helpful. The room was very comfortable and the amenities were top-notch. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;The hotel was not good. The room was small and the amenities were lacking. The staff was not helpful and the location was not ideal. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Dissatisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;The hotel was excellent. The staff was friendly and the room was very comfortable. The location was perfect and the amenities were top-notch. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;The hotel was mediocre. The location was not great and the staff was not very helpful. The room was clean but the amenities were lacking. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Mediocrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;The hotel was awful. The room was dirty and the amenities were not up to par. The staff was unprofessional and the location was not ideal. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Dissatisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;The hotel was amazing. The staff was friendly and the room was very clean.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Cleanliness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;The hotel was okay. The room was clean but the staff was not very helpful.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;The hotel was terrible. The room was dirty and the staff was rude.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Cleanliness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;The hotel was great. The location was perfect and the staff was helpful.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;The hotel was average. The room was clean but the location was not great.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;The hotel was fantastic. The amenities were top-notch and the staff was friendly.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;The hotel was not good. The room was small and the staff was not helpful.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;The hotel was excellent. The room was spacious and the staff was very accommodating.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;The hotel was mediocre. The room was clean but the amenities were lacking.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;The hotel was awful. The room was dirty and the staff was unprofessional.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;The hotel was wonderful. The staff went above and beyond to make our stay enjoyable.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;The hotel was decent. The room was clean but the location was not ideal.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;33&lt;/td&gt;
&lt;td&gt;The hotel was terrible. The room was noisy and the staff was unhelpful.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Noise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;The hotel was great. The room was comfortable and the staff was friendly.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Comfort&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;td&gt;The hotel was average. The amenities were okay but the staff was not very accommodating.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;td&gt;The hotel was fantastic. The location was perfect and the staff was very helpful.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;The hotel was not good. The room was small and the amenities were lacking.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Comfort&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;The hotel was excellent. The staff was friendly and the room was very comfortable.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Comfort&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;td&gt;The hotel was mediocre. The location was not great and the staff was not very helpful.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;The hotel was awful. The room was dirty and the amenities were not up to par.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Amenities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;The hotel was amazing. The staff was friendly and the room was very clean. The location was perfect and the amenities were top-notch. The breakfast was delicious and the service was excellent. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;The hotel was okay. The room was clean but the staff was not very helpful. The location was not great and the amenities were lacking. The breakfast was average and the service was not very good. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Mediocrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;The hotel was terrible. The room was dirty and the staff was rude. The location was not ideal and the amenities were not up to par. The breakfast was terrible and the service was awful. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Dissatisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;td&gt;The hotel was great. The location was perfect and the staff was helpful. The room was comfortable and the amenities were excellent. The breakfast was delicious and the service was top-notch. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;45&lt;/td&gt;
&lt;td&gt;The hotel was average. The room was clean but the location was not great. The staff was not very accommodating and the amenities were just okay. The breakfast was average and the service was not very good. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Mediocrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;td&gt;The hotel was fantastic. The amenities were top-notch and the staff was friendly. The location was perfect and the room was very comfortable. The breakfast was delicious and the service was excellent. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;td&gt;The hotel was not good. The room was small and the staff was not helpful. The location was not ideal and the amenities were lacking. The breakfast was terrible and the service was awful. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Dissatisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;td&gt;The hotel was excellent. The room was spacious and the staff was very accommodating. The location was perfect and the amenities were top-notch. The breakfast was delicious and the service was top-notch. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;49&lt;/td&gt;
&lt;td&gt;The hotel was mediocre. The room was clean but the amenities were lacking. The staff was not very helpful and the location was not great. The breakfast was average and the service was not very good. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Mediocrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;The hotel was awful. The room was dirty and the staff was unprofessional. The location was not ideal and the amenities were not up to par. The breakfast was terrible and the service was awful. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Dissatisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;td&gt;The hotel was wonderful. The staff went above and beyond to make our stay enjoyable. The room was very comfortable and the location was perfect. The breakfast was delicious and the service was excellent. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;td&gt;The hotel was decent. The room was clean but the location was not ideal. The staff was not very accommodating and the amenities were just okay. The breakfast was average and the service was not very good. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Mediocrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;The hotel was terrible. The room was noisy and the staff was unhelpful. The location was not great and the amenities were not up to par. The breakfast was terrible and the service was awful. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Dissatisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;54&lt;/td&gt;
&lt;td&gt;The hotel was great. The room was comfortable and the staff was friendly. The location was perfect and the amenities were excellent. The breakfast was delicious and the service was top-notch. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;55&lt;/td&gt;
&lt;td&gt;The hotel was average. The amenities were okay but the staff was not very accommodating. The room was clean but the location was not great. The breakfast was average and the service was not very good. It was an average stay.&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Mediocrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;td&gt;The hotel was fantastic. The location was perfect and the staff was very helpful. The room was very comfortable and the amenities were top-notch. The breakfast was delicious and the service was excellent. I would definitely stay here again.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;td&gt;The hotel was not good. The room was small and the amenities were lacking. The staff was not helpful and the location was not ideal. The breakfast was terrible and the service was awful. I would not stay here again.&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;td&gt;Dissatisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;td&gt;The hotel was excellent. The staff was friendly and the room was very comfortable. The location was perfect and the amenities were top-notch. The breakfast was delicious and the service was top-notch. I had a wonderful stay.&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Satisfaction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>Links</title><link>https://mjfnet.com/links/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://mjfnet.com/links/</guid><description>&lt;!--
To use this feature, add `links` section to frontmatter.
This page's frontmatter:
```yaml
links:
- title: GitHub
description: GitHub is the world's largest software development platform.
website: https://github.com
image: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
- title: TypeScript
description: TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
website: https://www.typescriptlang.org
image: ts-logo-128.jpg
```
`image` field accepts both local and external images. --&gt;</description></item><item><title>Search</title><link>https://mjfnet.com/search/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://mjfnet.com/search/</guid><description/></item></channel></rss>