Skip to content

Implement Rust MCP server to verify Auth feature#3

Open
bettercallsaulj wants to merge 36 commits intodev_rust_sdkfrom
dev_rust_sdk_auth
Open

Implement Rust MCP server to verify Auth feature#3
bettercallsaulj wants to merge 36 commits intodev_rust_sdkfrom
dev_rust_sdk_auth

Conversation

@bettercallsaulj
Copy link
Copy Markdown
Collaborator

This PR must be merged after #1

RahulHere added 30 commits March 16, 2026 22:51
Initialize the OAuth-protected MCP server example with Axum web framework.
This example demonstrates JWT token validation and scope-based access
control for MCP tools using FFI bindings to gopher-auth.

Project structure:
- Cargo.toml with Axum 0.7, Tower, Tokio, and supporting dependencies
- src/main.rs entry point with module declarations
- src/ffi/ module placeholder for gopher-auth FFI bindings
- src/middleware/ module placeholder for authentication middleware
- src/routes/ module placeholder for HTTP route handlers
- src/tools/ module placeholder for MCP tools with scope control

Dependencies include:
- axum 0.7 for HTTP routing with macros support
- tower/tower-http for middleware and CORS
- tokio for async runtime
- serde/serde_json for serialization
- libloading for dynamic library loading
- tracing for structured logging
- thiserror for error handling

Also updates .gitignore to exclude target directories in subdirectories.
Implement the AppError enum with thiserror for structured error handling.
The error type provides appropriate HTTP status codes and JSON responses
when converted to Axum responses.

Error variants:
- Config: Configuration errors (500 Internal Server Error)
- Auth: Authentication errors (401 Unauthorized)
- Ffi: FFI/native library errors (500 Internal Server Error)
- JsonRpc: JSON-RPC protocol errors with code (400 Bad Request)
- Internal: Internal server errors (500 Internal Server Error)

Features:
- IntoResponse implementation for seamless Axum integration
- JSON error response with error message and optional code field
- Comprehensive unit tests for response formatting and display

Adds http-body-util dev-dependency for test assertions.
Define the configuration struct for the OAuth-protected MCP server.
The struct contains all settings needed for server operation, OAuth
integration, and token validation.

Configuration fields:
- Server: host, port, server_url
- OAuth/IDP: auth_server_url, jwks_uri, issuer, client_id, client_secret,
  token_endpoint, oauth_authorize_url, oauth_token_url
- Scopes: allowed_scopes
- Cache: jwks_cache_duration, jwks_auto_refresh, request_timeout
- Auth bypass: auth_disabled

Implements:
- Default trait with sensible defaults (port 3001, localhost, etc.)
- default_disabled() constructor for testing with auth disabled
- Unit tests for default values
Implement parse_config_file function for parsing INI-style configuration
files. The parser handles standard INI format with specific behaviors
for the auth server configuration.

Parser features:
- Skip lines starting with '#' (comments)
- Skip empty lines
- Split only on first '=' to preserve URLs with query parameters
- Trim whitespace from both keys and values
- Support empty values

Unit tests cover:
- Basic key=value parsing
- Comment lines are skipped
- Empty lines are skipped
- Values containing '=' characters preserved correctly
- Leading/trailing whitespace trimmed
- Empty values handled
Implement build_from_map function to construct AuthServerConfig from
a parsed key-value HashMap. The function automatically derives OAuth
endpoints from auth_server_url when not explicitly provided.

Endpoint derivation (Keycloak-style OpenID Connect paths):
- jwks_uri: {auth_server_url}/protocol/openid-connect/certs
- issuer: {auth_server_url}
- oauth_authorize_url: {auth_server_url}/protocol/openid-connect/auth
- oauth_token_url: {auth_server_url}/protocol/openid-connect/token
- token_endpoint: {auth_server_url}/protocol/openid-connect/token

Features:
- Parse numeric fields (port, cache duration, timeout) with defaults
- Parse boolean fields (jwks_auto_refresh, auth_disabled) supporting
  both "true" and "1" values
- Derive server_url from host:port if not explicitly set
- Explicit values override derived endpoints

Unit tests cover all derivation and parsing scenarios.
Implement configuration validation and file loading for the auth server.
When authentication is enabled, validation ensures all required OAuth
fields are properly configured.

Validation rules (when auth_disabled is false):
- client_id must not be empty
- client_secret must not be empty
- jwks_uri must not be empty (or derivable from auth_server_url)

File loading (from_file method):
- Read INI-style configuration file from disk
- Parse content using parse_config_file
- Build configuration using build_from_map
- Validate the resulting configuration
- Return appropriate error for missing files or invalid configs

Unit tests cover:
- Validation passes with complete valid configuration
- Validation fails for each missing required field
- Validation skipped when auth_disabled is true
- File loading with valid configuration
- File loading error for missing file
- File loading error for invalid configuration
Implement CORS utilities matching the TypeScript implementation exactly
for browser compatibility with MCP clients.

CORS headers (as constants):
- Access-Control-Allow-Origin: *
- 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

Functions:
- options_handler(): async handler for CORS preflight (204 No Content)
- with_cors_headers(): wrapper to add CORS headers to any response

MCP-specific headers (Mcp-Session-Id, Mcp-Protocol-Version) are included
in the allowed headers for Model Context Protocol compatibility.

Unit tests verify headers, status codes, and body preservation.
Implement the health endpoint for server monitoring at /health path.
Returns JSON response with server status and optional metadata.

Response structure:
- status: always "ok" when the server is responding
- timestamp: ISO 8601 formatted current time (RFC 3339)
- version: optional server version (omitted when None)
- uptime: optional server uptime in seconds (omitted when None)

Components:
- HealthResponse struct with serde serialization
- HealthState struct tracking start_time and version
- health_handler async function for Axum routing

Features:
- skip_serializing_if for clean JSON output (no null fields)
- Instant-based uptime calculation from server start
- chrono UTC timestamp in RFC 3339 format

Unit tests verify response format, field omission, and uptime tracking.
Define OAuth and OpenID Connect metadata structs for discovery endpoints
per RFC specifications.

Type definitions:
- ProtectedResourceMetadata (RFC 9728): resource identifier, authorization
  servers, supported scopes, bearer methods, documentation URL
- AuthorizationServerMetadata (RFC 8414): issuer, endpoints, supported
  scopes, response types, grant types, auth methods, PKCE methods
- OpenIDConfiguration: extends RFC 8414 with userinfo endpoint, subject
  types, and ID token signing algorithms using serde flatten
- ClientRegistrationResponse (RFC 7591): client credentials, issued time,
  redirect URIs, grant/response types, auth method
- ClientRegistrationRequest: deserialization for registration requests

All optional fields use skip_serializing_if for clean JSON output.
Unit tests verify serialization format and field omission behavior.
Implement RFC 9728 protected resource metadata endpoint handler for
OAuth discovery at /.well-known/oauth-protected-resource.

Handler implementation:
- protected_resource_metadata(): returns ProtectedResourceMetadata JSON
- Serves both base path and /mcp sub-path
- Includes CORS headers via with_cors_headers wrapper

Metadata fields:
- resource: {server_url}/mcp
- authorization_servers: [server_url]
- scopes_supported: parsed from allowed_scopes config
- bearer_methods_supported: ["header", "query"]
- resource_documentation: {server_url}/docs

Helper function:
- parse_scopes(): splits space-separated scope string, filters empty
Implement RFC 8414 authorization server metadata endpoint handler for
OAuth discovery at /.well-known/oauth-authorization-server.

Handler implementation:
- authorization_server_metadata(): returns AuthorizationServerMetadata JSON
- Uses CORS headers via with_cors_headers wrapper

Endpoint resolution logic:
- issuer: config.issuer or fallback to server_url
- authorization_endpoint: config.oauth_authorize_url, or derived from
  auth_server_url, or fallback to {server_url}/oauth/authorize
- token_endpoint: config.oauth_token_url, or derived from auth_server_url,
  or fallback to {server_url}/oauth/token
- jwks_uri: from config (optional)
- registration_endpoint: {server_url}/oauth/register

Standard OAuth values:
- response_types_supported: ["code"]
- grant_types_supported: ["authorization_code", "refresh_token"]
- token_endpoint_auth_methods_supported: ["client_secret_basic",
  "client_secret_post", "none"]
- code_challenge_methods_supported: ["S256"]
Implement OIDC Discovery 1.0 endpoint handler at
/.well-known/openid-configuration extending RFC 8414 metadata.

Handler implementation:
- openid_configuration(): returns OpenIDConfiguration JSON with
  flattened AuthorizationServerMetadata plus OIDC-specific fields
- Uses CORS headers via with_cors_headers wrapper

OIDC-specific fields:
- userinfo_endpoint: {auth_server_url}/protocol/openid-connect/userinfo
  (only when auth_server_url is configured)
- subject_types_supported: ["public"]
- id_token_signing_alg_values_supported: ["RS256"]

Scope handling:
- merge_oidc_scopes(): merges base OIDC scopes (openid, profile, email)
  with configured allowed_scopes, deduplicating entries

Inherits all endpoint resolution logic from authorization_server_metadata.
Implement OAuth authorize redirect and dynamic client registration
endpoints for the auth server.

OAuth authorize endpoint (/oauth/authorize):
- oauth_authorize(): redirects to configured authorization server
- Forwards all query parameters to the redirect URL
- Uses config.oauth_authorize_url or derives from auth_server_url
- Returns 302 redirect with CORS headers
- Handles URL parsing errors gracefully with JSON error response

Dynamic client registration (/oauth/register):
- oauth_register(): returns client credentials (stateless)
- Uses configured client_id and client_secret
- Sets token_endpoint_auth_method based on secret presence:
  "client_secret_post" if secret configured, "none" otherwise
- Returns 201 Created with ClientRegistrationResponse
- Includes client_id_issued_at as current unix timestamp
- Sets client_secret_expires_at to 0 (never expires)

Both endpoints include CORS headers via with_cors_headers wrapper.
Define JSON-RPC 2.0 types and MCP tool structures for the Model Context
Protocol handler implementation.

Error codes module:
- PARSE_ERROR: -32700
- INVALID_REQUEST: -32600
- METHOD_NOT_FOUND: -32601
- INVALID_PARAMS: -32602
- INTERNAL_ERROR: -32603

JSON-RPC types:
- JsonRpcRequest: jsonrpc, id (optional), method, params (optional)
- JsonRpcResponse: with success() and error() constructors
- JsonRpcError: code, message, data (optional)

MCP tool types:
- ToolSpec: name, description, inputSchema (JSON Schema)
- ToolResult: content array, isError flag
- ToolContent: text/image content with type, text, data, mimeType fields

Helper methods:
- ToolResult::text() and ToolResult::error() constructors
- ToolContent::text() and ToolContent::image() constructors

All types use appropriate serde attributes for JSON field naming
(camelCase) and optional field skipping.
Implement the MCP handler with JSON-RPC 2.0 method dispatch for the
Model Context Protocol server.

New types:
- AuthContext: placeholder struct for JWT token payload with user_id,
  scopes, audience, token_expiry, authenticated fields
- ToolHandler: type alias for Arc<dyn Fn(Value, &AuthContext) -> ToolResult>
- ServerInfo: server name and version for initialize response
- McpHandler: main handler struct with tools registry

McpHandler implementation:
- new(): create handler with server_info (rust-auth-mcp-server v1.0.0)
- handle_request(): parse JSON-RPC request, validate version, dispatch
- handle_initialize(): return protocol version 2024-11-05 and capabilities
- handle_tools_list(): return registered tool specifications
- handle_tools_call(): look up tool, extract arguments, invoke handler
- handle_ping(): return empty object result

Request validation:
- Check for valid JSON-RPC object structure
- Validate jsonrpc field equals "2.0"
- Return METHOD_NOT_FOUND for unknown methods
Implement tool registration and endpoint handlers for the MCP handler module.

Changes:
- Add register_tool method to McpHandler for registering tools with specs and handlers
- Implement mcp_handler async endpoint handler that extracts state, auth context, and JSON body
- Implement mcp_options handler for CORS preflight requests
- Wire up handle_request to dispatch JSON-RPC methods and return responses with CORS headers
- Support both /mcp and /rpc endpoints using the same handlers
Implement weather tools module with deterministic weather simulation and OAuth scope checking.

Changes:
- Add has_scope helper to check for scope in space-separated string
- Add city_hash, get_condition, get_temp helpers for deterministic weather simulation
- Add access_denied helper for scope error responses
- Implement get-weather tool (no auth required)
- Implement get-forecast tool (requires mcp:read scope)
- Implement get-weather-alerts tool (requires mcp:admin scope)
- Add register_weather_tools function to register all tools with McpHandler
- Export weather_tools module from tools/mod.rs
- Add comprehensive tests for helpers and tool behavior
Implement OAuth authentication middleware module with token extraction and path-based access control.

Changes:
- Create oauth_auth module with AuthContext struct for JWT token validation results
- Add AuthState struct with optional GopherAuthClient and server config
- Implement extract_token function for Authorization header and query parameter
- Implement requires_auth method for path-based access control
- Define public paths (well-known, oauth, health) and protected paths (mcp, rpc, events, sse)
- Default to protected for unknown paths (fail secure)
- Add GopherAuthClient placeholder type for FFI integration
- Update middleware/mod.rs to export oauth_auth types
- Update mcp_handler.rs to re-export AuthContext from middleware
- Add comprehensive tests for token extraction and path matching
Implement OAuth authentication middleware with CORS preflight and error responses.

Changes:
- Add cors_preflight_response function returning 204 with CORS headers
- Add unauthorized_response function with RFC 6750 WWW-Authenticate header
- Add auth_middleware async function for request authentication
- Handle OPTIONS requests with CORS preflight response
- Check path access requirements before authentication
- Extract bearer token and create auth context
- Include placeholder for gopher-auth FFI token validation
- Insert AuthContext into request extensions for handlers
- Export new functions from middleware module
- Add tests for response status codes, headers, and content
Implement FFI bindings to the gopher-auth native library for JWT validation.

Changes:
- Add ValidationResult struct for token validation results
- Add TokenPayload struct for extracted JWT claims
- Implement GopherAuthClient with native library handle
- Add load_library function with cross-platform support
- Implement new constructor with library init and client creation
- Implement validate_token for JWT signature and claims validation
- Implement extract_payload for extracting subject, scopes, audience, expiration
- Implement set_option for configuring client options
- Implement destroy method and Drop trait for cleanup
- Add payload getter helpers for string and expiration fields
- Add free_string helper for native string cleanup
- Export types from ffi module
- Add tests for result types and payload fields
- Mark integration tests as ignored (require native library)
Implement complete main entry point with Axum router and middleware configuration.

Changes:
- Add AppState struct combining config, health, and MCP handler states
- Implement FromRef trait for state extraction in handlers
- Add print_banner function with ASCII art header
- Add print_endpoints function showing all available routes
- Initialize tracing subscriber with env-filter
- Load configuration from file or use defaults
- Initialize gopher-auth client when auth is enabled
- Set client options for cache, auto-refresh, and timeout
- Create MCP handler and register weather tools
- Build router with all endpoints (health, OAuth discovery, OAuth, MCP)
- Apply auth middleware layer
- Start server with TcpListener
- Update middleware to use FFI GopherAuthClient instead of placeholder
- Add GopherAuthClient::dummy() test helper for unit tests
Implement graceful shutdown with signal handling and resource cleanup.

Changes:
- Add shutdown_signal async function handling SIGINT and SIGTERM
- Use tokio::select! to wait for either signal
- Add watch channel for programmatic shutdown triggering
- Update server to use with_graceful_shutdown
- Add shutdown sequence with cleanup messages
- Print "Shutting down..." on shutdown initiation
- Print "Auth client destroyed" when cleaning up auth client
- Print "Goodbye!" on complete shutdown
- Auth client cleanup handled automatically via Drop trait
Create default configuration file and shell script for running the example.

Changes:
- Add server.config with default settings
- Configure host, port, and server_url
- Include OAuth/IDP settings placeholders
- Add scope configuration
- Add JWKS cache and refresh settings
- Default to auth_disabled=true for development
- Add run_example.sh launcher script
- Support --no-auth flag for quick testing
- Support --config flag for custom configuration
- Auto-detect cargo and build project
- Clean up temporary config on exit
Create comprehensive README with usage instructions and API documentation.

Changes:
- Add feature overview and requirements
- Document quick start and build instructions
- Explain configuration options with table
- Document all API endpoints with curl examples
- List available tools with scope requirements
- Add project structure overview
- Include testing instructions
- Document environment variables
Update submodule to track the br_release branch with version 0.1.2.

Changes:
- Update .gitmodules to specify br_release branch
- Update submodule reference to latest br_release commit
Restructures the FFI module to support both gopher-orch and gopher-auth
bindings as optional features. The auth example now uses the library's
shared FFI implementation.

Changes:
- Add auth feature flag to Cargo.toml
- Move src/ffi.rs to src/ffi/orch.rs module
- Add src/ffi/auth.rs with gopher-auth JWT validation bindings
- Add Auth error variant with cfg(feature = "auth")
- Update lib.rs to re-export auth types when feature is enabled
- Update auth example to depend on gopher-orch with auth feature
- Simplify example's FFI to thin wrapper with error conversion
Consolidates the FFI wrapper into a single mod.rs file, removing the
separate auth.rs. The wrapper is now minimal - just enough to support
testing without the native library.

Changes:
- Remove examples/auth/src/ffi/auth.rs
- Move wrapper code to examples/auth/src/ffi/mod.rs
- Keep only essential methods: new, validate_token, extract_payload,
  set_option, destroy, and test-only dummy()
RahulHere added 2 commits March 20, 2026 22:38
Add release pattern matching gopher-mcp-go for consistent versioning
and native library distribution across SDK implementations.

Changes:
- dump-version.sh: Updates Cargo.toml version, CHANGELOG.md, creates git tag
- install-native.sh: Downloads native libraries from GitHub releases
- release.yml: CI workflow to create releases with native binaries attached
- CHANGELOG.md: Initial changelog with Keep a Changelog format

Release flow:
1. Run ./dump-version.sh
2. Push to br_release branch with tag
3. CI creates GitHub Release with native libraries
Change realm from gopher-mcp-auth to gopher-mcp.
RahulHere added 3 commits March 20, 2026 23:27
Make the auth example self-contained so it can be used by third-party
developers without needing the full gopher-mcp-rust repository locally.

Changes:
- Update Cargo.toml to use SDK via git dependency instead of local path
- Rewrite run_example.sh to download native libs from GitHub releases
- Add SDK_VERSION and NATIVE_LIB_DIR environment variable support
- Update README with installation and troubleshooting instructions
- Add .gitignore for native/ directory and build artifacts
Enhance release system to support both GitHub Releases and crates.io:

dump-version.sh:
- Add --publish flag to publish to crates.io during release
- Add --dry-run flag to preview changes without executing
- Add --help flag with usage documentation
- Add Step 8 for optional crates.io publishing

release.yml:
- Add publish-crates job that runs after release job
- Trigger on PUBLISH_CRATES=true repo variable or [publish] in commit
- Add CARGO_REGISTRY_TOKEN secret for crates.io auth
- Update release notes with crates.io and GitHub install options
Change release workflow to publish to both GitHub and crates.io by default:
- Default behavior now publishes to GitHub + crates.io
- Add --skip-crates flag to publish to GitHub only
- Update help text and examples to reflect new defaults
- Show clear output indicating which targets are being published
FFI fixes:
- Fix library name: libgopher_auth -> libgopher-orch (the actual library)
- Fix gopher_auth_client_create parameter order to match C API
  (output param first, not last)
- Fix symbol name: gopher_auth_set_option -> gopher_auth_client_set_option
- Add DYLD_LIBRARY_PATH/LD_LIBRARY_PATH to library search paths

Package rename:
- Rename package from gopher-orch to gopher-mcp-rust
- Update lib name to gopher_mcp_rust
- Update all imports in examples/auth to use new crate name
- Use path dependency for local development
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