woffu-manager
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@woffu-managerHow many holidays do I have left, and when do they expire?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
woffu-adapter-mcp
An MCP server for Woffu: clock in the days you have left, see what time off you have and when it expires, and tell how much of your team is actually around next sprint.
Ask your agent, in whatever words you use:
"Which days haven't I clocked in this month?" — then confirm, and it fills them.
"How many holidays do I have left, and when do they expire?"
"Book me the 14th of August off" — then confirm, and it files the request.
"Who is off in August?" / "How much of the team is around next sprint?"
"Is Maria away the week of the 20th?"
"When is the next public holiday?"
Nothing is written to your timesheet or filed on your behalf without you seeing it first.
Tools
Read-only unless it says otherwise.
Tool | What it answers |
| Workdays with no hours logged, each with the exact time ranges that would be sent. Plus |
| Writes. Clocks in the days you name. Every one is re-checked against Woffu first, and refused with a reason rather than a blanket "not pending". |
| What is left in each bucket and when it expires — soonest first, because whatever is left in a bucket is lost on that date. |
| Your requests: |
| Writes. Files a request for a leave type, validated against Woffu first. Whole-day types take a date range; hourly types take a day and a time span. |
| Writes. Changes a request's dates, times or comment. The leave type cannot change — cancel and file anew for that. |
| Writes. Withdraws a request; a pending one is deleted and its balance returned. |
| The public holidays ahead. One costs no days from any bucket, and one next to a weekend makes a long weekend. |
| How many days each teammate is around over a range, and how many they are off. For sizing a sprint before committing to it. |
Clocking in
clock_in_days needs an explicit list of dates — there is no "clock in everything",
so you always see what is about to be logged. ranges overrides the schedule when you
want specific hours ([{"in": "09:00", "out": "13:30"}, ...]); omit it and each day
uses the theoretical schedule Woffu itself proposes, which is nearly always what you
meant. A weekend, a public holiday, an absence, a day already logged or one Woffu has
locked is refused by name, so you can tell which of those it was.
Working a public holiday is possible in Woffu, but only by filing a "work on a holiday" request. That is not implemented, so holidays are simply refused.
Requesting time off
request_time_off files a request for a leave type by name — the same names
get_time_off_balance lists ("Vacaciones", "Horas Ajuste Calendario"...). Whole-day
types take from and an optional to; the day count is whatever Woffu charges after its
own weekends and holidays, so a Friday-to-Monday range costs two days, not four. Hourly
types take a single from with startTime and endTime instead.
Every request is checked against Woffu before anything is written: an overdrawn balance, a weekend or a public holiday is refused with a reason rather than filed. Types that need a supporting document, and half-day requests, are out of scope — file those in Woffu directly.
edit_time_off_request and cancel_time_off_request both take a requestId from
list_time_off_requests. Editing changes dates, times or the comment; the leave type is
fixed, so to change that you cancel and file a new one. Cancelling a pending request
deletes it and returns the balance it held.
Team availability
Per person: workingDays in the range (weekends and public holidays already out),
daysOff already approved, pendingDaysOff still awaiting approval, and
availableDays — working days minus approved time off. totals carries the whole
team's capacity in person-days. Least-available first, since those are the people who
constrain the sprint.
Pending absences are not subtracted from availableDays: they are the risk, not the
plan, so they are reported next to it. Woffu does not count them as absent either.
It reads your own team by default — no id, no name, nothing to configure. Name
another team to look at it instead, or a person to narrow the answer to one of them.
Related MCP server: woffu-mcp
Install
Nothing to download. The server is published to npm, so npx fetches and runs it — the
package name is the whole prerequisite:
claude mcp add woffu \
-e WOFFU_HOST=yourcompany.woffu.com \
-e WOFFU_USERNAME=you@company.com \
-e WOFFU_PASSWORD='...' \
-- npx -y woffu-adapter-mcpnpx -y woffu-adapter-mcp always pulls the latest published version. Pin it if you would
rather it did not move under you: woffu-adapter-mcp@1.0.0 takes any published version.
Configuration
Variable | Required | Example | What it is |
| Yes |
| Your Woffu domain. Paste the whole URL if it is easier; only the host is kept. |
| Yes |
| Your Woffu login. |
| Yes |
| Used once, to request a token. Never stored. |
| No |
| Only to make |
| No |
| The office time zone, any IANA name. It decides which instant a 09:00 clock-in lands on. Defaults to |
The password is only used to request a token and is never stored. The token is cached,
in ~/.woffu-manager/token.json (mode 600), and Woffu issues it with a one-year lifetime
— so logging in happens once. Delete that file to force a new login.
GitHub Copilot, in VS Code
Put this in .vscode/mcp.json (or the user-level one, via MCP: Open User Configuration), then hit the Start action the file shows above the entry. Copilot
says servers, not mcpServers, and a local server has to declare "type": "stdio".
The password goes in as an input, so VS Code asks once, masks it and keeps it in the OS keychain — which means the file can be committed with the rest of the project without a password in it:
{
"inputs": [
{
"type": "promptString",
"id": "woffu-password",
"description": "Woffu password",
"password": true
}
],
"servers": {
"woffu": {
"type": "stdio",
"command": "npx",
"args": ["-y", "woffu-adapter-mcp"],
"env": {
"WOFFU_HOST": "yourcompany.woffu.com",
"WOFFU_USERNAME": "you@company.com",
"WOFFU_PASSWORD": "${input:woffu-password}"
}
}
}
}The tools show up under the 🛠️ icon in Copilot Chat, in Agent mode.
Any other MCP client
{
"mcpServers": {
"woffu": {
"command": "npx",
"args": ["-y", "woffu-adapter-mcp"],
"env": {
"WOFFU_HOST": "yourcompany.woffu.com",
"WOFFU_USERNAME": "you@company.com",
"WOFFU_PASSWORD": "..."
}
}
}
}Clone it only to work on it:
git clone git@github.com:alechagonzalo/woffu-adapter-mcp.git
cd woffu-adapter-mcp
npm install && npm testWhat counts as "pending"
Two layers, because neither is enough on its own:
From the month summary: weekends, holidays, events, absences (
absenceEvents/pendingAbsenceEvents: leave, adjustment days, sick leave), future days and days with hours already worked are all excluded.isUserEditableis the hard gate — Woffu refuses writes wherever it isfalse.From the day's slots: a day that was clocked in but not yet confirmed still shows zero hours in the summary. What gives it away is a real
signIdon its slots. Without this second check the same day would be clocked in twice.
Today is included, even mid-workday: its full theoretical schedule gets logged.
The Woffu API, in case it needs revisiting
POST /api/svc/accounts/authorization/token— OAuth2 login (grant_type=password). ReturnsaccessToken(a JWT carrying theUserId, which is where we read it from) andexpiresIn.GET /api/svc/core/diariesquery/users/{id}/diaries/summary/presence— days in a month.GET /api/svc/core/diariesquery/diarysummaries/{diarySummaryId}/workday/slots/self— a day's exact time ranges. The summary only gives first entry, last exit and total break; it never says where the break falls. Only this endpoint knows a workday is 09:00–13:30 plus 14:00–17:53. The slots come back withsignId: 0: they are the proposal the UI preloads, not actual clocked time.PUT /api/svc/core/users/{id}/diarysummaries/workday/slots/self— saves. Times travel in UTC,signTypegoes from 1 (proposal) to 3 (manual clock-in, which is how Woffu displays them), andsignStatusto 1. It answers 200 with an empty body.GET /api/users/calendar-events/next— upcoming holidays.Typeis 2 on every one seen so far; other values are surfaced rather than assumed to be holidays.GET /api/user-agreement-events/availability— time-off balance per bucket. Carries no dates.POST /api/users/allocations/row— the allocations behind one bucket, and the only place the expiry date lives. Body:{agreementEventId, pageIndex, pageSize, createdOnFilter: {filterType: "CurrentCycle"}, affectedCycleFilter: "CurrentCycle"}. The cycle filter matters:/allocations/tablereports across cycles and disagrees with the availability endpoint (12 days vs 9), while the current cycle agrees.GET /api/users/{id}— the profile.DepartmentIdis the user's team, and it is the same number the calendar wants as anEntityId, so nobody has to configure their own team.DepartmentNamecomes back empty; only the calendar spells the name out.GET /api/scope/search?query=&types=999678— resolves a team name to the{EntityId, EntityType}pair the calendar wants; a team answers withEntityType32.typesis a bitmask over the scopes the calendar's own search box asks for.POST /api/svc/core/calendar— the shared calendar: one entry per person, one per day, already clipped to the range.visibilitiesis not optional: with an empty list Woffu answers with the whole company rather than the team.keywordmatches a person's name.requestStates: 3brings back approved and pending absences. No total comes back, so paging runs until a short page.eventDuration/absenceEventsDurationare an enum, not a count of days:1is a full day and2is an hours-only absence (isTimeOnly: true,startDate"09:00" rather than a date). Summing them books a half-day as two days off —isTimeOnlyis what tells them apart. Absences do land on weekends and holidays, and cost nobody a day.presenceEventsis telework: the person is still available.POST /api/svc/core/calendar/totals-simple— presents/absents per day. Unused: it counts every weekend as "everyone absent". The per-person calendar above agrees with it exactly on working days, once the same absences are counted.GET /api/users/requests/list?statusType=— time-off requests.statusTypeis 10 pending, 20 approved, 30 denied; it is required, so all three are fetched to list everything. Amounts arrive as{Resource, Values}, where the resource name is the only hint at the unit, and hours come split as["8", "30"]meaning 8h30m.
scripts/inspect.mjs /api/some/endpoint dumps any endpoint using the cached token —
useful when reverse-engineering a new surface.
npm test covers the filters, the day-counting rules, and that the payload matches,
field for field, the one the web app sends.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/alechagonzalo/woffu-adapter-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server