SF-MCP: SAP SuccessFactors MCP Server
A secure Model Context Protocol (MCP) server providing 12 tools for querying and managing SAP SuccessFactors via OData APIs.
Overview
This MCP server enables Claude Desktop (or any MCP-compatible client) to interact with SAP SuccessFactors. It provides tools for configuration management, role-based permissions (RBP), data querying, and cross-instance comparison.
Features
Tools (12 total)
| Category | Tool | Description |
|---|---|---|
| Configuration | get_configuration | Retrieve OData metadata for any entity |
| Configuration | list_entities | Discover all available OData entities |
| Configuration | compare_configurations | Compare entity config between instances |
| RBP Security | get_rbp_roles | List all Role-Based Permission roles |
| RBP Security | get_role_permissions | Get permissions for specific roles |
| RBP Security | get_user_permissions | Get all permissions for a user |
| RBP Security | get_user_roles | Get roles assigned to a user |
| RBP Security | get_permission_metadata | Map UI labels to permission types |
| RBP Security | check_user_permission | Check if user has specific permission |
| RBP Security | get_dynamic_groups | List permission groups (dynamic groups) |
| Data Query | query_odata | Flexible OData queries with filtering |
| Data Validation | get_picklist_values | Get dropdown/picklist options |
Security Features
- Input Validation: Regex-based validation prevents OData injection attacks
- XXE Protection: Uses
defusedxmllibrary for safe XML parsing - Audit Logging: JSON-structured logs compatible with Cloud Logging
- Secret Manager: GCP Secret Manager integration for credential storage
- Credential Flexibility: Pass credentials per-request or use environment defaults
Prerequisites
- Python 3.10 or higher
- uv package manager
- SAP SuccessFactors account with API access
Installation
-
Clone or download this repository
-
Install dependencies
cd sf-mcp uv sync -
Configure credentials
Credentials can be provided in three ways (in order of precedence):
- Per-request parameters:
auth_user_idandauth_passwordon each tool call - Environment variables:
SF_USER_IDandSF_PASSWORD - GCP Secret Manager: Secrets named
sf-user-idandsf-password
Variable Required Description SF_USER_IDYes* Your SuccessFactors user ID (without @instance) SF_PASSWORDYes* Your SuccessFactors password SF_API_HOSTNo API host (defaults to api55preview.sapsf.eu)*Required unless using per-request credentials or Secret Manager
- Per-request parameters:
Usage
Running with MCP Dev Server
For development and testing:
uv run mcp dev main.py
Running Standalone
uv run main.py
Claude Desktop Integration
Add to your Claude Desktop configuration:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"sf-mcp": {
"command": "/path/to/uv",
"args": [
"--directory",
"/path/to/sf-mcp",
"run",
"main.py"
],
"env": {
"SF_USER_ID": "your_user_id",
"SF_PASSWORD": "your_password"
}
}
}
}
For Cloud Run deployment:
{
"mcpServers": {
"sf-mcp": {
"url": "https://sf-mcp-xxxxx-uc.a.run.app/mcp"
}
}
}
Tool Reference
Configuration Tools
get_configuration
Retrieves OData metadata for a specified SuccessFactors entity.
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Yes | SuccessFactors company ID |
entity | string | Yes | OData entity name (e.g., "User", "Position") |
environment | string | No | "preview" or "production" (default: preview) |
auth_user_id | string | No | Override default credentials |
auth_password | string | No | Override default credentials |
Example:
Get the configuration metadata for the "User" entity in instance "mycompany"
list_entities
Discover all available OData entities in an instance.
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Yes | SuccessFactors company ID |
category | string | No | Filter: "foundation", "employee", "talent", "platform", or "all" |
environment | string | No | "preview" or "production" (default: preview) |
auth_user_id | string | No | Override default credentials |
auth_password | string | No | Override default credentials |
Response:
{
"entities": ["User", "Position", "EmpEmployment", ...],
"count": 150,
"total_available": 500,
"by_category": {
"foundation": 85,
"employee": 120,
"talent": 45,
"platform": 30,
"other": 220
}
}
compare_configurations
Compare entity configuration between two instances (e.g., dev vs prod).
| Parameter | Type | Required | Description |
|---|---|---|---|
instance1 | string | Yes | First instance (e.g., dev) |
instance2 | string | Yes | Second instance (e.g., prod) |
entity | string | Yes | Entity to compare |
environment1 | string | No | Environment for instance1 (default: preview) |
environment2 | string | No | Environment for instance2 (default: production) |
auth_user_id | string | No | Override default credentials |
auth_password | string | No | Override default credentials |
Response:
{
"entity": "User",
"comparison": {
"fields_only_in_instance1": ["customField1"],
"fields_only_in_instance2": ["customField2"],
"fields_in_both": 45,
"type_differences": [],
"match_percentage": 95.5
},
"summary": {
"is_identical": false,
"differences_found": 2
}
}
RBP Security Tools
get_rbp_roles
Lists all Role-Based Permission roles.
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Yes | SuccessFactors company ID |
include_description | boolean | No | Include role descriptions (default: false) |
environment | string | No | "preview" or "production" (default: preview) |
auth_user_id | string | No | Override default credentials |
auth_password | string | No | Override default credentials |
get_role_permissions
Gets permissions assigned to specific RBP roles. Supports multiple role IDs.
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Yes | SuccessFactors company ID |
role_ids | string | Yes | Single ID or comma-separated: "10" or "10,20,30" |
locale | string | No | Locale for labels (default: en-US) |
environment | string | No | "preview" or "production" (default: preview) |
auth_user_id | string | No | Override default credentials |
auth_password | string | No | Override default credentials |
get_user_permissions
Gets all permissions for specific users based on their assigned roles.
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Yes | SuccessFactors company ID |
user_ids | string | Yes | Single ID or comma-separated: "admin" or "admin,user2" |
locale | string | No | Locale for labels (default: en-US) |
environment | string | No | "preview" or "production" (default: preview) |
auth_user_id | string | No | Override default credentials |
auth_password | string | No | Override default credentials |
get_user_roles
Gets all RBP roles assigned to a specific user.
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Yes | SuccessFactors company ID |
user_id | string | Yes | User ID to look up roles for |
include_permissions | boolean | No | Also fetch permissions for each role (default: false) |
environment | string | No | "preview" or "production" (default: preview) |
auth_user_id | string | No | Override default credentials |
auth_password | string | No | Override default credentials |
Response:
{
"user_id": "admin",
"roles": [
{"roleId": "10", "roleName": "Administrator", "roleDesc": "Full access"},
{"roleId": "20", "roleName": "HR Manager", "roleDesc": "HR functions"}
],
"role_count": 2
}
get_permission_metadata
Maps UI labels to permission types and values.
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Yes | SuccessFactors company ID |
locale | string | No | Locale for labels (default: en-US) |
environment | string | No | "preview" or "production" (default: preview) |
auth_user_id | string | No | Override default credentials |
auth_password | string | No | Override default credentials |
check_user_permission
Check if a user has a specific permission for a target user.
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Yes | SuccessFactors company ID |
access_user_id | string | Yes | User whose permission to check |
target_user_id | string | Yes | Target user of the permission |
perm_type | string | Yes | Permission type from metadata |
perm_string_value | string | Yes | Permission string value |
perm_long_value | string | No | Permission long value (default: -1L) |
environment | string | No | "preview" or "production" (default: preview) |
auth_user_id | string | No | Override default credentials |
auth_password | string | No | Override default credentials |
get_dynamic_groups
Lists dynamic groups (permission groups) used in RBP rules.
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Yes | SuccessFactors company ID |
group_type | string | No | Filter by group type |
environment | string | No | "preview" or "production" (default: preview) |
auth_user_id | string | No | Override default credentials |
auth_password | string | No | Override default credentials |
Data Query Tools
query_odata
Execute flexible OData queries against any SuccessFactors entity.
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Yes | SuccessFactors company ID |
entity | string | Yes | Entity name or entity with key: "User" or "User('admin')" |
select | string | No | Fields to return: "userId,firstName,lastName" |
filter | string | No | OData filter: "status eq 'active'" |
expand | string | No | Navigation properties: "empInfo,jobInfoNav" |
top | integer | No | Max records (default: 100, max: 1000) |
skip | integer | No | Records to skip for pagination |
orderby | string | No | Sort: "lastName asc" or "hireDate desc" |
environment | string | No | "preview" or "production" (default: preview) |
auth_user_id | string | No | Override default credentials |
auth_password | string | No | Override default credentials |
Examples:
# Get active users
entity="User", filter="status eq 'active'", select="userId,firstName,lastName"
# Get single user with expanded info
entity="User('admin')", expand="empInfo"
# Paginate through positions
entity="Position", top=100, skip=100
Response:
{
"entity": "User",
"results": [...],
"count": 100,
"next_skip": 200
}
Data Validation Tools
get_picklist_values
Get all values for a specific picklist (dropdown options).
| Parameter | Type | Required | Description |
|---|---|---|---|
instance | string | Yes | SuccessFactors company ID |
picklist_id | string | Yes | Picklist ID: "ecJobFunction", "nationality" |
locale | string | No | Locale for labels (default: en-US) |
include_inactive | boolean | No | Include inactive values (default: false) |
environment | string | No | "preview" or "production" (default: preview) |
auth_user_id | string | No | Override default credentials |
auth_password | string | No | Override default credentials |
Common Picklists:
ecJobFunction- Job functionsecJobCode- Job codesecPayGrade- Pay gradesecDepartment- Departmentsnationality- Countries/nationalitiesmaritalStatus- Marital status options
Response:
{
"picklist_id": "nationality",
"locale": "en-US",
"values": [
{"id": "US", "externalCode": "US", "label": "United States", "status": "active"},
{"id": "UK", "externalCode": "UK", "label": "United Kingdom", "status": "active"}
],
"count": 195,
"has_inactive": true
}
Common SuccessFactors Entities
| Category | Entities |
|---|---|
| Employee | User, EmpEmployment, EmpJob, PerPersonal, PerPhone, PerEmail |
| Foundation | FOCompany, FODepartment, FOJobCode, FOLocation, FOPayGrade |
| Position | Position, PositionEntity, PositionMatrixRelationship |
| Talent | Goal, GoalPlan, PerformanceReview, Competency |
| Recruiting | JobRequisition, Candidate, JobApplication |
Use list_entities to discover all available entities in your instance.
Cloud Deployment (Google Cloud Run)
Prerequisites
- Google Cloud account with billing enabled
- Google Cloud CLI installed
Deploy with Secret Manager (Recommended)
# Set project
export PROJECT_ID=your-gcp-project-id
gcloud config set project $PROJECT_ID
# Enable required APIs
gcloud services enable secretmanager.googleapis.com run.googleapis.com
# Create secrets
echo -n "your_user_id" | gcloud secrets create sf-user-id --data-file=-
echo -n "your_password" | gcloud secrets create sf-password --data-file=-
# Grant access to Cloud Run service account
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
gcloud secrets add-iam-policy-binding sf-user-id \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
gcloud secrets add-iam-policy-binding sf-password \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
# Build and deploy
gcloud builds submit --tag gcr.io/$PROJECT_ID/sf-mcp
gcloud run deploy sf-mcp \
--image gcr.io/$PROJECT_ID/sf-mcp \
--platform managed \
--region us-central1 \
--set-env-vars "GCP_PROJECT_ID=$PROJECT_ID"
Local Testing with Docker
# Build the image
docker build -t sf-mcp .
# Run locally
docker run -p 8080:8080 \
-e SF_USER_ID=your_user \
-e SF_PASSWORD=your_pass \
sf-mcp
# Test endpoint
curl http://localhost:8080/mcp
Project Structure
sf-mcp/
├── main.py # MCP server with 12 tools
├── pyproject.toml # Project dependencies
├── uv.lock # Dependency lock file
├── Dockerfile # Container image for Cloud Run
├── .env # Local credentials (gitignored)
├── .gitignore # Git ignore rules
└── README.md # This documentation
Dependencies
fastmcp>=2.0.0- Model Context Protocol SDKrequests>=2.31.0- HTTP client librarydefusedxml>=0.7.0- Safe XML parsing (XXE protection)python-dotenv>=1.0.0- Environment variable loaderuvicorn>=0.30.0- ASGI server for HTTP transport
Troubleshooting
Authentication Errors (HTTP 401)
- Verify credentials format: user ID without @instance
- Check password is correct
- Ensure API user has proper permissions in SuccessFactors
Validation Errors
The server validates all inputs to prevent injection attacks. If you see validation errors:
instance: Must contain only alphanumeric, underscores, hyphensentity: Must be valid OData entity namefilter: Cannot contain blocked keywords ($batch, $metadata, script tags)locale: Must be format like "en-US" or "de"
Server Disconnected
- Verify
uvpath in config is correct - Check logs:
tail -f ~/Library/Logs/Claude/mcp*.log - Test manually:
uv run mcp dev main.py
Security Considerations
- Input Validation: All parameters validated with regex patterns
- XXE Protection: XML parsing uses defusedxml library
- Audit Logging: All tool invocations logged in JSON format
- Secret Manager: Production credentials stored in GCP Secret Manager
- No Hardcoded Secrets: Credentials from environment or parameters only
- Minimal Permissions: Use dedicated API user with required permissions only
License
MIT License