Skip to content

kaul-vineet/LOB-MCPAppsSample

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

164 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Enterprise LOB Copilot MCP Apps

Ten enterprise LOB systems, one M365 Copilot agent, interactive React widgets with side-by-side support

Salesforce ServiceNow SAP HubSpot Flight Tracker DocuSign SAP SuccessFactors Workday Coupa Jira

Subtitle A multi-LOB MCP Apps platform for M365 Copilot β€” 10 enterprise systems, 225 tools, 6 React widgets in one agent
Author Vineet Kaul, PM Architect – Agentic AI, Microsoft
Date April 2026
Stack Python Β· FastMCP 1.26 Β· React 19 Β· Fluent UI v9 Β· Vite Β· @modelcontextprotocol/ext-apps Β· Docker Β· Dev Tunnels Β· M365 Agents Toolkit Β· Azure Container Apps

Python React Fluent UI MCP SDK MCP Apps M365 Docker Azure License

Tags: mcp mcp-apps copilot python react fluent-ui m365 salesforce servicenow sap hubspot flight-tracker docusign sap-successfactors workday coupa jira agentic-ai declarative-agent azure-container-apps bicep


TL;DR β€” Ten Python MCP servers (Salesforce CRM Β· ServiceNow ITSM Β· SAP S/4HANA Β· HubSpot Marketing Β· Flight Tracker Β· DocuSign eSignature Β· SAP SuccessFactors HR Β· Workday HCM Β· Coupa Procurement Β· Jira Projects) with React + Fluent UI widgets, running behind a single ASGI gateway (port 8080) or via Docker Compose, orchestrated by one M365 Copilot declarative agent with 225 tools across 10 runtimes. All new LOBs (and Flight/DocuSign) ship with mock mode β€” fully functional without credentials. Deploy to Azure Container Apps with one Bicep file.


Contents

πŸ“– New to this? See SETUP.md for a beginner-friendly step-by-step credential guide.


Architecture

MCP Server vs. MCP App β€” Why custom servers?

Generic MCP Server MCP App (what we built)
Returns Raw JSON β†’ LLM summarizes as text structuredContent β†’ renders a visual widget
UI None β€” text in chat Interactive tables with inline Create, Edit, Delete
Requires Just tools Tools + _meta.ui.resourceUri + widget HTML resource
Example "You have 5 leads: John Smith at Acme..." Live sortable table with ✎ edit buttons

Our MCP servers are MCP Apps β€” they return structured data that M365 Copilot renders as interactive widgets directly in the chat.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                           M365 Copilot                               β”‚
β”‚                                                                      β”‚
β”‚  "Show leads"  "Open incidents"  "POs"  "Flights"  "Sign"  "HR"    β”‚
β”‚        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                    β–Ό                                  β”‚
β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”‚
β”‚         β”‚      Enterprise LOB Copilot (Declarative Agent) β”‚          β”‚
β”‚         β”‚         225 tools Β· 10 runtimes                 β”‚          β”‚
β”‚         β””β”€β”€β”¬β”€β”€β”¬β”€β”€β”¬β”€β”€β”¬β”€β”€β”¬β”€β”€β”¬β”€β”€β”¬β”€β”€β”¬β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”Όβ”€β”€β”Όβ”€β”€β”Όβ”€β”€β”Όβ”€β”€β”Όβ”€β”€β”Όβ”€β”€β”Όβ”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚  β”‚  β”‚  β”‚  β”‚  β”‚  β”‚  β”‚  β”‚
             β–Ό  β–Ό  β–Ό  β–Ό  β–Ό  β–Ό  β–Ό  β–Ό  β–Ό  β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚         ASGI Gateway  :8080  (all 10 LOBs in-process)        β”‚
   β”‚  /sf  /sn  /sap  /hs  /ft  /ds  /saphr  /workday  /coupa  /jira β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Two deployment modes:

  • Gateway β€” all 10 LOBs in one Python process on port 8080 (dev tunnel / Azure Container Apps)
  • Docker Compose β€” 10 individual services on ports 3000–3009 (optional per-LOB isolation)

LOBs at a Glance

# LOB Port Tools Widget Mock Mode
1 πŸ›οΈ Salesforce CRM 3000 31 βœ… React + Fluent UI β€”
2 🎫 ServiceNow ITSM 3001 24 βœ… React + Fluent UI β€”
3 πŸ“¦ SAP S/4HANA 3002 14 βœ… React + Fluent UI sandbox default
4 🧑 HubSpot Marketing 3003 29 βœ… React + Fluent UI β€”
5 ✈️ Flight Tracker 3004 5 βœ… React + Fluent UI βœ… auto if no creds
6 βœ’οΈ DocuSign eSignature 3005 9 βœ… React + Fluent UI βœ… auto if no creds
7 πŸ‘€ SAP SuccessFactors HR 3006 20 ⬜ pending βœ… auto if no creds
8 πŸ’Ό Workday HCM 3007 46 ⬜ pending βœ… auto if no creds
9 πŸ›’ Coupa Procurement 3008 21 ⬜ pending βœ… always (COUPA_MOCK=true)
10 πŸ“‹ Jira Projects 3009 26 ⬜ pending βœ… auto if no creds
Gateway 8080 225

Project Structure

lob-mcp-apps/
β”œβ”€β”€ README.md
β”œβ”€β”€ .gitignore
β”œβ”€β”€ check_meta.py              ← static analysis: tool/manifest alignment
β”œβ”€β”€ regen_manifests.py         ← regenerate mcp-tools.json + ai-plugin.json
β”‚
β”œβ”€β”€ shared-mcp-lib/            # Shared auth/HTTP/logging/telemetry/settings helpers
β”‚   └── shared_mcp/
β”‚       β”œβ”€β”€ auth.py            # get_bearer_token(ctx) β€” OAuth Bearer extraction
β”‚       β”œβ”€β”€ http.py            # create_async_client() β€” httpx factory
β”‚       β”œβ”€β”€ logger.py          # get_logger() β€” structlog wrapper
β”‚       β”œβ”€β”€ settings.py        # Dataclass loaders for saphr/workday/coupa/jira
β”‚       └── telemetry.py       # track_tool() decorator + wrap_specs() β€” App Insights
β”‚
β”œβ”€β”€ sf-mcp-app/                # Salesforce CRM (port 3000)
β”‚   β”œβ”€β”€ sf_crm_mcp/
β”‚   β”‚   β”œβ”€β”€ salesforce_server.py  # 31 tools β€” Leads, Opps, Accounts, Contacts,
β”‚   β”‚   β”‚                         #   Cases, Tasks, Pipeline, Campaigns, Approvals
β”‚   β”‚   β”œβ”€β”€ salesforce.py         # OAuth2 + REST client (query/create/update/delete)
β”‚   β”‚   └── web/widget.html       # React + SLDS widget
β”‚   └── .env.example
β”‚
β”œβ”€β”€ snow-mcp-app/              # ServiceNow ITSM (port 3001)
β”‚   β”œβ”€β”€ servicenow_mcp/
β”‚   β”‚   β”œβ”€β”€ servicenow_server.py  # 24 tools β€” Incidents, Requests, Change Requests,
β”‚   β”‚   β”‚                         #   Problems, Approvals, Service Catalog, KB
β”‚   β”‚   └── web/widget.html
β”‚   └── .env.example
β”‚
β”œβ”€β”€ sap-mcp-app/               # SAP S/4HANA (port 3002)
β”‚   β”œβ”€β”€ sap_s4hana_mcp/
β”‚   β”‚   β”œβ”€β”€ sap_server.py         # 14 tools β€” POs, Business Partners, Materials
β”‚   β”‚   β”œβ”€β”€ sap_client.py
β”‚   β”‚   └── web/widget.html
β”‚   └── .env.example
β”‚
β”œβ”€β”€ hubspot-mcp-app/           # HubSpot Marketing (port 3003)
β”‚   β”œβ”€β”€ hubspot_mcp/
β”‚   β”‚   β”œβ”€β”€ hubspot_server.py     # 29 tools β€” Emails, Lists, Contacts, Campaigns
β”‚   β”‚   β”œβ”€β”€ hubspot_client.py
β”‚   β”‚   └── web/widget.html
β”‚   └── .env.example
β”‚
β”œβ”€β”€ flight-mcp-app/            # Flight Tracker (port 3004) β€” mock-ready
β”‚   β”œβ”€β”€ flight_mcp/
β”‚   β”‚   β”œβ”€β”€ flight_server.py      # 5 tools β€” departures, arrivals, state, track
β”‚   β”‚   └── web/widget.html
β”‚   └── .env.example
β”‚
β”œβ”€β”€ docusign-mcp-app/          # DocuSign eSignature (port 3005) β€” mock-ready
β”‚   β”œβ”€β”€ docusign_mcp/
β”‚   β”‚   β”œβ”€β”€ docusign_server.py    # 9 tools β€” envelopes, templates, send, void, sign
β”‚   β”‚   └── web/widget.html
β”‚   └── .env.example
β”‚
β”œβ”€β”€ saphr-mcp-app/             # SAP SuccessFactors HR (port 3006) β€” mock-ready
β”‚   β”œβ”€β”€ saphr_mcp/
β”‚   β”‚   └── saphr_server.py       # 20 tools β€” employees, leave, time, performance
β”‚   └── .env.example
β”‚
β”œβ”€β”€ workday-mcp-app/           # Workday HCM (port 3007) β€” mock-ready
β”‚   β”œβ”€β”€ workday_mcp/
β”‚   β”‚   └── workday_server.py     # 46 tools β€” workers, skills, learning, org
β”‚   └── .env.example
β”‚
β”œβ”€β”€ coupa-mcp-app/             # Coupa Procurement (port 3008) β€” mock-ready
β”‚   β”œβ”€β”€ coupa_mcp/
β”‚   β”‚   └── coupa_server.py       # 21 tools β€” requisitions, POs, suppliers, invoices
β”‚   └── .env.example
β”‚
β”œβ”€β”€ jira-mcp-app/              # Jira Projects (port 3009) β€” mock-ready
β”‚   β”œβ”€β”€ jira_mcp/
β”‚   β”‚   └── jira_server.py        # 26 tools β€” issues, sprints, boards, projects
β”‚   └── .env.example
β”‚
β”œβ”€β”€ gateway/                   # ASGI gateway β€” all 10 LOBs on port 8080
β”‚   β”œβ”€β”€ app.py                 # Starlette mounts at /sf /sn /sap /hs /ft /ds
β”‚   β”‚                          #   /saphr /workday /coupa /jira
β”‚   └── Dockerfile             # Build context must be project root
β”‚
β”œβ”€β”€ deploy/                    # Azure Container Apps IaC
β”‚   β”œβ”€β”€ main.bicep             # ACR + Log Analytics + CAE + managed identity
β”‚   β”‚                          #   + gtc-gateway container app
β”‚   β”œβ”€β”€ deploy.sh              # Provision β†’ az acr build β†’ az containerapp update
β”‚   └── parameters.example.bicepparam
β”‚
β”œβ”€β”€ widgets/                   # React widget source (6 LOBs)
β”‚   β”œβ”€β”€ src/{lob}/             # index.html, main.tsx, App.tsx, types.ts
β”‚   β”œβ”€β”€ src/shared/            # McpBridge, FluentWrapper, Toast, ErrorBoundary
β”‚   └── build.mjs              # Vite single-file build β†’ ../web/widget.html
β”‚
β”œβ”€β”€ lob-agent/                 # Enterprise LOB Copilot declarative agent
β”‚   β”œβ”€β”€ appPackage/
β”‚   β”‚   β”œβ”€β”€ declarativeAgent.json
β”‚   β”‚   β”œβ”€β”€ manifest.json
β”‚   β”‚   β”œβ”€β”€ ai-plugin.json     # 225 functions, 10 runtimes
β”‚   β”‚   β”œβ”€β”€ mcp-tools.json     # Tool schemas with _meta + widget URIs
β”‚   β”‚   └── instruction.txt
β”‚   └── skills/                # Per-LOB scenario prompts
β”‚
β”œβ”€β”€ docker-compose.yml         # All 10 LOBs (ports 3000–3009) + gateway profile
└── deploy/SetSail.ps1               # One-command startup (Docker + tunnel)

Prerequisites

Requirement Notes
Docker Desktop Latest β€” recommended for the gateway or individual LOBs
Python β‰₯ 3.11 Only if running without Docker
Node.js β‰₯ 18 For widget builds (cd widgets && npm run build)
M365 Agents Toolkit VS Code extension or teamsapp CLI
Dev Tunnels CLI devtunnel β€” or use Azure Container Apps (no tunnel needed)
M365 Developer Tenant Copilot license + sideloading enabled

LOB credentials (all optional β€” servers fall back to mock mode without them):

LOB Free tier available?
Salesforce Developer Edition org at developer.salesforce.com
ServiceNow Developer instance at developer.servicenow.com
SAP S/4HANA Free sandbox API key at api.sap.com
HubSpot Free CRM account + Private App token
Flight Tracker Free account at opensky-network.org (mock works without)
DocuSign Developer sandbox at developers.docusign.com (mock works without)
SAP SuccessFactors SAP BTP trial (mock works without)
Workday Workday tenant (mock works without)
Coupa Coupa sandbox (always mock by default)
Jira Atlassian Cloud free tier (mock works without)

Setup

1. Clone & configure credentials

git clone https://github.com/kaul-vineet/LOB-MCPAppsSample.git
cd lob-mcp-apps

Copy .env.example β†’ .env for each LOB and fill in credentials (leave blank for mock mode):

cp sf-mcp-app/.env.example       sf-mcp-app/.env
cp snow-mcp-app/.env.example     snow-mcp-app/.env
cp sap-mcp-app/.env.example      sap-mcp-app/.env
cp hubspot-mcp-app/.env.example  hubspot-mcp-app/.env
cp flight-mcp-app/.env.example   flight-mcp-app/.env
cp docusign-mcp-app/.env.example docusign-mcp-app/.env
cp saphr-mcp-app/.env.example    saphr-mcp-app/.env
cp workday-mcp-app/.env.example  workday-mcp-app/.env
cp coupa-mcp-app/.env.example    coupa-mcp-app/.env
cp jira-mcp-app/.env.example     jira-mcp-app/.env

2. Credential quick-reference

πŸ›οΈ Salesforce

Variable How to get
SF_INSTANCE_URL Your org URL β€” e.g. https://myorg.salesforce.com
SF_CLIENT_ID, SF_CLIENT_SECRET Setup β†’ App Manager β†’ New Connected App β†’ OAuth2 client_credentials

🎫 ServiceNow

Variable How to get
SERVICENOW_INSTANCE Instance hostname β€” e.g. dev12345
SERVICENOW_CLIENT_ID/SECRET Developer instance β†’ OAuth 2.0 app (AUTH_MODE=oauth)
SERVICENOW_USERNAME/PASSWORD Basic auth fallback (AUTH_MODE=basic)

πŸ“¦ SAP S/4HANA

Variable How to get
SAP_API_KEY Free from api.sap.com β†’ Copy API Key. Default SAP_MODE=sandbox

🧑 HubSpot

Variable How to get
HUBSPOT_ACCESS_TOKEN Settings β†’ Integrations β†’ Private Apps. Scopes: crm.objects.contacts.read/write

✈️ Flight Tracker (mock-ready)

Variable How to get
OPENSKY_CLIENT_ID/SECRET opensky-network.org free account β€” leave blank for mock

βœ’οΈ DocuSign (mock-ready)

Variable How to get
DOCUSIGN_INTEGRATION_KEY DocuSign Developer β†’ Apps β†’ JWT Grant
DOCUSIGN_USER_ID, DOCUSIGN_ACCOUNT_ID DocuSign developer account
DOCUSIGN_RSA_PRIVATE_KEY base64 -w0 rsa_key.pem β€” base64-encoded PEM

πŸ‘€ SAP SuccessFactors HR (mock-ready)

Variable How to get
SAP_SF_ODATA_URL, SAP_SF_TOKEN_URL SAP BTP β†’ SuccessFactors API
SAP_SF_COMPANY_ID, SAP_SF_CLIENT_ID SAP BTP service instance credentials

πŸ’Ό Workday (mock-ready)

Variable How to get
WORKDAY_BASE_URL, WORKDAY_TENANT Workday tenant URL

πŸ›’ Coupa (always mock by default)

Variable How to get
COUPA_INSTANCE_URL https://your-org.coupahost.com β€” set COUPA_MOCK=false for live

πŸ“‹ Jira (mock-ready)

Variable How to get
JIRA_BASE_URL https://your-org.atlassian.net
JIRA_PROJECT_KEY e.g. GTC

3. Dev Tunnel (one-time, local dev only)

devtunnel user login -d
devtunnel create gtc-tunnel --allow-anonymous
devtunnel port create gtc-tunnel -p 8080    # Gateway (recommended β€” single port)
# Or individual LOB ports if using docker-compose without gateway:
devtunnel port create gtc-tunnel -p 3000    # Salesforce
devtunnel port create gtc-tunnel -p 3001    # ServiceNow
# ... repeat for 3002–3009

Update lob-agent/appPackage/ai-plugin.json runtime URLs with your tunnel hostname, or run python regen_manifests.py after setting MCP_GATEWAY_URL.

4. Agent provisioning

cd lob-agent
teamsapp auth login m365
teamsapp provision --env dev

Or via ATK sidebar in VS Code: Lifecycle β†’ Provision.


Running

Option A: ASGI Gateway (Recommended) ⚑

All 10 LOBs in one process on port 8080. This is the mode used with Azure Container Apps and the standard dev tunnel setup.

# Docker (build context = project root)
docker compose --profile gateway up gateway

# Or run locally
pip install -e ./shared-mcp-lib -e ./gateway  # plus all 10 LOB packages
python -m gateway

# Tunnel
devtunnel host gtc-tunnel --allow-anonymous

Routes: /sf/mcp /sn/mcp /sap/mcp /hs/mcp /ft/mcp /ds/mcp /saphr/mcp /workday/mcp /coupa/mcp /jira/mcp

Option B: Docker Compose 🐳

Individual containers per LOB (ports 3000–3009):

docker compose up -d                          # all 10 LOBs
docker compose up salesforce servicenow sap   # specific LOBs only
devtunnel host gtc-tunnel --allow-anonymous

docker compose ps
docker compose logs -f
docker compose down

Option C: Python Venvs (dev)

# Install shared lib first
pip install -e ./shared-mcp-lib

# Then each LOB
pip install -e ./sf-mcp-app    && python -m sf_crm_mcp         # :3000
pip install -e ./snow-mcp-app  && python -m servicenow_mcp     # :3001
pip install -e ./sap-mcp-app   && python -m sap_s4hana_mcp     # :3002
pip install -e ./hubspot-mcp-app && python -m hubspot_mcp      # :3003
pip install -e ./flight-mcp-app  && python -m flight_mcp       # :3004
pip install -e ./docusign-mcp-app && python -m docusign_mcp    # :3005
pip install -e ./saphr-mcp-app   && python -m saphr_mcp        # :3006
pip install -e ./workday-mcp-app && python -m workday_mcp      # :3007
pip install -e ./coupa-mcp-app   && python -m coupa_mcp        # :3008
pip install -e ./jira-mcp-app    && python -m jira_mcp         # :3009

Deploy to Azure

Deploy the gateway to Azure Container Apps β€” no dev tunnel needed, stable Azure FQDN.

Prerequisites

az login
az account set -s <subscription-id>
az bicep install

Deploy

cp deploy/parameters.example.bicepparam deploy/parameters.bicepparam
# Edit parameters.bicepparam with real credentials (this file is .gitignored)

export RESOURCE_GROUP=gtc-rg
export LOCATION=eastus
export ACR_NAME=gtcregistry   # must be globally unique

bash deploy/deploy.sh

The script:

  1. Creates the resource group
  2. Deploys deploy/main.bicep β€” ACR, Log Analytics, Container Apps environment, managed identity, and the gtc-gateway container app (external ingress, port 8080)
  3. Builds all 11 images in ACR using az acr build (remote build β€” no Docker daemon needed locally)
  4. Updates the container app with the new images

After deploy β€” update the manifest URL

export MCP_GATEWAY_URL=https://<your-gateway-fqdn>
python regen_manifests.py

Then re-provision the Teams agent with the new URLs.

Bicep resources created

Resource SKU / Notes
Azure Container Registry Basic
Log Analytics Workspace PerGB2018, 30-day retention
Container Apps Environment Consumption plan
User-Assigned Managed Identity AcrPull on the registry
gtc-gateway Container App 1–3 replicas, external ingress, 1 vCPU / 2 GiB

Mock Mode

All six original LOBs (Flight, DocuSign) plus all four new LOBs (SAP HR, Workday, Coupa, Jira) support mock mode β€” realistic demo data with no real credentials needed.

LOB Mock triggers Mock data
Flight Tracker OPENSKY_CLIENT_ID absent 5 GTC-themed flights (Dubai→London, etc.) with live-style positions
DocuSign Credentials absent 5 mock envelopes (NDA, MSA, Cargo contract…), 4 templates
SAP SuccessFactors HR SAP_SF_ODATA_URL absent 5 mock employees, leave balances, org chart
Workday WORKDAY_BASE_URL absent Mock workers, skills, learning completions
Coupa COUPA_MOCK=true (default) Mock requisitions, POs, supplier list
Jira JIRA_BASE_URL absent Mock issues, sprint board, project list

Mock mode activates automatically β€” the agent and widget see identical response shapes as with live credentials.


Static Analysis β€” check_meta.py

Validates tool definitions stay in sync across all three sources of truth:

python check_meta.py              # exits 1 if drift detected
python check_meta.py --verbose    # full per-tool report
Check What it verifies
_meta presence Every tool in mcp-tools.json has _meta.ui.resourceUri
Plugin registration Every manifest tool appears in ai-plugin.json
Server drift Tools in server.py match mcp-tools.json

The four new LOBs (saphr, workday, coupa, jira) use programmatic tool registration (mcp.tool(...)() loop pattern). Check 3 will warn "no @mcp.tool decorators found" for these β€” this is expected. Checks 1 and 2 still pass and verify all 225 tools are correctly manifested.


Manifest Generation β€” regen_manifests.py

Regenerates mcp-tools.json and ai-plugin.json by importing all 10 server modules and reading their live tool registrations.

# Local dev (devtunnel URL)
python regen_manifests.py

# After Azure deployment β€” point at your Container Apps FQDN
MCP_GATEWAY_URL=https://<your-gateway-fqdn> python regen_manifests.py

Run after adding or renaming tools, then re-provision the agent.


Skills System

lob-agent/skills/ documents per-LOB scenario prompts used as conversation starters:

File LOB Example starter
crm-pipeline.md Salesforce "Show me the latest leads. I want to review qualified ones."
incident-triage.md ServiceNow "Show me open incidents sorted by priority."
procurement-check.md SAP "Show me recent purchase orders."
campaign-performance.md HubSpot "Which campaigns have the best open rates?"
flight-tracker.md Flight "Show me departures from Heathrow today."
docusign-envelopes.md DocuSign "Show me envelopes awaiting signature."
cross-lob-operations.md All LOBs "Give me an end-of-day operations check."

Tools Reference

πŸ›οΈ Salesforce CRM β€” 31 tools (port 3000)

Tool Description
sf__get_leads Latest 5 leads
sf__create_lead Create lead
sf__update_lead Update lead
sf__convert_lead Convert lead β†’ Account + Contact + Opportunity
sf__get_opportunities Latest 5 opportunities
sf__create_opportunity Create opportunity
sf__update_opportunity Update opportunity
sf__get_accounts Latest 5 accounts
sf__create_account Create account
sf__update_account Update account
sf__get_contacts Latest 5 contacts
sf__create_contact Create contact
sf__update_contact Update contact
sf__get_cases Latest 5 cases
sf__create_case Create case
sf__update_case Update case status/resolution
sf__get_tasks Latest 5 tasks/activities
sf__create_task Create task
sf__get_pipeline_dashboard Opportunity pipeline by stage (count + amount)
sf__get_campaigns Get campaigns; pass campaign_id for edit form
sf__create_campaign Create campaign
sf__update_campaign Update campaign
sf__get_pending_approvals Pending ProcessInstance workitems
sf__get_products Product catalog
sf__get_price_books Price books
sf__get_quotes Recent quotes
sf__create_quote Create quote from opportunity
sf__get_contracts Active contracts
sf__get_activities_timeline Activity history for a record

Full list: see sf_crm_mcp/salesforce_server.py β†’ TOOL_SPECS

🎫 ServiceNow ITSM β€” 24 tools (port 3001)

Tool Description
sn__get_incidents Latest incidents
sn__create_incident Create incident
sn__update_incident Update incident
sn__resolve_incident Resolve/close incident
sn__get_requests Latest service requests
sn__create_request Create request
sn__update_request Update request
sn__get_request_items Items for a request
sn__update_request_item Update request item
sn__get_change_requests Latest change requests
sn__create_change_request Create change request
sn__get_problems Get problems; pass number for edit form
sn__create_problem Create problem record
sn__update_problem Update problem record
sn__get_hr_cases Get HR cases; pass sys_id for edit form
sn__create_hr_case Create HR case
sn__update_hr_case Update HR case
sn__get_pending_approvals Pending approvals queue
sn__get_service_catalog_items Active catalog items
sn__add_work_note Add work note to a record
sn__get_knowledge_articles Knowledge base search
sn__create_incident_form Open incident creation form widget
sn__create_request_form Open request creation form widget
sn__approve_request Approve a pending approval
sn__reject_request Reject a pending approval
sn__get_user_profile User profile lookup
sn__get_cmdb_ci Configuration item from CMDB
sn__get_sla_details SLA metrics for an incident
sn__update_change_request Update change request

Full list: see servicenow_mcp/servicenow_server.py β†’ TOOL_SPECS

πŸ“¦ SAP S/4HANA β€” 14 tools (port 3002)

Tool Description
sap__get_purchase_orders Latest purchase orders
sap__get_business_partners Business partners / suppliers
sap__get_materials Materials master data
sap__create_purchase_order Create PO (mock ID in sandbox)
sap__update_purchase_order Update PO
sap__get_material_details Material detail by ID
sap__get_sales_orders Sales orders list
sap__get_invoices Accounts payable invoices
sap__get_goods_receipts Goods receipts for a PO
sap__get_cost_centers Cost center master data
sap__get_gl_accounts G/L account master data
sap__get_profit_centers Profit center list
sap__approve_purchase_order Approve / reject a PO workflow
sap__get_stock_overview Material stock levels

🧑 HubSpot Marketing β€” 29 tools (port 3003)

Tool Description
hs__get_emails Marketing emails with metrics
hs__get_lists Contact lists / segments
hs__get_list_contacts Contacts in a list
hs__add_to_list Add contact to list
hs__remove_from_list Remove contact from list
hs__update_list Edit list name
hs__create_contact Create contact
hs__get_contacts Recent contacts
hs__update_contact Update contact
hs__get_deals Recent deals
hs__create_deal Create deal
hs__get_companies Recent companies
hs__create_company Create company
hs__get_campaigns Marketing campaigns
hs__get_campaign_metrics Opens, clicks, conversions per campaign
hs__clone_email Clone a marketing email
hs__send_test_email Send test email to an address
hs__get_forms Landing page forms
hs__get_form_submissions Form submission entries
hs__get_workflows Active automation workflows
hs__enroll_in_workflow Enroll a contact in a workflow
hs__get_tickets Support tickets
hs__create_ticket Create support ticket
hs__update_ticket Update ticket status/owner
hs__get_owners HubSpot users / owners
hs__get_pipelines Deal pipelines
hs__update_deal Update deal stage/amount
hs__delete_contact Delete a contact

Full list: see hubspot_mcp/hubspot_server.py β†’ TOOL_SPECS

✈️ Flight Tracker β€” 5 tools (port 3004)

Tool Description
ft__get_airport_departures Departures from ICAO airport
ft__get_airport_arrivals Arrivals at ICAO airport
ft__get_aircraft_state Live position/speed/altitude
ft__get_flights_by_aircraft Flight history for aircraft
ft__get_aircraft_track Waypoints for a flight

βœ’οΈ DocuSign eSignature β€” 9 tools (port 3005)

Tool Description
ds__get_envelopes List envelopes with status filters
ds__get_envelope_details Recipient and signing progress
ds__get_templates Available signing templates
ds__send_envelope Send envelope from template
ds__void_envelope Void / cancel an envelope
ds__resend_envelope Resend to pending signers
ds__get_signing_url Generate embedded signing URL
ds__download_document Download signed document
ds__send_envelope_form Open send-envelope form widget

πŸ‘€ SAP SuccessFactors HR β€” 20 tools (port 3006)

Employee self-service: profile, leave balances, time-off requests, org chart, performance goals, learning, benefits, and more.

πŸ’Ό Workday HCM β€” 46 tools (port 3007)

Worker context, skills, required learning, org chart, absence management, compensation, and more.

πŸ›’ Coupa Procurement β€” 21 tools (port 3008)

Requisitions, purchase orders, suppliers, invoices, budgets, approval workflows, and more.

πŸ“‹ Jira Projects β€” 26 tools (port 3009)

Issues (CRUD), sprints, boards, projects, epics, comments, attachments, transitions, and more.


Widget Architecture

React 19 + Fluent UI v9 widgets compiled into single-file HTML via Vite + vite-plugin-singlefile. Six of the ten LOBs have live widgets; four (SAP HR, Workday, Coupa, Jira) are in progress.

Widget Design System Key Visual Elements
Salesforce Lightning SLDS Blue header, pill status badges, compact CRUD forms
SAP Fiori Shell bar, '72' font, Object Page layout
HubSpot Canvas Metric cards, percentage bars, coral/teal palette, 3-level drill-down
ServiceNow Now Design Teal shell, P1–P4 priority colors
Flight Tracker Fluent v9 Dark sky theme, live status badges, altitude/speed metrics
DocuSign DocuSign brand Envelope status cards, signer progress

Platform features

Feature Implementation
MCP Apps protocol @modelcontextprotocol/ext-apps β€” handshake + tool-result delivery
Side-by-side mode app.requestDisplayMode('fullscreen')
Auto theming Light/dark via host context
Skeleton loading CSS shimmer while data loads
Error boundaries Crash recovery UI with "Try Again"
CRUD operations Create/read/update across all 6 live LOBs
Toast notifications Success/error after save

Building widgets

cd widgets
npm install
npm run build          # all 6 widgets
npm run build:sf       # specific LOB

Output: widgets/dist/{lob}.html β†’ copied to {lob}-mcp-app/{pkg}/web/widget.html.


Logs & Monitoring

Container logs (stdout)

# Gateway mode
docker compose --profile gateway logs -f gateway

# Individual LOBs
docker compose logs -f salesforce   # :3000
docker compose logs -f servicenow   # :3001
docker compose logs -f sap          # :3002
docker compose logs -f hubspot      # :3003
docker compose logs -f flight       # :3004
docker compose logs -f docusign     # :3005
docker compose logs -f saphr        # :3006
docker compose logs -f workday      # :3007
docker compose logs -f coupa        # :3008
docker compose logs -f jira         # :3009

For structured telemetry β€” tool latencies, success rates, and record counts by LOB β€” see the Telemetry β€” App Insights section below.


Telemetry β€” App Insights

Every tool call is automatically instrumented via shared_mcp/telemetry.py. Each invocation fires a dependency event to Azure Application Insights β€” without blocking the tool response.

What is captured per tool call

Field Description
name Tool name (e.g. sf__get_leads)
target LOB label (e.g. salesforce-crm)
duration Wall-clock time in milliseconds
success true / false
resultType Value of structuredContent.type (e.g. leads, incidents)
recordCount Value of structuredContent.total
error Error message if the tool threw or returned an error
cloud_RoleName Deployment name (env var) β€” useful in multi-agent environments
cloud_RoleInstance Derived from the tool name prefix (sf, sn, sap, hs, ft, ds, saphr, wday, coupa, jira)

Setup

Step 1 β€” Get your App Insights connection string

Azure Portal β†’ Application Insights resource β†’ Overview β†’ Connection String (copy the full string).

Step 2 β€” Add to each .env file (or gateway environment)

APPINSIGHTS_CONNECTION_STRING=InstrumentationKey=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx;IngestionEndpoint=https://eastus.in.applicationinsights.azure.com/
APPINSIGHTS_ROLE_NAME=lob-mcp

Leave APPINSIGHTS_CONNECTION_STRING blank (or omit it) to disable telemetry entirely β€” zero overhead, no HTTP calls made.

Step 3 β€” In docker-compose.yml gateway block (already wired)

environment:
  GATEWAY_PORT: "8080"
  APPINSIGHTS_CONNECTION_STRING: ""   # paste connection string to activate
  APPINSIGHTS_ROLE_NAME: "lob-mcp"

Viewing telemetry in App Insights

Transactions view β€” App Insights β†’ Transaction Search β†’ filter by dependency

KQL β€” all tool calls in the last hour

dependencies
| where timestamp > ago(1h)
| where type == "MCP Tool"
| project timestamp, name, target, duration, success, resultCode
| order by timestamp desc

KQL β€” slowest tools

dependencies
| where type == "MCP Tool"
| summarize avg(duration), percentile(duration, 95), count() by name
| order by avg_duration desc

KQL β€” failure rate by LOB

dependencies
| where type == "MCP Tool"
| summarize total=count(), failures=countif(success == false) by target
| extend failure_pct = round(100.0 * failures / total, 1)
| order by failure_pct desc

KQL β€” record volume trend (e.g. leads returned)

dependencies
| where type == "MCP Tool" and name startswith "sf__"
| extend recordCount = toint(customDimensions["resultCount"])
| summarize total_records=sum(recordCount) by bin(timestamp, 1h)
| render timechart

Application Map

With cloud_RoleInstance set per LOB prefix, App Insights Application Map automatically draws one node per LOB showing call rates and failure percentages. Navigate to Application Insights β†’ Application Map.

Multi-agent environments

If the same MCP servers are shared across multiple agents, set a distinct APPINSIGHTS_ROLE_NAME per deployment (e.g. lob-mcp-prod, lob-mcp-uat). Query by cloud_RoleName to isolate traffic per agent.


Critical Troubleshooting

  1. MCP Apps handshake β€” Widget must send ui/initialize β†’ wait for host response β†’ send ui/notifications/initialized. Only then will the host deliver ui/notifications/tool-result.

  2. --allow-anonymous on the tunnel β€” Required on both devtunnel create and devtunnel host. Without it, M365 Copilot's backend can't reach your MCP endpoints.

  3. _meta placement β€” _meta.ui.resourceUri must be on the tool definition in mcp-tools.json, not inside inputSchema.

  4. Manifest drift β€” Run python check_meta.py after adding tools. Run python regen_manifests.py to regenerate manifests.

  5. callTool method β€” Widget-to-host calls must use method: 'tools/call' with arguments (not ui/callTool with args).

  6. Custom App Upload Enabled β€” ATK sidebar β†’ Accounts. Both "Custom App Upload Enabled βœ“" and "Copilot Access Enabled βœ“" must show.

  7. Gateway URL β€” After Azure deployment, update MCP_GATEWAY_URL and re-run regen_manifests.py, then re-provision the agent.

  8. New LOBs returning mock data β€” Expected when credentials aren't configured. Set the relevant env vars and restart to use live APIs.


Adding a New LOB

  1. Copy an app folder (e.g. flight-mcp-app/) and rename the Python package
  2. Replace the API client with your LOB's auth + REST calls
  3. Keep @mcp.tool(meta={"ui": {"resourceUri": WIDGET_URI}}) and types.CallToolResult with structuredContent returns
  4. Add the new app to gateway/app.py (Mount) and gateway/Dockerfile (COPY + pip install)
  5. Add a service to docker-compose.yml
  6. Add a Bicep containerApp resource (or update deploy/main.bicep)
  7. Run python regen_manifests.py β†’ python check_meta.py
  8. Re-provision the agent

References