Skip to main content
Glama
rezapars

Client Onboarding MCP

by rezapars

Client Onboarding MCP for Microsoft Copilot and Teams

Minimal Python 3.12 project with:

  • MCP server with client-session authentication and five tools

  • Separate mock backend server

  • REST/OpenAPI facade

  • Microsoft 365 Copilot plugin package files

  • Microsoft Teams app manifest

  • Docker, tests, local setup, auth placeholders, and examples

Architecture:

[Microsoft Teams]
        |
        v
[Microsoft Copilot]
        |
        v
[MCP Server]
        |
        v
[Mock Backend Server]

Folder Tree

.
|-- appPackage/
|   |-- adaptive-cards/
|   |   `-- client-result.json
|   |-- color.png
|   |-- declarativeAgent.json
|   |-- manifest.json
|   |-- mcp-tools.json
|   |-- openapi.yaml
|   |-- outline.png
|   |-- plugin.json
|   `-- plugin.openapi.json
|-- examples/
|   |-- curl.md
|   |-- json-rpc.md
|   |-- requests.http
|   `-- responses/
|       |-- basic_info_family.json
|       |-- basic_info_name.json
|       |-- auth_failed.json
|       |-- auth_required.json
|       |-- auth_success.json
|       |-- error_invalid_field.json
|       |-- facility_limit.json
|       |-- onboarding_status.json
|       `-- outreach_summary.json
|-- mcp_server/
|   |-- __init__.py
|   |-- __main__.py
|   |-- app.py
|   |-- auth.py
|   |-- backend_client.py
|   |-- config.py
|   |-- errors.py
|   |-- jsonrpc.py
|   |-- logging_config.py
|   |-- mcp_app.py
|   |-- models.py
|   |-- routes.py
|   |-- session.py
|   `-- tool_registry.py
|-- mock_backend/
|   |-- __init__.py
|   |-- __main__.py
|   |-- app.py
|   |-- config.py
|   |-- logging_config.py
|   |-- models.py
|   `-- routes.py
|-- tests/
|   |-- conftest.py
|   |-- test_backend.py
|   |-- test_mcp_http.py
|   `-- test_tool_registry.py
|-- .env.example
|-- .vscode/launch.json
|-- Dockerfile
|-- docker-compose.yml
|-- main.py
|-- openapi.yaml
|-- pytest.ini
|-- requirements.txt
`-- tools.json

Tools

The MCP exposes these five tools.

authenticate_client

Input:

{
  "client_id": "123",
  "otp": "9632"
}

Successful output:

{
  "authenticated": true,
  "client_id": "123",
  "message": "Authentication successful"
}

Failed output:

{
  "authenticated": false,
  "message": "Invalid OTP"
}

Business tools use the authenticated client_id stored in the MCP session. For local HTTP tests, keep the same x-mcp-session-id header across calls.

get_client_onboarding_status

Input:

{}

Output:

{
  "client_id": "123",
  "status": "Completed",
  "message": "Client onboarding completed successfully"
}

get_client_basic_info

Input:

{
  "field": "name"
}

Allowed field values:

  • name

  • family

Output:

{
  "client_id": "123",
  "field": "name",
  "value": "John"
}
{
  "client_id": "123",
  "field": "family",
  "value": "Doe"
}

get_client_facility_limit

Input:

{}

Output:

{
  "client_id": "123",
  "client_name": "John Doe",
  "facility_limit_eur": 75000000,
  "currency": "EUR",
  "formatted_limit": "EUR 75,000,000"
}

summarize_client_outreach

Input:

{}

Output includes the outreach reasons, highlights, and total questions to answer:

{
  "client_id": "123",
  "client_name": "John Doe",
  "outreach_count": 2,
  "questions_to_answer_count": 5,
  "reasons": ["Annual facility review", "Updated cash-flow forecast"],
  "summary": "John Doe has 2 outreach items. Reasons: Annual facility review, Updated cash-flow forecast. You should answer 5 questions in total."
}

Local Setup

Use Python 3.12.

python -m venv .venv312
.venv312\Scripts\activate
pip install -r requirements.txt
copy .env.example .env

Terminal 1:

uvicorn mock_backend.app:app --host 0.0.0.0 --port 8001 --reload

Terminal 2:

uvicorn mcp_server.app:app --host 0.0.0.0 --port 8000 --reload

Health checks:

curl http://localhost:8001/health
curl http://localhost:8000/health

REST API

OpenAPI 3.1 schema:

  • Static YAML: openapi.yaml

  • Runtime JSON: http://localhost:8000/openapi.json

  • Runtime YAML file endpoint: http://localhost:8000/openapi.yaml

Endpoints:

curl -X POST http://localhost:8000/api/v1/authenticate \
  -H "Content-Type: application/json" \
  -H "x-mcp-session-id: demo-session" \
  -d '{"client_id":"123","otp":"9632"}'
curl http://localhost:8000/api/v1/client/onboarding-status -H "x-mcp-session-id: demo-session"
curl http://localhost:8000/api/v1/client/basic-info/name -H "x-mcp-session-id: demo-session"
curl http://localhost:8000/api/v1/client/facility -H "x-mcp-session-id: demo-session"
curl http://localhost:8000/api/v1/client/outreach-summary -H "x-mcp-session-id: demo-session"
curl http://localhost:8000/api/v1/clients/123/onboarding-status
curl http://localhost:8000/api/v1/clients/123/basic-info/name
curl http://localhost:8000/api/v1/clients/123/basic-info/family
curl http://localhost:8000/api/v1/clients/123/facility
curl http://localhost:8000/api/v1/clients/123/outreach-summary

MCP JSON-RPC

List tools:

curl -X POST http://localhost:8000/mcp ^
  -H "Content-Type: application/json" ^
  -H "x-mcp-session-id: demo-session" ^
  -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\"}"

Authenticate first:

curl -X POST http://localhost:8000/mcp ^
  -H "Content-Type: application/json" ^
  -H "x-mcp-session-id: demo-session" ^
  -d "{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{\"name\":\"authenticate_client\",\"arguments\":{\"client_id\":\"123\",\"otp\":\"9632\"}}}"

Then call business tools with the same session ID and no client_id argument:

curl -X POST http://localhost:8000/mcp ^
  -H "Content-Type: application/json" ^
  -H "x-mcp-session-id: demo-session" ^
  -d "{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",\"params\":{\"name\":\"get_client_onboarding_status\",\"arguments\":{}}}"

The MCP Python SDK registration is in mcp_server/mcp_app.py. For stdio-style MCP testing:

python -m mcp_server.mcp_app

Mock Backend

The mock backend is a separate FastAPI service and returns hardcoded data only.

curl http://localhost:8001/status/123
curl http://localhost:8001/info/123/name
curl http://localhost:8001/info/123/family
curl http://localhost:8001/facility/123
curl http://localhost:8001/outreach/123/summary

MCP Client Session Authentication

MCP client-session authentication is enabled in the tool flow:

  • Tool: authenticate_client

  • Required input: client_id, otp

  • Demo OTP: 9632

  • Storage: in-memory only

  • Session key: x-mcp-session-id or mcp-session-id header for HTTP tests

If a business tool is called before authentication, the MCP tool returns:

{
  "error": "AUTH_REQUIRED",
  "message": "Please authenticate first using client_id and OTP"
}

Production replacement points are marked in session.py:

  • Replace the hardcoded OTP with real OTP validation.

  • Load client/user mappings from a database.

  • Bind session IDs to Microsoft Entra ID or Teams user identity.

  • Store sessions in Redis or another shared session store.

API Key Placeholder

API key validation is intentionally disabled for local development. This is separate from the MCP client-session authentication tool.

Enable API key validation:

MCP_AUTH_REQUIRED=true
MCP_API_KEY_HEADER=x-api-key
MCP_API_KEY=local-dev-key

Then call:

curl http://localhost:8000/api/v1/clients/123/onboarding-status -H "x-api-key: local-dev-key"

Production replacement points:

  • Replace APIKeyAuthMiddleware with Microsoft Entra ID JWT validation.

  • Register an app in Microsoft Entra ID.

  • Configure Teams bot and Copilot plugin auth references with the production app registration.

  • Store secrets in Azure Key Vault or a managed secret store.

Docker

docker compose up --build

Services:

  • MCP server: http://localhost:8000

  • Mock backend: http://localhost:8001

Copilot Package

Package files are in appPackage/.

  • manifest.json: Microsoft 365/Teams app manifest

  • declarativeAgent.json: declarative agent manifest

  • plugin.json: Microsoft 365 Copilot plugin manifest using RemoteMCPServer

  • plugin.openapi.json: OpenAPI fallback plugin manifest

  • mcp-tools.json: MCP tool discovery metadata mirror of the inline plugin metadata

  • openapi.yaml: OpenAPI schema for the REST facade

  • color.png and outline.png: Teams package icons

Before packaging:

  1. Replace the hosted domain in appPackage/plugin.json, appPackage/openapi.yaml, and appPackage/manifest.json if you move away from the current Railway URL.

  2. Replace the app ID in appPackage/manifest.json with a real package ID before production publishing.

  3. Zip the contents of appPackage/, not the folder itself.

  4. Upload the zip in Teams Developer Portal or Microsoft 365 Agents Toolkit.

Example Copilot prompts:

  • Authenticate client 123 with OTP 9632

  • Get my onboarding status

  • What is the client's name?

  • What is the client's family name?

  • What is the current limit on my company's facility?

  • Summarize my outreach highlights

Teams Local Testing

  1. Start both local services.

  2. Start an HTTPS tunnel:

ngrok http 8000
  1. Copy the HTTPS ngrok domain, for example https://abc123.ngrok-free.app.

  2. Replace the hosted domain in appPackage/manifest.json, appPackage/plugin.json, and appPackage/openapi.yaml.

  3. In Teams Developer Portal, import the app package zip.

  4. Validate the manifest and test in a personal scope first.

This project is packaged as a Copilot declarative agent. It does not implement a Bot Framework message handler.

Error Handling Examples

Invalid basic info field through REST:

curl http://localhost:8000/api/v1/clients/123/basic-info/age

Response shape:

{
  "error": {
    "code": "validation_error",
    "message": "Request validation failed.",
    "details": []
  }
}

Invalid MCP tool call:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "get_client_basic_info",
    "arguments": {
      "field": "age"
    }
  }
}

Response shape:

{
  "jsonrpc": "2.0",
  "id": 3,
  "error": {
    "code": -32602,
    "message": "Tool arguments failed validation.",
    "data": {
      "code": "invalid_arguments",
      "details": []
    }
  }
}

Logging

Both services log requests with method, path, status code, and elapsed time.

Log levels:

MCP_LOG_LEVEL=INFO
BACKEND_LOG_LEVEL=INFO

Tests

pytest -q

Tests cover:

  • Mock backend responses

  • REST facade responses

  • MCP tools/list

  • MCP tools/call

  • Tool validation

API Versioning Strategy

REST endpoints are versioned under /api/v1.

Recommended evolution:

  • Keep /api/v1 stable for existing Copilot plugins.

  • Add /api/v2 for breaking response or parameter changes.

  • Keep MCP tool names stable where possible.

  • Add new tool names for breaking tool contract changes.

  • Keep operationId values aligned with Copilot plugin function names.

References

  • Microsoft 365 Copilot plugins support REST APIs and MCP servers through declarative agent actions.

  • Current Microsoft plugin manifest schema version used here: v2.4.

  • Current Microsoft 365 app manifest schema used here: v1.22.

F
license - not found
-
quality - not tested
C
maintenance

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rezapars/simple-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server