PollySdk.McpGateway.Core 1.0.0

dotnet add package PollySdk.McpGateway.Core --version 1.0.0
                    
NuGet\Install-Package PollySdk.McpGateway.Core -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PollySdk.McpGateway.Core" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PollySdk.McpGateway.Core" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="PollySdk.McpGateway.Core" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PollySdk.McpGateway.Core --version 1.0.0
                    
#r "nuget: PollySdk.McpGateway.Core, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PollySdk.McpGateway.Core@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PollySdk.McpGateway.Core&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=PollySdk.McpGateway.Core&version=1.0.0
                    
Install as a Cake Tool

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>.

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. petstorepetstore_*).
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.Host binary runs with --transport http or --transport stdio.
  • Per-service HTTP endpoints ✅ — HTTP mode mounts one MCP endpoint per service at https://host/{route}/mcp (Streamable HTTP, stateless; required Route per backend).
  • Aggregate MCP proxy ✅ — /mcp combines 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 carries enum / format / nested properties / required.
Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

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 98 8/27/2026