* 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.
A workspace to build, deploy and manage AI agents and workflows.
Quickstart
Cloud-hosted: sim.ai
Self-hosted
npx simstudio
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.
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 — a database, built in |
Files — one store for your team and every agent |
Knowledge — your agents' memory |
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
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
- Clone and install:
git clone https://github.com/simstudioai/sim.git
cd sim
bun install
bun run prepare # Set up pre-commit hooks
- 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.
- 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"
- Run migrations:
cd packages/db && bun run db:migrate
- 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_KEYenvironment 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
- Framework: Next.js (App Router)
- Runtime: Bun
- Database: PostgreSQL with Drizzle ORM
- Authentication: Better Auth
- Schema Validation: Zod
- UI: Shadcn, Tailwind CSS
- Streaming Markdown: Streamdown
- State Management: Zustand, TanStack Query
- Flow Editor: ReactFlow
- Docs: Fumadocs
- Monorepo: Turborepo
- Realtime: Socket.io
- Background Jobs: Trigger.dev
- Remote Code Execution: E2B
- Isolated Code Execution: isolated-vm
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.





