Skip to content

MCP Server

The Payroll Engine MCP Server exposes payroll data as typed tools that AI clients (Claude Desktop, GitHub Copilot, Cursor, etc.) can invoke directly using natural language. It communicates via stdio transport using the Model Context Protocol and is built on PayrollEngine.Mcp.Core and PayrollEngine.Mcp.Tools.

Prerequisites

Read-Only by Design

The Payroll Engine MCP Server is a pure information and analysis tool. Write operations are not exposed — with two exceptions: get_employee_pay_preview and execute_payroll_report execute synchronous calculations that return results without persisting anything to the database.

This is a deliberate architectural decision. Payroll data is sensitive and consequential: a misplaced write operation could corrupt payrun results, trigger unintended retroactive chains, or leave silent mutations that are hard to detect. The risks of write access through an AI agent far outweigh any convenience gained.

Tasks that require data changes — such as case entries or tenant setup — are handled by purpose-built interfaces (WebApp, PayrollConsole, REST API) that provide the validation and confirmation workflows these operations need.

Access Control

The MCP Server controls access through two independent dimensions that answer different questions:

Dimension Controls Applied
Isolation Level Which records are returned At runtime, per query
Permissions Which tools are registered At startup, once

Isolation Level restricts data — a Tenant-isolated server cannot return records from another tenant regardless of which tools are active. Permissions restrict functionality — a tool that is not granted is invisible to the AI agent and does not appear in the tool list at all.

Isolation Level

Controls which records are returned at runtime. The isolation level is configured at startup and cannot be changed at runtime — ensuring that sensitive payroll data is always scoped correctly.

ValueDescription
MultiTenantFull access across all tenants (default)
TenantAll tool calls scoped to a single tenant
DivisionScoped to a single division within a tenant. Requires TenantIdentifier and DivisionName.
EmployeeSelf-service — single employee access. Requires TenantIdentifier and EmployeeIdentifier.

Roles

Controls which tools are registered at startup. Each tool belongs to exactly one role. A tool whose role is not granted is invisible to the AI agent.

Value Domain
HR Employee master data, case values, and audit trail
Payroll Payroll execution, results, preview calculations, temporal case value queries, and lookup resolution
Report Payroll report execution and result analysis
System Tenant and user management

Server — Always Available

The server tool is registered unconditionally — regardless of role permissions or isolation level.

Tool Description
get_server_info Returns server version, active isolation level, configured scope, and role permissions. Use to verify which build is running and how it is configured.

HR — Human Resources

Employee master data and organisational structure: who is employed, in which division, under what conditions, and how that data has changed over time. Includes the full case value history and the complete audit trail of all data mutations.

Organisation

Tool Description
list_divisions List all divisions of a tenant
get_division Get a division by name
get_division_attribute Get a single custom attribute of a division
list_employees List employees with optional OData filter (e.g. lastName eq 'Müller')
get_employee Get an employee by identifier
get_employee_attribute Get a single custom attribute of an employee

Case Values

Case values are the current and historical field values of an employee or company (e.g. salary, address, IBAN). The history contains all versions with their validity period.

Tool Description
list_employee_case_values Full case value history of an employee — all fields with start/end dates. Result includes employee context (identifier, first name, last name).
list_company_case_values Company-level case values of a tenant — all company fields with start/end dates.

Case Changes

Case changes are the audit trail of data mutations: each change records who made it, in which payroll, with what reason, and which field values were affected.

Tool Description
list_employee_case_changes Audit trail of employee data mutations. Answers "who changed what and when" for a specific employee. Supports OData filter and top. Result includes employee context.
list_company_case_changes Audit trail of company data mutations. Supports OData filter and top.

Payroll — Payroll Processing

Payroll execution and result analysis: payroll structure, payruns, payrun jobs, result values, preview calculations, temporal case value queries, and lookup resolution. A Payroll Specialist who needs to look up employees requires HR: Read in addition.

Structure

Tool Description
list_payrolls List all payrolls of a tenant
get_payroll Get a payroll by name
list_payruns List all payruns of a tenant
list_payrun_jobs List all payrun jobs, ordered by creation date descending. Includes period, status, employee count, and job timing. Result contains a divisions lookup (id → name) alongside the payrunJobs array.
list_payroll_wage_types Effective wage types of a payroll, merged across all regulation layers.
get_payroll_lookup_value Resolved lookup value for a given key or range value, merged across all regulation layers. Use lookupKey for exact-key lookups and rangeValue for progressive lookups (e.g. income bracket).

Results

Payroll results reflect what was calculated during payrun execution. Two complementary views are available:

Tool Description
list_payroll_result_values Flat list of all result values (wage types and collectors). Fully denormalized — each row includes employeeIdentifier, payrollName, periodName, payrunName, and jobName. Supports optional filter by employee or payroll, plus OData filter. Use for cross-employee or cross-period analysis.
get_consolidated_payroll_result All wage type results, collector results, and payrun results for one employee and one period in a single response. Use for a complete per-employee per-period overview. Result includes employee context and period boundaries.

Preview

Tool Description
get_employee_pay_preview Preview the payroll calculation for a single employee without persisting results. Returns wage type results, collector results, and payrun results. Requires McpServer:PreviewUserIdentifier to be configured.

Temporal Case Values

get_case_time_values queries case values as they were valid at a specific point in time, with three temporal perspectives:

Tool Description
get_case_time_values Case values valid at a specific point in time. Supports Employee, Company, and Global case types. When scoped to a single employee via employeeIdentifier, result includes employee context.

Report — Report Execution

Payroll report execution and result analysis. Reports are resolved across all regulation layers of the payroll and return one or more named result tables that the AI can analyse and summarise. Requires McpServer:PreviewUserIdentifier to be configured.

Tool Description
execute_payroll_report Execute a payroll report and return its result data set. The report is resolved across all regulation layers of the payroll. Use the parameters dictionary to pass report-specific input values (e.g. period, employee filter).

System — Administration

Tenant and user queries for cross-tenant administration and user management. In Tenant-isolated deployments the tenant context is fixed at startup — System tools are typically only needed for cross-tenant administration.

Tool Description
list_tenants List all tenants
get_tenant Get a tenant by identifier
get_tenant_attribute Get a single custom attribute of a tenant
list_users List all users of a tenant
get_user Get a user by identifier
get_user_attribute Get a single custom attribute of a user

Permissions

Each role is independently enabled or disabled per deployment. The default when the Permissions section is absent is Read for all roles.

Value Description
None Role tools are not registered — invisible to the AI agent
Read Query tools registered (default)

Role Isolation Level

Not every role is applicable at every isolation level. The table shows which permission values can be assigned per role and isolation level. Roles marked ☐ have no meaningful scope at that level — their tools operate on data that is not accessible within the isolation boundary.

✅ = permission can be assigned (None / Read)
☐ = not applicable at this isolation level

Role MultiTenant Tenant Division Employee
HR
Payroll
Report
System

Persona Examples

✅ = Read   ☐ = None

Persona HR Payroll Report System
HR Manager
Payroll Specialist
HR Business Partner
Controller / Analyst
Report Analyst
System Administrator
Developer

Case Value Temporal Perspectives

The get_case_time_values tool controls which data is returned through two independent date parameters:

Parameter Controls
valueDate What is valid — selects values whose Start <= valueDate < End
evaluationDate When the query is evaluated — filters out values created after this date

The combination of these two parameters defines the temporal perspective of the query.

valueDate — "What was valid on this day?"

Selects which value was active on a given date. A case field like Salary may have multiple entries with different validity periods — valueDate picks the one whose range covers the requested date.

evaluationDate — "What did the system know at this point?"

Filters out entries whose Created timestamp is later than the evaluation date. This is relevant for retroactive corrections: a salary correction entered today but backdated to January 1st has Start = 2025-01-01 but Created = today.

  • With evaluationDate = today: the correction is visible.
  • With evaluationDate = 2025-01-01: the correction is not visible (it was entered later).

Three Perspectives

Perspective Value Date Evaluation Date forecast Typical question
Historical target date = valueDate "What was true on Jan 1, as of Jan 1?"
Current knowledge target date today (default) "What do we now know about Jan 1?"
Forecast future date = valueDate name "What will be true on Jul 1 per the plan?"

HistoricalevaluationDate = valueDate: retroactive corrections entered after the target date are excluded. Use for audit and compliance.

Current knowledgeevaluationDate omitted or set to today: all corrections entered since are included. Use for reporting and data quality checks.

ForecastevaluationDate = valueDate with a forecast name: planned future values are included. evaluationDate must match valueDate (not today) to ensure future-dated forecast entries are visible. Use for budget planning and salary projections.

Parameters

Parameter Type Description
valueDate string (ISO 8601) The point in time for value validity. Default: today.
evaluationDate string (ISO 8601) The knowledge cutoff date. Default: today.
forecast string Forecast name. Omit for real values only.
employeeIdentifier string Employee identifier. Omit for all employees (tenant-wide).
caseFieldNames string (comma-separated) Filter by specific fields, e.g. "Salary,City,IBAN". Omit for all fields.

See Temporal Perspectives for a detailed explanation of the two time axes, including the retro 2×2 matrix and forecast entry expiry scenarios.

Example Prompts

List all tenants
Show me the employees of StartTenant
What case values does mario.nunez@foo.com have in StartTenant?
What changed in the employee data of mario.nunez@foo.com in January 2026?
List the lookup values of VatRates in SwissRegulation of CH-Monthly
What wage types are effective in the CH-Monthly payroll of StartTenant?
What was mario.nunez@foo.com's salary on 2025-01-01?
Show me all payroll results for Müller in March 2026
What would the payroll look like for Müller in April 2026?
Run the MonthlyPayslip report for the CH-Monthly payroll

Configuration

Backend connection settings in Server/appsettings.json:

{
  "ApiSettings": {
    "BaseUrl": "https://localhost",
    "Port": 443
  }
}

Sensitive settings (API key) go in apisettings.json — excluded from source control:

{
  "ApiSettings": {
    "ApiKey": "your-api-key"
  }
}

Isolation level and role permissions in appsettings.json:

{
  "McpServer": {
    "IsolationLevel": "Tenant",
    "TenantIdentifier": "acme-corp",
    "Permissions": {
      "HR":      "Read",
      "Payroll": "Read",
      "Report":  "Read",
      "System":  "None"
    }
  }
}

For Division isolation:

{
  "McpServer": {
    "IsolationLevel": "Division",
    "TenantIdentifier": "acme-corp",
    "DivisionName": "sales"
  }
}

For Employee isolation:

{
  "McpServer": {
    "IsolationLevel": "Employee",
    "TenantIdentifier": "acme-corp",
    "EmployeeIdentifier": "mario.nunez@acme.com"
  }
}

For payrun preview and report execution (get_employee_pay_preview, execute_payroll_report), configure a service account user that exists in the target tenant:

{
  "McpServer": {
    "PreviewUserIdentifier": "mcp-service@acme.com"
  }
}

Settings can also be provided as environment variables using the __ separator:

McpServer__IsolationLevel=Tenant
McpServer__TenantIdentifier=acme-corp
McpServer__DivisionName=sales
McpServer__EmployeeIdentifier=mario.nunez@acme.com
McpServer__PreviewUserIdentifier=mcp-service@acme.com
McpServer__Permissions__HR=Read
McpServer__Permissions__Payroll=Read
McpServer__Permissions__Report=Read
McpServer__Permissions__System=None
ApiSettings__BaseUrl=https://your-backend
ApiSettings__Port=443
AllowInsecureSsl=true

Client Setup

Claude Desktop

Add to %APPDATA%\Claude\claude_desktop_config.json:

{
  "mcpServers": {
    "payroll-engine": {
      "command": "dotnet",
      "args": [
        "run",
        "--project",
        "path/to/Server/PayrollEngine.Mcp.Server.csproj",
        "--no-launch-profile",
        "--no-build"
      ],
      "env": {
        "DOTNET_ENVIRONMENT": "Development",
        "ApiSettings__BaseUrl": "https://localhost",
        "ApiSettings__Port": "443",
        "AllowInsecureSsl": "true"
      }
    }
  }
}

Cursor

Add to ~/.cursor/mcp.json (global) or .cursor/mcp.json in the project root:

{
  "mcpServers": {
    "payroll-engine": {
      "command": "dotnet",
      "args": [
        "run",
        "--project",
        "path/to/Server/PayrollEngine.Mcp.Server.csproj",
        "--no-launch-profile",
        "--no-build"
      ],
      "env": {
        "DOTNET_ENVIRONMENT": "Development",
        "ApiSettings__BaseUrl": "https://localhost",
        "ApiSettings__Port": "443",
        "AllowInsecureSsl": "true"
      }
    }
  }
}

Docker

docker run --rm -i \
  -e ApiSettings__BaseUrl=https://your-backend \
  -e ApiSettings__Port=443 \
  ghcr.io/payroll-engine/payrollengine.mcp.server

See Also