diff --git a/MCPify/Core/Auth/ITokenProvider.cs b/MCPify/Core/Auth/ITokenProvider.cs
new file mode 100644
index 0000000..67a84a6
--- /dev/null
+++ b/MCPify/Core/Auth/ITokenProvider.cs
@@ -0,0 +1,15 @@
+namespace MCPify.Core.Auth;
+
+///
+/// Abstraction for retrieving authentication tokens.
+/// Separates token acquisition from token application.
+///
+public interface ITokenProvider
+{
+ ///
+ /// Retrieves an authentication token if available.
+ ///
+ /// Cancellation token
+ /// The token string, or null if no token is available
+ Task GetTokenAsync(CancellationToken cancellationToken = default);
+}
diff --git a/MCPify/Core/Auth/TokenProviders/AuthenticationFactoryTokenProvider.cs b/MCPify/Core/Auth/TokenProviders/AuthenticationFactoryTokenProvider.cs
new file mode 100644
index 0000000..e91911c
--- /dev/null
+++ b/MCPify/Core/Auth/TokenProviders/AuthenticationFactoryTokenProvider.cs
@@ -0,0 +1,40 @@
+namespace MCPify.Core.Auth.TokenProviders;
+
+///
+/// Token provider that wraps existing IAuthenticationProvider implementations.
+/// Used for server-managed authentication (OAuth, API keys, etc.).
+///
+public class AuthenticationFactoryTokenProvider : ITokenProvider
+{
+ private readonly Func? _authenticationFactory;
+ private readonly IServiceProvider _serviceProvider;
+
+ public AuthenticationFactoryTokenProvider(
+ Func? authenticationFactory,
+ IServiceProvider serviceProvider)
+ {
+ _authenticationFactory = authenticationFactory;
+ _serviceProvider = serviceProvider;
+ }
+
+ public async Task GetTokenAsync(CancellationToken cancellationToken = default)
+ {
+ if (_authenticationFactory == null)
+ {
+ return null;
+ }
+
+ var authProvider = _authenticationFactory.Invoke(_serviceProvider);
+
+ using var tempRequest = new HttpRequestMessage();
+ await authProvider.ApplyAsync(tempRequest, cancellationToken);
+
+ var authHeader = tempRequest.Headers.Authorization;
+ if (authHeader != null)
+ {
+ return $"{authHeader.Scheme} {authHeader.Parameter}";
+ }
+
+ return null;
+ }
+}
diff --git a/MCPify/Core/Auth/TokenProviders/CompositeTokenProvider.cs b/MCPify/Core/Auth/TokenProviders/CompositeTokenProvider.cs
new file mode 100644
index 0000000..f93d623
--- /dev/null
+++ b/MCPify/Core/Auth/TokenProviders/CompositeTokenProvider.cs
@@ -0,0 +1,29 @@
+namespace MCPify.Core.Auth.TokenProviders;
+
+///
+/// Token provider that tries multiple providers in order until one returns a token.
+/// Useful for implementing fallback behavior (e.g., try client token first, then server).
+///
+public class CompositeTokenProvider : ITokenProvider
+{
+ private readonly IEnumerable _providers;
+
+ public CompositeTokenProvider(IEnumerable providers)
+ {
+ _providers = providers ?? throw new ArgumentNullException(nameof(providers));
+ }
+
+ public async Task GetTokenAsync(CancellationToken cancellationToken = default)
+ {
+ foreach (var provider in _providers)
+ {
+ var token = await provider.GetTokenAsync(cancellationToken);
+ if (!string.IsNullOrEmpty(token))
+ {
+ return token;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/MCPify/Core/Auth/TokenProviders/McpContextTokenProvider.cs b/MCPify/Core/Auth/TokenProviders/McpContextTokenProvider.cs
new file mode 100644
index 0000000..f38143c
--- /dev/null
+++ b/MCPify/Core/Auth/TokenProviders/McpContextTokenProvider.cs
@@ -0,0 +1,27 @@
+namespace MCPify.Core.Auth.TokenProviders;
+
+///
+/// Token provider that retrieves tokens from the MCP context.
+/// Used when the MCP client manages authentication and provides tokens.
+///
+public class McpContextTokenProvider : ITokenProvider
+{
+ private readonly IMcpContextAccessor _mcpContextAccessor;
+
+ public McpContextTokenProvider(IMcpContextAccessor mcpContextAccessor)
+ {
+ _mcpContextAccessor = mcpContextAccessor;
+ }
+
+ public Task GetTokenAsync(CancellationToken cancellationToken = default)
+ {
+ var token = _mcpContextAccessor.AccessToken;
+
+ if (!string.IsNullOrEmpty(token) && !token.Contains(' '))
+ {
+ token = $"Bearer {token}";
+ }
+
+ return Task.FromResult(token);
+ }
+}
diff --git a/MCPify/Core/Auth/TokenProviders/NoTokenProvider.cs b/MCPify/Core/Auth/TokenProviders/NoTokenProvider.cs
new file mode 100644
index 0000000..985e929
--- /dev/null
+++ b/MCPify/Core/Auth/TokenProviders/NoTokenProvider.cs
@@ -0,0 +1,22 @@
+namespace MCPify.Core.Auth.TokenProviders;
+
+///
+/// Token provider that provides no authentication token.
+/// Used when no authentication is required (e.g., public APIs).
+///
+public sealed class NoTokenProvider : ITokenProvider
+{
+ ///
+ /// Singleton instance of the no-token provider.
+ ///
+ public static readonly NoTokenProvider Instance = new();
+
+ private NoTokenProvider()
+ {
+ }
+
+ public Task GetTokenAsync(CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult(null);
+ }
+}
diff --git a/MCPify/Core/Auth/TokenProviders/TokenProviderFactory.cs b/MCPify/Core/Auth/TokenProviders/TokenProviderFactory.cs
new file mode 100644
index 0000000..07a3c84
--- /dev/null
+++ b/MCPify/Core/Auth/TokenProviders/TokenProviderFactory.cs
@@ -0,0 +1,38 @@
+namespace MCPify.Core.Auth.TokenProviders;
+
+///
+/// Factory for creating ITokenProvider instances based on TokenSource configuration.
+///
+public static class TokenProviderFactory
+{
+ ///
+ /// Creates an ITokenProvider based on the specified TokenSource and authentication factory.
+ ///
+ /// Service provider for resolving dependencies
+ /// The token source configuration
+ /// Optional authentication factory for server-managed auth
+ /// An ITokenProvider instance
+ public static ITokenProvider Create(
+ IServiceProvider serviceProvider,
+ TokenSource tokenSource,
+ Func? authenticationFactory = null)
+ {
+ return tokenSource switch
+ {
+ TokenSource.Server => new AuthenticationFactoryTokenProvider(authenticationFactory, serviceProvider),
+
+ TokenSource.Client => new McpContextTokenProvider(
+ serviceProvider.GetRequiredService()),
+
+ TokenSource.Both => new CompositeTokenProvider(new ITokenProvider[]
+ {
+ new McpContextTokenProvider(serviceProvider.GetRequiredService()),
+ new AuthenticationFactoryTokenProvider(authenticationFactory, serviceProvider)
+ }),
+
+ TokenSource.None => NoTokenProvider.Instance,
+
+ _ => throw new ArgumentOutOfRangeException(nameof(tokenSource), tokenSource, "Invalid TokenSource value")
+ };
+ }
+}
diff --git a/MCPify/Core/McpifyOptions.cs b/MCPify/Core/McpifyOptions.cs
index 8254d47..adf5329 100644
--- a/MCPify/Core/McpifyOptions.cs
+++ b/MCPify/Core/McpifyOptions.cs
@@ -72,6 +72,35 @@ public class McpifyOptions
}
+///
+/// Defines where authentication tokens should be sourced from.
+///
+public enum TokenSource
+{
+ ///
+ /// MCPify server manages authentication (OAuth flows, API keys, etc.).
+ /// This is the default for backward compatibility.
+ ///
+ Server,
+
+ ///
+ /// MCP client provides the authentication token directly.
+ /// Use this when the client handles OAuth or other authentication flows.
+ ///
+ Client,
+
+ ///
+ /// Try client token first, then fall back to server authentication.
+ /// Useful for hybrid scenarios.
+ ///
+ Both,
+
+ ///
+ /// No authentication required.
+ ///
+ None
+}
+
///
/// Defines the available transport types for the MCP server.
///
@@ -142,6 +171,12 @@ public class LocalEndpointsOptions
/// A factory for creating an authentication provider for local endpoints.
///
public Func? AuthenticationFactory { get; set; }
+
+ ///
+ /// Specifies where authentication tokens should be sourced from.
+ /// Defaults to Both (hybrid) - tries client token first, then server authentication.
+ ///
+ public TokenSource TokenSource { get; set; } = TokenSource.Both;
}
///
@@ -183,4 +218,10 @@ public class ExternalApiOptions
/// A factory for creating an authentication provider for this API.
///
public Func? AuthenticationFactory { get; set; }
+
+ ///
+ /// Specifies where authentication tokens should be sourced from.
+ /// Defaults to Both (hybrid) - tries client token first, then server authentication.
+ ///
+ public TokenSource TokenSource { get; set; } = TokenSource.Both;
}
diff --git a/MCPify/Hosting/McpifyEndpointExtensions.cs b/MCPify/Hosting/McpifyEndpointExtensions.cs
index cf564f9..4f4825d 100644
--- a/MCPify/Hosting/McpifyEndpointExtensions.cs
+++ b/MCPify/Hosting/McpifyEndpointExtensions.cs
@@ -1,5 +1,6 @@
using MCPify.Core;
using MCPify.Core.Auth;
+using MCPify.Core.Auth.TokenProviders;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
@@ -87,7 +88,8 @@ string BaseUrlProvider()
DefaultHeaders = options.LocalEndpoints.DefaultHeaders
};
- var tool = new OpenApiProxyTool(descriptor, BaseUrlProvider, httpClient, services.GetRequiredService(), localOpts, options.LocalEndpoints.AuthenticationFactory);
+ var tokenProvider = TokenProviderFactory.Create(services, options.LocalEndpoints.TokenSource, options.LocalEndpoints.AuthenticationFactory);
+ var tool = new OpenApiProxyTool(descriptor, BaseUrlProvider, httpClient, services.GetRequiredService(), localOpts, tokenProvider);
toolCollection.Add(tool);
count++;
}
diff --git a/MCPify/Hosting/McpifyServiceRegistrar.cs b/MCPify/Hosting/McpifyServiceRegistrar.cs
index f8c1462..529ac81 100644
--- a/MCPify/Hosting/McpifyServiceRegistrar.cs
+++ b/MCPify/Hosting/McpifyServiceRegistrar.cs
@@ -4,6 +4,7 @@
using MCPify.Schema;
using MCPify.Tools;
using MCPify.Core.Auth;
+using MCPify.Core.Auth.TokenProviders;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Routing;
@@ -146,7 +147,8 @@ private async Task RegisterExternalEndpointsAsync()
apiOpts.DefaultHeaders[header.Key] = header.Value;
}
- var tool = new OpenApiProxyTool(descriptor, apiOptions.ApiBaseUrl, httpClient, _schema, apiOpts, apiOptions.AuthenticationFactory);
+ var tokenProvider = TokenProviderFactory.Create(_serviceProvider, apiOptions.TokenSource, apiOptions.AuthenticationFactory);
+ var tool = new OpenApiProxyTool(descriptor, apiOptions.ApiBaseUrl, httpClient, _schema, apiOpts, tokenProvider);
var decoratedTool = new SessionAwareToolDecorator(tool, _serviceProvider);
toolCollection.Add(decoratedTool);
count++;
@@ -215,7 +217,8 @@ string BaseUrlProvider()
? _options.LocalEndpoints.AuthenticationFactory
: null;
- var tool = new OpenApiProxyTool(descriptor, BaseUrlProvider, httpClient, _schema, localOpts, effectiveAuthFactory);
+ var tokenProvider = TokenProviderFactory.Create(_serviceProvider, _options.LocalEndpoints.TokenSource, effectiveAuthFactory);
+ var tool = new OpenApiProxyTool(descriptor, BaseUrlProvider, httpClient, _schema, localOpts, tokenProvider);
var decoratedTool = new SessionAwareToolDecorator(tool, _serviceProvider);
toolCollection.Add(decoratedTool);
count++;
diff --git a/MCPify/MCPify.csproj b/MCPify/MCPify.csproj
index e802386..500d042 100644
--- a/MCPify/MCPify.csproj
+++ b/MCPify/MCPify.csproj
@@ -8,7 +8,7 @@
true
true
MCPify
- 1.0.0-preview.1
+ 0.0.13-preview
Abdullah D.
Turn any ASP.NET Core API or OpenAPI spec into a Model Context Protocol (MCP) server. Seamlessly integrates with Claude Desktop and other MCP clients.
mcp;openapi;swagger;api;tools
diff --git a/MCPify/Tools/OpenApiProxyTool.cs b/MCPify/Tools/OpenApiProxyTool.cs
index 54b2207..b2a82bf 100644
--- a/MCPify/Tools/OpenApiProxyTool.cs
+++ b/MCPify/Tools/OpenApiProxyTool.cs
@@ -5,6 +5,7 @@
using Microsoft.OpenApi.Models;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
+using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
@@ -20,7 +21,7 @@ public class OpenApiProxyTool : McpServerTool
private readonly Func _apiBaseUrlProvider;
private readonly OpenApiOperationDescriptor _descriptor;
private readonly McpifyOptions _options;
- private readonly Func? _authenticationFactory;
+ private readonly ITokenProvider _tokenProvider;
private IReadOnlyList