Skip to main content
Glama
MCP Server for Visma Intempus

MCP Server for Visma Intempus

by borgels

mcp-server-intempus

MCP server for Visma Intempus — time registration, approval, balances (saldi), cases, employees and planning — with employee, approver and admin roles on one endpoint and per-user scoping.

Roles

One server, one endpoint. Every user holds one or more roles, and the tools they see are the union of those roles. With INTEMPUS_PROFILE=roles the roles arrive per request in X-MCP-Roles. The gateway derives that header from the user's Entra security groups (a roles table in hosts.json), and it is honored only together with INTEMPUS_TRUST_FORWARDED_USER=true; a user with no role gets 403. A fixed role or list (e.g. INTEMPUS_PROFILE=employee or employee,approver) gives every request the same roles, which is useful for stdio.

  • employee — self-service for full- and part-time employees, hard-scoped to the requesting user:

    • intempus_get_my_profile, intempus_list_my_work_reports

    • intempus_register_time

    • intempus_update_my_work_report / intempus_delete_my_work_report, only while the report is neither approved nor locked

    • intempus_get_my_balances, intempus_get_my_planning

  • approver — a read-only team view:

    • intempus_list_team

    • intempus_list_team_work_reports, e.g. what awaits approval

    • intempus_get_team_balances, intempus_get_team_planning

    Scope is the employees, departments and cases the user is responsible for in Intempus (responsible_for_employee / _department / _case), or everyone with INTEMPUS_APPROVER_UNASSIGNED_SCOPE=all when Intempus has no assignments. Approving happens in Intempus itself — see below.

  • admin — company-wide:

    • employees (intempus_manage_employee create/update/offboard), contracts, login access

    • approvers and responsibilities, which is what scopes the approver role

    • time for anyone, cases, customers, balances, schedules, planned work, reference data

    • allowlisted generic reads (intempus_get_resource)

    • the two-step intempus_prepare_admin_change → intempus_commit_prepared_operation for deletes and period locks (contract locked_date)

Every role gets intempus_whoami (shows your roles and linked employee), intempus_search_capabilities, intempus_list_cases and intempus_list_work_types.

Related MCP server: HR System MCP Server

Guarantees

  • Identity fails closed. The gateway-verified UPN (X-MCP-User, honored when INTEMPUS_TRUST_FORWARDED_USER=true and MCP_HTTP_TOKEN is set) must resolve to exactly one Intempus employee — by INTEMPUS_IDENTITY_MAP, then Intempus username, then work email, case-insensitive. The private personal_email is never used. Employee and approver tools never accept a foreign employee id as "me"; reports of others read as "not found".

  • Approvers stay in scope. A user without an approver level in Intempus gets nothing on the approver endpoint, and one with a level but no assignments sees nobody (unless INTEMPUS_APPROVER_UNASSIGNED_SCOPE=all).

  • Registrations are checked before they are sent against the rules Intempus enforces: a contract covering the date, outside the contract's locked period, a work type from the contract's work model, start/end times for interval work types, a case for project work, an open case. Editing or deleting an existing report is also refused when its current date lies in the locked period, so time cannot be moved out of or removed from a closed period. creation_id (a name-based UUID, SHA-256, of user + employee + idempotencyKey) makes retries safe — a retry returns the same report.

  • Irreversible admin changes are two-step (deleting work reports and schedules included — intempus_manage_work_report and intempus_manage_schedule only create and update): the prepare result is an HMAC-signed preview naming every affected record; commit accepts only an unaltered operation signed by this server, within 15 minutes, by the user who prepared it, whose steps still pass the allowlist. The gateway can gate the commit tool with a separate duty group.

  • Forwarded headers need the bearer token. The HTTP transport refuses to start with INTEMPUS_TRUST_FORWARDED_USER=true and no MCP_HTTP_TOKEN; X-MCP-User and X-MCP-Roles are honored only under both. The token is compared in constant time.

  • Writes need INTEMPUS_ENABLE_WRITES=true — checked in the policy and again in every write path.

  • Attribution. The API key belongs to one Intempus user, so Intempus' own history shows that user for every change. The JSONL audit (INTEMPUS_AUDIT_LOG) records the Entra user, roles, tool and a hash of the arguments (plus the operation hash on commits); requests without X-MCP-User are refused when trust is enabled.

  • No credentials in tool arguments or results: generated passwords are never echoed, password/PIN fields are stripped from raw reads, api_key/outlook_credential are not readable.

Not possible via Intempus' public API

Verified live on 2026-09-29 (details in docs/api-notes.md):

  • Approving time. approved is read-only, and approved_by_initial / approved_by_final can be set (to a user profile) without the report becoming approved. Approval stays in Intempus' web/approval app; the approver role shows what is waiting. Intempus' own MCP server (mcp.intempus.dk, needs an X-Security-Key from Intempus) advertises bulk approval — the way to get approval into Claude, if wanted.

  • Locking work reports (work_report/bulk/) needs Intempus' "work report state" feature, which ONE's account does not have (401 "…only be updated if the feature is enabled"). The prepare kind lock_work_reports works once Intempus enables it; period locks via the contract's locked_date work today.

  • Deleting employees. Intempus refuses (409) while a user profile exists, every employee gets one, and user profiles cannot be deleted. Employees are removed by offboarding: open contracts end on the last day and backend/approval logins are revoked.

Intempus facts this server relies on

Verified against a production account on 2026-09-29; details in docs/api-notes.md.

  • Auth Authorization: ApiKey <username>:<key>; the username is case-sensitive.

  • employee cannot be filtered on email, and username has no case-insensitive filter, so identity is matched in memory (cached 60 s).

  • explored_employee_balance and schedule answer HTTP 500 to cursor pagination; the client falls back to offset paging.

  • Relations are URIs in lists but embedded objects in some detail responses (e.g. work_type.work_model); both are handled.

  • Holiday balances accrue at month end: a balance "as of today" does not include this month's accrual yet.

  • Rate limit is 500 requests/minute per Intempus user, shared with every integration using the same key (e.g. bpc).

Relation to other Intempus integrations

Other systems may keep their own direct Intempus integration (cases out, approved time in). This server follows the common case convention: intempus_manage_case with projectNumber names the case "<projektnr> - <navn>", and refuses a create when the number or name already exists.

Configuration

See .env.example. Minimal production set: INTEMPUS_API_USER, INTEMPUS_API_KEY, INTEMPUS_PROFILE=roles, INTEMPUS_TRUST_FORWARDED_USER=true, INTEMPUS_ENABLE_WRITES=true, INTEMPUS_AUDIT_LOG=/data/audit.jsonl, INTEMPUS_OPERATION_SECRET, and MCP_HTTP_TOKEN (shared with the gateway).

Deployment

One container (INTEMPUS_PROFILE=roles) behind an authenticating MCP gateway. The gateway maps Entra security groups to roles (X-MCP-Roles):

Role

Entra group

employee

one group per company, e.g. employees

approver

approvers

admin

admins

duty group for intempus_commit_prepared_operation (toolGroups)

admin commit

Group membership must be direct (the app emits ApplicationGroup claims; nested groups are not included), and each group must be assigned to the gateway's enterprise app. Each person's Entra UPN must match their Intempus username or work email (or be mapped with INTEMPUS_IDENTITY_MAP).

Run

npm install
npm run dev          # stdio (acting user from INTEMPUS_DEFAULT_USER)
npm run dev:http     # streamable HTTP on :3000/mcp (stateless)
npm test
INTEMPUS_PROFILE=admin SMOKE_USER=you@example.com npm run smoke:live   # read-only; roles: employee | approver | admin   # read-only, against the real API

Docker images: ghcr.io/borgels/mcp-server-intempus (published on push to main).

Available Tools

11 tools
intempus_delete_my_work_reportDelete My Time Registration (Intempus)A
Destructive

Delete one of your own time registrations. Only possible while it is neither approved nor locked.

Inputs: workReportId (required).

intempus_get_my_balancesMy Balances (Intempus)A
Read-onlyIdempotent

Your balances (ferie, feriefri, timebank/flex, sygdom …) as computed by Intempus up to a date (default today). Holiday accrues at month end, so pass to = the end of the holiday year to see the full entitlement. Only balances visible to employees.

Inputs: to, from.

intempus_get_my_planningMy Planning (Intempus)C
Read-onlyIdempotent

Your planned work in a period (default: the next 14 days).

Inputs: to, from.

intempus_get_my_profileMy Profile (Intempus)A
Read-onlyIdempotent

Your Intempus employee record, contracts (work model, locked date) and approver levels.

Inputs: no named parameters.

intempus_list_casesList Cases (Intempus)B
Read-onlyIdempotent

Search cases/projects that accept time registrations, by number, name or customer.

Inputs: limit, query, includeInactive.

intempus_list_my_work_reportsMy Time Registrations (Intempus)B
Read-onlyIdempotent

Your time registrations in a period (default: last 31 days), with approval status and total.

Inputs: to, from, limit, approved.

intempus_list_work_typesList Work Types (Intempus)A
Read-onlyIdempotent

Work types (projekttimer, ferie, sygdom, kørsel …). Employees and approvers get the types of their own work model; admins may filter by work model or employee.

Inputs: query, employeeId, workModelId.

intempus_register_timeRegister Time (Intempus)A

Register time for yourself. Give a work type, optional case, a date and either startTime+endTime (optionally breakHours) or hours. Checks that you have a contract on the date, that the work type belongs to your work model and that the case is open. Pass idempotencyKey to make a retry safe.

Inputs: date (required), workTypeId (required), hours, caseId, +7 more.

intempus_search_capabilitiesSearch Intempus CapabilitiesA
Read-onlyIdempotent

Search the Intempus MCP server capabilities and examples. Use this first when deciding which tool to call.

Inputs: limit, query.

intempus_update_my_work_reportEdit My Time Registration (Intempus)A

Change one of your own time registrations. Only possible while it is neither approved nor locked.

Inputs: workReportId (required), date, hours, caseId, +7 more.

intempus_whoamiWho Am I (Intempus)B
Read-onlyIdempotent

Show this endpoint's profile and the Intempus employee (and approver levels) the session is bound to.

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 11 tools

    Disambiguation4/5

    Most tools have clearly distinct purposes (list work types, list cases, register/update/delete time, balances, planning). However, intempus_whoami and intempus_get_my_profile overlap in showing employee/approver info, which could cause an agent to pick the wrong one.

    Naming Consistency4/5

    All tools use the intempus_ prefix and snake_case, with a mostly consistent verb_noun pattern (list_work_types, get_my_balances, register_time). The outlier is intempus_whoami, which does not follow the verb_noun format, but this is a common exception.

    Tool Count5/5

    11 tools is well within the ideal range and each tool maps to a distinct operation within the employee self-service domain (time registration CRUD, profiles, balances, planning). No tool feels redundant or missing at the count level.

    Completeness4/5

    Core employee workflows are covered: time registration create/read/update/delete, work types, cases, balances, and planning. Approver-specific operations (e.g., approving/rejecting reports) are absent despite approver levels being mentioned, and there is no single-report retrieval, but these are minor workable gaps.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers