LBS Ballr MCP Server Setup Guide¶
Overview¶
The LBS Ballr MCP Server provides secure, controlled access to sports data via the Model Context Protocol (MCP). It integrates with Clerk OAuth for authentication and runs alongside your existing Aspire development environment.
Design Decisions¶
- Specific tools, not generic queries — only whitelisted tools are exposed; complete control over data access, type-safe.
- Direct Marten integration, not HTTP API — better performance, reuses domain contracts and existing infrastructure.
- Optional authentication with graceful degradation — anonymous users get basic data; authenticated users get richer data via the existing
UserContextinfrastructure. - Clerk JWT validation — reuses existing auth patterns in the platform.
Architecture¶
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ LLM Client │───▶│ MCP Server │───▶│ Domain Layer │
│ (Claude, etc.) │ │ (STDIO/JSON) │ │ (Marten DB) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌──────────────────┐
│ Clerk OAuth JWT │
│ Validation │
└──────────────────┘
Key Components¶
- MCP Server:
src/Apps/LBS.Ballr.McpServer/ - Authentication: Clerk OAuth JWT validation
- Data Access: Direct Marten queries to domain contracts
- Tools: Whitelisted, specific query tools only
Project Structure¶
src/Apps/LBS.Ballr.McpServer/
├── Program.cs # Aspire integration & DI setup
├── appsettings.json # Local configuration
├── Models/
│ └── NrlPlayerInfo.cs # Response models for MCP tools
├── Services/
│ ├── AuthenticationService.cs # MCP authentication orchestration
│ └── ClerkJwtService.cs # Clerk JWT validation
└── Tools/
├── AuthenticationTools.cs # Auth-related MCP tools
├── NrlPlayerTools.cs # NRL player search tools
├── CompetitionTools.cs # Competition tools (stubbed)
└── FixtureTools.cs # Fixture tools (stubbed)
Setup Instructions¶
1. Create Clerk OAuth Application¶
- Login to Clerk Dashboard: https://dashboard.clerk.com
- Navigate to your application (precious-ghoul-33)
- Go to: Configure → Applications → OAuth Applications
- Click "Add OAuth Application"
- Configure the OAuth app:
- Save and note down:
- Client ID (public)
- Client Secret (private - keep secure)
2. Update Configuration¶
Edit /src/Aspire/LBS.AspireHost/appsettings.json:
{
"ClerkOAuth": {
"ClientId": "YOUR_ACTUAL_CLIENT_ID_HERE",
"ClientSecret": "YOUR_ACTUAL_CLIENT_SECRET_HERE",
"Domain": "https://precious-ghoul-33.clerk.accounts.dev"
}
}
Replace the placeholder values with your actual Clerk OAuth credentials.
3. Run with Aspire¶
This will start: - Main API (LBS.Api) - MCP Server (LBS.Ballr.McpServer) - PostgreSQL database - Azure Storage emulator
The MCP server runs as a console application that communicates via STDIO using the MCP protocol.
Available MCP Tools¶
Authentication Tools¶
Authenticate¶
Validates a Clerk Bearer token and sets user context.
Parameters:
- bearerToken (string): Clerk JWT token
Returns:
{
"success": true,
"message": "Authentication successful",
"user": {
"id": "user_123",
"name": "John Doe",
"email": "john@example.com",
"roles": ["Member"],
"isAuthenticated": true
}
}
GetCurrentUser¶
Returns information about the currently authenticated user.
Returns:
{
"id": "user_123",
"name": "John Doe",
"email": "john@example.com",
"roles": ["Member"],
"isAuthenticated": true
}
Logout¶
Clears the current authentication session.
Returns:
Data Query Tools¶
SearchNrlPlayers¶
Search NRL SuperCoach players with comprehensive filtering options.
Parameters:
- bearerToken (string, optional): Authentication token for enhanced data
- searchTerm (string, optional): Player name search
- teams (string[], optional): Team names like "Panthers", "Storm"
- positions (string[], optional): Positions like "Halfback", "Fullback"
- minPoints (int, optional): Minimum total points
- maxPoints (int, optional): Maximum total points
- minPrice (decimal, optional): Minimum player price
- maxPrice (decimal, optional): Maximum player price
- minOwnership (decimal, optional): Minimum ownership percentage
- maxOwnership (decimal, optional): Maximum ownership percentage
- orderBy (string): Sort order like "totalPoints DESC"
- skip (int): Pagination offset
- take (int): Number of results to return
Returns:
{
"players": [
{
"displayName": "Nathan Cleary",
"teamName": "Panthers",
"position": "Halfback",
"currentPrice": 750000,
"totalPoints": 1250,
"averagePoints": 65.5,
"lastRoundPoints": 78,
"ownershipPercentage": 45.2,
"projectedPoints": 72.5,
"projectedMinutes": 80.0
}
],
"totalCount": 500,
"skip": 0,
"take": 20,
"hasMore": true
}
GetCompetitionTeams (Stubbed)¶
Returns team information for a competition.
Parameters:
- competitionCode (string): Competition like "NRL", "AFL"
- season (int, optional): Season year
Returns: Stubbed team data for development.
GetSportingEvents (Stubbed)¶
Returns upcoming sporting events.
Parameters:
- competitionCode (string, optional): Competition filter
- teams (string[], optional): Team name filters
- daysAhead (int): Number of days to look ahead
Returns: Stubbed sporting event data for development.
Authentication Flow¶
1. For Unauthenticated Users¶
Tools work without authentication but return basic data only:
// Basic player search - limited data
SearchNrlPlayers({
searchTerm: "Smith",
teams: ["Panthers"],
take: 10
})
2. For Authenticated Users¶
- Get Bearer Token: User authenticates via your web application using Clerk
- Authenticate with MCP: Pass the Bearer token to the MCP server
- Enhanced Data Access: Subsequent tool calls return richer data
// Step 1: Authenticate
Authenticate({
bearerToken: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
})
// Step 2: Enhanced player search - full data including projections
SearchNrlPlayers({
searchTerm: "Smith",
teams: ["Panthers"],
take: 10
})
3. User Context Integration¶
The MCP server integrates with your existing UserContext infrastructure:
- Thread-safe: Uses
AsyncLocalfor proper context flow - Existing patterns: Leverages
IRequireUserContextinterface - Role-based access: Tools can check user roles for enhanced features
Security Features¶
Whitelisted Tools Only¶
- No generic query access: Only specific, approved tools are exposed
- Controlled data exposure: Each tool explicitly defines what data it returns
- No SQL injection risk: Uses strongly-typed domain contracts
Authentication Controls¶
- Optional authentication: Tools work for anonymous users with limited data
- Enhanced data for authenticated users: Richer information when authenticated
- Role-based features: Different data based on user roles
- Secure token validation: Proper Clerk JWT validation
Data Access Controls¶
- Domain layer integration: Direct access to existing contracts
- No bypass mechanisms: Cannot access data outside defined tools
- Audit logging: All tool usage is logged with user context
Development¶
Adding New Tools¶
- Create the tool method in appropriate tool class:
[McpServerTool, Description("Description of what this tool does")]
public async Task<MyResult> MyNewTool(
[Description("Parameter description")] string parameter,
string? bearerToken = null)
{
// Optional authentication
if (!string.IsNullOrWhiteSpace(bearerToken))
{
await this.authService.AuthenticateFromTokenAsync(bearerToken);
}
var currentUser = this.authService.GetCurrentUser();
// Query domain contracts directly
using var session = this.documentStore.LightweightSession();
var results = await session.Query<SomeContract>()
.Where(x => x.Property == parameter)
.ToListAsync();
// Shape response for LLM consumption
return new MyResult { ... };
}
- Register the tool in
Program.cs:
Testing¶
# Build the MCP server
cd src/Apps/LBS.Ballr.McpServer
dotnet build
# Run with Aspire (includes MCP server)
cd src/Aspire/LBS.AspireHost
dotnet run
Troubleshooting¶
Common Issues¶
- MCP Server not starting
- Check that ModelContextProtocol package is properly installed
- Verify all project references are correct
-
Check Aspire host logs for startup errors
-
Authentication failing
- Verify Clerk OAuth configuration in appsettings.json
- Check that Client ID and Client Secret are correct
-
Ensure JWT token is valid and not expired
-
Database connection issues
- Verify PostgreSQL is running (started by Aspire)
- Check connection string in Aspire configuration
-
Ensure database migrations have been applied
-
Tools not returning data
- Check that NrlSuperCoachPlayerStats feature is enabled
- Verify Marten projections are built
- Check database has SuperCoach data
Logs¶
MCP server logs are available through: - Aspire Dashboard: http://localhost:15888 (when running via Aspire) - Console output: When running the MCP server directly
Next Steps¶
- Implement remaining tools: Complete the stubbed
CompetitionToolsandSportingEventTools - Enhanced authentication: Add role-based data filtering
- Performance optimization: Add caching for frequent queries
- Monitoring: Add telemetry and usage metrics
- Production deployment: Configure for production Clerk environment