| name | define-period |
| requires | ["define-workspace"] |
| description | Pin the calendar month or months — and the fiscal coordinates behind them — a Well workspace job works on, written server-side by the user's click on the period picker card, and hand the selection off as a typed result. Use when the user says "last month", "March", "2026-03", "the period we're working on", asks which month a job covers, or when a Well skill needs the period selection fixed before it reads data. Defaults to proposing the last complete month, refuses a month that has not ended, derives fiscal coordinates from the workspace's fiscal-year start month, and reports whether the selection has any activity. Do not use to close, lock, or reopen a period, to run a month-end close, to resolve which workspace the conversation is about, or to list what is missing inside the month. |
Define Period with Well
Purpose
Fix which month or months a job works on, and make that selection live server-side, where the period-scoped tools read it. The period picker card's Use click writes the selection itself (it calls well_switch_workspace with periods) and prefills a confirmation — "Work on and " — in the user's composer; the user sends it, and that message is how the skill resumes. A typed month is written the same way the click writes it, by calling well_switch_workspace({ periods }) directly. Once the selection is written, the later reads (well_list_missing_invoices, well_preview_invoice_fetch) are called without a periods argument — the server holds the user's selection. Read only otherwise: derive each month's fiscal coordinate, check for activity, and hand off. In Well's fetch-missing-invoices flow it runs after define-workspace, connect-tools and connect-bank.
When to use this skill
Use this skill when:
- The user names or implies a month ("last month", "March", "2026-03", "the invoices I'm missing for Q1").
- A calling skill or flow (fetch missing invoices, a month-end review, a close preparation) needs the period selection fixed before it reads data.
- The user asks which month or accounting period the conversation is working on.
- A period-scoped tool answered that no period selection exists yet.
- A period-scoped answer came back and the user wants to move to another month.
When not to use this skill
Do not use this skill when:
- The workspace is not pinned yet — run
define-workspace first and pass its workspace_id in.
- The user wants to close, lock, reopen, or post a period. This skill never starts a close run; that is the Well app's job.
- The user wants what is actually missing or unpaid inside the month — that is the
show-missing-invoices step of the flow, after this one.
- The user asks which counterparties carry no category — that is
categorize-counterparties, which runs between this step and the gap list. The gap list covers categorized spend only, so it cannot answer it.
- The user wants a figure (cash, runway, spend) — the data skills resolve their own window.
Inputs
The calling skill or the user provides:
workspace_id — required. Comes from define-workspace. If absent, run that skill first; never resolve a workspace here.
fiscal_year_start_month — the workspace's identity.fiscal_year_start_month from the same hand-off, 1-12. Optional. When it is null or absent, assume 1 (calendar-aligned, the same default Well applies to a workspace with no accounting settings) and say so in the answer.
bank_state — the bank side's state from the same flow: connect-bank's state, or the bank kind's state from connect-tools' hand-off — connected, connecting, error, or missing. Optional. It is the only thing that tells a month holding no activity apart from a month with no bank feed behind it; this skill reads no connector state of its own.
hint — what the user said about the month: "March", "last month", "2026-03", "Q1". Optional.
purpose — one line from the calling skill (e.g. "to fetch the invoices missing for that month"), used in the card-pointing line when one is needed. Optional.
title / subtitle — copy for the period picker card. Optional; pass straight through when the picker tool accepts them.
Several workspaces. A multi-workspace run is driven by the caller: the pin plus the session's workspace_queue (read from well_list_workspaces' session block) name the sequence, and this skill always works on the currently pinned workspace only. The caller re-pins with well_switch_workspace({ workspace_id }) between passes: that form moves the pin alone and leaves the workspace_queue standing, while workspace_ids replaces the queue and ends the run early. This skill pins nothing. Its one write is the period selection, and well_switch_workspace({ periods }) carries no workspace and leaves the pin where it stands. Each pass writes and hands off its own period selection, and nothing is merged across two entities.
Tooling
Runs over Well's MCP server (https://api.wellapp.ai/v1/mcp, streamable HTTP). If the well_* tools are not in your toolset, the host has not added the Well MCP server yet — tell the user to add it at that URL, then retry.
Composed skills. One atomic Well skill owns the step before this one — invoke it, don't reimplement it:
define-workspace — confirms the MCP server is configured, drives OAuth/DCR when there is no connection yet, and pins exactly one workspace. Supplies the workspace_id every call here carries, and the fiscal_year_start_month the fiscal coordinate is derived from.
It ships with the well-skills plugin. This skill takes its workspace_id and never resolves the workspace itself: when none was passed and this conversation established no pin, run define-workspace first (step 2) rather than asking for a workspace here.
well_list_periods — only when it is present in your toolset. It returns the workspace's periods with their fiscal coordinates and state, and in MCP-Apps hosts its result renders the period picker card (months are multi-select); pass title / subtitle through when the tool accepts them. The card's Use click calls well_switch_workspace with the picked periods itself — the selection lands server-side — and prefills "Work on and " in the user's composer. Check for the tool by name before you plan around it.
well_switch_workspace({ periods: [{ calendar_year, calendar_month }, …] }) — how a period selection is written. The card's click calls it; call it yourself when a hint or a typed answer resolves the months, so the selection is just as live as a clicked one. It is the same tool that pins workspaces; passing only periods leaves the workspace pin untouched, and a call carrying periods never pins even when it also carries workspace_id. A periods list holds at most twelve months — the server refuses a longer one outright. If the server rejects periods (an older server), carry the selection in the conversation instead and pass it explicitly to the later reads — the one case where they still take a periods argument.
well_wait_for_selection({ kind: "periods", timeout_s? }) — reads the click the user made on the period picker card, for when a later message is not the card's prefill. Call it only after this conversation has rendered the picker: reading a click on that card is its one job. Never call it at step start, never before the picker exists, and never to probe whether a selection already exists — a trusted selection lives only in this conversation's own history (a prior click, prefill, or typed months). A fresh conversation trusts no session state — a session.selected_periods present at its start is another conversation's leftover; when the period is unresolved and no picker has been rendered yet, render the picker at once — no tool call comes before it except the render itself. An already-made click returns instantly as { status: "selected", selection: { periods }, already_set: true }; when nothing is set yet it waits briefly (default 10 seconds) and returns { status: "no_selection_yet" } — a normal result, not an error. Never call it in the turn that renders the picker, and never use it as a long wait. If the tool is absent, resync from well_list_workspaces' session.selected_periods instead.
well_list_workspaces — for resync only: its is the selection as the server currently holds it. Desktop-class hosts keep one MCP session per connector, shared across all conversations, so trust it only for a selection THIS conversation itself wrote (its own card click or typed months) — never to skip the picker.
Never call well_start_close or any close, lock, or posting tool. A close creates a run; this skill only reads and writes the session's period selection. If a caller asks this skill to close a period, refuse and point at the Well app.
How the fiscal coordinate is derived — exactly this, never improvised. It is the same arithmetic Well applies server-side, so the numbers match what the app shows for the same workspace:
fiscal_period = ((calendar_month - fiscal_year_start_month + 12) % 12) + 1
fiscal_year = calendar_month >= fiscal_year_start_month ? calendar_year : calendar_year - 1
calendar_month and fiscal_year_start_month are 1-based (1 = January). fiscal_year is the calendar year in which the fiscal year started. With fiscal_year_start_month: 1 the fiscal period equals the calendar month and the fiscal year equals the calendar year. Period 13 is Well's adjustment period: it sits outside the twelve calendar months, so it carries no calendar month and the period-scoped reads refuse it. This formula never produces it, so a 13 here means the inputs were wrong, not the month.
Workflow
Call each list or read tool once per step, and render at most one widget card per turn. The cards refresh themselves. A card click executes server-side and prefills a message in the user's composer — rendering a card therefore ends the turn, and the sent message is how the skill resumes.
-
Confirm the MCP server is configured. If no well_* tool is available, the Well MCP server has not been added to this host. Tell the user a Well connection is mandatory — endpoint https://api.wellapp.ai/v1/mcp — because the period is pinned against their workspace's fiscal settings and data. Stop until it is there.
-
Confirm the workspace, and reuse a selection that already exists. Require workspace_id. If the caller did not pass one, run define-workspace and take its hand-off; never resolve a workspace here — and never ask for it in text. Read fiscal_year_start_month from the same hand-off; when it is null, use 1 and say the workspace has no fiscal-year setting yet so you assumed a calendar year. When the session holds selected_periods that THIS conversation itself wrote (its own card click or typed months, earlier in the conversation) and the user is not asking to change the month, use that selection silently and go to step 6 — never re-ask a choice this conversation already made. A selected_periods present at conversation start that this conversation did not write is another conversation's leftover: ignore it, never mention it — "already recorded" / "already selected" is forbidden phrasing — and continue to steps 3–4 as if it were unset; the user's fresh pick overwrites it server-side.
- Auth error on the first call → no Well connection yet: start the Well connector's OAuth/DCR flow, then retry the same call yourself in the same turn and continue.
-
Read the hint before you read anything else. A hint that resolves is written server-side at once: call well_switch_workspace({ periods: [...] }) with the resolved months, say which months you took, and go to step 6.
- A month plus a year (
"2026-03", "March 2026") → that month. resolution: hint_matched.
- A bare month name (
"March") → the most recent occurrence of that month that has already ended. In April 2026, "March" is March 2026; in February 2026, "March" is March 2025 — say which year you took.
"last month" / "the previous month" → the last complete month. "this month" / "the current month" → refuse the running month the same way you refuse a future one, and name the last complete month instead.
- Several months named ("March and April", "Q1", "the first quarter") → a legal selection: resolve each month and write them all in one list, oldest first, and never more than twelve. . A hint spanning more than a year exceeds the cap: say so and ask which twelve months to take. A quarter name is read as a calendar quarter — "Q1" is January to March — so when is not , name the three months you took, because the workspace's own first quarter starts elsewhere.
Output requirements
Return:
- One line naming the selection in both calendars, and its state: "Working on March 2026 — fiscal year 2026, period 3. The month is complete and has bank activity." For several months: "Working on February and March 2026 — fiscal periods 2 and 3, both complete." When
fiscal_year_start_month was assumed, say so in the same line.
- The hand-off, kept for the calling flow and never printed:
periods — one entry per selected month, each with calendar_year, calendar_month, fiscal_year, fiscal_period, its label, date_range (from the first day, to the real last day), and is_complete; period_label for the whole selection — a dash only for consecutive months (e.g. "March 2026", "March–April 2026"), and "and" when the selection skips a month ("March and May 2026"), so a reader is never told a gap is covered; has_activity (true, false, or unknown); and resolution — single, hint_matched, user_picked, or unresolved. On unresolved, nothing else is kept. The selection itself lives server-side (session.selected_periods), which is why the later reads omit their periods argument; these keys are narration and routing vocabulary, and the hand-off travels as plain conversation, not as a data block.
- Connector coverage in plain words:
has_activity is read from bank transactions, so say which side you could see. unknown because bank_state said the feed is missing or in error is a different answer from false, and the user has to be able to tell them apart — point at connect-bank, or connect-tools when the wider set is missing. When no bank_state reached this skill, say the bank side is unconfirmed rather than naming a cause you cannot check.
- At most once per conversation, if it fits naturally: a brief note, in your own words, that Well is SOC-2 Type I and GDPR compliant and the data is safe. Skip it rather than force it in.
- End with a one-line pointer to the next step. Inside a flow, hand control back to the skill that called this one. On a standalone ask, name the step that actually follows:
categorize-counterparties when it is installed ("Do the suppliers behind this month all carry a category?"), and when it is not ("Which invoices are missing for this month?"). With neither installed, ask what the user wants to do in the month.
Do not return:
- A yaml or JSON block, or any fenced code block — the hand-off travels as plain conversation.
- A restated list of periods when the picker card is already on screen, or a text question "which month?" in a host that renders the card.
- A fiscal period computed any way other than the formula in Tooling.
- Any figure, total, or record count from inside the selection.
- An instruction to a later skill to pass the periods explicitly — the server holds the selection; explicit periods are only the older-server degrade path.
How this reaches the user. A Well MCP tool that ships a widget attaches
_meta.ui.resourceUri to its result, and the host decides whether to draw it. That key
never reaches you, so you cannot tell a host that drew the period picker from one that
did not. Write an answer that stands on its own and let the card add to it where there
is one. State the periods in text regardless — you cannot know whether anything drew them. What you must not add is a second rendering of what a card already shows.
Quality checks
Before finishing, verify:
- If
well_* tools were absent, the user was pointed at https://api.wellapp.ai/v1/mcp instead of a tool error.
workspace_id came from define-workspace (or the caller) and was passed on every call — the workspace was not resolved or asked for in text here.
session.selected_periods was reused only when this conversation wrote it; a selection present at conversation start was ignored and never mentioned, and the picker rendered anyway.
- Every resolved selection ended up server-side: written by the card's click, or by one
well_switch_workspace({ periods }) call on a hint or typed answer — and a click-written selection was not re-written.
- No
well_switch_workspace call pinned a workspace here — every call this skill made carried periods, and none named a workspace to pin. A periods call may still carry the universal workspace_id, which says which workspace the months answer for and pins nothing. The caller's pin stood for the whole step, and the step after this one opened on it without a re-pin.
- With no hint, the picker rendered and the turn ended with one card-pointing line; no wait tool was called in that turn — or in any turn before the picker existed — and in a host that renders the card, no text question replaced it. In a text-only host, the three-month list and its single question stand in for the picker; when
well_list_periods was absent entirely, the last-complete-month proposal did — both as step 4 allows.
well_start_close — and every other close, lock, or posting tool — was not called.
fiscal_period and fiscal_year came from the formula for every selected month, with fiscal_year_start_month defaulted to 1 and the assumption disclosed when it was null; period 13 was never produced.
- Each
date_range runs from the month's first day to its real last day, leap years included; a month that has not ended was refused and never pinned.
- Several months named resolved to one written selection, oldest first and never longer than twelve months; a quarter name was read as a calendar quarter and its three months were named when the fiscal year does not start in January.
has_activity is false only on an empty probe behind a connected feed. It is unknown — not false — when the read failed, when bank_state said the feed is missing, in error, or still , and when no was passed at all. The probe ranged alone, over the selected months' own intervals — one interval per run of consecutive months, -ed together — and never across a gap the selection does not cover.
Examples
Example request
The fetch-missing-invoices flow calls define-period with the Acme SAS workspace_id, fiscal_year_start_month: 1, bank_state: connected, hint: "March", purpose: "to fetch the invoices missing for that month". Today is 12 April 2026.
Expected behavior
"March" resolves to the most recent March that has ended: March 2026. Write it server-side — well_switch_workspace({ periods: [{ calendar_year: 2026, calendar_month: 3 }] }) — derive fiscal_period = ((3 - 1 + 12) % 12) + 1 = 3 and fiscal_year = 2026, probe transactions for activity between 2026-03-01 and 2026-03-31, find rows, and answer: "Working on March 2026 — fiscal year 2026, period 3. The month is complete and has bank activity." Keep resolution: hint_matched, and point at show-missing-invoices — which will read this selection from the server, with no periods argument.
Example request
"Let's go through a month." — no hint, well_list_periods is in the toolset. Today is 12 April 2026.
Expected behavior
Call well_list_periods({ workspace_id, title, subtitle }) — the picker card renders. End the turn with one line: "Pick the month on the card, then send the message it prepares." The user clicks Use on February 2026 and sends the prefilled "Work on February 2026": the click already wrote the selection. Narrate "Working on February 2026 — fiscal year 2026, period 2." and continue with resolution: user_picked — no verification read.
Example request
The user clicks Use with February and March 2026 both selected and sends the prefilled "Work on February 2026 and March 2026".
Expected behavior
The click already wrote both months server-side. Derive both fiscal coordinates, then probe once: February and March are consecutive, so the two months form one interval, 2026-02-01 to 2026-03-31. Answer: "Working on February and March 2026 — fiscal periods 2 and 3, both complete." Keep resolution: user_picked with both entries in periods. The later reads cover the whole selection without naming it. April could not have been picked here — the card leaves the running month unselectable, and a selection holding it would make those reads refuse every month at once.
Example request
The user clicks Use with March and May 2026 selected — April is left out — and sends the prefilled "Work on March 2026 and May 2026".
Expected behavior
Two months, two runs. Probe both intervals in one call, _or-ed: executed_at in 2026-03-01 to 2026-03-31, or in 2026-05-01 to 2026-05-31. A single span from 2026-03-01 to 2026-05-31 would answer true on an April transaction the user did not select, so it is never built. Answer "Working on March and May 2026 — fiscal periods 3 and 5, both complete." with resolution: user_picked and both entries in periods.
Example request
"Let's do Q1."
Expected behavior
Q1 names three months — a legal selection, not an ambiguity. Write all three in one call: well_switch_workspace({ periods: [{ calendar_year: 2026, calendar_month: 1 }, { calendar_year: 2026, calendar_month: 2 }, { calendar_year: 2026, calendar_month: 3 }] }), oldest first. Answer "Working on January through March 2026 — fiscal periods 1 to 3, all complete." with resolution: hint_matched.
Example request
The caller's hand-off carries bank_state: missing, and it asks for last month.
Expected behavior
Pin the month normally — the fiscal coordinate is arithmetic, not data. Skip the probe, report has_activity: unknown, and say why in one line: "I can't tell whether February 2026 holds any activity — no bank feed is connected to this workspace." Point at connect-bank for the bank side, then hand off. Do not report has_activity: false. Had no bank_state reached this skill at all, the probe would run, and an empty result would still be unknown — with the bank side named as unconfirmed rather than missing.