MCP Server
Expose your registered AI tools, prompts, and resources to external MCP clients over HTTP.
Quick Start
builder.Services
.AddCoreAIServices()
.AddCoreAIOrchestration()
.AddCoreAIMcpServer()
.AddCoreAIFtpMcpResources()
.AddCoreAISftpMcpResources();
AddCoreAIMcpServer() registers the shared prompt and resource services. FTP and SFTP resource handlers now live in the optional CrestApps.Core.AI.Ftp and CrestApps.Core.AI.Sftp packages, so hosts opt into those transport dependencies explicitly.
Problem & Solution
External AI clients — IDE assistants, chat agents, orchestration frameworks — need a standardized way to discover and call your application's tools, read your prompts, and access your resources. The Model Context Protocol (MCP) provides that standard.
The MCP server framework:
- Exposes your registered AI tools so external clients can invoke them
- Serves prompts from the catalog and from code-registered
McpServerPromptinstances - Serves resources (files, data, templates) via pluggable resource type handlers
- Supports OpenID Connect, API key, or no-auth for development
- Uses the official ModelContextProtocol C# SDK for HTTP transport
Registered Services
AddCoreAIMcpServer() registers these services:
| Service | Implementation | Lifetime | Purpose |
|---|---|---|---|
IMcpServerPromptService | DefaultMcpServerPromptService | Scoped | Lists and retrieves server prompts |
IMcpServerResourceService | DefaultMcpServerResourceService | Scoped | Lists, templates, and reads server resources |
Optional transport resource packages
| Extension | Package | Purpose |
|---|---|---|
AddCoreAIFtpMcpResources() | CrestApps.Core.AI.Ftp | Registers the FTP/FTPS MCP resource type handler |
AddCoreAISftpMcpResources() | CrestApps.Core.AI.Sftp | Registers the SFTP MCP resource type handler |
Server Endpoint Setup
Use the ModelContextProtocol SDK to map the SSE endpoint and wire in your tool, prompt, and resource handlers:
app.MapMcpSse(mcpBuilder =>
{
mcpBuilder.WithHttpTransport(options =>
{
options.ServerInfo = new() { Name = "My MCP Server", Version = "1.0" };
});
mcpBuilder.WithListToolsHandler(async (context, ct) =>
{
// Return your registered AI tools
});
mcpBuilder.WithCallToolHandler(async (context, ct) =>
{
// Invoke a tool by name, delegate to your tool registry
});
mcpBuilder.WithListPromptsHandler(async (context, ct) =>
{
var service = context.Services.GetRequiredService<IMcpServerPromptService>();
return new ListPromptsResult { Prompts = await service.ListAsync() };
});
mcpBuilder.WithGetPromptHandler(async (context, ct) =>
{
var service = context.Services.GetRequiredService<IMcpServerPromptService>();
return await service.GetAsync(context, ct);
});
mcpBuilder.WithListResourcesHandler(async (context, ct) =>
{
var service = context.Services.GetRequiredService<IMcpServerResourceService>();
return new ListResourcesResult { Resources = await service.ListAsync() };
});
mcpBuilder.WithListResourceTemplatesHandler(async (context, ct) =>
{
var service = context.Services.GetRequiredService<IMcpServerResourceService>();
return new ListResourceTemplatesResult
{
ResourceTemplates = await service.ListTemplatesAsync()
};
});
mcpBuilder.WithReadResourceHandler(async (context, ct) =>
{
var service = context.Services.GetRequiredService<IMcpServerResourceService>();
return await service.ReadAsync(context, ct);
});
});
See the MVC Example for a complete working server setup.
Prompt Serving
IMcpServerPromptService
Serves prompts from two sources:
- Catalog prompts —
McpPromptentries stored in the named catalog (INamedCatalog<McpPrompt>) - SDK prompts —
McpServerPromptinstances registered directly in DI
public interface IMcpServerPromptService
{
Task<IList<Prompt>> ListAsync();
Task<GetPromptResult> GetAsync(
RequestContext<GetPromptRequestParams> request,
CancellationToken cancellationToken = default);
}
The DefaultMcpServerPromptService merges both sources, with catalog prompts taking precedence when names collide.
McpPrompt Model
public sealed class McpPrompt : CatalogItem, INameAwareModel
{
public string Name { get; set; }
public DateTime CreatedUtc { get; set; }
public string Author { get; set; }
public string OwnerId { get; set; }
public Prompt Prompt { get; set; } // MCP SDK Prompt with arguments
}
Resource Serving
IMcpServerResourceService
Serves resources and resource templates from the catalog and code-registered McpServerResource instances:
public interface IMcpServerResourceService
{
Task<IList<Resource>> ListAsync();
Task<IList<ResourceTemplate>> ListTemplatesAsync();
Task<ReadResourceResult> ReadAsync(
RequestContext<ReadResourceRequestParams> request,
CancellationToken cancellationToken = default);
}
How resource reading works:
- The service checks if any registered
McpServerResourcematches the URI - If not, it parses the URI to extract the resource
ItemId(from the path segment after://) - Looks up the
McpResourcecatalog entry and resolves theIMcpResourceTypeHandlerby the entry'sSource(type key) - For template URIs, extracts variables using
McpResourceUri.TryMatch()and passes them to the handler
McpResource Model
public sealed class McpResource : SourceCatalogEntry, IDisplayTextAwareModel
{
public string DisplayText { get; set; }
public DateTime CreatedUtc { get; set; }
public string Author { get; set; }
public string OwnerId { get; set; }
public Resource Resource { get; set; } // MCP SDK Resource (URI, name, description, MIME type)
}
URI Templates
Resources can use URI templates with {variable} placeholders. The McpResourceUri utility handles matching and variable extraction:
ftp://my-resource/{path}
recipe-step-schema://my-resource/{stepName}
When a client requests ftp://my-resource/reports/2024/sales.csv, the framework matches it against the template and extracts { "path": "reports/2024/sales.csv" }. The last variable in a template can match multi-segment paths.
Resource Type Handlers
Optional transport resource handlers:
| Type | Handler | Description |
|---|---|---|
ftp | FtpResourceTypeHandler | Registered by AddCoreAIFtpMcpResources() from CrestApps.Core.AI.Ftp |
sftp | SftpResourceTypeHandler | Registered by AddCoreAISftpMcpResources() from CrestApps.Core.AI.Sftp |
Registering Custom Resource Types
builder.Services.AddMcpResourceType<BlobStorageResourceHandler>("blob", entry =>
{
entry.DisplayName = new LocalizedString("Blob Storage", "Azure Blob Storage");
entry.Description = new LocalizedString("Blob Desc", "Reads content from Azure Blob Storage.");
entry.SupportedVariables =
[
new McpResourceVariable("path")
{
Description = new LocalizedString("Blob Path", "The blob path within the container.")
},
];
});
For a full guide on implementing custom resource type handlers, see Resource Types.
Authentication
McpServerOptions
Configure server authentication via McpServerOptions:
services.Configure<McpServerOptions>(options =>
{
options.AuthenticationType = McpServerAuthenticationType.OpenId;
options.RequireAccessPermission = true;
});
McpServerAuthenticationType
| Type | Description |
|---|---|
OpenId | (Default) Uses OpenID Connect authentication via the "Api" scheme. Most secure option for production. |
ApiKey | Uses a predefined API key provided via the Authorization header. Configure the key in McpServerOptions.ApiKey. |
None | Disables authentication. Only for local development. |
Permission Control
When using OpenId authentication, the RequireAccessPermission property (default: true) controls whether the AccessMcpServer permission is checked. When set to false, any authenticated user can access the MCP server.
Example — API key authentication:
services.Configure<McpServerOptions>(options =>
{
options.AuthenticationType = McpServerAuthenticationType.ApiKey;
options.ApiKey = builder.Configuration["Mcp:ServerApiKey"];
});
Tool Exposure
When your application acts as an MCP server, registered AI tools are exposed to external clients. The server endpoint's WithListToolsHandler and WithCallToolHandler callbacks delegate to your tool registry:
- List tools — Returns metadata (name, description, JSON schema) for all registered tools
- Call tool — Resolves the tool by name from the registry and invokes it with the provided arguments
Tools registered via AddCoreAITool<T>() (see Custom Tools) are automatically available to MCP clients.
Server Metadata
IMcpServerMetadataProvider
Implementations provide server metadata for the MCP handshake:
public interface IMcpServerMetadataCacheProvider
{
Task<McpServerCapabilities> GetCapabilitiesAsync(McpConnection connection);
Task InvalidateAsync(string connectionId);
}
McpServerMetadata
public sealed class McpServerMetadata
{
public bool UseLocalServer { get; set; }
}
File Provider Integration
IMcpFileProviderResolver
Allows resource handlers to resolve IFileProvider instances by name, enabling resources served from media libraries, web roots, or custom file stores:
public interface IMcpFileProviderResolver
{
IFileProvider Resolve(string providerName);
}
Resource Export
IMcpResourceHandler
Hook into resource export to strip sensitive data:
public interface IMcpResourceHandler
{
void Exporting(ExportingMcpResourceContext context);
}
public sealed class ExportingMcpResourceContext
{
public readonly McpResource Resource;
public readonly JsonObject ExportData; // Modify to remove credentials
}
Metadata Prompt Generation
IMcpMetadataPromptGenerator
Generates structured system prompts describing MCP server capabilities so the AI model can reason about when to invoke them:
public interface IMcpMetadataPromptGenerator
{
string Generate(IReadOnlyList<McpServerCapabilities> capabilities);
}
The generated prompt is injected into the model context, giving the AI awareness of available MCP capabilities without manually listing each tool in the system prompt.
Configuration
McpOptions
McpOptions holds the registry of resource types. Resource types are added via AddMcpResourceType<T>():
services.Configure<McpOptions>(options =>
{
// Inspect registered resource types
foreach (var (type, entry) in options.ResourceTypes)
{
Console.WriteLine($"{type}: {entry.DisplayName}");
}
});
Each McpResourceTypeEntry provides:
| Property | Description |
|---|---|
Type | Unique type identifier string |
DisplayName | Localized display name |
Description | Localized description |
SupportedVariables | Array of McpResourceVariable describing URI template variables |
- Configuring server authentication (OpenID, API key, or none)
- Managing prompts and resources through the admin dashboard
- Registering and configuring resource types
- Viewing server capabilities and health status