Skip to main content
Glama
README.md
# Outlook MCP Server

This server uses Microsoft Entra's OAuth 2.0 On-Behalf-Of flow. The client
application sends an access token issued for this MCP API; the server validates
that token and exchanges it for a delegated Microsoft Graph access token.

## Entra application contract

- Client application ID: `9b544eca-bd87-4849-b7fb-e96c944cdca8`
- MCP/API application ID: `a7e86069-52f5-46b7-9a06-41d411c47410`
- Tenant ID: `46c98d88-e344-4ed4-8496-4ed7712e255d`
- MCP delegated scope: `api://a7e86069-52f5-46b7-9a06-41d411c47410/access_as_user`
- OBO Graph scope: `https://graph.microsoft.com/.default`

The bearer token sent to `/mcp` must be an access token for the MCP API, not
an ID token or a Graph access token. It must contain `access_as_user` in `scp`
and identify the client application in `azp` (v2 token) or `appid` (v1 token).

The MCP application registration must have delegated Microsoft Graph
permissions with admin consent and a confidential-client credential. In
Kubernetes, create the credential separately; never add its value to this
repository:

```powershell
kubectl create secret generic outlook-mcp-entra `
  --namespace catalyst-prod `
  --from-literal=client-secret='<secret-value>'
```

For local development, copy `.env.example` to `.env`, provide
`ENTRA_CLIENT_SECRET`, and export/load those variables before starting the
server. The production manifest reads the secret from `outlook-mcp-entra`.

A production-ready, forkable template for building MCP (Model Context Protocol) servers that deploy to the Catalyst Kubernetes platform.

This template uses the same proven SDK patterns running in production today (math-mcp-server, hsdes-mcp-server). Copy this folder, fill in your tools, and deploy in under 30 minutes.

Generated servers conform to the Intel IT MCP engineering standard (**IT-MCP-STD-001**): standardized naming, MCP-native tools tagged with `annotations` + governance `_meta`, risk tiers (R0-R3) with runtime enforcement of R2/R3 writes, data-freshness tags, and server-side telemetry (structured JSON logs with a correlation id and gateway-validated caller). A `registry.yaml` manifest records the server for the registry. (The deployed reference servers `math-mcp-server` and `hsdes-mcp-server` predate this convention.)

## Prerequisites

Before using this template, make sure you have:

- **Python 3.12+** installed locally
- **Podman** (for container builds) — [setup guide](../../docs/CATALYST-MCP-DEPLOYMENT-GUIDE.md#2-podman-vm-setup)
- **kubectl** configured with a Catalyst cluster kubeconfig
- **Harbor access** to push images to `amr-registry.caas.intel.com/catalyst/`

See the [full deployment guide](../../docs/CATALYST-MCP-DEPLOYMENT-GUIDE.md) for detailed prerequisites and access setup.

## Quick Start

### 1. Copy the template

```powershell
cp -r templates/mcp-server-template my-new-server
cd my-new-server
```

### 2. Find and replace all customization points

Search for `>>> CUSTOMIZE` across all files and replace the placeholders:

```powershell
# See all customization points
grep -rn "CUSTOMIZE" .
```

At minimum, replace:
- `your-server-name` → your actual server name (e.g., `jira-mcp-server`)
- `API_BASE_URL` → the upstream REST API you're wrapping
- Tool definitions in `server.py` → your actual tools

### 3. Define your tools

Edit `server.py` and replace the example tools (`core.greeting.get`, `core.item.get`, `core.item.update`) with your own. Tools are meaningful actions, not a 1:1 mirror of API endpoints — apply the test "would a user describe this action in natural language?".

1. Add a `types.Tool(...)` entry to the `TOOLS` list with:
   - `name` as `<domain>.<capability>.<verb_object>` (the backing system never appears in a tool name)
   - `description` + JSON Schema `inputSchema`
   - `annotations=types.ToolAnnotations(...)` — map `side_effects` to hints: `none`/`read` => `readOnlyHint=True`, `write_irreversible` => `destructiveHint=True`, `idempotentHint` from `intel.it/idempotent`
   - `_meta={...}` Intel governance tags: `risk_tier` (R0-R3), `side_effects`, `idempotent`, `data_classification`, plus (for data tools) `latency_class` / `answer_type` / `source_system`
2. Add a matching `case "<domain>.<capability>.<verb_object>":` block in `call_tool()`
3. Use `_api_request()` for upstream API calls, `_ok()` / `_err()` for responses
4. For R2 (reversible write) tools, require `reason`, `target_identifiers`, `idempotency_key`; for R3 (irreversible) require `reason`, `target_identifiers`, `approval_id`. The dispatcher enforces these before any upstream call and emits an AUDIT log
5. Update `registry.yaml` so the registry record matches the server's tools

### 4. Test locally

```powershell
python -m venv .venv
.venv\Scripts\activate        # Windows
# source .venv/bin/activate   # Linux/macOS
pip install -r requirements.txt
python server.py
```

Verify the server is running:

```powershell
# Health check
curl http://localhost:8000/health

# List tools (MCP JSON-RPC)
curl -X POST http://localhost:8000/mcp/ ^
  -H "Content-Type: application/json" ^
  -H "Accept: application/json, text/event-stream" ^
  -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}}"

# Call the greeting tool
curl -X POST http://localhost:8000/mcp/ ^
  -H "Content-Type: application/json" ^
  -H "Accept: application/json, text/event-stream" ^
  -d "{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{\"name\":\"core.greeting.get\",\"arguments\":{\"name\":\"World\"}}}"
```

### 5. Build the container

```powershell
podman build -t your-server-name:1.0.0 .
```

### 6. Push to Harbor registry

```powershell
podman tag your-server-name:1.0.0 amr-registry.caas.intel.com/catalyst/your-server-name:1.0.0
podman push --tls-verify=false amr-registry.caas.intel.com/catalyst/your-server-name:1.0.0
```

### 7. Deploy to Kubernetes

```powershell
# Set kubeconfig for your cluster
$env:KUBECONFIG = "path/to/kube-configs/amr-its-compute-cluster.yaml"

kubectl apply -f k8s-deploy.yaml
kubectl apply -f ingress.yaml

# Watch pod status
kubectl get pods -n catalyst -l app=your-server-name -w
```

### 8. Verify the deployment

```powershell
curl https://api-suite-dev.catalyst.intel.com/your-server-name/health
```

## Auth Patterns

### No Auth (Default)

The template ships with no authentication — any client with network access can call your tools. This is appropriate for internal demo servers and tools that don't access sensitive APIs.

### Bearer Token Passthrough

If your upstream API requires a user-provided Bearer token (e.g., Intel SSO `id_token` for HSDES, ServiceNow, etc.), enable the auth middleware:

1. **In `server.py`:** Uncomment the `TokenExtractorASGI` class, the `ContextVar`, and the `get_bearer_token()` helper function (clearly marked in the file)
2. **In the ASGI wiring section:** Swap the `Mount` line to use `TokenExtractorASGI`:
   ```python
   # Comment out this line:
   # Mount("/mcp", app=session_manager.handle_request),
   # Uncomment this line:
   Mount("/mcp", app=TokenExtractorASGI(session_manager.handle_request)),
   ```
3. **In `_api_request()`:** Uncomment the token forwarding lines to attach the Bearer token to upstream requests

See `servers/hsdes-mcp-server/` for a complete working example of this pattern.

## VS Code MCP Client Configuration

### No-auth server

Add to your `.vscode/mcp.json` or VS Code settings:

```json
{
  "servers": {
    "your-server-name": {
      "type": "http",
      "url": "https://api-suite-dev.catalyst.intel.com/your-server-name/mcp/"
    }
  }
}
```

### Auth server (Bearer token)

```json
{
  "servers": {
    "your-server-name": {
      "type": "http",
      "url": "https://api-suite-dev.catalyst.intel.com/your-server-name/mcp/",
      "headers": {
        "Authorization": "Bearer ${input:your_server_token}"
      }
    }
  },
  "inputs": [
    {
      "id": "your_server_token",
      "type": "promptString",
      "description": "Bearer token (Intel SSO id_token) for your-server-name",
      "password": true
    }
  ]
}
```

## File Overview

| File | Purpose |
|------|---------|
| `server.py` | MCP server with tool definitions and ASGI wiring |
| `requirements.txt` | Python dependencies (pinned to tested versions) |
| `Dockerfile` | Multi-stage container build with non-root user |
| `k8s-deploy.yaml` | Kubernetes Deployment + Service |
| `ingress.yaml` | Traefik Ingress + Middleware for external HTTPS access |
| `registry.yaml` | IT-MCP-STD-001 registry manifest (server + tool metadata) |
| `.env.example` | Environment variable reference (copy to `.env`) |
| `.vscode/mcp.json` | VS Code MCP client configuration |

## Full Documentation

For the complete deployment pipeline including Podman VM proxy setup, Harbor authentication, kubeconfig management, and troubleshooting:

**[docs/CATALYST-MCP-DEPLOYMENT-GUIDE.md](../../docs/CATALYST-MCP-DEPLOYMENT-GUIDE.md)**