main
13 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
678acfe7ef |
fix(mcp): eager-auth flag so claude.ai's connector dialog detects OAuth, not "None" (#2159)
claude.ai's two-step "Add custom connector" dialog probes the server URL without credentials and pre-fills the Authentication choice from the answer. Our lazy-auth endpoint (issue #1814) answers 200 on an anonymous initialize, which the dialog reads as an authless server: it suggests "None", and a connector added with that default never opens the sign-in when the challenge arrives later. Per Anthropic's connector docs a 401 is the only answer it reads as OAuth ("Claude does not honor a WWW-Authenticate header on a 200 response"). - `auth=required` on the endpoint URL (extensions/general/mcp-server/ auth-mode.ts) turns lazy auth off for that URL: every tokenless request, initialize included, answers the 401 + WWW-Authenticate challenge. Callers with a token are unaffected; the bare URL keeps lazy auth for Claude Code, the plugin, Cursor and ChatGPT, and existing connector records are untouched. - The links we control carry the flag: Settings -> API & MCP (install link and copy block), the onboarding checklist, both docs pages and claude-plugin/CONNECTORS.md (plugin 1.2.3). The docs' Path A now describes the eager flow (sign-in opens on Add) instead of telling users to override the dialog's "None". - Tests: eager-auth.test.ts (401 on initialize/tools/list/public tools, namespaced metadata pointer, token no-op, exact-flag only); checklist link shape updated. Companion: gnubok-website PR (Kom igång connector link + regenerated connect-claude / anslut-claude pages). Claude-Session: https://claude.ai/code/session_013yw62FMXGSzo6icFDiBwP3 Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
b160f7290d |
docs(plugin): starter prompt nudges the connector's onboarding guide (#1965)
Several E2E runs composed the first reply from tool descriptions before the skill loaded (questionnaire instead of the guided round). Four words in the published prompt point the agent at the guide from message one; the rest of the prompt stays memory-first and universal. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
cd9fb5b717 |
feat(mcp): byte-exact SIE upload path + brevity and memory-first onboarding (#1954)
From the fourth E2E run: the agent correctly refused to reproduce a 104 KB SIE file token by token (silent mid-verifikat truncation) and dead-ended to the web wizard, and its replies were walls of compliance prose. 1. gnubok_create_sie_upload: signed same-origin upload URL (reuses the pending-document infra; .se/.sie/.si only, 50 MB HTTP cap). gnubok_sie_preflight and gnubok_import_sie accept upload_id as the byte-exact source, plus optional sha256 (hex of the raw bytes) verified on the upload_id/base64 paths so truncation is DETECTED, never silent. Inline content above 120k chars is refused with a pointer to the upload flow. Scope bookkeeping:write (same intent as import_sie). 2. Skill: brevity rule (max ~8 short lines per reply, one warning per step, no legal essays), memory-first rule (check what is already known before asking the opening questions), the upload-first SIE step, and gnubok_explain_voucher_gap after import for skipped voucher numbers. 3. CONNECTORS.md starter prompt rewritten memory-first so it stays copy-paste ready without the user's own data in it. Plugin v1.2.2. tools/list ceiling 62K to 62.4K documented in the bench. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4af7469523 |
feat(onboarding): minimal input: orgnr + moms period is the whole ask (#1952)
* feat(onboarding): minimal input: orgnr + moms period is the whole ask
Two fixes from the third E2E attempt (2026-08-26):
1. accounting_method is now optional in CompanySetupSchema and defaults by
form in planCompanySetup: aktiebolag = accrual (the norm), enskild
firma = cash (the common small-EF choice; legal under 3 MSEK, BFL 4
kap 4 paragraf). The plan flags the default (resolved.accountingMethodDefaulted)
and gnubok_create_company's preview carries accounting_method_defaulted
so the readback names it and the user overrides in the same 'ja'.
Never silent: the preview is the checkpoint. Applies to the MCP tool
and POST /api/v1/companies (additive; response shows the resolved
value). The lookup tool's still_to_ask no longer lists it.
2. The agent refused a real orgnr because the user said 'nytt bolag' and
the registry showed an established company ('Stopp. Numret matchar
inte ett nytt bolag'): lookup instructions now state that an
established company with F-skatt/VAT is the NORMAL case (new = new to
Accounted) and the orgnr is never second-guessed for looking
established.
Skill + plugin (v1.2.1) updated; API skill regenerated; DECISIONS.md entry.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(onboarding): surface the kontantmetod 3-MSEK condition on the defaulted cash method
Compliance-review finding on #1952: the EF cash default carries a legal
eligibility condition (turnover normally under 3 MSEK, BFL 4 kap 4 §)
that a client not reading the onboarding skill would never see. The
create preview now carries accounting_method_note with the condition
whenever cash was defaulted, and the v1 pitfall states it for API
integrators. The registry cannot verify turnover, so the confirm-time
human check is the gate; the default itself stays (a brand-new EF has
zero turnover by definition).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
e7e4efbfbc |
feat(oauth): one-click consent: all scopes pre-selected, list collapsed, Allow above the fold (#1953)
The read-only default forced every agent-first user to scroll a scope
list and hand-tick write rows before the flow could work. Founder call
2026-08-26: pre-check ALL scopes when the client requests none (Claude's
connector case), collapse the scope list into an expandable details fold
('Alla förvalda, visa och justera'), and keep the Allow button visible
without scrolling.
Why this is defensible: every write is STAGED for explicit approval
before anything touches the ledger, each scope row stays individually
untickable inside the fold, the warn line states the staging rule right
above the button, and the grant is revocable under Inställningar >
API-nycklar. A client that requests explicit scopes still gets exactly
that set (RFC 6749 3.3 least-privilege unchanged), and the tampered/empty
POST fallback stays read-only.
CONNECTORS.md gains the share link (connectorName/connectorUrl params)
plus the starter prompt to pair with it.
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
188816652d |
docs(api,mcp): tool counts, changelog backfill, version-header honesty, lazy auth, endpoint map (#1929)
Brings the developer-facing API and MCP docs back in line with origin/main (audit 2026-08-26). Docs only; no runtime behaviour changes. - Tool counts: the server registers 153 tools; docs said 90+/100+/120. All now say "150+" (connect-claude, gnubok-mcp README, plugin README, mcp-server rules, CLAUDE.md, registry entry with refreshed updatedAt). Not derived from the tools array: lib/ must not import @/extensions/. - REST changelog: backfilled the additive 2026-08 changes (#1909 report date ranges + PDFs, #1864 POST /companies, #1773 vat-declarations, #1405 PATCH settings, #1724/#1788 customer personal_number, #1809 cash_account_id filter). API version date unchanged. - Version headers: Gnubok-Deprecation is planned, not emitted; the Gnubok-Version request header is not read today (version.ts comment, versioning page, conventions overlay, regenerated skills/accounted-api). - connect-claude Path A documents lazy auth (connector works before an account exists; sign-in on the first company-scoped call). - MCP server README: real Anthropic SDK call sites, real resource URIs, pending-operations widget, public-tools/tasks/origin-guard/pii-guard. Rules file gains Lazy auth + feedback/tasks paragraphs. - api-routes endpoint map regenerated from the filesystem (560 routes, 55 families incl. v1, agent, reconciliation account-keyed, dimensions, peppol, rot-rut, webshop-orders, mileage, billing, skatteverket, receipt-hunt). - gnubok-mcp/accounted-mcp: /settings?tab=api is the pre-redesign URL; now /settings/api (README + help hints, no version bump). Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
8ddc77fdfd |
feat(mcp): org-number-first onboarding: gnubok_lookup_company prefills the company from the registry (#1940)
The onboarding flow now mirrors the web wizard: ask for the organisationsnummer first, look the company up in the public registry (one TIC Lens call through the extracted extensions/general/tic/lib/lookup.ts, shared with the /lookup HTTP route), and present the facts for confirmation instead of interrogating the user. The new gnubok_lookup_company tool (companies:read, company-independent, default catalog) returns the registry facts, a prefilled suggested_create_company_input, and a still_to_ask list that encodes the same fact-vs-question rules as lib/onboarding-journey/reducer.ts: F-skatt is a fact both ways, VAT is a fact only when positively registered (ML 17 kap 24 paragraf), moms period and accounting method are always asked, an enskild firma's verksamhetsnamn is the user's choice, and a known fiscal year becomes a confirm question. Registry outages degrade to the full question list instead of failing onboarding. The onboarding skill and the plugin's /accounted:setup command are updated to the orgnr-first flow (plugin 1.2.0). tools/list ceiling bumped 61.2K to 61.5K with the reason documented in the bench. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a1af9adb05 |
docs(connector): name the Claude.ai auth mode and OAuth client to pick (#1914)
Claude.ai's Add custom connector dialog auto-detects Authentication
"None" for a server that answers the handshake without credentials,
which is exactly what lazy auth does; a user who accepts that default
gets an error instead of the sign-in on the first company-scoped call.
State the two correct choices ("Required when the server asks", DCR
client registration) in the bridge README and the plugin's CONNECTORS.md.
Claude-Session: https://claude.ai/code/session_018wCdzRTatKiDByKB8hCNT6
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
159823583c |
feat(plugin): /accounted:setup command, CONNECTORS.md, v1.1.0 (#1902)
The Claude plugin is the one-click install for Cowork and Claude Code, so it should also be the entry to agent-first onboarding (#1814). /accounted:setup connects the bundled connector (creating the account on the sign-in screen if needed), hands off to the server-side onboarding skill when the account has no company, then the bank and Skatteverket links. CONNECTORS.md documents the single bundled connector the way Anthropic's own plugins do. Version 1.1.0 so marketplaces that sync on version bumps pick it up. The plugin-refs guard now also validates commands/*.md against the server. Claude-Session: https://claude.ai/code/session_018wCdzRTatKiDByKB8hCNT6 Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e92365b86e |
feat(mcp): distribution polish for agent-first onboarding: CIMD, plugin start skill, bridge hint (#1814 PR 4) (#1866)
* feat(mcp): distribution polish for agent-first onboarding: CIMD, plugin start skill, bridge hint Fourth PR of agent-first onboarding (#1814). - The OAuth AS metadata advertises client_id_metadata_document_supported next to the existing `none` token auth, the pair Claude.ai, Claude Code and Codex look for to use CIMD instead of registering a DCR client per connection. authorize/token never keyed on client_id (the redirect-URI allowlist is the trust boundary), so nothing else changes; DCR stays for ChatGPT. - The plugin's start skill no longer sends a user without an account to the website: the /mcp OAuth screen creates the account, and a NO_COMPANY_YET briefing failure routes to the onboarding skill and accounted_create_company. README updated to match. - `npx accounted-mcp` without ACCOUNTED_API_KEY prints the OAuth alternative (Claude Code, Codex, Claude.ai connector) and that the account can be created on the sign-in screen; package README too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wCdzRTatKiDByKB8hCNT6 * fix(oauth): do not advertise CIMD until redirect URIs are matched against the client document CodeRabbit on #1866: advertising client_id_metadata_document_supported makes Claude and Codex send URL client_ids and expects an exact redirect_uri match against that document; the authorize endpoint only checks the global allowlist and never fetches client metadata. The flag is withheld until an SSRF-safe, cached CIMD fetch with exact redirect matching exists. DCR stays the registration path (stateless, so free). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wCdzRTatKiDByKB8hCNT6 --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4dfdb35e13 |
feat(plugin): ready the Claude Code plugin for directory submission (#1245)
The plugin has been installable from erp-mafia/accounted since #1088, but three things would fail review at the Claude plugin directory: - `homepage` pointed at https://app.gnubok.se/docs/api/connect-claude, which 404s. next.config.ts redirects /docs/api/* to the separate docs.gnubok.se repo, where that page was never ported, so app/docs/api/connect-claude is unreachable dead code. Point homepage at the plugin README instead. - `license: MIT` was a bare claim with no artifact next to it. Add the MIT text and make the README explicit that the plugin is MIT while the platform it connects to is a separate AGPL-3.0 work. - Version stayed at 0.1.0 for a plugin that has been live and working. Also give /accounted:start a path for a user who installs from the directory with no Accounted account: previously it only handled a not-yet-authenticated MCP server, and a cold user would hit OAuth with nowhere to go. Both manifests pass `claude plugin validate`, the same check the review pipeline runs on every submission. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
53e343ee92 |
Bug/invalid imports (#1146)
* feat: add Accounted MCP namespace * fix(bookkeeping): stop flagging verifikat whose underlag lives on a referenced supplier invoice The missing-underlag surfaces only accepted a document directly linked to the entry, so payment verifikat for supplier invoices (doc on the registration entry per design) and entries whose doc was pinned to the bank transaction before matching were falsely flagged; opening the entry showed the referenced doc and cleared the warning client-side, and it came back on reload. - verifikat_without_documents + transactions_without_documents now treat an entry as covered when a supplier invoice referencing it (registration or payment FK, or a supplier_invoice_payments row) carries a document anchored to a journal entry (BFL 5 kap 7 paragraf hänvisning till underlag; anchoring required because the WORM deletion guards key on document_attachments.journal_entry_id) - match-supplier-invoice routes (dashboard + v1) propagate the transaction's pinned document onto the payment verifikat, mirroring the categorize route; migration backfills rows already written (open unlocked periods, company-guarded, never steals a linked doc) - /api/documents/counts, the transactions-page badges, the bulk "Inget underlag krävs" count and the push-notification scheduler share the same reference-aware predicate, so every surface agrees with the RPC - counts route validates journal_entry_ids as UUIDs (they are interpolated into a PostgREST or-filter) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(transactions): align table columns flush with page edges Collapse the checkbox gutter column to zero width and hang the hover-revealed checkbox/expand chevron in the page margins, drop the outer padding so DATUM sits flush left and STATUS flush right, and tuck the overflow-menu dots under the middle of the STATUS header. Applied to both the inbox and history tables so they stay identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(arsredovisning): tie anlaggningstillgangar note to booked depreciation The ARL 5:8 roll-forward note recomputed depreciation from its own day-based linear formula (365.25/12 month length, non-inclusive day count, linear only), drifting ~20 kr per year per asset from the ledger-driven resultat- and balansrakning and misstating non-linear methods entirely. Note figures now come from posted depreciation_schedules rows (the same source disposeAsset reverses), falling back to the engine's computeAnnualDepreciation when nothing is posted; pre-onboarding opening balances iterate prior years through the engine. Adds a note-vs-trial-balance tie-out warning (accounts 1000-1299, over 1 kr) surfaced before download. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(stripe): move connect and sync surface from settings to import page Stripe's transaction feed is a continuous import source in the same category as the PSD2 bank connection, so its connect/sync surface now lives on the import page as a source card (mode=stripe), gated "kommer snart" on hosted like before; self-hosted keeps the full panel. - Import page: Stripe card after Koppla bank, renders the existing StripeSettingsPanel via the settings-panel registry - OAuth callback and panel cleanup return to /import?mode=stripe - Settings > Betalningar retired: nav item removed, route redirects, PaymentsSettingsContent deleted, legacy ?tab=payments mapped - New import.stripe_* strings in sv+en; dead settings_nav.payments removed Crons and sync logic unchanged; payment-link settings stay in the invoicing section. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(underlag): paginate missing-underlag cron and harden doc-surface queries Resolve PR review findings on bug/invalid-imports: - notification-scheduler: fetchAllRows on all 5 global reads; past 1000 rows the capped reads produced false "saknade underlag" notifications - bulk-missing: LOOKUP_CHUNK 300->150 so the twice-embedded .or() id list stays under the PostgREST URL limit - bulk-missing + transactions page: UUID-guard the .or()-interpolated id lists, matching documents/counts - match-supplier-invoice (dashboard + v1): log documentId/journalEntryId on the non-fatal doc-link warning - well-known/oauth-protected-resource: document the tool_namespace allow-list - messages/en: reword stripe_description - DECISIONS.md: record the asset ibAck tie-out and Tailwind !important calls Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(tic): convert registrationDate from Unix seconds to millisecond epoch in lookup and profile tests --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
e33cc2428d |
feat(plugin): official Claude Code plugin with marketplace and seven workflow skills (#1088)
Ships an installable Claude Code plugin (/plugin marketplace add erp-mafia/accounted) that bundles the MCP connection (OAuth, zero-key) with seven short workflow skills following the Swedish bookkeeping rhythm: start, bookkeep, check, month-close, vat, payroll, year-end. Wrappers are deliberately thin: they ground in the agent briefing and Accounted:// resources, load server-side workflow skills and regulatory atoms via gnubok_load_skill at need, and stage every write for user approval. No knowledge is duplicated into the plugin. A vitest cross-checks every skill slug, atom id, resource URI, and tool name the wrappers reference against the MCP server source, so a server rename fails CI instead of a user's chat session. Assessment and follow-ups in dev_docs/claude_plugin.md (local, dev_docs is unpublished by design). Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |