sales-mcp-ui-poc
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@sales-mcp-ui-pocShow me the sales dashboard and the Contoso expansion opportunity."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
page_type: sample languages:
typescript
bicep products:
azure urlFragment: sales-mcp-ui-poc name: Sales companion MCP Apps sample description: "Build an Azure Functions remote MCP server with an interactive Microsoft 365 Copilot sales workspace."
Sales companion MCP Apps PoC
An Azure Functions Remote MCP TypeScript application that renders one bundled MCP Apps workspace for this flow:
sales dashboard -> opportunity detail -> editable quote preview -> explicitly confirmed SIMULATED quote save
The PoC never writes to CRM. Dashboard and opportunity reads use deterministic synthetic data by default or a server-configured read-only MCP server. The dashboard includes responsive, accessible pipeline bar and stage-mix donut charts alongside the detailed table. Quote drafts and simulated saves are persisted in Azure Blob Storage.
Prerequisites
Node.js 22
Azure Functions Core Tools 4.0.7030 or later
Azurite for local Blob/queue storage
Azure Developer CLI (
azd) for the parent-managed deployment flowMicrosoft 365 Agents Toolkit 6.12.0 or later for sideloading
Related MCP server: ticket-triage-mcp
Setup, build, and test
Set-Location <path-to-clone>
npm install
npm testnpm test builds the single-file UI, compiles the server and tests, and runs the Node built-in test entry point. To build without tests:
npm run build:app
npm run buildLocal run
Start Azurite first. The checked-in local.settings.json uses UseDevelopmentStorage=true, contains no credentials, and defaults to synthetic data.
Set-Location <path-to-clone>
npm startMCP endpoint:
http://localhost:7071/runtime/webhooks/mcpThe Functions MCP extension requires the existing host.json configuration, including webhookAuthorizationLevel: Anonymous. In Azure, App Service Authentication/Easy Auth still requires an authenticated Entra token before the request reaches the Functions host.
Configuration
Setting | Values/default | Purpose |
|
| Selects dashboard/opportunity source. Invalid values fail explicitly. |
| Empty by default | Server-side Streamable HTTP MCP endpoint used only when |
|
| Blob container for drafts and simulated saved quotes. |
|
| Clearly labelled scope used only when a trusted authenticated principal ID is unavailable. |
|
| Azurite connection string. Do not commit real credentials. |
| Set by Bicep in Azure | Existing storage account Blob endpoint. |
| Set by Bicep in Azure | Existing user-assigned managed identity client ID. |
Data modes
Synthetic mode returns stable IDs:
opp-contoso-expansionopp-fabrikam-pilotopp-northwind-renewal
Every response is labelled Synthetic demo data and Simulation - not CRM.
MCP mode uses the official @modelcontextprotocol/sdk Client with StreamableHTTPClientTransport. The upstream allowlist is fixed to:
get_sales_dashboardwith no argumentsget_opportunitywith{ "opportunityId": "..." }
The upstream must return normalized integer-cent and basis-point fields matching src/functions/salesData.ts, through structuredContent or JSON text. Connection, tool, schema, and parsing errors are returned explicitly; there is no synthetic fallback.
Example local override:
$env:DATA_MODE = "mcp"
$env:CRM_MCP_URI = "http://localhost:3001/mcp"
npm startPublic MCP surface
Tool | Behavior |
| Read-only dashboard. |
| Read-only opportunity and fixed quote catalog. |
| Creates or revises an identity-scoped Blob-backed draft. |
| Requires the exact |
| Reads a saved simulated quote. |
All tools point to ui://sales/workspace.html, served as text/html;profile=mcp-app. Read tools advertise readOnlyHint. Tool responses include text plus structuredContent.
Quote persistence and identity
Money is stored as integer cents and discounts as integer basis points. A preview creates server revision 1; editing requires the exact current revision and increments it using Blob ETag optimistic concurrency. Save uses a deterministic quote ID derived from the draft and conditional Blob creation. Repeating the same revision and idempotency key returns the original saved record.
Blob paths are scoped by a SHA-256 hash of a trusted principal ID when the Functions trigger exposes one. Anonymous/local hosts often do not expose a principal, so the PoC uses the labelled POC_IDENTITY_FALLBACK. Production is blocked until a Microsoft 365 authenticated call proves that the expected Entra principal reaches InvocationContext.triggerMetadata and produces per-user isolation.
The existing Bicep storage account and user-assigned managed identity are reused. The existing RBAC module grants Blob Data Owner and Queue Data Contributor as required by the template. Bicep declares the sales-quotes container; no new infrastructure stack is introduced.
Azure deployment flow
The parent workflow owns validation and deployment. This project is prepared for:
azd env new <environment-name>
azd env set PRE_AUTHORIZED_CLIENT_IDS <approved-client-id-list>
azd env set VNET_ENABLED false
azd provision
azd deployThe deployed endpoint is:
https://<function-app-name>.azurewebsites.net/runtime/webhooks/mcpTo switch the deployed app to a read-only CRM MCP source, set DATA_MODE=mcp and CRM_MCP_URI as server-side Function App settings through the approved deployment/configuration process. Do not expose CRM_MCP_URI to the app UI.
Microsoft 365 package
appPackage contains a declarative agent and a pinned RemoteMCPServer plugin with only the five PoC tools. Configure MCP_SERVER_URL, MCP_DA_OAUTH_CLIENT_ID, and the publisher values in the Agents Toolkit environment before provisioning. Agents Toolkit creates the Teams app ID and Enterprise token-store references used when packaging.
Validate and sideload the generated package in the target tenant, then complete interactive consent and verify the five tools, inline MCP App rendering, callbacks, and trusted per-user identity. Tenant-wide publication remains an administrator-controlled step.
Copilot Cowork skills
cowork/ contains a Unified App Manifest, MCP tool descriptions, and three portable Agent Skills inspired by the L2Q Cowork package structure:
pipeline-review-pack— reconciled pipeline analysis plus optional Excel workbook, PowerPoint narrative, and executive email draft.opportunity-research-brief— grounded Word meeting brief and optional email draft without invented contacts or activity.simulated-quote-review— server-calculated quote preview, optional Word review, and explicitly confirmed simulated save.
The package uses the same five MCP tools as the M365 declarative agent. Replace the endpoint and OAuth token-store reference in cowork/manifest.json before upload; never place credentials in the skill files. Build it with:
Set-Location <path-to-clone>
.\cowork\scripts\package.ps1Cowork skills are instructions that orchestrate connector tools; they are not an authorization boundary. Server-side validation, ownership checks, the exact CONFIRM_SIMULATED_SAVE token, current revision, and idempotency key remain mandatory. Cowork MCP Apps may render the same ui://sales/workspace.html resource, but host capability and confirmation behavior must be verified in the target tenant.
Architecture artifacts
Draw.io review record — final artifact only; intermediate passes were not retained
The diagrams distinguish Entra token issuance, Easy Auth enforcement, Function managed identity, host-mediated widget rendering, optional upstream CRM MCP access, and the unproven per-user identity-isolation production gate.
Security caveats
This is a PoC, not a CRM write integration.
save_demo_quoteis state-changing only within the simulation Blob container.The remote CRM tool contract is read-only and hardcoded.
The MCP URI is server-side configuration only.
Easy Auth and the existing Entra template must remain enabled for deployed use.
M365-to-Functions identity propagation is a production gate, not an assumption.
Synthetic data must not be represented as real customer or pipeline data.
No real credentials belong in
local.settings.json, manifests, or source.
Telemetry
The Azure deployment configures Application Insights for operational traces,
request correlation, failures, and latency. Do not log prompts, tokens,
credentials, or CRM payloads. To opt out for a deployment, remove the
APPLICATIONINSIGHTS_CONNECTION_STRING and
APPLICATIONINSIGHTS_AUTHENTICATION_STRING Function App settings after
deployment; a later azd provision may restore them.
Cleanup
Local dependency and runtime caches are ignored; restore dependencies with
npm ci. Run azd down only when you intend to delete the sample's Azure
resources, and remove any sideloaded Microsoft 365 package separately.
Trademarks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.
This server cannot be deployed
Maintenance
Related MCP Connectors
Read-only MCP access to sessions, funnels, campaigns, errors, live visitors, and anomalies.
Read-only MCP for Copart and IAA/IAAI search, history, filters, locations, shipping, and usage.
Query your org's data in natural language — read-only MCP access to SQL, NoSQL, files & warehouses.
Governed data discovery, exact queries, decisions, simulations, and runtime utilities over MCP.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceProvides read-only access to Salesforce orgs via MCP, enabling SOQL queries, sObject descriptions, and object listing using OAuth 2.0 Client Credentials.-
- AlicenseBqualityCmaintenanceEnables support agents to look up customer account details and billing or incident information through two read-only MCP tools for triage workflows.2MIT
- AlicenseBqualityCmaintenanceEnables read-only access to Epicor Kinetic ERP data, including sales orders, customers, parts, and shipments, through secure MCP tools.4MIT
- FlicenseNot gradedqualityBmaintenanceEnables secure, read-only querying of only predefined logical database views through MCP tools and a REST API, using validated SQL and semantic view metadata.-