PollySdk.McpGateway.Hosting.Http
1.0.0
dotnet add package PollySdk.McpGateway.Hosting.Http --version 1.0.0
NuGet\Install-Package PollySdk.McpGateway.Hosting.Http -Version 1.0.0
<PackageReference Include="PollySdk.McpGateway.Hosting.Http" Version="1.0.0" />
<PackageVersion Include="PollySdk.McpGateway.Hosting.Http" Version="1.0.0" />
<PackageReference Include="PollySdk.McpGateway.Hosting.Http" />
paket add PollySdk.McpGateway.Hosting.Http --version 1.0.0
#r "nuget: PollySdk.McpGateway.Hosting.Http, 1.0.0"
#:package PollySdk.McpGateway.Hosting.Http@1.0.0
#addin nuget:?package=PollySdk.McpGateway.Hosting.Http&version=1.0.0
#tool nuget:?package=PollySdk.McpGateway.Hosting.Http&version=1.0.0
McpGateway
Converts existing service protocols into the Model Context Protocol (MCP) so agents can call
existing services as MCP tools — automatically, with no per-endpoint code. Today it ships
OpenAPI/REST, OData, and upstream MCP proxy adapters; gRPC and SOAP slot in behind the
same IServiceAdapter seam.
You point McpGateway at an API description (an OpenAPI document, remote or local), and every operation becomes an MCP tool. The agent lists the tools and calls them; McpGateway translates each call into the real HTTP request and returns the response.
Project structure
src/McpGateway/
├─ McpGateway.Abstractions/ # contracts: IServiceAdapter, GatewayTool, BackendConfig, GatewayNaming
├─ McpGateway.Core/ # GatewayEngine, GatewayEngineBuilder, ToolCurator (no protocol deps)
├─ McpGateway.Adapters.OpenApi/ # OpenApiAdapter + AddOpenApi extension (Microsoft.OpenApi 3.x)
├─ McpGateway.Adapters.OData/ # ODataAdapter + AddOData extension (Microsoft.OData.Edm)
├─ McpGateway.Adapters.Mcp/ # proxies upstream HTTP/SSE/stdio MCP servers
├─ McpGateway.Adapters.DotNet/ # loads trusted MCP tool assemblies in-process
├─ McpGateway.Hosting.Stdio/ # stdio transport implementation
├─ McpGateway.Hosting.Http/ # Streamable HTTP transport implementation
├─ McpGateway.Host/ # executable; selects transport with --transport
└─ McpGateway.Tests/ # MSTest suite
Each protocol is its own package, so a consumer pulls only the dependencies it uses — referencing the OpenAPI adapter never drags in OData/gRPC/SOAP libraries.
| Project | Type | Purpose |
|---|---|---|
McpGateway.Abstractions |
library | Stable contracts every other project depends on. No dependencies. |
McpGateway.Core |
library | The gateway engine + builder + curation. Protocol-agnostic; no adapter deps. |
McpGateway.Adapters.OpenApi |
library | OpenAPI adapter + AddOpenApi extension. Carries Microsoft.OpenApi. |
McpGateway.Adapters.OData |
library | OData adapter + AddOData extension. Carries Microsoft.OData.Edm. |
McpGateway.Adapters.Mcp |
library | Proxies tools from upstream Streamable HTTP, SSE, or stdio MCP servers. |
McpGateway.Adapters.DotNet |
library | Loads trusted IDotNetMcpPlugin assemblies in-process through paired memory streams. |
McpGateway.Hosting.Stdio |
library | Runs the configured aggregate gateway over stdio. |
McpGateway.Hosting.Http |
library | Runs aggregate /mcp and isolated /{route}/mcp endpoints. |
McpGateway.Host |
exe | Single publishable host; requires --transport http or --transport stdio. |
McpGateway.Tests |
tests | MSTest suite for the adapters and tool curation. |
Future adapters (McpGateway.Adapters.Grpc, .Soap) join as sibling packages, each referencing only
McpGateway.Core plus its own protocol library, and shipping their own Add* extension — so Core
stays free of protocol dependencies.
NuGet package
PollySdk.McpGateway.Core contains the cross-platform McpGateway.Core and
McpGateway.Abstractions assemblies. Publish it manually through publish-nuget.yml, or publish a
GitHub release tagged mcpgateway-core-v<version>.
PollySdk.McpGateway.Hosting.Http contains the HTTP hosting library, Core, Abstractions, OpenAPI,
OData, upstream MCP, and .NET adapters, plus authentication. It is intended for an ASP.NET Core
executable that wants a complete HTTP gateway dependency. Publish it manually through
publish-nuget.yml, or publish a GitHub release tagged mcpgateway-hosting-http-v<version>.
How OpenAPI becomes MCP tools
| OpenAPI | → | MCP tool |
|---|---|---|
operationId |
→ | name — namespaced + sanitized (see below) |
summary / description |
→ | description |
parameters + requestBody |
→ | inputSchema (JSON Schema) |
verb + path (GET /pets) |
→ | invocation binding (used when the tool is called) |
Tool naming. Tools are named {backend}_{operationId} (e.g. petstore_getPetById). The
separator is _, not ., because MCP clients commonly restrict tool names to
[A-Za-z0-9_-] (Anthropic/OpenAI function names reject .). All names are sanitized to that
charset via GatewayNaming. Routing is by exact tool name, so the separator is purely cosmetic.
Running the host
The executable requires an explicit transport. Run the aggregate MCP server over stdio:
dotnet run --project src/McpGateway/McpGateway.Host -- --transport stdio
In stdio mode, logs go to stderr because stdout is the JSON-RPC channel. Both API backends and upstream MCP servers are exposed through one namespaced tool list.
HTTP endpoints and MCP proxy
HTTP mode exposes every configured source through the aggregate /mcp endpoint and also mounts one
isolated endpoint per source at /{route}/mcp:
dotnet run --project src/McpGateway/McpGateway.Host -- --transport http
Shared configuration for both modes lives in McpGateway.Host/appsettings.json. Set the HTTP listen
protocol/port under HttpHost:
{
"HttpHost": {
"Urls": [ "http://0.0.0.0:8080" ]
}
}
Use https://0.0.0.0:8443 for HTTPS, or provide multiple URLs in the array. The same value can be
provided as a semicolon-delimited string.
Publishing
The host is configured as a self-contained single-file binary. Editable configuration and spec
files are copied beside the executable. The default runtime is win-x64; pass -r to publish for
another runtime.
dotnet publish src/McpGateway/McpGateway.Host -c Release
Run the published binary as either McpGateway.Host.exe --transport http or
McpGateway.Host.exe --transport stdio. Configuration defaults to the executable directory, so the
side-by-side appsettings.json and relative specs paths work regardless of the caller's working
directory. Pass --contentRoot <path> to use another configuration directory.
Relative SpecPath values are resolved against that content root.
https://mcpgateway_host/mcp → petstore_* + northwind_* + upstream-MCP tools
https://mcpgateway_host/petstore/mcp → bare petstore tool names
https://mcpgateway_host/northwind/mcp → bare northwind tool names
Each source has a required Route for its isolated endpoint. The HTTP transport is stateless, while
stdio upstream clients and stateful HTTP upstream clients remain connected for the host lifetime.
Aggregate names retain the {source}_ prefix; isolated routes remove it for clients and restore it
internally when dispatching calls.
{
"McpGateway": {
"Backends": [
{ "Name": "petstore", "Route": "petstore", "Kind": "openapi", "BaseUrl": "...", "SpecUrl": "..." },
{ "Name": "northwind", "Route": "northwind", "Kind": "odata", "BaseUrl": "..." }
]
}
}
An agent can connect to all tools with { "url": "https://mcpgateway_host/mcp" } or one source with
{ "url": "https://mcpgateway_host/petstore/mcp" }.
Configuration
Each entry in McpGateway:Backends is a BackendConfig:
| Field | Required | Purpose |
|---|---|---|
Name |
✅ | Tool-namespace prefix (e.g. petstore → petstore_*). |
BaseUrl |
✅ | Root URL the operations are invoked against. |
Kind |
optional | Adapter to use: openapi (default) or odata. |
SpecUrl |
one of | URL of the description (OpenAPI doc, or OData $metadata; OData defaults to {BaseUrl}/$metadata). |
SpecPath |
one of | Local path to the description (checked before SpecUrl). |
IncludeTags |
optional | Only expose operations carrying one of these tags. |
IncludeOperations |
optional | Only expose these operationIds. |
ApiKey |
optional | Static credential injected on every backend request. Ignored when AuthScheme is set. |
ApiKeyHeader |
optional | Header for ApiKey (default Authorization). |
AuthScheme |
optional | Dynamic outbound auth: ManagedIdentity, OBO, Passthrough, PAT, AzureCli, Default, or None (default). Acquires a Bearer token per request via the Polly.Auth credential framework and sends it as Authorization: Bearer …. |
AuthScopes |
optional | Scopes/resources requested when acquiring the token for AuthScheme (e.g. ["https://graph.microsoft.com/.default"]). |
AllowWrites |
optional | When true, exposes write tools (OData create/update/delete + Actions). Default false. |
Dynamic schemes are configured under a top-level Auth section (TenantId, ClientId,
ManagedIdentityClientId, PatTokens, …) bound by Polly.Auth. OBO and Passthrough require an
inbound HTTP request and so work only in HTTP mode; ManagedIdentity, AzureCli, Default, and
PAT work in both modes.
Upstream MCP servers
Entries under McpGateway:McpServers are exposed alongside OpenAPI/OData backends:
| Field | Required | Purpose |
|---|---|---|
Name |
✅ | Aggregate tool prefix. |
Route |
✅ | Isolated endpoint segment at /{Route}/mcp. |
Transport |
optional | http (default), sse, or stdio. |
Url |
HTTP/SSE | Absolute upstream MCP endpoint. |
Command |
stdio | Executable used to start the upstream MCP server. |
Args |
optional | stdio command arguments. |
WorkingDirectory |
optional | stdio child working directory. |
InheritEnvironmentVariables |
optional | Whether stdio inherits the gateway environment; default true. |
Environment |
optional | Environment overrides for the stdio child. |
PassEnvironment |
optional | Required parent environment-variable names copied to the child. |
Headers |
optional | Static HTTP headers. An explicit Authorization header wins over AuthScheme. |
AuthScheme |
optional | Local/app-only outbound HTTP auth: Default, AzureCli, ManagedIdentity, PAT, MsalDesktop, or None. |
AuthScopes |
optional | Scopes/resources used to acquire the HTTP bearer token. |
IncludeTools |
optional | Only expose matching upstream tool names. |
{
"McpGateway": {
"McpServers": [
{
"Name": "filesystem",
"Route": "filesystem",
"Transport": "stdio",
"Command": "npx",
"Args": [ "-y", "@modelcontextprotocol/server-filesystem", "D:\\data" ],
"InheritEnvironmentVariables": true
},
{
"Name": "github",
"Route": "github",
"Transport": "http",
"Url": "https://github.example.com/mcp",
"AuthScheme": "AzureCli",
"AuthScopes": [ "api://github-mcp/.default" ]
}
]
}
}
The HTTP host does not require inbound authentication for local single-user use. OBO and
Passthrough are rejected for upstream MCP servers. HTTP tokens are checked before each operation;
when the credential refreshes, the upstream MCP client reconnects with the new bearer token. For
stdio, prefer letting the child authenticate through inherited Azure CLI/Default credentials or
explicitly passed environment variables.
In-process .NET MCP plugins
Entries under McpGateway:DotNetPlugins load referenced/trusted tool assemblies in the gateway
process. No child process or network endpoint is created. The plugin registers its dependencies,
declares its MCP tool types, and the adapter uses the normal MCP SDK reflection and invocation path.
| Field | Required | Purpose |
|---|---|---|
Name |
yes | Aggregate tool prefix. |
Route |
HTTP | Isolated endpoint at /{Route}/mcp. |
Assembly |
yes | Referenced assembly name or DLL path. |
PluginType |
optional | Fully-qualified IDotNetMcpPlugin type. May be omitted when the assembly has exactly one implementation. |
ConfigurationPath |
optional | Plugin JSON file; relative paths resolve from the gateway content root. Gateway configuration overrides this file. |
IncludeTools |
optional | Allowlist of plugin tool names before gateway namespacing. |
{
"McpGateway": {
"DotNetPlugins": [
{
"Name": "azure",
"Route": "azure",
"Assembly": "McpTools.Azure",
"PluginType": "McpTools.Azure.AzureToolsPlugin",
"ConfigurationPath": "plugins/McpTools.Azure.json"
}
]
}
}
The assembly and its dependencies must be available to the host. McpGateway.Host references
McpTools.Azure and copies its configuration to plugins/McpTools.Azure.json, so the example
works without starting the standalone Azure server. Only load trusted assemblies: plugins execute
with the gateway process's permissions.
Azure SDK tools: Kusto, ADF, and ADLS
The sibling sources/McpTools.Azure project is a standalone stdio MCP server. It uses Azure SDK
clients directly, so it does not depend on a MetricHub REST deployment. Configure named targets in
..\McpTools.Azure\appsettings.json:
{
"Auth": {
"TenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"ManagedIdentityClientId": ""
},
"AzureTools": {
"AuthScheme": "Default",
"Kusto": {
"DefaultDatabase": "metrics",
"MaxRows": 1000,
"TimeoutSeconds": 120,
"Databases": {
"metrics": {
"ClusterUri": "https://example.kusto.windows.net",
"Database": "MetricHub",
"Description": "MetricHub telemetry and operational data"
}
}
},
"DataFactory": {
"DefaultFactory": "metrics",
"Factories": {
"metrics": {
"SubscriptionId": "00000000-0000-0000-0000-000000000000",
"ResourceGroupName": "example-rg",
"FactoryName": "example-adf",
"Description": "Metric ingestion pipelines",
"AllowWrites": false
}
}
},
"Adls": {
"DefaultAccount": "metrics",
"DefaultPathAlias": "metrichub",
"MaxListItems": 1000,
"MaxReadBytes": 1048576,
"MaxQueryRows": 1000,
"QueryTimeoutSeconds": 120,
"QueryMemoryLimitMb": 512,
"QueryThreads": 4,
"Accounts": {
"metrics": {
"AccountName": "examplestorage",
"Description": "Metric input and output files",
"AllowWrites": false
}
},
"PathAliases": {
"metrichub": {
"Account": "metrics",
"FileSystem": "filesystem",
"RootPath": "MetricHub",
"Description": "MetricHub files in the filesystem container"
},
"raw-events": {
"Account": "metrics",
"FileSystem": "raw",
"RootPath": "events",
"Description": "Raw event files in a separate container"
}
}
}
}
}
Supported stdio authentication modes are Default, AzureCli, ManagedIdentity, and
MsalDesktop. Default works with local Azure CLI/developer credentials and managed identity.
ADF run/cancel and ADLS write/create/move/delete operations require AllowWrites: true on the
selected target. Kusto accepts tabular queries and .show metadata commands but rejects other
management commands.
Run the server directly:
dotnet run --project ..\McpTools.Azure
The server exposes kusto_query, kusto_list_databases, kusto_list_tables,
kusto_get_schema, kusto_sample, adf_list_factories, adf_list_pipelines,
adf_get_pipeline, adf_list_pipeline_runs, adf_get_pipeline_run, adf_run_pipeline,
adf_cancel_pipeline_run, adls_list_accounts, adls_list_path_aliases, adls_list_paths,
adls_read_file, adls_query_data, adls_write_file, adls_create_directory,
adls_move_path, and adls_delete_path.
adls_query_data uses DuckDB's azure extension to stream Parquet, CSV, or JSON directly from
ADLS. Each entry under AzureTools:Adls:PathAliases is a directory shortcut that binds an account,
container (FileSystem), optional RootPath, and description. The agent can call
adls_list_path_aliases, choose the relevant directory, generate DuckDB SQL, and execute it
without any separate database configuration:
{
"pathAlias": "metrichub",
"sql": "SELECT * FROM read_parquet('/output/*.parquet') WHERE event_date >= DATE '2026-07-01'"
}
Supported readers are read_parquet, read_csv, read_csv_auto, read_json,
read_json_auto, read_ndjson, and read_ndjson_auto. Reader paths are relative to the selected
path-alias root. The example resolves to
abfss://filesystem@examplestorage.dfs.core.windows.net/MetricHub/output/*.parquet.
Full URLs and paths outside the selected path-alias root are rejected.
The non-SQL ADLS tools use the same aliases. To list everything under a configured shortcut:
{
"pathAlias": "bizchatlt-prod-dir",
"path": "",
"recursive": false
}
This invokes adls_list_paths at the alias root. adls_read_file, adls_write_file,
adls_create_directory, adls_move_path, and adls_delete_path also accept alias-relative paths;
they no longer require callers to provide an account or container.
For compatibility, providing the separate path argument still exposes that source as a view
named data; for example:
SELECT scenario, count(*) AS rows
FROM data
WHERE event_date >= DATE '2026-07-01'
GROUP BY scenario
ORDER BY rows DESC
Only one guarded SELECT/WITH statement is accepted. DuckDB receives a fresh, short-lived
https://storage.azure.com/.default token through a temporary in-memory secret scoped to the
selected path-alias root. Local filesystem access, extension autoload/install during the user query,
and temporary disk spilling are disabled. The signed DuckDB azure extension is installed into
DuckDB's extension cache on first use; ADLS data remains remote and is streamed directly by the
extension.
McpGateway.Host references the Azure project and enables it in-process through
McpGateway:DotNetPlugins by default:
{
"Name": "azure",
"Route": "azure",
"Assembly": "McpTools.Azure",
"PluginType": "McpTools.Azure.AzureToolsPlugin",
"ConfigurationPath": "plugins/McpTools.Azure.json"
}
Aggregate tools are named azure_*; HTTP mode also exposes bare Azure tool names at /azure/mcp.
The standalone McpTools.Azure stdio server remains available when process isolation is preferred.
Example 1 — remote OpenAPI, curated by tag
{
"McpGateway": {
"Backends": [
{
"Name": "petstore",
"BaseUrl": "https://petstore3.swagger.io/api/v3",
"SpecUrl": "https://petstore3.swagger.io/api/v3/openapi.json",
"IncludeTags": [ "pet" ]
}
]
}
}
Example 2 — local hand-written spec (a REST API with no discovery endpoint)
The API needs no OpenAPI support of its own — you describe its endpoints in a local OpenAPI file and McpGateway proxies the calls. No network is touched for discovery.
{
"McpGateway": {
"Backends": [
{
"Name": "customers",
"BaseUrl": "https://legacy.example.com",
"SpecPath": "specs/customers.openapi.json",
"IncludeOperations": [ "getCustomer", "listCustomers" ]
}
]
}
}
Example 3 — API key auth
{
"McpGateway": {
"Backends": [
{
"Name": "billing",
"BaseUrl": "https://api.billing.internal",
"SpecUrl": "https://api.billing.internal/openapi.json",
"ApiKey": "Bearer ${BILLING_TOKEN}",
"ApiKeyHeader": "Authorization"
}
]
}
}
Example 3b — dynamic auth (Managed Identity)
The gateway acquires a token per request instead of using a static key. Configure the credential
framework under Auth, then select a scheme + scopes on the backend.
{
"Auth": {
"ManagedIdentityClientId": "${UAMI_CLIENT_ID}"
},
"McpGateway": {
"Backends": [
{
"Name": "graph",
"BaseUrl": "https://graph.microsoft.com/v1.0",
"SpecPath": "specs/msgraph.openapi.json",
"AuthScheme": "ManagedIdentity",
"AuthScopes": [ "https://graph.microsoft.com/.default" ]
}
]
}
}
The host includes specs/msgraph.openapi.json, a curated read-oriented Microsoft Graph v1.0
surface. See docs/MicrosoftGraph-Onboarding.md for local
Azure CLI authentication, exposed operations, and permission guidance.
Example 4 — OData service
Set Kind: "odata". McpGateway reads the service's $metadata (defaulting to {BaseUrl}/$metadata)
and, per entity set, generates a query_{Set} tool (OData $filter/$select/$orderby/$top)
plus a get_{Set} tool for single-key lookups.
{
"McpGateway": {
"Backends": [
{
"Name": "northwind",
"Kind": "odata",
"BaseUrl": "https://services.odata.org/V4/Northwind/Northwind.svc"
}
]
}
}
→ tools like northwind_query_Products (call with { "filter": "UnitPrice gt 20", "top": 10 })
and northwind_get_Products (call with { "ProductID": 7 }). Declared Functions become
read tools automatically. Set "AllowWrites": true to also expose create_/update_/delete_{Set}
and Actions (writes are off by default for safety).
Tool curation matters: a full OpenAPI spec or OData model can emit many operations and overwhelm
the model. Use IncludeTags / IncludeOperations to expose only what an agent needs.
Using it in-process (e.g. Polly)
Reference McpGateway.Core + the adapter packages you need, then build an engine directly:
using McpGateway.Core;
using McpGateway.Adapters.OpenApi; // brings the AddOpenApi extension into scope
var engine = new GatewayEngineBuilder()
.AddOpenApi(new BackendConfig
{
Name = "petstore",
BaseUrl = "https://petstore3.swagger.io/api/v3",
SpecUrl = "https://petstore3.swagger.io/api/v3/openapi.json",
IncludeTags = new[] { "pet" },
})
.Build();
var tools = await engine.ListToolsAsync(); // -> petstore_getPetById, ...
var result = await engine.CallToolAsync("petstore_getPetById", args);
Design notes
- The engine remains MCP-SDK-free; MCP SDK dependencies are isolated to the MCP and .NET plugin adapters plus the hosting packages.
- Adapters cache discovery, so listing and invoking tools share one parse of the spec.
- Upstream MCP clients are lazy, long-lived, and disposed with the aggregate engine.
- stdio hosts must log to stderr — the host configures this.
Roadmap
- OpenAPI ✅ and OData ✅ — OData reads (query, get-by-key, Functions) plus opt-in writes
(create/update/delete + Actions, gated by
AllowWrites). - Selectable host transport ✅ — one
McpGateway.Hostbinary runs with--transport httpor--transport stdio. - Per-service HTTP endpoints ✅ — HTTP mode mounts one MCP endpoint per service at
https://host/{route}/mcp(Streamable HTTP, stateless; requiredRouteper backend). - Aggregate MCP proxy ✅ —
/mcpcombines OpenAPI, OData, and upstream HTTP/SSE/stdio MCP tools behind one namespaced endpoint. - gRPC (server reflection) → SOAP/WSDL adapters.
- OData writes ✅ — create/update/delete + Actions (opt-in
AllowWrites); Functions are read tools. Next: bound functions/actions and respecting Capabilities annotations (InsertRestrictions, …). - Deeper schema fidelity ✅ — the OpenAPI adapter emits full JSON Schema: resolves
$refs, inlines real request-body schemas, and carriesenum/format/ nestedproperties/required.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Azure.Identity (>= 1.21.0)
- Microsoft.Identity.Client (>= 4.84.1)
- Microsoft.OData.Edm (>= 8.4.3)
- Microsoft.OpenApi (>= 3.7.0)
- ModelContextProtocol (>= 1.4.0)
- ModelContextProtocol.AspNetCore (>= 1.4.0)
- ModelContextProtocol.Core (>= 1.4.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 101 | 8/27/2026 |