Skip to content

Show Claude users how to add apps from the chat - #1441

Merged
helpcodeai merged 4 commits into
mainfrom
feat/claude-setup-discoverability
Nov 2, 2026
Merged

helpcodeai merged 4 commits into
mainfrom
feat/claude-setup-discoverability

Conversation

@helpcodeai

@helpcodeai helpcodeai commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Most new Cloud workspaces that connect from the Claude connectors directory never search for an app, in the chat or on the website. After approving, they land back in Claude with an empty workspace. Nothing in the default tool set told Claude that apps can be added from the chat, and nothing named an app. A request like "help me with my Etsy shop" gave Claude no reason to call AnythingMCP. The in-chat setup tools existed but sat several hops deep, and a search for "etsy" in an empty workspace returned nothing. We also had no record of whether new workspaces call the meta tools at all.

This PR changes what the default (Claude) profile says and answers. It does not change which tools it lists, their names or their input schemas. It also measures the calls and adds an optional one-click app picker to the Cloud post-approve page.

Changes

  • Instrumentation. New server-only product event shared_tool_called (packages/backend/src/mcp-server/shared-tool-calls.ts):
    • Metadata: tool, profile, client (the OAuth client name, when known) and setupTool (the AnythingMCP Setup tool that anythingmcp_run_read_tool / anythingmcp_run_write_tool ran).
    • Dedup: at most one row per workspace, tool (and setup tool) and UTC day. A bounded in-process key set is checked first, then one database read per key and day. The tool call never waits for either.
    • Also: new server-only event post_approve_app_picked. tool, profile and setupTool are now allowed metadata keys; profile and setupTool accept only fixed values.
  • Default-profile wording (shared-toolset.ts):
    • Instructions: SHARED_TOOLSET_INSTRUCTIONS now opens with "Apps can be added right here in the chat". It names Telegram, Shopify, WooCommerce, Odoo, Gmail and Etsy (all in the catalog) and gives the exact run_read_tool / run_write_tool calls for setup_find_connectors, setup_install_connector and setup_get_status. Secrets only go through the link. anythingmcp_get_configuration_url moved to the end, as the fallback.
    • Descriptions: anythingmcp_list_connectors and anythingmcp_search_tools now say they also show apps that can still be added, and name a few. The configuration-url description mentions adding from the chat.
    • ChatGPT: instructions unchanged.
  • Search fallback. When anythingmcp_search_tools finds no workspace tool for a non-empty query, it now does one of two things. Setup tools that only match generic words such as "shop" do not count as a find.
    • App added but not set up: if a matching connector waits for its setup, the answer returns notReadyYet with the finish link.
    • Otherwise: for a caller who may add connectors (deps.setup), it runs the same setup_find_connectors search with limit 3. It returns canBeAdded: [{ adapter, name, whatSetupInvolves, howToAdd }] plus a one-line hint. Already installed apps are left out.
    • howToAdd: on the default profile it is { tool: 'anythingmcp_run_write_tool', arguments: { tool: 'setup_install_connector', arguments: { adapter } } }. On ChatGPT it is anythingmcp_add_connector. For File Search entries it is the wizard setupUrl.
    • Rules: the per-profile exclusions (isExcludedAdapterSlug / sharedEndpointAccess) apply unchanged, because this runs the setup service's find.
    • Other callers: a caller who may not add apps gets a hint pointing to an admin or the dashboard. Any failure leaves the search answer as it was.
  • Setup tools by name. run_read_tool / run_write_tool already accepted the setup tools without a prior describe_tool; a spec now proves it for all three. Calling a setup tool without permission used to return "No tool named …". It now says that adding apps is open to admins and editors and points to the dashboard.
  • list_connectors. A draft (for example, an app picked on the post-approve page) already appears under needsSetup with its finishSetupUrl. The hint now tells Claude to give that link first.
  • Post-approve page (renderFirstConnectorOffer). Cloud only, same conditions as before: empty workspace, ADMIN or EDITOR, and the client did not ask for one server's URL.
    • Picker: Telegram, Shopify, WooCommerce, Odoo, Printify and Google Gemini (all slugs exist, none needs the provider to approve an app), plus an "Another app" text field. The picker is hidden until its script runs, so without JavaScript the page is the same as before.
    • Picking an app: posts JSON to the new POST /auth/first-app. The endpoint checks:
      • the CSRF double-submit token in its own signed cookie, first_app_csrf;
      • the identity in the signed login_user cookie that the paused authorization already relies on;
      • that the OAuth session is still pending;
      • that the adapter is one of the page's own six apps.
    • How the app is added: the workspace and granted servers are resolved the way the shared endpoint resolves them. Then the setup provider's canSetUp and install run with no secrets, which keeps the role check, plan limit, hourly cap and security event. The endpoint logs post_approve_app_picked (status: added | failed). The page then shows "Telegram added — you'll finish it in Claude" and the example request becomes "Finish setting up my Telegram connector."
    • Another app: typing only changes the example request; nothing is installed.
    • Continue: keeps its href to the OAuth callback; the callback and OAuth flow are untouched. When the redirect host is claude.ai, the same click also opens https://claude.ai/new?q=<request> in a new tab (window.open in the click handler).
    • Default example: now "Connect my Telegram bot to AnythingMCP." If an install fails, the page says so and Continue still works.
  • Docs. docs/integrations/claude.md gains a paragraph on the post-approve page and on adding apps from the chat. llms-full.txt is regenerated to match.

Follow-ups (second and third commits)

  • Where a setup call comes from. ConnectorSetupService.find and install take an optional third argument, { via }. Only the server sets it; tool arguments never reach it.
    • A post-approve pick now logs setup_completed with via: 'post_approve', so it does not count as a chat setup (via: 'mcp').
    • Search-fallback lookups log catalog_search with via: 'mcp_search_fallback', apart from setup_find_connectors searches (via: 'mcp').
    • Without the argument both methods behave exactly as before. The change in connector-setup.service.ts stays inside find, install and their event logging.
  • Catalog size. The setup_find_connectors description used a hard-coded "265 ready connectors"; the catalog has 691. It now reads the count from the catalog when the module loads, rounded down to ten ("690+"). This is the virtual setup tool, so it is the same on both profiles. The ChatGPT anythingmcp_find_connectors description never had a number and is unchanged.
  • Picker accessibility. While a pick runs and after it succeeds, the app buttons are marked aria-disabled instead of disabled, and the "Another app" field becomes read-only. A disabled button had dropped keyboard focus to the page body.

Reviewed tool list: intentional change

The default profile still lists the same eight tools, with the same names, input schemas and annotations; shared-toolset.discoverability.spec.ts now pins all three explicitly. The instructions and the descriptions of anythingmcp_list_connectors, anythingmcp_search_tools and anythingmcp_get_configuration_url changed on purpose. The existing guarding specs compare against SHARED_TOOLSET_INSTRUCTIONS and still pass; the new spec checks the new text.

Type

  • Bug fix
  • New feature
  • Documentation
  • Refactoring
  • Performance improvement
  • Other (describe below)

Cloud vs self-hosted

  • Cloud: /mcp serves the fixed tool set (sharedEndpointMode() is fixed when DEPLOYMENT_MODE=cloud), so the new wording, the search fallback and shared_tool_called apply. The post-approve page and POST /auth/first-app exist only on Cloud.
  • Self-hosted: /mcp serves direct tools by default (unchanged), so none of the tool-set changes apply unless an operator sets MCP_SHARED_ENDPOINT_TOOLS=fixed. In that case they behave as on Cloud, and shared_tool_called rows go to the instance's own product_events table. The post-approve page is never shown on self-hosted (maybeOfferFirstConnector returns early), and POST /auth/first-app answers 404 there (spec).

Testing

  • Added/updated unit tests
  • Tested manually (describe steps below)
  • No tests needed (explain why)

New and updated specs:

  • shared-toolset.discoverability.spec.ts covers:
    • the reviewed names, input schemas and annotations;
    • the instructions text, and that the apps it names exist in the catalog;
    • the ChatGPT instructions, unchanged;
    • the descriptions, including the catalog size read from the catalog on both profiles;
    • the search fallback: canBeAdded, howToAdd runs as given, the ChatGPT variant, no fallback on a match / an empty query / a connector filter, failure-safe, a caller who may not add apps, notReadyYet;
    • the real-catalog profile rules: the default profile hides Binance and serves Stripe read-only; ChatGPT hides Stripe and offers Binance;
    • the catalog_search event with via: 'mcp_search_fallback';
    • setup tools by name without describe_tool;
    • the needsSetup hint;
    • call reporting.
  • shared-tool-calls.spec.ts covers the dedup per day, tool, setup tool and workspace, the database guard, the bounded key set, a call without an OAuth client, and that it never throws.
  • shared-endpoint.tool-calls.spec.ts covers the controller wiring, and that the server-set via reaches the setup service.
  • connector-setup.service.spec.ts covers via defaulting to mcp, plus mcp_search_fallback and post_approve.
  • product-event.spec.ts covers the metadata enums, and that both new events are refused from a page.
  • login.controller.spec.ts covers:
    • the page with and without the picker, and its CSRF cookie;
    • data-open-chat only for claude.ai;
    • the picker endpoint: success with the exact setup context and via: 'post_approve'; CSRF refusal (missing or wrong token); no login cookie; non-admin refusal; unknown app; install failure; 404 on self-hosted;
    • the page script run in a minimal DOM: a pick changes the request and keeps focus, a failure keeps everything usable, "Another app" changes the request, Continue opens Claude with the request.

Commands run in packages/backend, after npm ci at the root and npx prisma generate:

Command Result
npx tsc --noEmit -p tsconfig.json exit 0, no errors
npm run lint 0 errors; 11 warnings, all in files this PR does not touch; --fix rewrote no other file
npx jest --maxWorkers=3 src/mcp-server src/auth src/audit src/adapters 760 suites passed (2 skipped); 25481 tests passed, 1784 skipped

The full backend npm test was not run locally; the CI backend job ran it and passed.

Commands run from the repository root:

Command Result
node scripts/validate-adapters.mjs exit 0
node --test scripts/validate-adapters.test.mjs scripts/adapter-new.test.mjs scripts/generate-llms-txt.test.mjs 58 passed, 0 failed
node --test scripts/satellites/satellites.test.mjs exit 0
node scripts/adapter-count.mjs --check "every quoted count equals 691 adapters"
node scripts/generate-llms-txt.mjs --check exit 0 after regenerating llms-full.txt (the CI run before that failed on it)

No adapter JSON or counted text changed.

Manual visual check: I rendered the post-approve page with and without the card-trial line and checked it with Playwright in Chromium at 390 and 1280 px.

  • Nothing scrolls sideways at either width, the app buttons wrap to two rows, and nothing errors in the console.
  • Tab order: Copy, the six apps, Another app, Continue, the dashboard link, then the card link when the trial line is shown.
  • A keyboard pick (network stubbed) shows "Adding Telegram…" and then "Telegram added — you'll finish it in Claude". The request text changes, focus stays on the picked app, and that app is shown in the brand colour.
  • Continue goes to the callback and opens claude.ai/new?q=… in a new tab.

Checklist

  • My code follows the existing code style
  • I have tested my changes locally
  • I have updated documentation (if applicable)
  • All existing tests pass (the related directories locally; the full suite in CI)
  • This PR has a descriptive title

Notes and open points

  • The new-chat link. The claude.ai/new?q= behaviour is not documented by Anthropic. If it stops pre-filling the request, the new tab opens an empty chat and nothing else breaks.
  • Duplicate first rows. shared_tool_called is best-effort: two backend processes can both write the first row of a day for the same workspace and tool. That is rare and harmless for a "did it happen" count.
  • Reading the funnel. Read post_approve_app_picked against empty_workspace_prompt (how many pick). Read setup_completed with via: 'post_approve' (how many picked apps get installed), and shared_tool_called with setupTool (whether the chat then finishes them).

Related Issues

None.

Rewrite the default /mcp profile's instructions and the list/search tool
descriptions so they say up front that apps are added in the chat. A
search that matches nothing in the workspace now lists catalog apps that
can be added, with the exact call. Record shared tool calls once per
workspace, tool and UTC day (shared_tool_called), and add an optional
quick-setup app picker to the Cloud post-approve page
(post_approve_app_picked). Default example request is now Telegram.
…size

Pass an optional, server-set via to the setup service's find and install:
post-approve picks log setup_completed via 'post_approve' and fallback
catalog lookups log catalog_search via 'mcp_search_fallback', so neither
counts as a chat setup or search. setup_find_connectors states the
catalog size from the catalog instead of a hard-coded 265. The picker
marks chips aria-disabled instead of disabled, so keyboard focus stays
on the picked app.
@helpcodeai
helpcodeai requested a review from D3nisty as a code owner November 2, 2026 08:56
@helpcodeai
helpcodeai enabled auto-merge (squash) November 2, 2026 11:21
@helpcodeai
helpcodeai merged commit cdd4a03 into main Nov 2, 2026
14 checks passed
@helpcodeai
helpcodeai deleted the feat/claude-setup-discoverability branch November 2, 2026 11:34
@github-actions github-actions Bot locked and limited conversation to collaborators Nov 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant