* feat(mcp): make the agent surface self-describing (staging _meta, company identity, clean summaries)
A pass over the MCP server's agent-facing surface so an agent can act
correctly without parsing description prose:
- Machine-readable staging contract: deriveToolMeta() attaches _meta to
tools/list (and search detail=full) — { requires_approval, approve_tool,
preflight? } — keyed off the STAGED_OPERATION_SCHEMA output schema. Literal
_meta (e.g. UI widget hints) wins on collision. TOOL_PREFLIGHT_MAP names the
read-only pre-flight for the few writes that have one (year-end readiness,
VAT validate, depreciation proposal). Guarded by staging-meta.test.ts.
- Company identity in gnubok_get_agent_briefing: returns a `company` block
(id, name, org_number, entity_type, accounting_method) so the agent can
confirm WHICH entity it operates on and pick the right settlement account
(accrual = credit 1510; cash = debit 19xx) before any write. Best-effort —
a missing row never blocks the briefing. Covered by agent-briefing.test.ts.
- toSummary(): trims the long, keyword-stuffed SKILL.md frontmatter into clean
one-liners for gnubok_list_skills / gnubok_get_agent_briefing so the client
never truncates one mid-sentence; full bodies stay in gnubok_load_skill.
Covered by to-summary.test.ts.
- bank-reconciliation skill: a match/link decision tree (what you have x
whether a verifikat exists) and kontant- vs faktureringsmetoden settlement
accounts.
- Prose/description clarifications: "Stages"/"Stages for approval" on the
link tools; propose_dispositioner/accruals note there is no dedicated MCP
poster; server-info documents _meta and the legacy gnubok_ tool prefix.
All 34 touched MCP tests pass. Merged cleanly on top of #759/#760 (server.ts).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(mcp): make accounting_method description state the full settlement posting
Review (swedish-accounting-compliance): the agent-briefing schema described
accrual as "credit 1510 on payment", which reads as a one-sided entry. Spell
out both sides (payment debits 19xx AND credits 1510) so an agent can't infer a
single-leg posting that violates BFL 5 kap double-entry. Mirrors the precision
already in the bank-reconciliation skill body. Payload-size guard still passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
57 lines
2.4 KiB
TypeScript
57 lines
2.4 KiB
TypeScript
import type { SupabaseClient } from '@supabase/supabase-js'
|
|
import type { Skill } from './types'
|
|
import { monthEndCloseSkill } from './month-end-close'
|
|
import { quarterlyVatReviewSkill } from './quarterly-vat-review'
|
|
import { yearEndCloseSkill } from './year-end-close'
|
|
import { invoicingRulesSkill } from './invoicing-rules'
|
|
import { payrollMonthlySkill } from './payroll-monthly'
|
|
import { bankReconciliationSkill } from './bank-reconciliation'
|
|
import { kreditfakturaProcessSkill } from './kreditfaktura-process'
|
|
import { customerOnboardingSkill } from './customer-onboarding'
|
|
import { loadAtomsAsSkills, loadReferenceById } from './atoms'
|
|
|
|
/** Static workflow skills the server ships with. Tier: 'workflow'. */
|
|
export const workflowSkills: Skill[] = [
|
|
monthEndCloseSkill,
|
|
quarterlyVatReviewSkill,
|
|
yearEndCloseSkill,
|
|
invoicingRulesSkill,
|
|
payrollMonthlySkill,
|
|
bankReconciliationSkill,
|
|
kreditfakturaProcessSkill,
|
|
customerOnboardingSkill,
|
|
]
|
|
|
|
/** @deprecated Use `workflowSkills` for the static set, or `loadAllSkills(supabase)`
|
|
* for the unified list (workflows + atoms). Kept for backwards compatibility
|
|
* with prior imports. */
|
|
export const skills = workflowSkills
|
|
|
|
/**
|
|
* Resolve a skill by slug. Checks the static workflow array first (synchronous,
|
|
* always available), then falls back to the registry-backed atom set
|
|
* (asynchronous, supabase-bound).
|
|
*/
|
|
export async function findSkill(slug: string, supabase?: SupabaseClient): Promise<Skill | null> {
|
|
const wf = workflowSkills.find((s) => s.slug === slug)
|
|
if (wf) return wf
|
|
if (!supabase) return null
|
|
const atoms = await loadAtomsAsSkills(supabase)
|
|
const atom = atoms.find((s) => s.slug === slug)
|
|
if (atom) return atom
|
|
// Reference children (e.g. "horizontal/swedish-vat/vat-compliance-reference")
|
|
// are excluded from the listed atom set above, so resolve them directly. This
|
|
// is what makes a SKILL.md footer's gnubok_load_skill(<reference id>) work.
|
|
return loadReferenceById(supabase, slug)
|
|
}
|
|
|
|
/** Workflow skills + registry-loaded atoms in one list. */
|
|
export async function loadAllSkills(supabase: SupabaseClient): Promise<Skill[]> {
|
|
const atoms = await loadAtomsAsSkills(supabase)
|
|
return [...workflowSkills, ...atoms]
|
|
}
|
|
|
|
export type { Skill, SkillTier } from './types'
|
|
export { SKILL_MIME_TYPE, SKILL_URI_PREFIX, skillUri, skillSlugFromUri } from './types'
|
|
export { loadAtomsAsSkills, toSummary, __resetAtomCache } from './atoms'
|