|
| 1 | +--- |
| 2 | +title: Dynamic MCP Routing |
| 3 | +parent: MCP |
| 4 | +grand_parent: Extensibility |
| 5 | +nav_order: 3 |
| 6 | +--- |
| 7 | + |
| 8 | +# Dynamic MCP Routing |
| 9 | + |
| 10 | +A Power Platform connector that routes MCP Streamable HTTP traffic to one of several MCP server instances, selected at configuration time via a dropdown. Demonstrates how to use a catalog service, `x-ms-dynamic-values`, `x-ms-agentic-protocol`, and a `script.csx` URL rewriter to give a single connector access to multiple independent MCP servers. |
| 11 | + |
| 12 | +## Architecture |
| 13 | + |
| 14 | +``` |
| 15 | + ┌─────────────────────┐ |
| 16 | + │ Copilot Studio │ |
| 17 | + │ (MCP client) │ |
| 18 | + └──────────┬───────────┘ |
| 19 | + │ |
| 20 | + ┌────────────┴────────────┐ |
| 21 | + │ Power Platform Connector│ |
| 22 | + │ x-ms-agentic-protocol │ |
| 23 | + │ mcp-streamable-1.0 │ |
| 24 | + └────────────┬────────────┘ |
| 25 | + │ |
| 26 | + ┌─────────────────┼─────────────────┐ |
| 27 | + │ script.csx rewrites URL based on │ |
| 28 | + │ selected instance from dropdown │ |
| 29 | + └─────────────────┬─────────────────┘ |
| 30 | + │ |
| 31 | + ┌──────────────────────┼──────────────────────┐ |
| 32 | + ▼ ▼ ▼ |
| 33 | + /instances/contoso/mcp /instances/fabrikam/mcp /instances/northwind/mcp |
| 34 | + │ │ │ |
| 35 | + └──────────────────────┴──────────────────────┘ |
| 36 | + MCP Server |
| 37 | + (single process, path-based routing) |
| 38 | +``` |
| 39 | + |
| 40 | +**Three components:** |
| 41 | + |
| 42 | +1. **Catalog server** (`src/catalog/`) — REST endpoint returning a list of MCP server instances with their endpoint URLs. The connector's `ListInstances` operation hits this to populate the instance dropdown. |
| 43 | + |
| 44 | +2. **MCP server** (`src/mcp-server/`) — Single Express server hosting multiple independent MCP servers at `/instances/:id/mcp`. Each instance advertises its own tools (`list_projects`, `get_project_details`) with instance-specific data. Stateless: a fresh `Server` + `StreamableHTTPServerTransport` is created per request. |
| 45 | + |
| 46 | +3. **Power Platform connector** (`connector/`) — Swagger definition with two operations: |
| 47 | + - `ListInstances` (internal, for dropdown) — calls the catalog |
| 48 | + - `InvokeMCP` — annotated with `x-ms-agentic-protocol: mcp-streamable-1.0`, with an `instanceUrl` parameter populated via `x-ms-dynamic-values` |
| 49 | + - `script.csx` rewrites the `InvokeMCP` request URL from the catalog host to the selected instance's MCP endpoint |
| 50 | + |
| 51 | +## How It Works |
| 52 | + |
| 53 | +### 1. Instance Discovery |
| 54 | + |
| 55 | +The connector's `InvokeMCP` parameter uses `x-ms-dynamic-values` to call `ListInstances`, which returns instances with their `mcpUrl`: |
| 56 | + |
| 57 | +```json |
| 58 | +[ |
| 59 | + { "id": "contoso", "name": "Contoso", "mcpUrl": "https://host/instances/contoso/mcp" }, |
| 60 | + { "id": "fabrikam", "name": "Fabrikam", "mcpUrl": "https://host/instances/fabrikam/mcp" } |
| 61 | +] |
| 62 | +``` |
| 63 | + |
| 64 | +The agent builder picks an instance from the dropdown when adding the connector action. |
| 65 | + |
| 66 | +### 2. URL Rewriting |
| 67 | + |
| 68 | +The swagger `host` points at the catalog server. When Copilot Studio calls `InvokeMCP`, `script.csx` intercepts the request, reads the `instanceUrl` query parameter, and rewrites the full URL (scheme, host, port, path) to the selected instance's MCP endpoint: |
| 69 | + |
| 70 | +```csharp |
| 71 | +var targetUri = new Uri(instanceUrl); |
| 72 | +var builder = new UriBuilder(Context.Request.RequestUri) |
| 73 | +{ |
| 74 | + Scheme = targetUri.Scheme, |
| 75 | + Host = targetUri.Host, |
| 76 | + Port = targetUri.Port, |
| 77 | + Path = targetUri.AbsolutePath |
| 78 | +}; |
| 79 | +``` |
| 80 | + |
| 81 | +### 3. MCP Protocol |
| 82 | + |
| 83 | +Each instance endpoint is a fully independent MCP server. Copilot Studio handles the MCP protocol (`initialize`, `tools/list`, `tools/call`) natively. The mock data includes three fictional organizations with project portfolio data. |
| 84 | + |
| 85 | +## Sample Structure |
| 86 | + |
| 87 | +``` |
| 88 | +dynamic-mcp-routing-typescript/ |
| 89 | +├── src/ |
| 90 | +│ ├── catalog/ |
| 91 | +│ │ └── index.ts # Catalog REST server |
| 92 | +│ └── mcp-server/ |
| 93 | +│ ├── index.ts # Multi-instance MCP server |
| 94 | +│ └── data.ts # Mock instances, projects, details |
| 95 | +├── connector/ |
| 96 | +│ ├── apiDefinition.swagger.json # Swagger with x-ms-agentic-protocol |
| 97 | +│ ├── apiProperties.json # No connection parameters |
| 98 | +│ └── script.csx # URL rewriter for dynamic routing |
| 99 | +├── scripts/ |
| 100 | +│ ├── deploy.sh # One-step deploy for macOS / Linux |
| 101 | +│ └── deploy.ps1 # One-step deploy for Windows |
| 102 | +├── package.json |
| 103 | +├── tsconfig.json |
| 104 | +└── README.md |
| 105 | +``` |
| 106 | + |
| 107 | +## Quick Start |
| 108 | + |
| 109 | +### Prerequisites |
| 110 | + |
| 111 | +- [Node.js 18+](https://nodejs.org/) |
| 112 | +- [Dev Tunnels CLI](https://learn.microsoft.com/azure/developer/dev-tunnels/get-started) |
| 113 | +- [paconn CLI](https://learn.microsoft.com/connectors/custom-connectors/paconn-cli) (`pip install paconn`) |
| 114 | +- A [Power Platform environment](https://admin.powerplatform.microsoft.com/) with Copilot Studio access |
| 115 | + |
| 116 | +### 1. Install and Build |
| 117 | + |
| 118 | +```bash |
| 119 | +npm install |
| 120 | +npm run build |
| 121 | +``` |
| 122 | + |
| 123 | +### 2. Start Servers |
| 124 | + |
| 125 | +In two terminals: |
| 126 | + |
| 127 | +```bash |
| 128 | +# Terminal 1 — Catalog server (port 3000) |
| 129 | +npm run start:catalog |
| 130 | + |
| 131 | +# Terminal 2 — MCP server (port 3001) |
| 132 | +npm run start:mcp |
| 133 | +``` |
| 134 | + |
| 135 | +### 3. Create Dev Tunnels |
| 136 | + |
| 137 | +**Using the CLI:** |
| 138 | + |
| 139 | +```bash |
| 140 | +devtunnel host -p 3000 -p 3001 --allow-anonymous |
| 141 | +``` |
| 142 | + |
| 143 | +This outputs two URLs like: |
| 144 | + |
| 145 | +``` |
| 146 | +https://abc123-3000.euw.devtunnels.ms (catalog) |
| 147 | +https://abc123-3001.euw.devtunnels.ms (MCP) |
| 148 | +``` |
| 149 | + |
| 150 | +Restart the catalog server with the MCP tunnel URL: |
| 151 | + |
| 152 | +```bash |
| 153 | +MCP_SERVER_BASE=https://abc123-3001.euw.devtunnels.ms npm run start:catalog |
| 154 | +``` |
| 155 | + |
| 156 | +### 4. Deploy the Connector |
| 157 | + |
| 158 | +Update `connector/apiDefinition.swagger.json` — set `host` to your catalog tunnel hostname (e.g. `abc123-3000.euw.devtunnels.ms`), then: |
| 159 | + |
| 160 | +```bash |
| 161 | +python3 -m paconn login |
| 162 | +python3 -m paconn create \ |
| 163 | + -e YOUR_ENVIRONMENT_ID \ |
| 164 | + -d connector/apiDefinition.swagger.json \ |
| 165 | + -p connector/apiProperties.json \ |
| 166 | + -x connector/script.csx |
| 167 | +``` |
| 168 | + |
| 169 | +Or use the one-step deploy script that handles login, servers, tunnel, and connector deployment: |
| 170 | + |
| 171 | +**Bash (macOS / Linux):** |
| 172 | + |
| 173 | +```bash |
| 174 | +./scripts/deploy.sh YOUR_ENVIRONMENT_ID [TENANT_ID] |
| 175 | +``` |
| 176 | + |
| 177 | +**PowerShell (Windows):** |
| 178 | + |
| 179 | +```powershell |
| 180 | +.\scripts\deploy.ps1 -EnvironmentId YOUR_ENVIRONMENT_ID [-TenantId TENANT_ID] |
| 181 | +``` |
| 182 | + |
| 183 | +### 5. Configure Copilot Studio |
| 184 | + |
| 185 | +1. Open your agent in [Copilot Studio](https://copilotstudio.microsoft.com/) |
| 186 | +2. Go to **Actions** > **Add an action** > search for "Dynamic MCP Connector" |
| 187 | +3. Select an instance from the dropdown |
| 188 | +4. The agent now has access to the MCP tools for that instance |
| 189 | + |
| 190 | +## Example Queries |
| 191 | + |
| 192 | +Once the connector is added to an agent: |
| 193 | + |
| 194 | +- "What projects are available?" — calls `list_projects` |
| 195 | +- "Show me the details for the ERP rollout" — calls `get_project_details` with `projectId: "erp-rollout"` |
| 196 | +- "What are the risks on the supply chain project?" — calls `get_project_details` for Fabrikam |
| 197 | + |
| 198 | +## Development |
| 199 | + |
| 200 | +**Add a new instance:** Add an entry to the `instances` array in `src/mcp-server/data.ts` and `src/catalog/index.ts`, along with its projects and details. |
| 201 | + |
| 202 | +**Add a new tool:** Register additional tools in the `createServer` function in `src/mcp-server/index.ts` using `ListToolsRequestSchema` and `CallToolRequestSchema` handlers. |
| 203 | + |
| 204 | +**Remove dynamic routing:** If you only need a single MCP server, simplify by removing the catalog server and `script.csx`, and point the swagger `host` directly at the MCP server. |
| 205 | + |
| 206 | +## Resources |
| 207 | + |
| 208 | +- [Model Context Protocol](https://modelcontextprotocol.io/) |
| 209 | +- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) |
| 210 | +- [MCP in Copilot Studio](https://learn.microsoft.com/microsoft-copilot-studio/mcp-overview) |
| 211 | +- [Dev Tunnels](https://learn.microsoft.com/azure/developer/dev-tunnels/) |
| 212 | +- [Custom Connector CLI (paconn)](https://learn.microsoft.com/connectors/custom-connectors/paconn-cli) |
| 213 | +- [`x-ms-agentic-protocol`](https://learn.microsoft.com/connectors/custom-connectors/mcp-overview) |
0 commit comments