MCP Hub
Back to servers

SageFs

Sage Mode for F# development — REPL with solution or project loading, Live Testing for FREE, Hot Reload, and session management.

Stars
36
Forks
1
Updated
Feb 28, 2026
Validated
Mar 1, 2026

SageFs

A live F# development server that eliminates the edit-build-run cycle. Edit code, save, see changes in your browser — instantly. No restart. No rebuild. Just flow.

SageFs is a .NET global tool that turns F# Interactive into a full development environment: project loading, sub-second hot reload, file watching, multi-session isolation, a web dashboard, an MCP server that gives AI agents live access to your running code, and live unit testing that runs affected tests on every edit — across every editor, every major .NET test framework — for free.

License .NET Save→Green

SageFs Hot Reload Demo — edit F# code and see changes in the browser instantly


Why SageFs?

The problem: F# development has a painful feedback loop. Change a line → wait for dotnet build → restart your app → navigate back to where you were → check if it worked. For web apps, this can be 30-60 seconds per change. Interactive development with dotnet fsi helps, but it can't load your project's dependencies, doesn't watch files, and has no IDE integration.

SageFs fixes all of this:

  • Sub-second hot reload — Save a .fs file and your running web server picks up the change in ~100ms. Harmony patches method pointers at runtime — no restart, no rebuild. Browsers auto-refresh via SSE.
  • Live unit testing — Edit code and affected tests run automatically in under 500ms. Gutter markers show pass/fail on test code and coverage on production code — across every editor. No IL instrumentation, no separate test runner. See details below.
  • Full project context in the REPL — All your NuGet packages, project references, and namespaces are loaded automatically. No #r directives. It's your actual project, live.
  • AI agents that can compile and run your code — SageFs exposes an MCP server so AI tools (Copilot, Claude, etc.) can execute F# code, type-check, explore .NET APIs, and run tests — all against your real project.
  • One server, every frontend — Start the daemon once. Connect from VS Code, Neovim, the terminal, a GPU-rendered GUI, a web dashboard, or all of them at the same time. They all share the same live session state.
  • Crash-proof sessions — An Erlang-style supervisor auto-restarts the daemon on crash. Worker sessions run in isolated sub-processes — one crash doesn't take down the others.

Prerequisites

  • .NET 10 SDK
  • Docker (optional) — only needed if you use --persist for PostgreSQL event sourcing via Marten. Without Docker, SageFs runs fully in-memory with zero external dependencies.

Installation

SageFs is a .NET global tool published on NuGet:

dotnet tool install --global SageFs

Verify it installed:

sagefs --help

To update to the latest version:

dotnet tool update --global SageFs
Build from source
git clone https://github.com/WillEhrendreich/SageFs.git
cd SageFs
dotnet build && dotnet pack SageFs -o nupkg
dotnet tool install --global SageFs --add-source ./nupkg --no-cache

Getting Started

Navigate to any F# project directory and run:

sagefs --proj MyApp.fsproj

What happens:

  1. SageFs starts a daemon — a background server that stays running
  2. It builds your project and loads all dependencies into an F# Interactive session
  3. It starts watching your source files for changes
  4. It opens an MCP server on http://localhost:37749/sse (for AI agents)
  5. It opens a live dashboard at http://localhost:37750/dashboard

That's it. SageFs is running. Open the dashboard, press Ctrl+Enter on some F# code, and see the result immediately.


How to Use SageFs

SageFs is a daemon — one server, many clients. Start it once, connect from anywhere.

VS Code Extension

The SageFs extension turns VS Code into a live F# development environment with inline eval results, real-time diagnostics, hot reload controls, live unit testing with inline pass/fail markers, and session management — all powered by the running SageFs daemon.

What you get:

  • Alt+Enter — Evaluate the current selection or ;;-delimited code block. Results appear as inline decorations right next to your code.
  • Alt+Shift+Enter — Evaluate the entire file
  • CodeLens — Clickable "▶ Eval" buttons above every ;; block, plus live test result CodeLens (✓ Passed / ✗ Failed) above test functions
  • Live test decorations — Inline ✓/✗/● markers on test lines, updated in real-time via SSE. Failed tests also appear as native VS Code diagnostics (squiggles).
  • Native Test Explorer — Tests appear in VS Code's built-in Test Explorer panel via a TestController adapter, with pass/fail status synced from the daemon
  • Live diagnostics — Type errors and warnings stream in via SSE as you edit, appearing as native VS Code squiggles
  • Hot Reload sidebar — A tree view in the activity bar showing all project files with watch toggles. Toggle individual files, directories, or watch/unwatch everything at once.
  • Session Context sidebar — See loaded assemblies, opened namespaces, failed opens, and warmup details for the active session
  • Type Explorer sidebar — Browse .NET types and namespaces interactively from the activity bar
  • Test policy controls — Enable/disable live testing, run all tests, or configure run policies (unit on keystroke, integration on save, browser on demand) from the command palette
  • Call graph viewer — Visualize test dependency graphs via sagefs.showCallGraph
  • Event history — Browse recent pipeline events via sagefs.showHistory QuickPick
  • Dashboard webview — Open the SageFs dashboard directly inside VS Code as a webview panel
  • Status bar — Shows the active project, eval count, supervised status, and restart count. Click it to open the web dashboard.
  • Multi-session support — Create, switch, and manage multiple sessions from the command palette
  • Auto-start — Detects .fsproj/.sln/.slnx files and offers to start SageFs automatically
  • Ionide integration — Hijacks Ionide's FSI: Send Selection commands so Alt+Enter routes through SageFs instead of plain FSI

Installing the VS Code Extension

Note: The SageFs VS Code extension is not published on the VS Marketplace. Install it manually from a .vsix file.

Option A: Download from GitHub Releases (recommended)

Each GitHub Release includes a .vsix file. Download the latest and install:

code --install-extension sagefs-<version>.vsix

Reload VS Code and you're ready to go.

Option B: Build from source

cd sagefs-vscode
npm install
npm run compile
npx @vscode/vsce package
code --install-extension sagefs-*.vsix

Extension Settings

SettingDefaultDescription
sagefs.mcpPort37749SageFs MCP server port
sagefs.dashboardPort37750SageFs dashboard port
sagefs.autoStarttrueAutomatically start SageFs when opening F# projects
sagefs.projectPath""Explicit .fsproj path (auto-detect if empty)

Note: The VS Code extension is written entirely in F# using Fable — no TypeScript. The F# source compiles to JavaScript, giving you type-safe extension code with the same language as your project.

Neovim Plugin

sagefs.nvim is a full-featured Neovim frontend — 24 Lua modules, 800+ tests, zero failures. Pure Lua core (testable with busted outside Neovim) plus a thin integration layer for vim APIs.

-- lazy.nvim
{
  "WillEhrendreich/sagefs.nvim",
  ft = { "fsharp" },
  opts = { port = 37749, auto_connect = true },
}

What you get:

  • Alt+Enter — Evaluate the ;;-delimited cell under cursor. Shift+Alt+Enter — Evaluate and advance to next cell. Visual mode evaluation too.
  • Inline results — Success/error output as virtual text at the ;; boundary, multi-line output rendered below
  • Gutter signs — ✓/✖/⏳ indicators for cell state, plus flash animation when evaluation starts
  • Stale detection — Editing a cell automatically marks its result as stale
  • CodeLens-style markers — "▶ Eval" virtual text above idle/stale cells
  • SSE live updates — Subscribes to the SageFs event stream with exponential backoff reconnect (1s→32s). Full state recovery on reconnect.
  • Live diagnostics — F# errors/warnings streamed via SSE into vim.diagnostic
  • Check on saveBufWritePost sends .fsx file content for type-checking (configurable)
  • Live test gutter signs — Pass/fail/running/stale signs per test in the sign column
  • Live test panel:SageFsTestPanel for a persistent split with test results, <CR> to jump to source
  • Tests for current file:SageFsTestsHere shows tests covering the file you're editing
  • Test policy controls:SageFsTestPolicy for category+policy drill-down
  • Pipeline trace:SageFsPipelineTrace shows the three-speed pipeline state
  • Coverage gutter signs — Green=covered, Red=uncovered per-line signs from FCS symbol graph
  • Coverage panel:SageFsCoverage with per-file breakdown and totals
  • Type explorer:SageFsTypeExplorer for assembly→namespace→type→members drill-down, or :SageFsTypeExplorerFlat for fuzzy pick
  • Session management — Create, switch, stop, reset sessions via picker
  • Hot reload controls — Per-file toggle, watch-all, unwatch-all
  • Daemon lifecycle:SageFsStart/:SageFsStop to manage the daemon from Neovim
  • Status dashboard:SageFsStatus with daemon, session, tests, coverage, and config
  • History browser:SageFsHistory with preview of past evaluations
  • Export to .fsx:SageFsExport exports session history as executable F# script
  • Call graph:SageFsCallers/:SageFsCallees for symbol dependency navigation
  • Code completion — Omnifunc-based completions via SageFs completion endpoint
  • Combined statuslinerequire("sagefs").statusline() → session │ testing │ coverage │ daemon

33 commands, 11 user autocmd events, and full parity with VS Code features. See the sagefs.nvim README for full setup, keybindings, and architecture details.

Visual Studio Extension

The SageFs Visual Studio extension in sagefs-vs/ uses the VisualStudio.Extensibility SDK with a thin C# shell and all real logic in an F# core library (SageFs.VisualStudio.Core).

Note: The VS extension is in early development. Core eval, CodeLens, and session features work. Live testing gutter markers, coverage, and some advanced features are not yet implemented. See the sagefs-vs README for current status.

What you get:

  • Alt+Enter — Evaluate selection, Shift+Alt+Enter — Evaluate file, Ctrl+Alt+Enter — Evaluate ;;-delimited block
  • CodeLens — "▶ Eval" buttons on every F# function, type, and module
  • Error List integration — SageFs diagnostics stream into the native VS Error List via SSE
  • Session Context tool window — Live dashboard showing connection status, assemblies, namespaces, warmup details
  • Hot reload — Toggle files, directories, watch/unwatch all from the Extensions menu
  • Daemon lifecycle — Start/stop the SageFs daemon from the Extensions menu
  • Multi-session — Create, switch, reset, and hard-reset sessions
  • Output window — All eval results and command feedback logged to the SageFs output channel

Not yet implemented: Live test gutter markers, coverage gutter signs, test panel, test policy picker, type explorer, call graph, history browser, status dashboard, code completion UI (HTTP client ready, VS SDK doesn't expose completion API yet).

AI Agent (MCP)

SageFs speaks Model Context Protocol. Point your AI tool at the MCP endpoint and it becomes a live F# development partner — executing code, type-checking, exploring APIs, and running tests against your real project.

GitHub Copilot (CLI & VS Code)

Edit your MCP config file (usually ~/.config/.copilot/mcp-config.json or wherever your Copilot MCP servers are configured):

{
  "mcpServers": {
    "sagefs": {
      "type": "sse",
      "url": "http://localhost:37749/sse",
      "headers": {},
      "tools": ["*"]
    }
  }
}

In VS Code, you can also add it to .vscode/mcp.json in your workspace:

{
  "servers": {
    "sagefs": {
      "type": "sse",
      "url": "http://localhost:37749/sse"
    }
  }
}
Claude Code (CLI)

Add a .mcp.json file to your project root:

{
  "mcpServers": {
    "sagefs": {
      "type": "sse",
      "url": "http://localhost:37749/sse"
    }
  }
}

Or configure globally via claude mcp add --transport sse sagefs http://localhost:37749/sse.

Claude Desktop

Edit claude_desktop_config.json (Settings → Developer → Edit Config):

{
  "mcpServers": {
    "sagefs": {
      "type": "sse",
      "url": "http://localhost:37749/sse"
    }
  }
}
Any MCP-compatible client

SageFs exposes a standard Model Context Protocol SSE endpoint:

http://localhost:37749/sse

Connect with any MCP client that supports SSE transport. No API key required — it's a local server.

Agent Edit→Test→Poll workflow

The ideal AI agent loop with SageFs:

  1. Edit code — agent writes to .fs files (via editor or filesystem tools)
  2. Tests auto-run — SageFs's file watcher detects the change, runs affected tests automatically
  3. Poll status — agent calls get_live_test_status to see pass/fail results
Agent edits src/MyModule.fs
  → SageFs detects change (~100ms)
  → Affected tests run (~200-500ms)
  → Agent polls get_live_test_status → sees green/red

Alternatively, for explicit control: run_tests with pattern or category filters runs tests synchronously and returns results directly. Use timeout_seconds to set a deadline.

The key insight: you don't need to call send_fsharp_code to trigger tests. Just edit files. SageFs's pipeline handles the rest. Use send_fsharp_code for exploratory work, prototyping, and REPL-style development.

REPL Client

sagefs connect

A text-based REPL that connects to the running daemon. Type F# code, get results. Use #help for commands, #sessions to manage multiple sessions.

Terminal UI

sagefs tui

A multi-pane terminal interface: editor, output, diagnostics, session context. Navigate with Tab, manage sessions with keyboard shortcuts. Tree-sitter syntax highlighting, mouse support, and the Kanagawa color theme by default.

GPU-Rendered GUI

sagefs gui

A native GPU-rendered window via Raylib with the same layout as the TUI. Both the TUI and GUI share the same rendering abstraction (Cell[,] grid) — same keybindings, same layout, same features.

Web Dashboard

Already running at http://localhost:37750/dashboard. Submit code, view session status, manage sessions — all from the browser. Powered by Falco.Datastar for real-time SSE updates.

All of these connect to the same daemon. Open multiple at once — they all see the same state.

Frontend Feature Matrix

FeatureTUIRaylib GUIWeb DashboardVS CodeVisual StudioNeovim
Eval code
Eval filen/a¹n/a¹
Eval block (;;)
Inline results
Diagnostics
Create / switch session
Stop session
Reset / hard reset
Session context
Hot reload toggle
Watch / unwatch all
Code completion—²
SSE live updates
Themes—²
CodeLensn/a¹n/a¹n/a¹
Project discovery
Session resume
Live test gutter signs—³—³
Test panel / results
Coverage gutter signs—³—³—³
Coverage panel
Test policy controls
Pipeline trace
Type explorer
Call graph
History browser
Daemon lifecycle
Status dashboard
Branch coverage gutters
Display density presets
SSE session scoping
Filterable test panel

¹ n/a — Feature is architecturally inapplicable. TUI/Raylib are REPL interfaces (eval file = just type code); CodeLens requires an editor with source buffers. ² — VS Extensibility SDK (out-of-process, v17.14) does not yet expose completion provider or theme color contribution APIs. The HTTP client (GetCompletionsAsync) is implemented; UI integration awaits SDK support. ³ Server-side data is ready (LineAnnotation, SSE events). Editor UI integration pending.


Key Features

🧪 Live Unit Testing

Visual Studio Enterprise charges ~$250/month for Live Unit Testing. SageFs does it better — across every editor, every major .NET test framework — for free.

Edit code → tests run automatically → results appear inline, in under 500ms.

✓ let ``should add two numbers`` () =       ← passed (12ms)
✗ let ``should reject negative`` () =       ← failed: Expected Ok but got Error
● let ``should handle empty`` () =          ← detected, not yet run
▸ let validate x =                          ← covered by 3 tests, all passing
○ let unusedHelper () = ()                  ← not reached by any test

Gutter markers appear in your editor (VS Code, Neovim, TUI, Visual Studio) showing test status on test code and test reachability on production code. Lightweight FCS-based coverage requires no IL instrumentation; optional IL branch-level probes provide three-state branch coverage (fully covered, partially covered, uncovered) with shape+color gutter signs for accessibility.

How it's different from VS Enterprise:

VS Enterprise Live TestingSageFs Live Testing
Speed5-30 seconds (full MSBuild rebuild)200-500ms (FSI hot reload, no build)
ScopeRebuilds all impacted projectsScope-level: just the function being edited
Broken codeMust compile to instrumentTree-sitter works on broken/incomplete code
FrameworksMSTest, xUnit, NUnit only+ Expecto, TUnit, extensible provider model
Coverage methodIL instrumentation (heavy)Dual: FCS typed AST symbol graph (lightweight) + IL branch-level probes
EditorsVisual Studio onlyVS Code, Neovim, TUI, GUI, Visual Studio, web dashboard
Cost~$250/monthFree, MIT licensed

Three-speed feedback pipeline:

  1. ~50ms — Tree-sitter detects test attributes in broken/incomplete code → immediate gutter markers
  2. ~350ms — F# Compiler Service type-checks → namespace disambiguation, dependency graph, reachability annotations
  3. ~500ms — Harmony patches + affected-test execution → ✓/✗ results inline

Tests are categorized automatically (Unit, Integration, Browser, Property, Benchmark) with smart run policies — unit and property tests run on every keystroke, integration tests run on save, browser tests run on demand. All configurable.

What's next:

  • As-you-type evaluation — Edit a function body and tests re-run without saving. Tree-sitter extracts the scope, POSTs to SageFs, FSI redefines the binding. Currently requires save; as-you-type is in development for all editors.
  • VS Code coverage gutter markers — Coverage annotations on production code lines (server data ready, UI not yet connected)
  • Raylib GUI gutter rendering — Gutter icons in the GPU-rendered GUI frontend
  • Visual Studio gutter markers — Margin glyphs via the VS Extensibility SDK
Implementation details (for contributors)
  • Pure domain model with SHA256-stable TestId, full Elm state management
  • Two-tier provider: attribute-based (xUnit/NUnit/MSTest/TUnit) + custom (Expecto)
  • Tree-sitter tests.scm query for instant detection in broken code
  • FCS SymbolGraphBuilder for dependency maps, SymbolDiff for change detection
  • PipelineDebounce with per-stage cancellation, AdaptiveDebounce for FCS backoff
  • SourceMapping bridges tree-sitter locations to reflection-discovered tests
  • OTEL instrumentation: histograms for all pipeline stages, zero-cost when no collector
  • SSE push: test_summary + test_results_batch events with JsonFSharpConverter
  • 2900+ tests including FsCheck property-based state machine tests

🔥 Hot Reload

This is the headline feature. Save a .fs file and SageFs:

  1. Detects the change (~500ms debounce)
  2. Sends #load to FSI (~100ms)
  3. Harmony patches method pointers at runtime — no restart
  4. Connected browsers auto-refresh via SSE (add SageFs.DevReloadMiddleware to your app)
// Add to your Falco/ASP.NET app for auto browser refresh:
open SageFs.DevReloadMiddleware

webHost [||] {
  use_middleware middleware
  // your routes...
}

Edit a handler, save the file, and the browser refreshes with the new code — all in under a second.

The VS Code extension also gives you per-file and per-directory hot reload toggles, so you control exactly which files trigger live patching.

🤖 AI-Native (MCP Server)

SageFs doesn't just expose static tools to AI agents — it uses an affordance-driven state machine that only presents tools valid for the current session state. An agent connecting to a warming-up session sees get_fsi_status; once ready, it sees send_fsharp_code, check_fsharp_code, etc. Invalid tool calls return structured errors with alternatives. This eliminates wasted tokens from agents guessing which tools work.

The MCP response strategy is also optimized for LLM context windows — echoed code is stripped, boilerplate moves to ServerInstructions (sent once), events use delta cursors instead of re-sending everything.

ToolWhat it does
send_fsharp_codeExecute F# code (each ;; is a transaction — failures are isolated)
check_fsharp_codeType-check without executing (pre-validate before committing)
get_completionsCode completions at cursor position
explore_typeBrowse members of any .NET type
explore_namespaceBrowse types in a namespace
cancel_evalCancel a running evaluation (recover from infinite loops)
create_sessionSpin up a new isolated FSI session
hard_reset_fsi_sessionRebuild and reload (after source file changes)
get_live_test_statusQuery live test state with optional file filter
run_testsRun tests on demand with pattern/category filters
set_run_policyControl which test categories auto-run and when
set_live_testingEnable/disable the live testing pipeline
get_pipeline_traceDebug the three-speed pipeline waterfall

Full tool list →

Security scope: The MCP server binds to localhost only and has no authentication. This is by design — SageFs is a local development tool. The HTTP and SSE endpoints are tunnelable (e.g., via VS Code Remote or SSH port forwarding) but are not authenticated, so exposing them to untrusted networks is not recommended. If you need remote access, use SSH tunneling or a VPN.

📦 Project & Solution Support

sagefs --proj MyApp.fsproj       # Load one project
sagefs --sln MySolution.sln      # Load entire solution
sagefs                           # Auto-detect in current directory
sagefs --bare                    # No project, just bare FSI

SageFs loads all NuGet packages, project references, and namespaces automatically. No manual #r directives needed.

👁️ File Watching

Source files are watched automatically. The escalation chain: .fs/.fsx changes → incremental #load reload (~100ms). .fsproj changes → soft reset. Rapid saves are debounced (500ms). Failed reloads are atomic — old definitions stay valid. Disable with --no-watch.

🔀 Multi-Session

Run multiple F# sessions simultaneously — different projects, different states. Each session is an isolated worker sub-process so one crash doesn't take down the others. Create, switch, and stop sessions from any frontend (VS Code, Neovim, REPL, dashboard, or MCP). SSE events are tagged with SessionId so each editor only renders results from its active session — no cross-talk between windows watching different projects.

🛡️ Supervised Mode (Watchdog)

sagefs --supervised --proj MyApp.fsproj

Wraps the daemon in an Erlang-style supervisor with exponential backoff (1s → 2s → 4s → max 30s). After 5 consecutive crashes within 5 minutes, it reports the failure. The watchdog state is exposed via /api/system/status and shown in the VS Code status bar. Use this when leaving SageFs running all day.

⚡ Standby Pool

Hard resets are fast because SageFs maintains a standby pool of pre-warmed FSI sessions. When you reset, the active session is replaced with an already-warm one from the pool — near-instant recovery instead of a 30-60 second rebuild.

📊 Event Sourcing

All session events (evals, resets, diagnostics, errors) are stored in PostgreSQL via Marten. This provides a durable history of your development sessions. Future tooling will enable session replay and analytics on top of your coding patterns.


Common Workflows

F# Web Development (Falco)

sagefs --proj MyWebApp.fsproj

Edit your handlers → save → SageFs hot-reloads → browser auto-refreshes. Sub-second feedback loop. Add SageFs.DevReloadMiddleware to your pipeline for automatic browser refresh.

AI-Assisted Development

Start SageFs, configure your AI tool's MCP settings, and your AI agent becomes a live F# development partner — it can execute code, check types, explore APIs, and run tests through SageFs. The affordance-driven tool exposure means agents succeed on the first attempt instead of guessing.

REPL-Driven Development

sagefs connect

Prototype functions, test ideas, explore APIs — with your full project loaded. Everything you'd do in dotnet fsi but with your actual project dependencies available.

Test-Driven Development

Run Expecto tests directly inside SageFs — no separate test runner needed. Write a test, evaluate it, see red/green immediately. Change code, re-run, iterate. The REPL is your test runner.


CLI Reference

SageFs - F# Interactive daemon with MCP, hot reloading, and live dashboard

Usage: SageFs [options]                Start daemon (default mode)
       SageFs --supervised [options]   Start with watchdog auto-restart
       SageFs connect                  Connect to running daemon (REPL)
       SageFs tui                      Terminal UI client for running daemon
       SageFs gui                      GPU-rendered Raylib GUI client
       SageFs stop                     Stop running daemon
       SageFs status                   Show daemon info
       SageFs worker [options]         Internal: worker process

Options:
  --version, -v          Show version information
  --help, -h             Show this help message
  --mcp-port PORT        Set custom MCP server port (default: 37749)
  --supervised           Run under watchdog supervisor (auto-restart on crash)
  --bare                 Start a bare FSI session — no project/solution loading
  --no-watch             Disable file watching — no automatic #load on changes
  --no-resume            Skip restoring previous sessions on daemon startup
  --prune                Mark all stale sessions as stopped and exit
  --proj FILE            Load project from .fsproj file
  --sln FILE             Load all projects from solution file
  --dir DIR              Set working directory
  --reference:FILE       Reference a .NET assembly
  --load:FILE            Load and compile an F# source file at startup
  --use:FILE             Use a file for initial input/prompt config
  --lib DIR [DIR...]     Directories to search for referenced assemblies
  --other ARGS...        Pass remaining arguments to FSI

Environment Variables:
  SageFs_MCP_PORT        Override MCP server port (same as --mcp-port)
  SAGEFS_BIND_HOST       Bind address (default: localhost, use 0.0.0.0 for Docker)

Examples:

# Start daemon (default)
sagefs                           # Auto-detect project
sagefs --proj MyApp.fsproj       # Specific project
sagefs --sln MySolution.sln      # Specific solution
sagefs --bare                    # No project, just bare FSI

# Connect clients
sagefs connect                   # REPL client
sagefs tui                       # Terminal UI
sagefs gui                       # GPU GUI (Raylib)

# Manage daemon
sagefs stop                      # Stop running daemon
sagefs status                    # Show daemon info

# Production / long-running
sagefs --supervised              # Auto-restart on crash

# Advanced
sagefs --mcp-port 8080           # Custom MCP port
sagefs --no-watch                # Disable file watcher
sagefs --use:script.fsx          # Run script on startup
sagefs --reference:Lib.dll       # Reference an assembly

Configuration

Per-Directory Config

Create .SageFs/config.fsx in your project directory:

{ DirectoryConfig.empty with
    Load = Projects ["src/MyApp.fsproj"; "tests/MyApp.Tests.fsproj"]
    InitScript = Some "setup.fsx" }

Precedence: CLI args > .SageFs/config.fsx > auto-discovery.

Startup Profile

SageFs auto-loads ~/.SageFs/init.fsx on session start, if it exists. Use it for personal helpers, open statements, or custom setup that should apply to every session.


Troubleshooting

"SageFs daemon not found" — Make sure the daemon is running (sagefs --proj ... in another terminal). Clients auto-discover via HTTP health check on port 37749. Run sagefs status to check.

"Session is still starting up" — The FSI session is loading your project. Wait for the "ready" message. Large projects may take 30-60 seconds. The standby pool makes subsequent resets much faster.

Build errors after code changes — If you changed .fs files and the REPL seems stale, run hard_reset_fsi_session (via MCP) or #hard-reset (in the REPL). This rebuilds and reloads. Note: file watching handles most cases automatically — you shouldn't need manual resets often.

Port already in use — Another SageFs instance is running. Use sagefs stop or sagefs --mcp-port 8080. Check sagefs status to see what's running.

Running in Docker — Set SAGEFS_BIND_HOST=0.0.0.0 so the daemon listens on all interfaces (required for container port mapping). The default localhost only binds to the loopback interface.

Hot reload not working — Make sure your app uses SageFs.DevReloadMiddleware for browser auto-refresh. Check the SageFs console for 🔥 or 📄 messages confirming file changes are detected.

SSE connections dropping — Proxies and load balancers may close idle connections. SageFs sends keepalive comments every 15 seconds on SSE endpoints. If using a reverse proxy (nginx, Cloudflare Tunnel), set the proxy timeout to at least 60 seconds.

Live testing not running — Verify live testing is enabled with set_live_testing (MCP) or check the status bar. By default, only unit tests auto-run on every change; integration and browser tests require explicit triggers. Use set_run_policy to configure per-category behavior.

Tests discovered but not executing — Check the run policy for each test category. Integration, browser, and benchmark tests default to OnDemand — they won't auto-run. Use run_tests (MCP tool) to trigger them manually, or change the policy with set_run_policy.

Where are the logs? — The daemon console shows real-time output. For structured logging, SageFs supports OTEL export — connect a collector (e.g., Aspire dashboard) to see traces and metrics. The daemon startup message shows the dashboard URL (default: http://localhost:37750/dashboard).


FSI Quirks & Rewrites

FSI (F# Interactive) is a REPL — it evaluates expressions and definitions, not files. This gives SageFs instant feedback, but FSI has behavioral differences from compiled F# that can surprise newcomers. SageFs applies automatic rewrites to smooth over the biggest gotcha, and this section documents what happens and why.

uselet Rewrite (Automatic)

What happens: When you send code to SageFs, any indented use binding inside a function or computation expression is silently rewritten to let before FSI evaluates it.

// You write:                       // FSI receives:
let processFile path =              let processFile path =
  use stream = File.OpenRead(path)    let stream = File.OpenRead(path)
  use reader = new StreamReader(s)    let reader = new StreamReader(s)
  reader.ReadToEnd()                  reader.ReadToEnd()

Why: FSI doesn't support use bindings inside nested scopes (functions, async {} blocks, task {} CEs, etc.) even though compiled F# does. Without this rewrite, common patterns with IDisposable resources would fail with cryptic errors in the REPL.

What this means for you:

  • ⚠️ Disposables are NOT automatically disposed in the REPL. In compiled code, use calls .Dispose() at scope exit. In FSI (after rewrite), it's just let — no disposal happens. For short-lived REPL experiments this is fine. For long-running sessions with file handles or database connections, be aware.
  • Top-level use bindings are NOT rewritten — only indented ones inside functions/CEs.
  • Your source files are never modified — the rewrite happens in-memory on the code sent to FSI.
  • The rewrite is logged — check the daemon console for "FSI Compatibility: Rewrote 'use' to 'let'" if you want to see when it fires.

Other FSI vs Compiled Differences

These are not rewritten by SageFs — they're inherent FSI behaviors to be aware of:

  • Redefinition is additive. Sending let x = 1;; then let x = 2;; doesn't error — it shadows the previous x. Both definitions exist in memory; only the latest is visible.
  • ;; terminates an interaction. Each ;; is an independent evaluation. If one fails, previous definitions from earlier ;; boundaries are still alive.
  • No [<EntryPoint>]. FSI doesn't run main — it evaluates definitions and expressions interactively.
  • Assembly loading is session-scoped. #r directives and loaded DLLs persist for the session lifetime. To unload, reset the session.

Want to Improve This?

The rewrite logic lives in SageFs.Core/FsiRewrite.fs (~25 lines) with tests in SageFs.Tests/FsiRewriteTests.fs. If you know of a better way to handle use in FSI, or want to add rewrites for other FSI quirks, PRs are very welcome. The middleware pipeline in SageFs.Core/Middleware/FsiCompatibility.fs makes it easy to add new transforms.


MCP Tools Reference

ToolDescription
send_fsharp_codeExecute F# code. Each ;; marks a transaction boundary.
check_fsharp_codeType-check without executing. Returns diagnostics.
get_completionsCode completions at a cursor position.
cancel_evalCancel a running evaluation.
load_fsharp_scriptLoad an .fsx file with partial progress.
get_recent_fsi_eventsRecent evals, errors, and loads with timestamps.
get_fsi_statusSession health, loaded projects, statistics, affordances.
get_startup_infoProjects, features, CLI arguments.
get_available_projectsDiscover .fsproj/.sln/.slnx in working directory.
explore_namespaceBrowse types and functions in a .NET namespace.
explore_typeBrowse members and properties of a .NET type.
get_elm_stateCurrent UI render state (editor, output, diagnostics).
reset_fsi_sessionSoft reset — clear definitions, keep DLL locks.
hard_reset_fsi_sessionFull reset — rebuild, reload, fresh session.
create_sessionCreate a new isolated FSI session.
list_sessionsList all active sessions.
stop_sessionStop a session by ID.
switch_sessionSwitch active session by ID.
set_live_testingEnable/disable live unit testing.
get_live_test_statusCurrent test state: summary, per-test status, timing. Filter by file.
run_testsRun tests explicitly — optionally filter by name pattern or category.
set_run_policySet auto-run policy per test category (every/save/demand/disabled).
get_pipeline_tracePipeline timing: tree-sitter, FCS, execution, test counts.

Architecture

SageFs is a daemon-first architecture. One server, many clients.

                ┌───────────────┐
                │  SageFs Daemon│
                │  ┌─────────┐  │
                │  │ FSI Actor│  │
                │  │ (Eval +  │  │
                │  │  Query)  │  │
                │  └─────────┘  │
                │  ┌─────────┐  │
                │  │  File    │  │
                │  │ Watcher  │  │
                │  └─────────┘  │
                │  ┌─────────┐  │
                │  │  MCP     │  │
                │  │ Server   │  │
                │  └─────────┘  │
                └──┬──┬──┬──┬───┘
                   │  │  │  │
     ┌──────┐  ┌──┴┐ ┌┴──┐ ┌┴──────┐  ┌────────┐
     │VS Code│  │TUI│ │GUI│ │ Web   │  │AI Agent│
     │Plugin │  │   │ │   │ │ Dash  │  │ (MCP)  │
     └──────┘  └───┘ └───┘ └───────┘  └────────┘
     ┌──────┐  ┌───────┐
     │Neovim│  │ REPL  │
     │Plugin│  │Connect│
     └──────┘  └───────┘
  • Daemon — Runs FSI actors, MCP server, file watcher, hot reload engine, web dashboard
  • Worker Sessions — Isolated FSI sub-processes per project (Erlang-style fault isolation)
  • Clients — VS Code, Neovim, REPL, TUI, GUI, dashboard, AI agents — all connect over HTTP/SSE
  • Dual Renderer — TUI and GUI share the same Cell[,] grid abstraction. Same keybindings, same layout.

Testing

# Run all tests (Expecto — use dotnet run, not dotnet test)
dotnet run --project SageFs.Tests

# Filter by name
dotnet run --project SageFs.Tests -- --filter "Snapshot"
dotnet run --project SageFs.Tests -- --filter "Hot Reload"

The test suite includes 2900+ tests: unit tests, FsCheck property-based tests, snapshot tests (Verify), state machine property tests, and Docker-based integration tests via Testcontainers.


License

MIT — see LICENSE

Acknowledgments

Reviews

No reviews yet

Sign in to write a review