feat(core): retrieve org qualification methods + per-lead custom fields#113
Open
ArtyETH06 wants to merge 16 commits into
Open
feat(core): retrieve org qualification methods + per-lead custom fields#113ArtyETH06 wants to merge 16 commits into
ArtyETH06 wants to merge 16 commits into
Conversation
Two new default-surface read composites resolving product#3768:
- leadbay_get_qualification_methods — returns the org's AI-agent
qualification questions (the "qualification methods") with created_at +
lang, plus the caller's is_admin flag and a web-app edit hint. Read-only
for everyone; the API exposes no write endpoint for these questions.
- leadbay_get_lead_custom_fields — returns the CRM custom-field VALUES on a
single lead as {id, name, type, value}. The lead payload embeds each
field's definition (verified live), so no /crm/custom_fields join is
needed; the catalog is fetched only as a defensive fallback. Fires
LEAD_SEEN on read, in parity with the research tools.
Both were already partially reachable only via the ADVANCED-gated
get_taste_profile / list_mappable_fields (definitions only); the per-lead
custom_fields array was silently dropped (untyped on LeadPayload).
Adds LeadCustomFieldEntry + LeadPayload.custom_fields, two routing
templates, WORKFLOWS.md rows, and registry/audit entries
(COMPOSITE_FILE_TOOL_NAMES, TOOLS_WITH_ROUTING, output-schema
conformance CASES). Verified live end-to-end on the test org.
Co-Authored-By: Claude <noreply@anthropic.com>
Adds the modify surface for custom fields, completing the read+write set:
- leadbay_update_custom_field — rename and/or change type+config in place
(POST /crm/custom_fields/{id} → 204, verified live). Partial-merge over the
current definition so rename-only keeps the type and retype-only keeps the
name. Same EXTERNAL_ID/PRICE config validation as create.
- leadbay_delete_custom_field — DELETE /crm/custom_fields/{id} → 204.
Destructive (drops the field's values from every lead), so it requires an
explicit confirm:true; without it the tool previews the field and does
nothing. destructiveHint:true.
Both granular-shaped, registered in compositeWriteTools (default surface,
write-gated like create_custom_field). Conformance CASES + new unit test
files added; live update+delete round-trip verified.
Qualification methods intentionally get NO modify tool — the API exposes no
write endpoint for ai_agent_questions (every verb 404s); they stay read-only
with the web-app edit hint.
Co-Authored-By: Claude <noreply@anthropic.com>
The qualification-questions write endpoint DOES exist — it's the org root
POST /organizations/{orgId} with {ai_agent_lead_questions:[string,...]} → 204
(full-replace; confirmed live with the web app's exact payload). My earlier
404 was probing the wrong path (/ai_agent_questions).
Adds leadbay_set_qualification_methods:
- set / add / remove modes; reads the current list and posts the full
resulting array (matches the full-replace endpoint).
- shrinking the list requires confirm:true (removing a question changes how
every lead is scored); add does not.
- enforces the backend cap (max 5 questions) with an actionable hint instead
of a raw 400.
- invalidates the taste-profile cache after a write.
Live-verified: add hits the 5-cap correctly; remove-without-confirm previews;
remove+restore round-trips cleanly (account left as found).
This supersedes the earlier "no write endpoint" note — qualification methods
are now fully modifiable, alongside custom fields.
Co-Authored-By: Claude <noreply@anthropic.com>
Adds workflow #32 (Modify qualification methods) exercising leadbay_set_qualification_methods, and drops the now-stale "read-only" note from #30. Scenario adds a question against an org already at the 5-question cap — exercises the write tool + cap handling while mutating nothing. Live run: 5/5/5/5, invariants 2/2, org questions byte-for-byte unchanged after. Co-Authored-By: Claude <noreply@anthropic.com>
Adds workflow #33 (Modify custom fields) exercising the full create → update → delete lifecycle, including the delete confirm-gate. Self-contained: the scenario creates a throwaway field, renames it, and deletes it with confirm, so the run cleans up after itself. Live run: 5/5/5/5, invariants 3/3, catalog byte-for-byte unchanged after (only the pre-existing field remains). Co-Authored-By: Claude <noreply@anthropic.com>
The read tool's description + admin hint still said editing happens in the
Leadbay web app with "no MCP edit tool yet" — stale since
leadbay_set_qualification_methods landed. Repoint the hint, is_admin doc,
description, and rendering footnote at leadbay_set_qualification_methods.
Confirmed against the backend repo: both modify surfaces (qualification
questions + custom fields) are authenticate("admin"), i.e. org-admin only —
which every user is for their own org, so the modify tools work for everyone
in practice. No per-lead custom-field VALUE write route exists (values flow
through the import pipeline only).
Co-Authored-By: Claude <noreply@anthropic.com>
The previous wf32 scenario ("add a question" against a capped list) let the
agent self-confirm a destructive removal to make room, mutating live data.
Rewrite it as a remove-then-readd round-trip on a single named question: it
nets back to the original set, exercises remove(confirm)+add, and never leaves
the org mutated. Live re-run: 5/5/5/5, question set byte-for-byte unchanged.
Co-Authored-By: Claude <noreply@anthropic.com>
…qualification-questions-custom-fields # Conflicts: # WORKFLOWS.md # packages/core/src/composite/_composite-file-names.ts
…n Wayland/Snap OAuth-on-install (.dxt) spawned xdg-open "successfully" but no tab opened on Wayland + Snap-browser setups (the Ubuntu default). Claude Desktop strips XDG_RUNTIME_DIR — which broke the existing WAYLAND_DISPLAY reconstruction (it reads that dir) — and strips DBUS_SESSION_BUS_ADDRESS, so a Snap/Flatpak browser couldn't be reached and the launch silently no-op'd. browserLaunchEnv now rebuilds XDG_RUNTIME_DIR from /run/user/<uid> when stripped, then derives WAYLAND_DISPLAY (wayland-N socket) and DBUS_SESSION_BUS_ADDRESS (<runtimeDir>/bus) from it. Existing values are never overridden. Confirmed live: with the fix a browser tab opens from a fully-stripped env; without it, nothing. New test file oauth-browser-env-wayland.test.ts. Co-Authored-By: Claude <noreply@anthropic.com>
Follow-up to the XDG_RUNTIME_DIR/WAYLAND/DBUS reconstruction: the browser
still failed to open from Claude Desktop's stripped child env because an
X11/XWayland browser (Chrome/Brave/Electron) needs the X authority cookie to
connect to the display. Without XAUTHORITY the X server rejects the client
("Authorization required... Missing X server") and the browser segfaults —
xdg-open returns 0, but no tab opens.
browserLaunchEnv now injects XAUTHORITY from the Mutter Xwayland cookie at
<runtimeDir>/.mutter-Xwaylandauth.*, falling back to ~/.Xauthority. Existing
value never overridden. Confirmed live: with XAUTHORITY the browser launches
instead of segfaulting. Test coverage extended.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude Desktop won't replace an installed extension with the same version number, so the XAUTHORITY browser-open fix didn't take until the version changed. Bumps package.json + server.json. Co-Authored-By: Claude <noreply@anthropic.com>
leadbay_update_custom_field and leadbay_create_custom_field sent the config
object verbatim. The backend's per-type config models are strict
(PriceFieldConfig = {currency}, Date/DateTime = {format}, ExternalId =
{urlTemplate}, TEXT/NUMBER = none), so any extra key — a stale `format` left
from the previous type on a type CHANGE, both url_template + urlTemplate, or a
currency on a non-PRICE field — triggers a 500 "JSON deserialization error".
Reproduced live on staging: TEXT→PRICE with an over-broad config 500s.
Add sanitizeConfigForType (new _custom-field-config.ts) that narrows config to
exactly the key(s) the target type accepts, and apply it in both tools before
building the request body. Live-verified on staging: TEXT→PRICE/EUR now sends
{currency:"EUR"} and succeeds; full create→update→delete lifecycle clean.
Co-Authored-By: Claude <noreply@anthropic.com>
…ropped)
The agent passes config as a JSON STRING (e.g. config:'{"currency":"EUR"}'),
not an object. sanitizeConfigForType only read object properties, so a string
config yielded no currency → backend 400 "PRICE requires a currency config" on
a TEXT→PRICE update even though the rename landed. Observed live on staging.
sanitizeConfigForType now JSON-parses a string config before narrowing, and
both tools sanitize BEFORE the EXTERNAL_ID url_template check (so that check
sees the parsed value too). Live-verified: the exact stringified payload the
agent sent now narrows to {currency:"EUR"}.
Co-Authored-By: Claude <noreply@anthropic.com>
P2 — set_qualification_methods: gate confirm on the ACTUAL removed questions, not on count. A remove+add or `set` that keeps the count the same still drops a scoring question; the old `next.length < previousCount` check let that through without confirm. Now computes the removed set and requires confirm whenever any existing question would be dropped. P2 — get_qualification_methods: fetch ai_agent_questions DIRECTLY instead of via resolveTasteProfile (Promise.allSettled substitutes [] on a rejected fetch). A transient backend/auth failure now surfaces as an error rather than a false "no questions configured" that could lead a caller to overwrite real questions. P3 — create/update_custom_field: the tools parse a stringified config, so the input schema now advertises it (type: ["object","string","null"]) and the param types accept string — making the recovery path valid for schema- validating clients. New tests: same-count swap confirm gate, and fetch-failure-surfaces. Co-Authored-By: Claude <noreply@anthropic.com>
The package ships CHANGELOG.md but it started at 0.23.0, so consumers installing 0.23.1 had no release notes for the new qualification-method + custom-field tools. Adds the 0.23.1 entry. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Read + modify for qualification methods and CRM custom fields over MCP.
Tools
leadbay_get_qualification_methodsleadbay_set_qualification_methods(add/remove/replace)leadbay_list_mappable_fieldsleadbay_create_custom_field/update/deleteleadbay_get_lead_custom_fieldsleadbay_import_leads)All on the default surface. Modify tools are write-gated and admin-scoped server-side (every user is admin of their own org, so they work for everyone). Removals/deletes require
confirm:true;set_qualification_methodsenforces the backend's 5-question cap. Custom-field config is sanitized per type (and tolerates a stringified config) so the backend's strict deserializer doesn't reject it.Notes
WORKFLOWS.md.Verification
pnpm -r build && pnpm -r test && pnpm -r typecheckgreen.u.-token path..dxt) verified manually on staging end-to-end: read + add/remove qualification questions, and create → rename/retype (PRICE/EUR) → delete custom fields, all working.Closes https://github.com/leadbay/product/issues/3768