Deployment
The Quick start covers npx-based local stdio installs — one user, one machine, the right answer for individual use.
Self-hosting in HTTP mode is required for two scenarios:
- Microsoft Copilot Studio — Copilot Studio can’t spawn local processes, so it needs a hosted HTTP endpoint
- Shared team instance — one deploy, many users, each providing their own SWSD token per request
This page covers Docker (works anywhere) and a complete Azure Container Apps walkthrough (the recommended path for Copilot Studio integration).
Docker
Section titled “Docker”The published image at ghcr.io/mikimatsub/mcp-swsd:latest runs on any container platform.
Quick smoke test
Section titled “Quick smoke test”docker run --rm -d \ --name swsd-mcp \ -p 3000:3000 \ -e SWSD_TRANSPORT=http \ -e SWSD_TRUST_PROXY=1 \ -e SWSD_BASE_URL=https://api.samanage.com \ ghcr.io/mikimatsub/mcp-swsd:latest
# Verifycurl http://localhost:3000/healthz# Expect: {"ok":true}The /mcp endpoint accepts MCP requests with the user’s token in the Authorization: Bearer <token> or X-SWSD-Token: <token> header (per-request, not configured server-side).
Image properties
Section titled “Image properties”- Base:
node:24-alpine, pinned by SHA256 digest for supply-chain safety - Multi-stage build: dev dependencies dropped from runtime layer
- Non-root user: runs as
node(UID 1000) - HEALTHCHECK baked in:
node -e "fetch('/healthz')..."every 30s
Image tags:
:latest— the latest main-branch commit:sha-XXXXXXX— pinned to a specific commit (recommended for production)
Browse all tags at GitHub Packages.
Azure Container Apps
Section titled “Azure Container Apps”The recommended path for Microsoft Copilot Studio integration. Same Microsoft ecosystem (auth, networking, identity all integrate cleanly), scale-to-zero pricing, public HTTPS endpoint with auto-managed TLS, no Kubernetes complexity.
Typical cost for a single team’s usage: $0–5/month.
Prerequisites
Section titled “Prerequisites”- An Azure subscription (free tier available)
- Azure CLI installed locally — verify with
az --version. Install instructions. - A SolarWinds Service Desk admin token for verification (each end user will use their own; this is just for the smoke test)
Step 1: Login to Azure
Section titled “Step 1: Login to Azure”az loginBrowser opens, you authenticate, terminal confirms.
Step 2: Create a resource group
Section titled “Step 2: Create a resource group”az group create \ --name swsd-mcp-rg \ --location eastusOther location options: westus2, westeurope, northeurope, southeastasia. See az account list-locations -o table for the full list.
Step 3: Create a Container Apps environment
Section titled “Step 3: Create a Container Apps environment”az containerapp env create \ --name swsd-mcp-env \ --resource-group swsd-mcp-rg \ --location eastusThis takes ~3 minutes.
Step 4: Deploy the swsd-mcp container
Section titled “Step 4: Deploy the swsd-mcp container”az containerapp create \ --name swsd-mcp \ --resource-group swsd-mcp-rg \ --environment swsd-mcp-env \ --image ghcr.io/mikimatsub/mcp-swsd:latest \ --target-port 3000 \ --ingress external \ --env-vars \ SWSD_TRANSPORT=http \ SWSD_BASE_URL=https://api.samanage.com \ SWSD_TRUST_PROXY=1 \ SWSD_PROFILE=full \ SWSD_RATE_LIMIT_MAX=200 \ --min-replicas 0 \ --max-replicas 3 \ --cpu 0.25 \ --memory 0.5GiKey flags:
| Flag | What it does |
|---|---|
--target-port 3000 | The container’s internal listen port (matches the Dockerfile’s EXPOSE) |
--ingress external | Accept traffic from the public internet (required for Copilot Studio) |
SWSD_TRUST_PROXY=1 | Tell Express to trust Container Apps’ reverse proxy so req.ip shows the real client (rate-limit accuracy) |
SWSD_PROFILE=full | Register all 23 tools (or pick agent, triage, knowledge) |
SWSD_RATE_LIMIT_MAX=200 | Slightly higher than default since this is a shared instance |
--min-replicas 0 | Scale to zero when idle (the magic that makes this nearly free) |
--max-replicas 3 | Cap concurrent instances to control cost spikes |
--cpu 0.25 --memory 0.5Gi | Right-sized for low/medium traffic |
This takes ~2 minutes.
Step 5: Get the public URL
Section titled “Step 5: Get the public URL”az containerapp show \ --name swsd-mcp \ --resource-group swsd-mcp-rg \ --query properties.configuration.ingress.fqdn \ --output tsvOutputs something like swsd-mcp.bluepebble-12345abc.eastus.azurecontainerapps.io. Save this — you’ll need it for Copilot Studio in the next step.
Step 6: Verify
Section titled “Step 6: Verify”curl https://YOUR_FQDN/healthz# Expect: {"ok":true}
# Test that /mcp rejects unauthenticated requestscurl -X POST -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' \ https://YOUR_FQDN/mcp# Expect: 401 with "Missing token..." messageIf both responses match, the deployment is healthy.
Cost monitoring
Section titled “Cost monitoring”Azure Container Apps charges for vCPU-seconds and memory-GiB-seconds when active, plus a small per-request fee. With min-replicas 0, idle time is free.
| Usage pattern | Approximate cost |
|---|---|
| Personal testing (10s of requests/day) | $0–1/month |
| Small team (100s of requests/day) | $1–5/month |
| Active team (1000s of requests/day) | $5–20/month |
| Large org (10,000+ requests/day) | $20–100+/month |
Monitor via Azure Portal → Cost Management. Free tier credits often cover this entirely for the first year.
Updating to a new version
Section titled “Updating to a new version”The Container App auto-pulls the new :latest image on the next cold start. To force an immediate update:
az containerapp update \ --name swsd-mcp \ --resource-group swsd-mcp-rg \ --image ghcr.io/mikimatsub/mcp-swsd:latestOr pin to a specific version:
az containerapp update \ --name swsd-mcp \ --resource-group swsd-mcp-rg \ --image ghcr.io/mikimatsub/mcp-swsd:sha-XXXXXXXTearing down
Section titled “Tearing down”# Delete just the container app (keeps the environment)az containerapp delete --name swsd-mcp --resource-group swsd-mcp-rg --yes
# Or delete everything in the resource groupaz group delete --name swsd-mcp-rg --yes --no-waitTroubleshooting
Section titled “Troubleshooting”| Symptom | Likely cause | Fix |
|---|---|---|
curl /healthz hangs | Container starting up (cold start) | Wait 30–60 seconds, retry. Cold starts are normal with min-replicas 0. |
404 from /healthz | Wrong FQDN, or container failed to start | az containerapp logs show --name swsd-mcp --resource-group swsd-mcp-rg --follow |
403 from /mcp | SWSD_ALLOWED_ORIGINS is set and rejecting your origin | Either remove the env var (safe behind Container Apps’ ingress) or add the calling origin to the allowlist |
| Token forwarded but SWSD returns 401 | Token expired or invalid | Generate a fresh token in SWSD UI |
Hardening for production
Section titled “Hardening for production”The recipe above is suitable for team use within a trusted network. For broader exposure:
- Azure AD authentication at the Container Apps level (Easy Auth) — restrict who can even reach
/mcpbefore the SWSD token check - VNet integration + private endpoints — bring the endpoint inside your corporate network
- IP allowlisting via Container Apps ingress restrictions — limit to known office/VPN IPs
- Custom domain + your own TLS certificate — looks more polished, allows custom DNS-rebinding-prevention
Each of these adds operational complexity in exchange for security.
Microsoft Copilot Studio
Section titled “Microsoft Copilot Studio”After deploying the HTTP server (above), you import a Swagger 2.0 connector spec into Copilot Studio.
Per-profile Swagger files live in copilot-studio/ on GitHub:
triage.swagger.yaml(8 tools)agent.swagger.yaml(21 tools, default)knowledge.swagger.yaml(11 tools)full.swagger.yaml(23 tools)
Import steps
Section titled “Import steps”- Pick the file matching your
SWSD_PROFILE. Edit thehost:line:to your deployed FQDN (nohost: REPLACE_WITH_YOUR_HOST.example.comhttps://prefix):host: swsd-mcp.bluepebble-12345abc.eastus.azurecontainerapps.io - In Copilot Studio: Add a tool → Custom connector → New connector → Import from OpenAPI file
- Wizard authentication settings:
- Authentication type: API Key
- Parameter label:
SWSD API Token(or anything user-friendly) - Parameter name:
X-SWSD-Token(must match exactly) - Parameter location: Header
- Save the connector
Test the connection
Section titled “Test the connection”In Copilot Studio’s connector test pane:
- Click Test → New connection
- Paste a valid SWSD API token
- Click Test operation on
InvokeMCP - Provide a minimal MCP JSON-RPC body for
tools/list:
{ "jsonrpc": "2.0", "method": "tools/list", "id": 1}Successful response: a JSON-RPC object with the tools array containing the entries for your selected profile.
x-ms-agentic-protocol: mcp-streamable-1.0is the Microsoft extension declaring the endpoint speaks MCP over Streamable HTTP — already present in the bundled Swagger files.- Copilot Studio dropped MCP-over-SSE support in August 2025; only Streamable HTTP is supported, which is what these connectors declare.
Other platforms
Section titled “Other platforms”The Docker image runs anywhere — AWS App Runner, GCP Cloud Run, Render, Fly.io, your own VM. Concrete recipes for those platforms are not yet written; PRs welcome.