Skip to content

v0.7.12: mcp servers ui/ux fixes, nuqs for query param management#5165

Merged
icecrasher321 merged 3 commits into
mainfrom
staging
Jun 22, 2026
Merged

v0.7.12: mcp servers ui/ux fixes, nuqs for query param management#5165
icecrasher321 merged 3 commits into
mainfrom
staging

Conversation

@icecrasher321

@icecrasher321 icecrasher321 commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

waleedlatif1 and others added 2 commits June 21, 2026 20:51
* feat(url-state): introduce nuqs for type-safe query-param state

Add nuqs and migrate ad-hoc URL query-param handling to typed parsers.

- Wrap the provider tree in `NuqsAdapter` (app/layout.tsx).
- Co-locate typed param modules:
  - logs/search-params.ts — timeRange/level/workflowIds/folderIds/triggers/
    search parsers (history: 'replace', clearOnDefault) preserving the exact
    prior wire encoding (kebab time-range tokens, comma-joined arrays).
  - integrations/[block]/search-params.ts — ephemeral `connect` literal param.
- Replace the logs filter store's hand-rolled URL sync (initializeFromURL /
  syncWithURL / popstate) with a URL-backed `useLogFilters` hook over
  useQueryStates; the zustand store now holds only the non-URL viewMode toggle.
- Migrate logs.tsx (executionId + search), logs-toolbar, dashboard, and the
  integration detail `connect` deep-link (read-then-strip) to nuqs.

URL keys, defaults, and history semantics are unchanged.

* feat(url-state): migrate deferred sites to nuqs + add url-state rule

Migrate the deferred query-param sites to typed nuqs parsers, each with a
co-located search-params.ts single source of truth:

- settings/[section]: mcpServerId deep-link
- files: folderId (history: push) + new compose flag
- knowledge/[id]: addConnector read-then-strip deep-link
- knowledge/[id]/[documentId]: page (int, default 1) + chunk deep-link

Workflow editor intentionally left store-backed (socket-synced / high-frequency
/ persisted-preference view-state); documented in the rule's carve-out.

Add .claude/rules/sim-url-state.md (decision framework, conventions, server
cache + debounced-input patterns, editor carve-out); cross-link from CLAUDE.md
and sim-queries.md.

* feat(url-state): migrate remaining view-state to nuqs + harness updates

Make the URL the single source of truth for shareable view-state across the
remaining sweep-confirmed sites:

- settings/mcp: replace initialServerId prop + effect-sync with a direct
  useQueryState (mcpServerId, history: push); stop prop-drilling from settings
- integrations: selectedCategory + debounced search; add Suspense boundary
- tables: debounced search + sort/dir + row-count/owner filters (activeTable
  stays route state — selecting a table navigates to tables/[tableId]); wire
  the existing loading.tsx as the Suspense fallback
- knowledge/[id]: pagination page param
- settings/recently-deleted: tab + sort/dir + debounced search
- settings/admin: committed search (q) + pagination offset
- settings/mothership: tab + environment
- skills: editingSkill object -> skillId deep-link (derive from useSkills); add
  Suspense boundary
- files: shareFileId deep-link added to files/search-params
- landing integrations + models directories: debounced search + category/
  provider filter; add Suspense boundaries

Harness: add a When-to-use decision table, the sort (sort+dir) convention, the
selected-entity deep-link pattern, and nuqs doc links to sim-url-state.md; add
the /you-might-not-need-url-state command and wire it into /cleanup.

* fix(nuqs): revert landing-page param migrations and tighten workspace URL-state

- Revert integrations/models landing pages to static SEO HTML (drop nuqs migration + their search-params files)
- MCP settings: refresh tools only for an initial deep-linked server id, not on subsequent user selections
- Add a Suspense boundary with real chrome around the nuqs-using integration detail page
- Trim inaccurate "server component reads these params" TSDoc from search-params files (createSearchParamsCache is unused)
- Export mcpServerIdUrlKeys from the settings search-params file instead of inlining the options in mcp.tsx
- Convert two new relative imports (logs use-log-filters, tables loading) to absolute
- Wrap setSelectedCategory in useCallback; clear active skillId edit param when opening the create-skill form

* fix(logs): add logs-page Suspense boundary and co-locate nuqs params

- Wrap <Logs/> in <Suspense fallback={<LogsLoading/>}> so the nuqs reads
  (useLogFilters, executionId) have a boundary ancestor like sibling pages.
- Co-locate the executionId param in logs/search-params.ts (read-only,
  intentionally not stripped) and consume it in logs.tsx.
- Migrate log-details activeTab to a deep-linkable nuqs tab param (single
  LogDetails instance; preview path uses ExecutionSnapshot, not LogDetails).
- Align cleanup.md description pass order with the numbered steps.
- Replace mothership.tsx local Tab type with exported MothershipTab.

* fix(url-state): honor deep-linked log-details tab on first mount

* improvement(nuqs): adopt limitUrlUpdates debounce + add eq to array parser

Replace the hand-rolled debounced-search pattern (local useState mirror +
useDebounce + URL write-back effect + ref-guarded reconcile effect) with
nuqs's built-in limitUrlUpdates: debounce() across logs, integrations,
tables, and recently-deleted. The input is now controlled directly by the
instant nuqs value; only the URL write is debounced. Query keys / expensive
filters still derive a debounced value off the instant value; cheap in-memory
filters read it directly. admin (commit-on-submit) intentionally left alone.

Add an eq to parseAsTriggers (TriggerType[]) so clearOnDefault can detect the
empty-array default and strip it from the URL, per nuqs createParser docs.

Update .claude/rules/sim-url-state.md to prescribe the debounce pattern and the
createParser eq requirement for array/object/Date values.

* feat(nuqs): migrate table-detail sort, KB document filters, and calendar view to URL state

- Table detail: sort+dir to nuqs; Filter stays in useState (recursive/nested, too large for URL)
- Knowledge base: search (debounced), enabled filter, sort+dir to nuqs; tagFilterEntries stays in useState (rich rule objects)
- Scheduled-tasks calendar: scope + date-only anchor (parseAsIsoDate, nullable, derive-today) to nuqs
- Add Suspense boundaries to table-detail and scheduled-tasks pages
- Document parseAsIsoDate / nullable-dynamic-default pattern in sim-url-state.md

* fix(nuqs): resolve 7 PR review findings on URL query-param state

- logs: clear the log-details `tab` param when the sidebar closes so a
  lingering `?tab=trace` no longer carries into the next opened log;
  deep-linked tabs still open on first mount.
- logs dashboard: drive the in-memory workflow filtering off the same
  debounced search value the stats query uses (passed as a prop) so the
  chart and list stay consistent while typing.
- knowledge document: make the URL `chunk` param the single source of
  truth for the open chunk (back/forward, deep links, and external
  navigation now drive the editor) instead of a one-time useState seed.
- logs: drop the redundant `setUrlSearchQuery('')` after `resetFilters()`
  (resetFilters already clears `search`).
- files: use a per-call `{ history: 'replace' }` override for the
  `shareFileId` share-modal open/close writes so toggling the modal does
  not pollute the back/forward stack; folder navigation keeps `push`.
- tables + recently-deleted: trim search input before deriving the URL
  value so whitespace-only input no longer writes `?search=%20`.

* fix(url-state): trim whitespace-only search in integrations filter

* fix(url-state): clear log tab on all close paths + trim KB search

* fix(scheduled-tasks): use local-time date parser for calendar anchor (avoid UTC day-shift)

* feat(home): migrate ?resource deep-link to nuqs (URL as source of truth)

Replace the banned window.history.replaceState effect on the home/Chat
surface with a nuqs useQueryState('resource') binding. The URL is now the
single source of truth for the selected resource.

- Add co-located home/search-params.ts (resource param, history: replace)
- useChat accepts a controlled activeResourceState binding; home passes the
  nuqs-backed tuple. The workflow editor copilot keeps internal useState so
  its resource selection stays out of the URL (editor carve-out)
- Preserve the old effect's url.hash='' fragment strip in the binding setter
  (fragment-only rewrite, not a param mutation)
- Drop initialResourceId SSR prop from both page entries (nuqs reads the URL
  on mount; no dual source) and wrap Home in Suspense for useSearchParams

* docs(home): note nuqs deferred-flush ordering in resource hash-strip

* docs(url-state): convert inline comments to TSDoc
… overrides lack of clarity (#5164)

* fix(mcp): missing isDeployed in contract breaking settings, parameter overrides lack of clarity

* address comments

* address ux concern

* address stray 404

* address stale fallback based on live state

* fix

* fix more things

* simplify state mgmt

* add tooltip for server selection
@icecrasher321 icecrasher321 requested a review from a team as a code owner June 22, 2026 04:33
@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jun 22, 2026 5:48am

Request Review

@cursor

cursor Bot commented Jun 22, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Wide client URL-state migration touches many routes and shared query keys; regressions could break deep links or back/forward behavior. MCP API/schema changes affect tool metadata served to external clients but are localized to MCP routes.

Overview
Introduces nuqs as the standard for shareable client view-state: NuqsAdapter in the root layout, co-located search-params.ts parsers, and migrations away from manual useSearchParams / history.replaceState / Zustand URL sync across Home (?resource=), Files, Integrations, Knowledge, Logs (useLogFilters replaces filter-store URL sync), Skills, Tables, Scheduled Tasks calendar, and several Settings views (MCP server id, Mothership tab/env, admin pagination, recently deleted).

Adds Suspense boundaries on affected page entries with real-chrome fallbacks, documents conventions in .claude/rules/sim-url-state.md, extends /cleanup with /you-might-not-need-url-state, and wires useChat to accept an optional URL-controlled activeResourceState for mothership while keeping the workflow copilot on local state.

MCP: workflow tool create/update APIs and list responses now surface parameterDescriptionOverrides; tools/list descriptions fall back through trimmed tool text → meaningful workflow description → generic execute string. Docs clarify Start-block vs per-tool parameter descriptions. Workflow MCP servers settings UI consolidates “Add Workflows” into the header chip.

Reviewed by Cursor Bugbot for commit 951ad42. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 951ad42. Configure here.

Comment thread apps/sim/app/workspace/[workspaceId]/home/home.tsx
Comment thread apps/sim/app/workspace/[workspaceId]/logs/logs.tsx

const [searchTerm, setSearchTerm] = useState('')
const [showAddModal, setShowAddModal] = useState(false)
const [selectedServerId, setSelectedServerId] = useState<string | null>(null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Back leaves server query param

Low Severity

This change reads mcpServerId from the URL on mount but onBack only clears local selectedServerId. The query param stays set, so a refresh or shared link reopens the server detail after the user returned to the list.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 951ad42. Configure here.

@greptile-apps

greptile-apps Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR has two main parts: a nuqs adoption that migrates all URL query-param state across the workspace from hand-rolled history.replaceState + Zustand store synchronization to type-safe nuqs parsers, and a set of MCP server fixes that introduce a sparse parameterDescriptionOverrides DB column, fix the missing isDeployed field in the workflow contract, and refactor the deploy modal to work from the deployed workflow snapshot rather than the live editor state.

  • nuqs migration: The logs filter store shrinks from 327 lines to 17 (view-mode toggle only); typed parsers are colocated with each feature in search-params.ts files; back/forward and initialization are handled by nuqs. The URL wire format is intentionally preserved for backward compatibility.
  • MCP parameter description overrides: A new parameter_description_overrides column stores only the sparse diff of user-supplied descriptions vs. Start-block defaults; the full materialized schema is recomputed on sync. A legacy extraction path migrates existing tools on their first save. The serve route now falls back through getMeaningfulWorkflowDescription → workflow name when a tool description is absent.
  • isDeployed contract fix: The field was present in the DB query but missing from the Zod wire contract; adding it as .optional() avoids a silent parse failure for consumers that depend on it.

Confidence Score: 3/5

Safe to merge with the data-loss edge case in mind — an unavailable deployed state during the save flow can silently clear all parameter descriptions for any tool first opened after the migration.

The MCP parameter-description overrides migration is the highest-risk path: all existing tools start with an empty parameterDescriptionOverrides column, so the legacy extraction path runs for every tool on first open. If the deployed-state fetch fails (network error, race, or undeployed workflow) and the user proceeds to save, the extracted overrides are {} and descriptions stored only in the old parameterSchema are permanently lost with no warning or rollback. The nuqs migration is clean and the URL wire-format is backward-compatible, but the MCP save path deserves a guard before merging.

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/mcp/mcp.tsx — the seeding effect and toolDescriptionForSave logic around deployed-state unavailability.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/mcp/mcp.tsx Major refactor: derives parameter schema from deployed workflow state instead of live editor state; introduces legacy override extraction path for tools created before the overrides column. Data-loss edge case when deployed state unavailable.
apps/sim/lib/mcp/orchestration/workflow-mcp-lifecycle.ts prepareWorkflowMcpTool now stores null description (instead of a fallback) and tracks parameterDescriptionOverrides separately from the full materialized schema. Legacy parameterSchema path still accepted and migrated server-side.
apps/sim/lib/mcp/workflow-tool-schema.ts Replaces generateParameterSchema with applyDescriptionOverrides + extractDescriptionOverrides + pruneOverridesToSchema; adds getMeaningfulWorkflowDescription shared between serve route and UI.
apps/sim/lib/mcp/workflow-mcp-sync.ts syncMcpToolsForWorkflow now applies per-tool description overrides before deciding budget (correctness fix) and updates each tool individually instead of batching by schema type.
apps/sim/stores/logs/filters/store.ts Dramatically simplified from 327-line URL-sync store to a 17-line view-mode-only store; filter state moved to nuqs-backed useLogFilters hook.
apps/sim/app/workspace/[workspaceId]/logs/hooks/use-log-filters.ts New hook replacing hand-rolled URL sync; exposes the same action surface as the old store, backed entirely by nuqs typed query params. Debounce, back/forward, and initialization are all handled by nuqs.
apps/sim/app/workspace/[workspaceId]/logs/search-params.ts Defines typed nuqs parsers for all logs filters. parseAsTriggers uses an order-sensitive custom eq, which is benign in practice. clearOnDefault+replace history matches the old behavior.
apps/sim/lib/api/contracts/workflow-mcp-servers.ts Adds parameterDescriptionOverrides to create/update schemas and the tool response type; marks the legacy parameterSchema as accepted during the transition window.
apps/sim/lib/api/contracts/workflows.ts Adds missing isDeployed field to workflowListItemSchema — this was previously absent from the contract despite being present in the DB query and schema.
packages/db/migrations/0247_workflow_mcp_tool_param_desc_overrides.sql Adds parameter_description_overrides column with a safe NOT NULL DEFAULT '{}'::json — all existing rows get an empty map, which is correctly handled by the migration path in the lifecycle layer.

Reviews (1): Last reviewed commit: "fix(mcp): missing isDeployed in contract..." | Re-trigger Greptile

Comment thread apps/sim/app/workspace/[workspaceId]/logs/search-params.ts
…bugs, persist hygiene, useState) (#5166)

* fix(queries): close React Query key/fetch-arg drift cache collisions

Several query hooks fetched with an identifier that was absent from their
queryKey, so distinct fetch args shared one cache entry. Thread the missing
args into the key factories and update all callsites/invalidations.

- organization: useOrganization always fetched the ACTIVE org via
  getFullOrganization() while caching under detail(orgId). Pass orgId through
  to the better-auth call (query.organizationId); active-org behavior unchanged.
- logs: logKeys.detail now keys on (workspaceId, logId) to prevent cross-
  workspace collision; updated useLogDetail, useLogByExecutionId, prefetchLogDetail,
  useCancelExecution optimistic path, and external callsites.
- inbox: inboxKeys.taskList now includes cursor/limit (pagination args were sent
  but omitted from the key); keepPreviousData pagination UX preserved.
- a2a: narrow create/update byWorkflows() invalidation to byWorkflow(ws, wf)
  since their responses reliably carry both ids; delete/publish stay broad.

Not bugs (verified, left unchanged):
- kb/connectors update/delete invalidate knowledgeKeys.detail(kbId), which is a
  prefix of connectorKeys.all(kbId) — connector list/detail are invalidated
  transitively by React Query prefix matching.

Harness: add a key-fetch-arg-drift check to check-react-query-patterns.ts that
flags a camelCase identifier the queryFn forwards into the fetch but is absent
from the queryKey (excludes the requestJson contract arg, PascalCase/SCREAMING
constants, and signal/pageParam machinery). Document the rule in sim-queries.md.
tables.useTable annotated rq-lint-allow (tableId globally unique; workspaceId is
only an authz scope).

* fix(stores): whitelist durable fields in persist partialize

chat/terminal/panel persist configs leaked actions and transient state
into localStorage. Replace the chat full-state spread with an explicit
durable whitelist, and add partialize to terminal and panel (which had
none) so isResizing and _hasHydrated are no longer persisted. Panel keeps
activeTab + panelWidth because the layout.tsx blocking script reads them
from panel-state to set data-panel-active-tab before hydration (SSR
tab-flash prevention).

Harden sim-stores doctrine: persist MUST use an explicit partialize
whitelist; never persist transient flags or _hasHydrated.

* fix(state): model component useState as single source of truth

- edit-knowledge-base-modal: reset fields on closed→open via prevOpenRef
  render idiom instead of mirroring props into state through useEffect
  (a prop change while open no longer clobbers in-progress edits)
- use-verification: collapse contradictory isLoading/isVerified/isInvalidOtp
  booleans into a single status enum + errorMessage; consumer derives flags
- contact-form / demo-request-modal: derive busy/success from the mutation
  object; delete duplicated submitSuccess local state
- sim-hooks.md: add state-shape rule (no props-into-state, status enum,
  derive mutation state)

* fix(verify): clear lingering message on complete OTP (restore parity)

* docs(state): convert inline reset comment to TSDoc

* docs(state): tighten harness rules for accuracy (queryFn forwards, partialize whitelist, mutation-flag caveat)

* fix(verify): block auto-verify while a resend is in flight (restore parity)

* fix(logs): key cancel optimistic detail by route workspaceId (not the log row)
@icecrasher321 icecrasher321 merged commit aaca750 into main Jun 22, 2026
27 of 31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants