Waleed bbff34a43e fix(mcp): route object params to JSON editor, keep invalid drafts out of tool args (#5570)
* fix(mcp): route object-typed params to JSON editor, keep invalid drafts out of tool args

Two follow-up gaps from the earlier MCP schema fix:

- getInputType only routed array-typed and non-primitive-enum params to
  the long-input JSON editor; a plain object-typed param (no enum) fell
  through to the default short-input, which stores raw text via
  toString() and never round-trips a real object.

- The long-input onChange fell back to storing the raw typed text
  whenever JSON.parse failed (needed to keep the controlled textarea
  responsive mid-edit), but that meant an incomplete/invalid edit
  (e.g. `{"a":1` before the closing brace) could persist into the
  actual tool arguments. If executed in that state, the MCP execute
  route's array-coercion step would silently wrap the malformed string
  into a corrupted array instead of failing validation.

Invalid-edit text now lives in local `invalidJsonDrafts` state, keyed
by param name, instead of the real argument store — the textarea still
reflects every keystroke, but the persisted tool argument is always
either the last successfully parsed value or untouched. Drafts reset
when the selected tool changes.

* fix(mcp): reset invalid JSON drafts on schema change, not just tool change

The draft reset only fired when the selected tool id changed, so a
same-tool schema refresh (e.g. re-discovering tools from the live MCP
server) could leave a stale invalid draft displayed under a param name
whose shape had since changed. Key the reset off both the tool id and
a signature of the effective schema's properties, so any change to
what's actually being edited clears stale drafts.

* fix(mcp): key draft reset off both schema sources, not the resolved toolSchema

toolSchema resolves to cachedSchema || selectedToolConfig?.inputSchema,
so a live-only schema refresh (the discovered tool's inputSchema
changes but the cached _toolSchema snapshot doesn't) left the reset
key unchanged and could keep a stale invalid draft on screen. Track a
signature of each schema source independently so a change in either
one clears drafts, regardless of which source toolSchema resolves to.

* fix(mcp): sign whole schema for draft reset; invalidate drafts on external value changes

Two more follow-up gaps:

- schemaSignature only serialized schema.properties, so a same-tool
  refresh that changed only top-level fields like `required` (properties
  byte-identical) left the reset key unchanged. Sign the entire schema
  instead of cherry-picking fields, so nothing schema-level can be missed.

- A draft only reset on tool/schema change, so if the persisted argument
  changed for any other reason (undo/redo, a diff baseline switch, a
  collaborator's concurrent edit), the draft could keep shadowing the
  now-current value in the editor while execution used the real one.
  Drafts now carry a baseline signature of the value they were typed
  against; a draft only displays while that baseline still matches the
  live persisted value, so any external change makes it fall back to
  showing the real value instead of stale text.

* fix(mcp): restore comma-separated array input; stop spurious draft reset on tool load

Two more follow-up gaps:

- Holding every JSON.parse failure in a local draft blocked the
  documented comma-separated array shorthand (see the placeholder text)
  from ever reaching toolArgs, since plain comma-separated text is
  never valid JSON. Only an in-progress JSON array/object literal
  (starting with `[` or `{`) needs to stay in the draft until valid;
  plain array-typed text that isn't attempting JSON persists
  immediately as before, letting the execute route's existing
  comma-split/wrap coercion handle it as designed.

- draftResetKey always included the live selectedToolConfig schema
  signature, even when cachedSchema wins the `toolSchema` resolution.
  That segment flips from empty to populated the moment mcpTools
  finishes an unrelated async load, wiping in-progress drafts though
  neither the rendered schema nor the stored args changed. The live
  signature now only factors into the key when there's no cached
  snapshot for toolSchema to prefer.

* fix(mcp): reset drafts on genuine live schema refresh, not just its first load

Excluding the live schema signature whenever a cached snapshot exists
(the prior fix for a Cursor finding about mcpTools' initial load
spuriously wiping drafts) went too far the other way: a genuine
same-tool live schema refresh while a cached snapshot is still present
would no longer reset drafts either.

Track the live schema signature unconditionally, but only treat a
change as a real reset trigger when it goes from one non-empty
signature to a *different* non-empty one. The bare empty → non-empty
transition (mcpTools completing its initial fetch) is excluded, since
that's not a schema change; a populated → differently-populated
transition (an actual re-discovery) still resets drafts regardless of
whether a cached snapshot is present.

* fix(mcp): compare live schema against last-known-non-empty, not prior render

Comparing the live schema signature only against the immediately
preceding render's value meant a schema that dropped to empty and then
reappeared with different content was invisible to the reset check —
both the drop (X → '') and the reappearance ('' → Y) look like a bare
empty/non-empty transition, which was deliberately excluded to avoid
resetting on mcpTools' initial load. Track the last non-empty value
actually observed instead, so a transient empty gap no longer erases
the baseline: the schema reset now fires correctly when the tool
reappears with a genuinely different schema, while a real first-ever
load (no prior non-empty value at all) still doesn't spuriously reset.

* fix(mcp): re-baseline live schema tracker fresh on every tool switch

lastNonEmptyLiveSchemaSignature carried over across a tool switch
whenever the newly-selected tool's live schema hadn't loaded yet in
that same render (still empty). When it loaded a moment later, the
comparison was against the *previous* tool's signature, so the new
tool's first schema load could be misread as a "genuine refresh" and
wipe drafts the user had already started typing against the new tool.
A tool/cached-schema change now always re-baselines the live-schema
tracker to the new tool's current signature (even if still empty), so
the "same tool" refresh comparison never bleeds across tool switches.
2026-07-10 14:37:39 -07:00

Sim.ai Documentation Discord X

Ask DeepWiki Set Up with Cursor

Sim — Integrate, Context, Build, and Monitor AI agents

A workspace to build, deploy and manage AI agents and workflows.

Quickstart

Cloud-hosted: sim.ai

Open sim.ai

Self-hosted

npx simstudio

Open http://localhost:3000

Docker must be installed and running. Use -p, --port <port> to run Sim on a different port, or --no-pull to skip pulling the latest Docker images.

The Sim platform — chat on the left, the visual workflow builder on the right

Capabilities

  • Connect 1,000+ integrations and every major LLM
  • Add Slack, Notion, HubSpot, Salesforce, databases, and more
  • Build agents visually, conversationally, or with code
  • Ingest files, knowledge bases, and structured table data
  • Monitor runs, logs, schedules, and workflow activity

One workspace, every surface

Chat and workflows are just the start — tables, files, knowledge, and scheduled tasks all live in the same workspace.

Tables in Sim — structured data your agents can query

Tables — a database, built in

Files in Sim — documents for your team and every agent

Files — one store for your team and every agent

Knowledge bases in Sim — synced docs your agents can search

Knowledge — your agents' memory

Scheduled tasks in Sim — recurring agent runs on a calendar

Scheduled tasks — runs on your schedule

Self-hosting

Docker Compose

git clone https://github.com/simstudioai/sim.git && cd sim
docker compose -f docker-compose.prod.yml up -d

Open http://localhost:3000

Sim also supports local models via Ollama and vLLM. See the Docker self-hosting docs for setup details.

Manual Setup

Requirements: Bun, Node.js v20+, PostgreSQL 12+ with pgvector

  1. Clone and install:
git clone https://github.com/simstudioai/sim.git
cd sim
bun install
bun run prepare  # Set up pre-commit hooks
  1. Set up PostgreSQL with pgvector:
docker run --name simstudio-db -e POSTGRES_PASSWORD=your_password -e POSTGRES_DB=simstudio -p 5432:5432 -d pgvector/pgvector:pg17

Or install manually via the pgvector guide.

  1. Configure environment:
cp apps/sim/.env.example apps/sim/.env
# Create your secrets
perl -i -pe "s/your_encryption_key/$(openssl rand -hex 32)/" apps/sim/.env
perl -i -pe "s/your_internal_api_secret/$(openssl rand -hex 32)/" apps/sim/.env
perl -i -pe "s/your_api_encryption_key/$(openssl rand -hex 32)/" apps/sim/.env
# DB configs for migration
cp packages/db/.env.example packages/db/.env
# Edit both .env files to set DATABASE_URL="postgresql://postgres:your_password@localhost:5432/simstudio"
  1. Run migrations:
cd packages/db && bun run db:migrate
  1. Start development servers:
bun run dev:full  # Starts Next.js app and realtime socket server

Or run separately: bun run dev (Next.js) and cd apps/sim && bun run dev:sockets (realtime).

Chat API Keys

Chat is a Sim-managed service. To use Chat on a self-hosted instance:

  • Go to https://sim.ai → Settings → Chat keys and generate a Chat API key
  • Set COPILOT_API_KEY environment variable in your self-hosted apps/sim/.env file to that value

Environment Variables

See the environment variables reference for the full list, or apps/sim/.env.example for defaults.

Tech Stack

Next.js · Bun · PostgreSQL · Drizzle · Better Auth · Tailwind — and the rest of the stack

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Built by the Sim team in San Francisco

Languages
TypeScript 77%
MDX 20.8%
JavaScript 1.9%
CSS 0.1%