Dynamic MCP Routing
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.
Architecture
flowchart TD
CS["Copilot Studio<br/>(MCP client)"]
CONN["Power Platform Connector<br/>x-ms-agentic-protocol: mcp-streamable-1.0"]
SCRIPT["script.csx<br/>Rewrites URL based on<br/>selected instance"]
CAT["Catalog Server<br/>GET /instances"]
MCP["MCP Server<br/>(single process, path-based routing)"]
C["/instances/contoso/mcp"]
F["/instances/fabrikam/mcp"]
N["/instances/northwind/mcp"]
CS -->|"MCP protocol"| CONN
CONN -->|"ListInstances"| CAT
CONN -->|"InvokeMCP"| SCRIPT
SCRIPT -->|"rewritten URL"| MCP
MCP --> C
MCP --> F
MCP --> N
Three components:
-
Catalog server (
src/catalog/) — REST endpoint returning a list of MCP server instances with their endpoint URLs. The connector’sListInstancesoperation hits this to populate the instance dropdown. -
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 freshServer+StreamableHTTPServerTransportis created per request. -
Power Platform connector (
connector/) — Swagger definition with two operations:ListInstances(internal, for dropdown) — calls the catalogInvokeMCP— annotated withx-ms-agentic-protocol: mcp-streamable-1.0, with aninstanceUrlparameter populated viax-ms-dynamic-valuesscript.csxrewrites theInvokeMCPrequest URL from the catalog host to the selected instance’s MCP endpoint
How It Works
1. Instance Discovery
The connector’s InvokeMCP parameter uses x-ms-dynamic-values to call ListInstances, which returns instances with their mcpUrl:
[
{ "id": "contoso", "name": "Contoso", "mcpUrl": "https://host/instances/contoso/mcp" },
{ "id": "fabrikam", "name": "Fabrikam", "mcpUrl": "https://host/instances/fabrikam/mcp" }
]
The agent builder picks an instance from the dropdown when adding the connector action.
2. URL Rewriting
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:
var targetUri = new Uri(instanceUrl);
var builder = new UriBuilder(Context.Request.RequestUri)
{
Scheme = targetUri.Scheme,
Host = targetUri.Host,
Port = targetUri.Port,
Path = targetUri.AbsolutePath
};
3. MCP Protocol
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.
Sample Structure
dynamic-mcp-routing-typescript/
├── src/
│ ├── catalog/
│ │ └── index.ts # Catalog REST server
│ └── mcp-server/
│ ├── index.ts # Multi-instance MCP server
│ └── data.ts # Mock instances, projects, details
├── connector/
│ ├── apiDefinition.swagger.json # Swagger with x-ms-agentic-protocol
│ ├── apiProperties.json # No connection parameters
│ └── script.csx # URL rewriter for dynamic routing
├── scripts/
│ ├── deploy.sh # One-step deploy for macOS / Linux
│ └── deploy.ps1 # One-step deploy for Windows
├── package.json
├── tsconfig.json
└── README.md
Quick Start
Prerequisites
- Node.js 18+
- Dev Tunnels CLI
- paconn CLI (
pip install paconn) - A Power Platform environment with Copilot Studio access
1. Install and Build
npm install
npm run build
2. Start Servers
In two terminals:
# Terminal 1 — Catalog server (port 3000)
npm run start:catalog
# Terminal 2 — MCP server (port 3001)
npm run start:mcp
3. Create Dev Tunnels
Using the CLI:
devtunnel host -p 3000 -p 3001 --allow-anonymous
This outputs two URLs like:
https://abc123-3000.euw.devtunnels.ms (catalog)
https://abc123-3001.euw.devtunnels.ms (MCP)
Restart the catalog server with the MCP tunnel URL:
MCP_SERVER_BASE=https://abc123-3001.euw.devtunnels.ms npm run start:catalog
4. Deploy the Connector
Update connector/apiDefinition.swagger.json — set host to your catalog tunnel hostname (e.g. abc123-3000.euw.devtunnels.ms), then:
python3 -m paconn login
python3 -m paconn create \
-e YOUR_ENVIRONMENT_ID \
-d connector/apiDefinition.swagger.json \
-p connector/apiProperties.json \
-x connector/script.csx
Or use the one-step deploy script that handles login, servers, tunnel, and connector deployment:
Bash (macOS / Linux):
./scripts/deploy.sh YOUR_ENVIRONMENT_ID [TENANT_ID]
PowerShell (Windows):
.\scripts\deploy.ps1 -EnvironmentId YOUR_ENVIRONMENT_ID [-TenantId TENANT_ID]
5. Configure Copilot Studio
- Open your agent in Copilot Studio
- Go to Tools > Add tool > filter by Model Context Protocol
- Search for “Dynamic MCP Connector” and add it
- Under Inputs, select an instance from the Instance dropdown (e.g. “Contoso”, “Fabrikam”, “Northwind”)
- The Tools section will populate with the MCP tools for the selected instance — tools won’t appear until you pick an instance
- Click Save
Example Queries
Once the connector is added to an agent:
- “What projects are available?” — calls
list_projects - “Show me the details for the ERP rollout” — calls
get_project_detailswithprojectId: "erp-rollout" - “What are the risks on the supply chain project?” — calls
get_project_detailsfor Fabrikam
Development
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.
Add a new tool: Register additional tools in the createServer function in src/mcp-server/index.ts using ListToolsRequestSchema and CallToolRequestSchema handlers.
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.