hr-mcp-server

fabianwilliams/hr-mcp-server

3.2

If you are the rightful owner of hr-mcp-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.

The HR MCP Server is a Microsoft Model Context Protocol server deployed on Azure Container Apps, offering persistent HR candidate management tools for AI assistants.

Tools
5
Resources
0
Prompts
0

HR MCP Server - Azure Container Apps with Table Storage Persistence

A Microsoft HR MCP (Model Context Protocol) server deployed to Azure Container Apps with full Azure Table Storage persistence. This server provides HR candidate management tools for AI assistants like Claude Desktop and integrates seamlessly with Copilot Studio.

πŸ™ Inspiration & Credit

This project builds upon the excellent foundational work by Paolo Pialorsi, a Microsoft Developer Advocate. His original Lab 6 - MCP Server from the Copilot Camp series demonstrated how to expose a simple, in-memory MCP server via HTTP for learning and experimentation purposes.

πŸ“š View the original lab here:
https://microsoft.github.io/copilot-camp/pages/make/copilot-studio/06-mcp/

While Paolo's version was tailored for educational use, this project extends his concept by:

  • Deploying the MCP server fully remote to Azure Container Apps
  • Replacing in-memory lists with persistent Azure Table Storage
  • Enabling streaming and concurrent access for real-world scenarios

Kudos to Paolo for providing a solid springboard and inspiration for this work.

πŸŽ‰ Success Story: Persistent Conversations Achieved!

Production URL: https://hr-mcp-server.jollyflower-9d7ab707.eastus2.azurecontainerapps.io
Status: βœ… Production Ready with Full Persistence!

πŸš€ Live Demo: End-to-End Persistence Proof

1. Claude Desktop Integration - Adding Candidates

Claude Desktop Tools Available Claude Desktop showing the hr-mcp-server with 5 HR tools available

Claude Desktop Tool Details All HR candidate management tools enabled and ready to use

Claude Desktop Adding User Successfully adding "Fabs Willy" as an AI Developer candidate via Claude Desktop

2. Azure Table Storage Persistence - Data Survives Everything

Azure Storage Explorer Proof Azure Storage Explorer showing all candidates persisted in the Candidates table, including the newly added "Fabs Willy" and "TestPersist Storage" entries

3. MCP Inspector Verification - Cross-Platform Access

MCP Inspector Proof MCP Inspector independently accessing the same persistent data, proving cross-platform compatibility

πŸ† Key Achievement: True Persistent Conversations

The Holy Grail: Data persists across:

  • βœ… Container restarts - Data survives scaling events
  • βœ… Application deployments - Updates don't lose data
  • βœ… Multiple clients - Claude Desktop, MCP Inspector, Copilot Studio
  • βœ… Concurrent connections - Multiple users simultaneously
  • βœ… Platform independence - Works with any MCP-compatible client

πŸ“‹ Available Tools

The HR MCP server provides comprehensive candidate management:

  • list_candidates - Retrieve all candidates from Azure Table Storage
  • search_candidates - Search candidates by name, email, skills, or role
  • add_candidate - Add new candidates with full persistence
  • update_candidate - Modify existing candidate information
  • remove_candidate - Remove candidates from the system

πŸ”§ Technology Stack

  • .NET 8.0 - ASP.NET Core web application
  • Model Context Protocol - HTTP streaming transport
  • Azure Container Apps - Serverless container hosting with auto-scaling
  • Azure Table Storage - NoSQL persistent storage for candidate data
  • Azure Container Registry - Private container image storage
  • GitHub Actions - Automated CI/CD pipeline

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Claude/AI     │───▢│  HR MCP Server   │───▢│ Azure Table     β”‚
β”‚   Clients       β”‚    β”‚ (Container Apps) β”‚    β”‚   Storage       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                β”‚
                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                       β”‚ Azure Container  β”‚
                       β”‚    Registry      β”‚
                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸš€ Quick Start

For Claude Desktop

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "hr-server": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://hr-mcp-server.jollyflower-9d7ab707.eastus2.azurecontainerapps.io"
      ]
    }
  }
}

For MCP Inspector

Test the server directly:

npx @modelcontextprotocol/inspector https://hr-mcp-server.jollyflower-9d7ab707.eastus2.azurecontainerapps.io

For Microsoft Copilot Studio

Integrate the HR MCP Server with Copilot Studio using the provided YAML connector specification:

  1. Download the connector YAML file (try the main version first):

    curl -O https://raw.githubusercontent.com/fabianwilliams/hr-mcp-server/main/copilot-studio-connector.yaml
    
  2. Import in Copilot Studio:

    • Go to your Copilot Studio environment
    • Navigate to Connectors β†’ Custom connectors
    • Click Import from OpenAPI file
    • Upload the copilot-studio-connector.yaml file
  3. Configure the connector:

    • Host: hr-mcp-server.jollyflower-9d7ab707.eastus2.azurecontainerapps.io
    • Protocol: MCP Streamable (recommended)
    • Authentication: None required (public endpoint)
  4. Use in your Copilot:

    • Add the HR MCP Server connector to your copilot
    • Access all 5 HR candidate management tools
    • Enjoy persistent data across all interactions
🚨 Troubleshooting Copilot Studio Import

If you get validation errors during import, try the alternative connector:

curl -O https://raw.githubusercontent.com/fabianwilliams/hr-mcp-server/main/copilot-studio-connector-alternative.yaml

Note: The alternative uses /mcp path instead of / root path, which may be required in some Copilot Studio environments.

YAML Specifications:

  • (main)
  • (fallback)

For detailed Copilot Studio MCP integration guidance, see: Microsoft Learn - Extend agents with MCP

πŸ“¦ Local Development

# Clone the repository
git clone https://github.com/fabianwilliams/hr-mcp-server.git
cd hr-mcp-server

# Build and run locally
dotnet build
dotnet run

# The server will be available at http://localhost:5000

Note: For local development, configure the TABLE_STORAGE_CONN_STRING environment variable with your Azure Storage connection string.

☁️ Deployment Journey: From Challenges to Success

🎯 What We Achieved

βœ… Azure Table Storage Integration - Full CRUD operations with persistence
βœ… Concurrent Connection Support - Multiple clients access shared data
βœ… Automatic Data Seeding - Initial candidates loaded from JSON on first run
βœ… Production CI/CD Pipeline - Automated deployments via GitHub Actions
βœ… Cross-Platform Compatibility - Works with Claude Desktop, MCP Inspector, Copilot Studio
βœ… Container Scaling - Auto-scales from 0-10 replicas based on demand

πŸ”§ Technical Solutions Implemented

1. Azure Table Storage Persistence

Challenge: In-memory storage lost data on container restarts
Solution: Comprehensive Azure Table Storage integration following SapwoodRemoteMCPServer patterns:

// Pattern that works in Azure Container Apps
var connectionString = Environment.GetEnvironmentVariable("TABLE_STORAGE_CONN_STRING");
var tableClient = new TableClient(connectionString, "Candidates");
await foreach (var entity in tableClient.QueryAsync<CandidateTableEntity>(e => e.PartitionKey == "Candidate"))

Table Schema:

Table: "Candidates"
PartitionKey: "Candidate" 
RowKey: candidate.Email (unique identifier)
Properties: FirstName, LastName, CurrentRole, SkillsJson, SpokenLanguagesJson
2. Environment Variable Configuration

Challenge: IConfiguration.GetConnectionString() didn't work in Container Apps
Solution: Direct Environment.GetEnvironmentVariable() access matching Azure Functions pattern

3. Async Table Creation

Challenge: Synchronous table operations in constructor caused startup hangs
Solution: Lazy async table creation on first operation via EnsureTableExistsAsync()

4. Container App Configuration

Challenge: Multiple deployment issues with ports, authentication, and revisions
Solutions:

  • Updated target port from 80 to 8080 to match ASP.NET Core default
  • Configured Azure Container Registry authentication with managed identity
  • Proper revision management to avoid multiple active versions
5. Data Seeding Strategy

Challenge: Bootstrap empty tables with initial data
Solution: DataSeedingService automatically loads from JSON if table is empty

πŸ” Environment Configuration

Required Environment Variables

  • TABLE_STORAGE_CONN_STRING - Azure Storage connection string for persistence

Container App Settings

The server automatically:

  1. Creates the "Candidates" table if it doesn't exist
  2. Seeds initial data from Data/candidates.json if table is empty
  3. Handles concurrent connections via Azure Table Storage
  4. Scales automatically based on demand

πŸ“Š Performance & Scaling

  • Auto-scaling: 0-10 replicas based on HTTP requests
  • Cold start: ~2-3 seconds for first request after scale-down
  • Response time: <100ms for candidate operations
  • Concurrent sessions: Unlimited via Azure Table Storage
  • Data persistence: Guaranteed across all scaling events

πŸ›‘οΈ Security

  • HTTPS Only: TLS 1.3 encryption for all communications
  • Managed Identity: Secure Azure Container Registry access
  • Environment Variables: Connection strings stored securely in Container App
  • No Secrets in Code: All credentials managed by Azure platform

πŸ“ˆ Monitoring & Observability

  • Application Insights: Integrated telemetry and distributed tracing
  • Container Apps Metrics: CPU, memory, request metrics, and scaling events
  • Table Storage Metrics: Operation latency, success rates, and throughput
  • MCP Session Logging: Request/response logging for debugging

🎯 Deployment Success Metrics

MetricStatusDetails
Container Startupβœ…Starts in <3 seconds
Table Storage Connectionβœ…Auto-creates tables on first run
Data Persistenceβœ…Survives restarts, deployments, scaling
Concurrent Accessβœ…Multiple clients supported
Claude Desktop Integrationβœ…5 tools available and working
MCP Inspector Compatibilityβœ…Independent access verified
CI/CD Pipelineβœ…Automated deployments working

🚨 Lessons Learned: Critical Implementation Details

1. Environment Variable Access Patterns

❌ Doesn't work in Container Apps: configuration.GetConnectionString("TableStorage")
βœ… Works perfectly: Environment.GetEnvironmentVariable("TABLE_STORAGE_CONN_STRING")

2. Table Storage Query Syntax

❌ Complex approach: TableClient.CreateQueryFilter($"PartitionKey eq 'Candidate'")
βœ… Simple approach: tableClient.QueryAsync<T>(e => e.PartitionKey == "Candidate")

3. Constructor I/O Operations

❌ Blocks startup: Synchronous operations in service constructors
βœ… Lazy initialization: Async operations called on first use

4. Container App Networking

❌ Port mismatch: Default ingress port 80 vs ASP.NET Core port 8080
βœ… Port alignment: Configure ingress target port to match application

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Test with both Claude Desktop and MCP Inspector
  4. Ensure data persists across container restarts
  5. Submit pull request

πŸ“„ License

Based on Microsoft MCP server samples. See LICENSE for terms.

πŸ†˜ Support

For issues with:

  • Deployment: Review the technical solutions above
  • MCP Protocol: https://modelcontextprotocol.io/
  • Azure Container Apps: Azure documentation
  • Table Storage: Azure Storage documentation

πŸŽ‰ Achievement Unlocked: Full MCP Server persistence with Azure Table Storage!

Deployment Status: βœ… Production Ready
Last Updated: July 24, 2025
Maintainer: Fabian Williams
Built with: Claude Code (claude.ai/code)