Content
<p align="center">
<img src="docs/images/logo.png" width="96" alt="Sales Assistant logo" />
</p>
<h1 align="center">Sales Assistant — Lead-to-Quote MCP Server</h1>
<p align="center">
<strong>Microsoft 365 Copilot</strong> · FastMCP · Salesforce CRM · Streamable HTTP · Azure Container Apps
</p>
An enterprise-grade [Model Context Protocol](https://modelcontextprotocol.io/) server that gives fintech sales professionals a complete **Lead-to-Quote** workflow without ever leaving the flow of work. It powers two surfaces:
1. **A Microsoft 365 Copilot Declarative Agent** — chat against your Salesforce pipeline with rich, branded HTML widgets, built with the M365 Agents Toolkit (VS Code) and the RemoteMCP plugin.
2. **A Copilot Cowork plugin** — five co-work skills (research briefs, follow-up automation, pitch decks, meeting scheduling, pipeline review packs) that turn Salesforce signals into finished email, Word, PowerPoint and Excel artifacts. See [`cowork/`](cowork/README.md).
---
## 📸 Screenshots
| Sales Dashboard | Pipeline Summary |
|---|---|
| [](docs/images/dashboard.png) | [](docs/images/pipeline.png) |
| Account 360 (with M365 signals) | Opportunities (Kanban + table) |
|---|---|
| [](docs/images/account_360.png) | [](docs/images/opportunities.png) |
| Leads | Dark mode |
|---|---|
| [](docs/images/leads.png) | [](docs/images/dashboard-dark.png) |
> Widgets carry the Microsoft 365 Copilot look — a light surface, the signature rainbow accent bar, Segoe UI typography — and adapt automatically to **light, dark and high-contrast** themes.
---
## ✨ Features
| Capability | Details |
|---|---|
| **Salesforce CRM** | Leads, Accounts, Contacts, Opportunities, Quotes, Products, Activities |
| **Authentication** | Client Credentials, OAuth Bearer pass-through, Entra SSO (switchable via env) |
| **UI Widgets** | Copilot-branded HTML+Skybridge widgets bound to `_meta.structuredContent`, with light/dark/contrast theming |
| **M365 Integration** | Agenda/email helpers for Teams, Outlook via M365 Tooling Servers |
| **Cowork plugin** | Five co-work skills that orchestrate Salesforce data with Cowork's built-in email/Word/PowerPoint/Excel |
| **Market Intelligence** | Designed to pair with external market-intelligence MCP tools for company, macro and market context |
| **Compliance** | MiFID II / GDPR / FCA compliance flags and deal desk approval tracking |
| **Transport** | Streamable HTTP (`/mcp`) or stdio |
| **Cloud** | One-command deploy to Azure Container Apps (`scripts/deploy-l2q.ps1`) |
---
## 🏗 Architecture
```
Microsoft 365 Copilot
│
├── Declarative Agent ──► RemoteMCP plugin
│ │
└── Cowork plugin ──► remoteMcpServer connector
│
▼
Sales Assistant MCP Server (FastMCP / Streamable HTTP)
hosted on Azure Container Apps
│
┌─────────────────────────┼─────────────────────────┐
▼ ▼ ▼
Salesforce REST API M365 Tooling Servers Market-intelligence MCP
(SOQL) Teams · Outlook · Calendar (external, in agent)
Leads / Accounts / (referenced, not proxied) company · macro · headlines
Contacts / Opps /
Quotes / Products
```
Each tool returns:
- **`content`** – Narrative text with prompt suggestions so the agent can continue a natural conversation.
- **`_meta.structuredContent`** – JSON data consumed by the HTML+Skybridge widgets at `ui://widgets/<name>.html`.
Most tools are **read-only**. Create/update tools (`create_record`, `update_record`) are write operations and require explicit confirmation (`confirm=true`).
---
## 🚀 Quick Start (local)
### 1. Prerequisites
- Python 3.11+
- Salesforce Connected App with **Client Credentials** flow enabled
- (Optional) Entra ID app registration for SSO
### 2. Install
```powershell
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -e .[dev]
```
### 3. Configure
```powershell
Copy-Item env\l2q.example.env env\l2q.env
```
Edit `env/l2q.env`:
```dotenv
# Auth mode: client_credentials | oauth_bearer | entra_sso
SF_AUTH_MODE=client_credentials
SF_INSTANCE_URL=https://your-org.my.salesforce.com
SF_CLIENT_ID=3MVG9...
SF_CLIENT_SECRET=...
SF_API_VERSION=v59.0
# Only needed for entra_sso mode
AAD_APP_CLIENT_ID=
AAD_APP_TENANT_ID=
```
### 4. Run locally
```powershell
scripts\run-l2q.ps1
# OR
python -m l2q.cli --transport http --host 0.0.0.0 --port 8080
```
The server listens at `http://localhost:8080/mcp`.
### 5. Test with MCP Inspector
1. Open MCP Inspector → New HTTP Connection → `http://localhost:8080/mcp`
2. Add header `Authorization: Bearer <token>` (for oauth_bearer / entra_sso modes)
3. Call `get_sales_dashboard` to see your morning briefing
---
## ☁️ Deploy to Azure
The server ships with an idempotent deployment script that builds the image in
Azure Container Registry (cloud build) and rolls it out to Azure Container Apps.
```powershell
az login
.\scripts\deploy-l2q.ps1
# Optional: .\scripts\deploy-l2q.ps1 -Location eastus -ImageTag v1.2.0
```
It provisions / updates (all idempotent, safe to re-run):
| Resource | Name | Notes |
|---|---|---|
| Resource Group | `rg-l2q-dev` | |
| Container Registry | `acrl2qdev` | Basic SKU |
| Container Apps Env | `cae-l2q-dev` | auto-creates Log Analytics |
| Container App | `ca-l2q-dev` | scale-to-zero, 0–3 replicas, 0.5 vCPU / 1 GiB |
On success it prints the live endpoints:
```
MCP endpoint : https://<app>.<region>.azurecontainerapps.io/mcp
Health check : https://<app>.<region>.azurecontainerapps.io/health
```
Set the Salesforce credentials on the running app:
```powershell
az containerapp update --name ca-l2q-dev --resource-group rg-l2q-dev `
--set-env-vars SF_AUTH_MODE=client_credentials `
SF_INSTANCE_URL=https://your-org.my.salesforce.com `
SF_CLIENT_ID=... SF_CLIENT_SECRET=...
```
### Verify the deployment
```powershell
$base = "https://<app>.<region>.azurecontainerapps.io"
# Health
Invoke-RestMethod "$base/health" # -> {"status":"ok"}
# MCP handshake + tool list
$h = @{ "Content-Type"="application/json"; "Accept"="application/json, text/event-stream" }
Invoke-WebRequest "$base/mcp" -Method Post -Headers $h `
-Body '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```
A healthy server responds to `initialize` with `serverInfo.name = "l2q"` and
serves the full Salesforce tool catalog (58 tools).
---
## 🛠 Tools Reference
### Lead Management
| Tool | Description |
|---|---|
| `search_leads` | Search by name, company, status or industry |
| `get_lead` | Full lead detail |
| `get_recent_leads` | All open leads (most recent first) |
| `get_leads_by_status` | Filter by status (New / Working / Qualified) |
### Account Management
| Tool | Description |
|---|---|
| `search_accounts` | Search financial institution accounts |
| `get_account` | Full account detail |
| `get_account_contacts` | Contacts at an account |
| `get_account_opportunities` | Opportunities for an account |
### Contact Management
| Tool | Description |
|---|---|
| `search_contacts` | Search by name, account or title |
| `get_contact` | Full contact profile |
| `get_contact_activity_history` | Call / email / meeting history |
### Opportunity Management
| Tool | Description |
|---|---|
| `search_opportunities` | Filter by stage, value, account |
| `get_opportunity` | Full opportunity detail |
| `get_open_opportunities` | Open pipeline opportunities |
| `get_opportunities_by_stage` | Deals at a specific stage |
| `get_pipeline_summary` | Pipeline by stage with totals |
### Quote Management
| Tool | Description |
|---|---|
| `search_quotes` | Find quotes by name or status |
| `get_quote` | Full quote with pricing |
| `get_quote_pdf_package` | Retrieve generated quote PDF artifacts/download URLs for email workflows |
| `get_quote_products` | Line items for a quote |
| `get_opportunity_quotes` | All quotes on an opportunity |
### Product Catalog
| Tool | Description |
|---|---|
| `search_products` | Search the product catalog |
| `get_product` | Product details and list price |
### Activities & Tasks
| Tool | Description |
|---|---|
| `get_activity_timeline` | Timeline for account/opp/contact |
| `get_open_tasks` | Open tasks sorted by priority |
### Create & Update Workflows (Draft-first)
| Tool | Description |
|---|---|
| `draft_create_record` | Build a pre-create draft form for account/contact/case/opportunity/task/quote/lead/event |
| `create_record` | Create the record in Salesforce after explicit confirmation |
| `get_record_for_update` | Load a single record as an editable update draft |
| `update_record` | Apply updates in Salesforce after explicit confirmation |
| `get_task` / `get_event` / `get_case` | Retrieve single objects for update flows |
### Guided Entity Resolution & Lookups
| Tool | Description |
|---|---|
| `search_reference_records` | Search accounts/contacts/opportunities/leads/cases for searchable picker dropdowns |
### Reporting
| Tool | Description |
|---|---|
| `run_salesforce_report` | Run a Salesforce report and render tabular results in widget UI |
### Compliance & Deal Desk
| Tool | Description |
|---|---|
| `get_compliance_flags` | MiFID II / GDPR / FCA flags |
| `get_deal_desk_status` | Approval process status |
### M365 Integration Helpers
| Tool | Description |
|---|---|
| `prepare_meeting_agenda` | Teams meeting agenda from Salesforce data |
| `prepare_email_summary` | Professional email/proposal draft |
| `get_sales_dashboard` | Morning briefing: pipeline + tasks + leads |
| `capture_lead_from_email` | Build a lead draft from Outlook email context |
> **Teams / Outlook / Calendar**: Use the [M365 Tooling Servers](https://learn.microsoft.com/en-us/microsoft-agent-365/tooling-servers-overview) directly from your Copilot Declarative Agent for scheduling meetings and sending emails.
---
## 🎨 UI Widgets
HTML+Skybridge widgets are registered as MCP resources at `ui://widgets/<name>.html`:
| Widget | Purpose |
|---|---|
| `dashboard.html` | Sales dashboard – pipeline + tasks + leads |
| `leads.html` | Lead list with status filter |
| `opportunities.html` | Kanban + table view |
| `quotes.html` | Quote cards + line items |
| `accounts.html` | Account cards + product catalog |
| `contacts.html` | Contact cards + detail profile |
| `pipeline.html` | Funnel + stacked bar chart |
| `activities.html` | Timeline + agenda/email viewer |
Each widget reads `structuredContent` from the MCP `_meta` via the Skybridge `postMessage` protocol.
All widgets now render in **compact mode by default** (for inline chat iframes) and include a **top-right Maximize/Restore button** so the user can expand to full-height working mode as needed.
Create/update forms now support **search-and-pick lookup flows** for relationship IDs (`AccountId`, `ContactId`, `WhoId`, `WhatId`, `OpportunityId`, and opportunity `PrimaryContactId`) to avoid manual ID typing.
### Prompt patterns for guided create flows
- **Meeting-driven create:**
- `I want to create an opportunity for the meeting with Jim that I just had`
- Agent flow: run `search_reference_records` to find the account/contact → open `opportunity_form.html` with prefilled data and candidate lookups.
- **Blank create:**
- `Create a new opportunity`
- Agent flow: run `draft_create_record object_type=opportunity` → use in-form pickers (Search/Pick) via `search_reference_records` to choose account/contact before create.
### Prompt Library – L2Q lifecycle + advanced automation (with context expectations)
The prompts below are written for a Copilot Declarative Agent that can:
- read **emails**, **Teams meeting summaries**, and **Teams messages**,
- use this **L2Q MCP server**,
- use an external **market-intelligence MCP** for company/market context.
When a prompt references “this deal/account/lead”, expected resolution flow is:
1. Resolve entity from the current artifact context (email/thread/meeting),
2. If ambiguous, show candidates and ask user to pick,
3. Continue with deeper analysis/drafting.
#### 6 prompts for L2Q phases + lifecycle reporting
| Title | Prompt | Context source & expected flow |
|---|---|---|
| **Lead Intake Snapshot** | From recent client emails, Teams messages, and meeting summaries, produce a lead intake summary with qualification signals, urgency, and recommended next action. | Artifact-led: starts from current conversation artifacts; asks for entity confirmation only if multiple companies are referenced. |
| **Qualification Decision** | For lead `[lead name or ID]`, assess qualify/nurture/disqualify and explain why using communication evidence plus external company and market context. | Entity-led: if no ID is provided, resolve by name and ask user to confirm best match. |
| **Opportunity Readiness Check** | For opportunity `[name or ID]`, assess stage readiness, missing required data, stakeholder gaps, and what must be completed before stage progression. | Entity-led: if unspecified, shortlist active opportunities and let user choose one. |
| **Quote Readiness Review** | For opportunity `[name or ID]`, evaluate quote readiness using recent client interactions and external market/company signals, then list blockers and next steps. | Entity-led: requires selected opportunity, then combines internal communication context + external intelligence. |
| **Close Plan & Commit Confidence** | For opportunity `[name or ID]`, create a close plan with milestones, owners, confidence drivers, and top slippage risks. | Entity-led: one selected opportunity; returns plan and confidence narrative. |
| **Weekly L2Q Report** | Generate my weekly lifecycle report: phase distribution, phase movement, stalled deals, top risks, and next-week priorities. | Portfolio-led: runs across user scope; asks for region/team filter only if needed. |
#### 6 prompts for high-value, complex automation
| Title | Prompt | Context source & expected flow |
|---|---|---|
| **At-Risk Deal Rescue** | Identify the most at-risk open opportunity in my portfolio and produce a two-week rescue plan using communication signals and external market/company context. | Portfolio-first then drill-down: proposes top at-risk deal, confirms, then generates rescue actions. |
| **Executive Forecast Narrative** | Build this week’s executive forecast narrative combining pipeline movement, communication evidence, and external market developments. | Portfolio-led synthesis for leadership reporting; no single-entity selection required. |
| **Smart Meeting Prep Pack** | For account `[name]`, create a meeting prep pack with objective, talking points, likely objections, and market-aware responses based on latest interactions. | Account-led: resolves account, then pulls artifact context + external updates. |
| **Post-Meeting Auto-Orchestration** | From this meeting summary, generate follow-up actions, owner assignments, timeline updates, and a polished client follow-up message aligned to deal strategy. | Artifact-led: starts from current meeting summary; if multiple linked deals exist, asks user to pick target deal. |
| **Renewal Risk & Expansion Plan** | For account `[name]`, create a renewal risk assessment and expansion plan using relationship signals, engagement patterns, and external outlook. | Account-led with multi-deal roll-up where applicable; returns 30/60/90 plan. |
| **Board-Ready Pipeline Pack** | Build a board-ready pipeline package for `[portfolio/team/region]` with confidence rationale, concentration risk, and recommended management interventions. | Portfolio-led: asks scope if not provided, then composes pack narrative and priorities. |
### Local widget preview with sample data
Use the preview script to render all widgets in a browser without running MCP tools:
```powershell
scripts\run-widget-preview.ps1
```
Then open `http://127.0.0.1:8090/` and select any widget page.
You can also choose a custom host/port:
```powershell
scripts\run-widget-preview.ps1 -HostName 127.0.0.1 -Port 8091
```
---
## 🔐 Authentication Modes
Set `SF_AUTH_MODE` in your env file:
| Mode | How it works |
|---|---|
| `client_credentials` | Server acquires a Salesforce token using `SF_CLIENT_ID` + `SF_CLIENT_SECRET` (Connected App) |
| `oauth_bearer` | Caller passes a Salesforce OAuth token in `Authorization: Bearer …` header – server forwards it |
| `entra_sso` | Caller passes a Microsoft Entra ID JWT – server validates it then forwards to Salesforce |
---
## 🐳 Docker
```bash
docker build -t l2q-mcp .
docker run -p 8080:8080 \
-e SF_AUTH_MODE=client_credentials \
-e SF_INSTANCE_URL=https://your-org.my.salesforce.com \
-e SF_CLIENT_ID=... \
-e SF_CLIENT_SECRET=... \
l2q-mcp
```
The server exposes a `/health` endpoint at `GET /health` that returns `{"status": "ok"}` — suitable for container orchestration liveness/readiness probes.
---
## 🧪 Testing
```bash
pip install -e .
python -m pytest tests/ -v
```
---
## 📋 M365 Copilot Declarative Agent Setup
1. Create a new Declarative Agent in VS Code using M365 Agents Toolkit
2. Add the **RemoteMCP** plugin with endpoint `https://<your-server>/mcp`
3. Add the [M365 Tooling Servers](https://learn.microsoft.com/en-us/microsoft-agent-365/tooling-servers-overview) for Teams, Outlook and Calendar
4. (Optional) Add a **market-intelligence MCP** plugin to the same agent for external company/market context
5. Set `Authorization: Bearer <Entra token>` in the RemoteMCP plugin config
---
## 🤝 Copilot Cowork Plugin
The [`cowork/`](cowork/README.md) folder contains a Copilot Cowork plugin that
reuses this same MCP server as a `remoteMcpServer` connector. Where the
Declarative Agent is the **chat** surface for fast lookups, the Cowork plugin is
the **co-work** surface for deep work — it produces finished email, Word,
PowerPoint and Excel artifacts. It ships five skills across two fintech personas:
| Skill | Persona | Built-in surfaces |
|---|---|---|
| `account-research-brief` | Account executive | Word, Email |
| `deal-followup-automation` | Account executive | Email, Word |
| `pitch-deck-builder` | Account executive | PowerPoint |
| `client-meeting-scheduler` | Account executive | Calendar, Email |
| `pipeline-review-pack` | Sales manager | Excel, PowerPoint, Email |
Package it with [`cowork/scripts/package.ps1`](cowork/scripts/package.ps1) and
sideload `sales-assistant-cowork.zip`. See [`cowork/README.md`](cowork/README.md)
for the full story, connector auth, and packaging steps.
---
## 🏢 About
Sales Assistant is a Microsoft 365 Copilot extensibility sample for **fintech
B2B sales** — built for professionals managing large, complex deals with
financial-institution clients in a highly regulated environment (MiFID II / GDPR
/ FCA). It demonstrates an MCP server powering both a Declarative Agent and a
Copilot Cowork plugin over live Salesforce data.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available