Skip to content

Implement C# MCP server with auth feature#3

Open
bettercallsaulj wants to merge 24 commits intodev_csharp_sdkfrom
dev_csharp_sdk_auth
Open

Implement C# MCP server with auth feature#3
bettercallsaulj wants to merge 24 commits intodev_csharp_sdkfrom
dev_csharp_sdk_auth

Conversation

@bettercallsaulj
Copy link
Copy Markdown
Collaborator

This PR must be merged after #1

RahulHere added 24 commits March 18, 2026 21:52
This commit establishes the foundational structure for the C# Auth MCP
Server example, which demonstrates OAuth-protected MCP endpoints with
JWT validation and scope-based access control.

Project Structure:
- AuthMcpServer.sln: Solution file containing both projects
- AuthMcpServer: Main web server project targeting .NET 8.0
- AuthMcpServer.Tests: xUnit test project with FluentAssertions

Folder Structure (AuthMcpServer):
- Config/: Configuration loading and models
- Middleware/: CORS and OAuth authentication middleware
- Routes/: HTTP endpoint handlers
- Models/JsonRpc/: JSON-RPC 2.0 request/response models
- Models/Mcp/: MCP tool specification and result models
- Models/OAuth/: OAuth metadata models (RFC 9728, RFC 8414)
- Services/: MCP handler and auth context services
- Tools/: Weather tool implementations

Additional Files:
- .gitignore: Standard C# project ignores
- Program.cs: Minimal API setup with placeholder health endpoint
This commit implements the configuration loading system for the C# Auth
MCP Server, allowing the server to be configured via INI-style config
files that match the format used by the TypeScript and C++ examples.

Configuration Properties:
- Server: host, port, server_url
- OAuth: auth_server_url, jwks_uri, issuer, client_id, client_secret,
  oauth_authorize_url, oauth_token_url
- Scopes: allowed_scopes (default: openid profile email mcp:read mcp:admin)
- Cache: jwks_cache_duration, jwks_auto_refresh, request_timeout
- Development: auth_disabled (bypasses all authentication)

Tests Added (17 tests):
- INI parsing: comments, empty lines, whitespace trimming, equals in values
- Config building: full config, defaults, case-insensitive keys
- Endpoint derivation: JWKS URI, issuer, authorize URL, token URL
- Override behavior: explicit values take precedence over derivation
- File loading: non-existent file returns default disabled config
This commit implements the JSON-RPC 2.0 protocol models required for
MCP communication. These models handle request/response serialization
with proper JSON property naming per the JSON-RPC specification.

New Files:
- Models/JsonRpc/JsonRpcRequest.cs: Request object with jsonrpc version,
  id (supports int, string, or null), method name, and optional params
  as JsonElement for flexible parameter handling.

- Models/JsonRpc/JsonRpcErrorCodes.cs: Static class defining standard
  JSON-RPC 2.0 error codes:
  - ParseError (-32700): Invalid JSON
  - InvalidRequest (-32600): Not a valid Request object
  - MethodNotFound (-32601): Method unavailable
  - InvalidParams (-32602): Invalid parameters
  - InternalError (-32603): Internal error
This commit implements the MCP tool-related models for defining tool
specifications and handling tool invocation results.

New Files:
- Models/Mcp/ToolSpec.cs: Tool specification with name, description,
  and inputSchema (JSON Schema object). Used for tools/list responses
  to describe available tools to MCP clients.

- Models/Mcp/ToolContent.cs: Content item within a tool result. Supports
  text content (type="text" with text field) and binary content
  (type="image" with base64 data and mimeType). Null fields are omitted
  during serialization.

- Models/Mcp/ToolResult.cs: Tool invocation result containing a list of
  content items and an isError flag. Provides factory methods:
  - Text(string): Create successful text result
  - Error(string): Create error result with isError=true
  The isError field is omitted when false (JsonIgnoreCondition.WhenWritingDefault).
This commit implements OAuth discovery metadata models per RFC 9728
(Protected Resource Metadata), RFC 8414 (Authorization Server Metadata),
OpenID Connect Discovery, and RFC 7591 (Dynamic Client Registration).

New Files:
- Models/OAuth/ProtectedResourceMetadata.cs (RFC 9728):
  Describes the protected resource with resource URI, authorization
  servers, supported scopes, bearer methods, and documentation URL.

- Models/OAuth/AuthorizationServerMetadata.cs (RFC 8414):
  OAuth 2.0 authorization server configuration including issuer,
  authorization/token endpoints, JWKS URI, registration endpoint,
  supported scopes, response types, grant types, auth methods,
  and code challenge methods.

- Models/OAuth/ClientRegistrationResponse.cs (RFC 7591):
  Dynamic client registration response with client_id, client_secret,
  issued_at timestamps, redirect URIs, grant/response types, and
  token endpoint auth method.
This commit implements CORS middleware that sets proper headers on ALL
responses, which is critical for MCP Inspector and browser-based clients.

New Files:
- Middleware/CorsMiddleware.cs: ASP.NET Core middleware that:
  - Sets CORS headers on every response before passing to next middleware
  - Handles OPTIONS preflight requests with 204 No Content
  - Provides static SetCorsHeaders() method for use by other handlers

CORS Headers Set:
- Access-Control-Allow-Origin: * (allow all origins)
- Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD
- Access-Control-Allow-Headers: Accept, Accept-Language, Content-Language,
  Content-Type, Authorization, X-Requested-With, Origin, Cache-Control,
  Pragma, Mcp-Session-Id, Mcp-Protocol-Version
- Access-Control-Expose-Headers: WWW-Authenticate, Content-Length, Content-Type
- Access-Control-Max-Age: 86400 (24 hours cache)

Program.cs Changes:
- Added CorsMiddleware as first middleware in pipeline
- This ensures CORS headers are set even on error responses
This commit implements the health check endpoint for server status
monitoring and uptime tracking.

New Files:
- Routes/HealthEndpoints.cs: Extension method to map health endpoints
  - GET /health returns JSON with status, version, and uptime
  - Tracks server start time for uptime calculation
  - Uptime formatted as seconds with "s" suffix

Response Format:
{
  "status": "healthy",
  "version": "1.0.0",
  "uptime": "123s"
}

Program.cs Changes:
- Replaced inline health endpoint with MapHealthEndpoints() call
- Follows extension method pattern for endpoint registration
This commit implements the OAuth 2.0 Protected Resource Metadata
endpoint per RFC 9728, which is essential for MCP clients to discover
the server's OAuth configuration.

New Files:
- Routes/OAuthEndpoints.cs: Extension method to map OAuth routes
  - GET /.well-known/oauth-protected-resource
  - GET /.well-known/oauth-protected-resource/mcp (same response)

Response Format (RFC 9728):
{
  "resource": "{serverUrl}/mcp",
  "authorization_servers": ["{serverUrl}"],
  "scopes_supported": ["openid", "profile", "email", "mcp:read", "mcp:admin"],
  "bearer_methods_supported": ["header", "query"],
  "resource_documentation": "{serverUrl}/docs"
}
This commit implements the OAuth 2.0 Authorization Server Metadata
endpoint per RFC 8414, enabling MCP clients to discover OAuth endpoints.

Changes to Routes/OAuthEndpoints.cs:
- Added GET /.well-known/oauth-authorization-server endpoint
- Added BuildAuthorizationServerMetadata() helper method

Endpoint Derivation:
- authorization_endpoint: Uses config.OAuthAuthorizeUrl if set,
  otherwise derives from auth_server_url
- token_endpoint: Uses config.OAuthTokenUrl if set,
  otherwise derives from auth_server_url
- issuer: Uses config.Issuer if set, otherwise falls back to serverUrl
This commit implements the OpenID Connect Discovery endpoint, which
extends the authorization server metadata with OIDC-specific fields.

Changes to Routes/OAuthEndpoints.cs:
- Added GET /.well-known/openid-configuration endpoint
- Added BuildOpenIdConfiguration() helper method

Scope Merging:
Base OIDC scopes (openid, profile, email) are merged with config
scopes and deduplicated. This ensures standard OIDC scopes are always
available even if not explicitly configured.

Userinfo Endpoint:
Only included when auth_server_url is configured, otherwise null.
This allows the server to operate without a full IdP backend.
Implements the OAuth authorization redirect endpoint that forwards
requests to the identity provider (IdP) with all query parameters
preserved.

Summary:
- Add GET /oauth/authorize endpoint that redirects to the configured
  OAuth authorization URL or derives it from auth_server_url
- Forward all query parameters (client_id, redirect_uri, scope, etc.)
  to the IdP authorization endpoint
- Return 302 Found redirect (not 301 permanent) for proper OAuth flow
- Handle URL construction errors with 500 response and OAuth error format
- Add comprehensive integration tests:
  - Test redirect status code is 302
  - Test redirect location points to correct auth endpoint
  - Test all query parameters are forwarded correctly
  - Test CORS headers are present on redirect response
- Add NoRedirectHandler for testing redirect behavior without following
Implements OAuth dynamic client registration per RFC 7591 for MCP
clients that need to register with the authorization server.

Summary:
- Add POST /oauth/register endpoint that returns pre-configured client
  credentials in stateless mode (no actual registration database)
- Parse redirect_uris from request body and include in response
- Return client_id and client_secret from server configuration
- Set token_endpoint_auth_method based on client_secret presence:
  - "client_secret_post" when client_secret is configured
  - "none" when no client_secret (public client)
- Return 201 Created status code per RFC 7591
- Include standard OAuth response fields:
  - client_id_issued_at (current timestamp)
  - client_secret_expires_at (0 = never expires)
  - grant_types: authorization_code, refresh_token
  - response_types: code

Changes:
- Add using System.Text.Json for JsonElement parsing
- Add client registration endpoint in OAuthEndpoints.cs
- Add client registration tests in OAuthEndpointsTests.cs
- Update test config with ClientId and ClientSecret
Implements the authentication context service that holds user and
token information for protected endpoints.

Summary:
- Create AuthContext class with immutable properties:
  - UserId: identifier from token
  - Scopes: space-separated scope string
  - Audience: token audience claim
  - TokenExpiry: Unix timestamp of token expiration
  - IsAuthenticated: whether context is authenticated
- Implement HasScope method for scope-based access control:
  - Returns true for empty/null required scope (no restriction)
  - Returns false for empty/null context scopes (no permissions)
  - Splits scope string and performs case-insensitive match
- Add Empty() factory for unauthenticated requests:
  - All properties empty/zero
  - IsAuthenticated = false
- Add Anonymous() factory for auth-disabled mode:
  - UserId = "anonymous"
  - IsAuthenticated = true
  - TokenExpiry = 1 hour from now
  - Passes all provided scopes
Implements OAuth authentication middleware for protecting MCP
endpoints with bearer token validation.

Summary:
- Create OAuthAuthMiddleware for request authentication:
  - Skip auth for public paths: /health, /.well-known/*, /oauth/*,
    /authorize, /favicon.ico
  - Allow all requests when AuthDisabled=true with anonymous context
  - Require bearer token for protected endpoints (/mcp, /rpc, etc.)
  - Extract token from Authorization header (Bearer scheme)
  - Fall back to access_token query parameter
  - Set AuthContext in HttpContext.Items for downstream access
- Implement SendUnauthorized response per RFC 6750:
  - Return 401 Unauthorized status
  - Set WWW-Authenticate header with realm, resource_metadata, scope
  - Include error and error_description in header
  - Return JSON error body
  - Ensure CORS headers on 401 responses
Enhances the OAuth authentication middleware with proper header value
escaping per RFC 6750 and adds comprehensive tests.

Summary:
- Make EscapeHeaderValue method public for testing and reuse
- Add documentation comment for EscapeHeaderValue
- Implement proper escaping for WWW-Authenticate header values:
  - Escape backslash to double backslash (\ -> \\)
  - Escape quotes to escaped quotes (" -> \")
  - Handle empty/null strings gracefully
- Add comprehensive unit tests for header value escaping:
  - Test backslash escaping
  - Test quote escaping
  - Test combined escaping of both characters
  - Test empty string handling
  - Test null string handling
  - Test preservation of normal characters

Changes:
- Make EscapeHeaderValue public in OAuthAuthMiddleware.cs
- Add 6 header value escaping tests in OAuthAuthMiddlewareTests.cs
Implements the MCP JSON-RPC 2.0 handler service for processing
client requests and dispatching to registered tools.

Summary:
- Create McpHandler service with tool registration:
  - RegisterTool method to add tools with spec and handler
  - GetTools method to retrieve all registered tool specs
  - HandleRequest method for processing JSON-RPC requests
- Implement JSON-RPC method dispatching:
  - initialize: returns protocol version, capabilities, server info
  - tools/list: returns all registered tools
  - tools/call: invokes tool handler with arguments
  - ping: returns empty object for health checks
  - Unknown methods return METHOD_NOT_FOUND error
- Create JsonRpcException for structured error responses:
  - Wraps error code, message, and optional data
  - Integrates with JsonRpcError model
Adds comprehensive tests for MCP JSON-RPC method handlers including
edge cases and error handling scenarios.

Summary:
- Test request ID preservation for string and null IDs
- Test tools/call returns proper ToolResult structure:
  - Content array with type and text
  - Error flag for error results
- Test initialize capability details (tools.listChanged = false)
- Test exception handling in tool handlers:
  - Catches and wraps handler exceptions
  - Returns INTERNAL_ERROR with exception message
- Test tools/call works without arguments property
- Verify JSON-RPC 2.0 compliance for all method responses

Changes:
- Add 7 additional method handler tests in McpHandlerTests.cs
Implements the HTTP endpoints for MCP JSON-RPC requests with proper
error handling and JSON parsing.

Summary:
- Create McpEndpoints extension method for WebApplication
- Implement POST /mcp endpoint for MCP requests:
  - Parse JSON-RPC request from body
  - Handle JSON parse errors with PARSE_ERROR response
  - Handle missing method with INVALID_REQUEST response
  - Delegate to McpHandler for request processing
  - Return JSON-RPC response
- Implement POST /rpc as alias for /mcp endpoint
- Add comprehensive integration tests:
  - Test valid request returns 200
  - Test initialize returns protocol version
  - Test tools/list returns registered tools
  - Test tools/call invokes tool correctly
  - Test invalid JSON returns parse error
  - Test missing method returns invalid request
  - Test CORS headers present on response
  - Test /rpc works as alias for /mcp
  - Test auth required when not disabled (401 response)
Implements example weather tools demonstrating scope-based access
control for MCP tool execution.

Summary:
- Create WeatherTools static class with Register method
- Implement get-weather tool (public, no scope required):
  - Returns simulated weather data for a city
  - Includes temperature, condition, humidity, windSpeed
  - Uses city name hash for deterministic results
- Implement get-forecast tool (requires mcp:read scope):
  - Returns 5-day weather forecast
  - Checks AuthContext for mcp:read scope
  - Returns error result if scope missing
- Implement get-weather-alerts tool (requires mcp:admin scope):
  - Returns weather alerts for a region
  - Checks AuthContext for mcp:admin scope
  - Returns error result if scope missing
Completes the C# Auth MCP Server implementation with full integration
and convenience scripts for running the server.

Summary:
- Update Program.cs with complete server setup:
  - Print ASCII banner on startup
  - Load configuration from file or default
  - Register McpHandler as singleton service
  - Wire up CORS middleware first
  - Map public endpoints (health, OAuth discovery)
  - Apply OAuthAuthMiddleware to protected paths
  - Map MCP endpoints for JSON-RPC handling
  - Register weather tools with McpHandler
  - Print available endpoints on startup
  - Print authentication status (enabled/disabled)
  - Configure server URL from config
Updates the gopher-orch submodule configuration to track the br_release
branch and updates to the latest commit on that branch.

Changes:
- Add branch = br_release to .gitmodules
- Update submodule to commit c8e7c40606db330142632ecf90aaa8777bc42a3a
Include the Auth MCP Server example in the build process with its own
build step and test execution.

Changes:
- Add CMake flags to disable native examples/tests (not needed for C# SDK)
- Add Step 5: Build Auth MCP Server example and tests
- Add Step 6: Run SDK tests and auth example tests
- Add run instructions for auth example in final output
Refactor OAuth models and AuthContext from the example project to the
core SDK for reusability across projects.

New SDK files:
- src/GopherOrch/Auth/AuthContext.cs
- src/GopherOrch/Auth/OAuth/AuthorizationServerMetadata.cs (RFC 8414)
- src/GopherOrch/Auth/OAuth/ProtectedResourceMetadata.cs (RFC 9728)
- src/GopherOrch/Auth/OAuth/ClientRegistrationResponse.cs (RFC 7591)
- src/GopherOrch/Auth/OAuth/OpenIdConfiguration.cs

Changes:
- Add GopherOrch.Auth and GopherOrch.Auth.OAuth namespaces to SDK
- Add SDK project reference to AuthMcpServer example
- Update example and tests to use SDK auth types
- Remove duplicate auth files from example
The run scripts change to AuthMcpServer/ directory but server.config
is in the parent examples/auth/ directory. Pass the correct config
path so the server can find and load the configuration file.
@bettercallsaulj bettercallsaulj changed the title Implement C#MCP server with auth feature Implement C# MCP server with auth feature Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant