Skip to content

Commit 4a3a6e4

Browse files
committed
Fix BC MCP proxy to target current MCP server endpoint
Microsoft moved the Business Central MCP server from api.businesscentral.dynamics.com/v2.0/<env>/mcp to a dedicated host at mcp.businesscentral.dynamics.com, with environment routing via HTTP headers (TenantId, EnvironmentName, Company). The previous proxy URL returns 400 Bad Request after a successful sign-in, even when the Azure app registration is correctly configured. Update both the Python (bc-mcp-proxy) and .NET (BcMCPProxy) samples: - Default Url/BaseUrl -> https://mcp.businesscentral.dynamics.com - Default scope -> https://mcp.businesscentral.dynamics.com/.default - Drop the /v2.0/<env>/mcp path suffix from the request URL - Send TenantId and EnvironmentName headers in addition to Company Verified end-to-end against a sandbox environment: the proxy completes the MCP initialize handshake, lists tools, and successfully invokes List_Customers / List_Items via bc_actions_invoke.
1 parent c0783dd commit 4a3a6e4

5 files changed

Lines changed: 15 additions & 10 deletions

File tree

samples/BcMCPProxy/Models/ConfigOptions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ public class ConfigOptions
1616

1717
public string ClientId { get; set; } = "3acde393-18cc-4b12-803c-4c85fa111c21";
1818

19-
public string TokenScope { get; set; } = "https://api.businesscentral.dynamics.com/.default";
19+
public string TokenScope { get; set; } = "https://mcp.businesscentral.dynamics.com/.default";
2020

21-
public string Url { get; set; } = "https://api.businesscentral.dynamics.com";
21+
public string Url { get; set; } = "https://mcp.businesscentral.dynamics.com";
2222

2323
public string Environment { get; set; } = "Production";
2424

samples/BcMCPProxy/Runtime/MCPServerProxy.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@ public async Task RunAsync()
4343
var transportOptions = new HttpClientTransportOptions
4444
{
4545
Name = "Test Server",
46-
Endpoint = new Uri(this.configOptions.Url.TrimEnd('/') + "/v2.0/" + this.configOptions.Environment + "/mcp"),
46+
Endpoint = new Uri(this.configOptions.Url.TrimEnd('/')),
4747
TransportMode = HttpTransportMode.StreamableHttp,
4848
AdditionalHeaders = new Dictionary<string, string>
4949
{
50-
{ "Company", HttpUtility.UrlDecode(this.configOptions.Company) },
50+
{ "TenantId", this.configOptions.TenantId },
51+
{ "EnvironmentName", this.configOptions.Environment },
52+
{ "Company", HttpUtility.UrlDecode(this.configOptions.Company) },
5153
{ "X-Client-Application", "BcMCPProxy" }
5254
}
5355
};

samples/BcMCPProxyPython/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ Example interactions:
9494
| Company | `--Company` | `BC_COMPANY` | Business Central company name | Yes |
9595
| Configuration Name | `--ConfigurationName` | `BC_CONFIGURATION_NAME` | Name of the Business Central configuration | No |
9696
| Custom Auth Header | `--CustomAuthHeader` | `BC_CUSTOM_AUTH_HEADER` | Pre-issued bearer token (skips device flow) | No |
97-
| Base URL | `--BaseUrl` | `BC_BASE_URL` | Base API URL (default: `https://api.businesscentral.dynamics.com`) | No |
98-
| Token Scope | `--TokenScope` | `BC_TOKEN_SCOPE` | OAuth scope (default: `https://api.businesscentral.dynamics.com/.default`) | No |
97+
| Base URL | `--BaseUrl` | `BC_BASE_URL` | MCP server URL (default: `https://mcp.businesscentral.dynamics.com`) | No |
98+
| Token Scope | `--TokenScope` | `BC_TOKEN_SCOPE` | OAuth scope (default: `https://mcp.businesscentral.dynamics.com/.default`) | No |
9999
| Log Level | `--LogLevel` | `BC_LOG_LEVEL` | Logging level (default: `INFO`) | No |
100100
| Debug | `--Debug` | `BC_DEBUG=1` | Enable verbose logging | No |
101101

samples/BcMCPProxyPython/bc_mcp_proxy/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ class ProxyConfig:
1414

1515
tenant_id: Optional[str] = None
1616
client_id: Optional[str] = None
17-
token_scope: str = "https://api.businesscentral.dynamics.com/.default"
18-
base_url: str = "https://api.businesscentral.dynamics.com"
17+
token_scope: str = "https://mcp.businesscentral.dynamics.com/.default"
18+
base_url: str = "https://mcp.businesscentral.dynamics.com"
1919
environment: str = "Production"
2020
company: Optional[str] = None
2121
configuration_name: Optional[str] = None

samples/BcMCPProxyPython/bc_mcp_proxy/proxy.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ def _build_transport_headers(config: ProxyConfig) -> dict[str, str]:
9191
headers: dict[str, str] = {
9292
"X-Client-Application": config.server_name,
9393
}
94+
if config.tenant_id:
95+
headers["TenantId"] = config.tenant_id
96+
if config.environment:
97+
headers["EnvironmentName"] = config.environment
9498
if config.company:
9599
headers["Company"] = unquote(config.company)
96100
if config.configuration_name:
@@ -99,8 +103,7 @@ def _build_transport_headers(config: ProxyConfig) -> dict[str, str]:
99103

100104

101105
def _build_endpoint_url(config: ProxyConfig) -> str:
102-
base = config.base_url.rstrip("/")
103-
return f"{base}/v2.0/{config.environment}/mcp"
106+
return config.base_url.rstrip("/")
104107

105108

106109
def run_sync(config: ProxyConfig) -> None:

0 commit comments

Comments
 (0)