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.
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
.fsfile 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
#rdirectives. 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
--persistfor 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:
- SageFs starts a daemon — a background server that stays running
- It builds your project and loads all dependencies into an F# Interactive session
- It starts watching your source files for changes
- It opens an MCP server on
http://localhost:37749/sse(for AI agents) - 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
TestControlleradapter, 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.showHistoryQuickPick - 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/.slnxfiles and offers to start SageFs automatically - Ionide integration — Hijacks Ionide's
FSI: Send Selectioncommands 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
.vsixfile.
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
| Setting | Default | Description |
|---|---|---|
sagefs.mcpPort | 37749 | SageFs MCP server port |
sagefs.dashboardPort | 37750 | SageFs dashboard port |
sagefs.autoStart | true | Automatically 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 save —
BufWritePostsends.fsxfile content for type-checking (configurable) - Live test gutter signs — Pass/fail/running/stale signs per test in the sign column
- Live test panel —
:SageFsTestPanelfor a persistent split with test results,<CR>to jump to source - Tests for current file —
:SageFsTestsHereshows tests covering the file you're editing - Test policy controls —
:SageFsTestPolicyfor category+policy drill-down - Pipeline trace —
:SageFsPipelineTraceshows the three-speed pipeline state - Coverage gutter signs — Green=covered, Red=uncovered per-line signs from FCS symbol graph
- Coverage panel —
:SageFsCoveragewith per-file breakdown and totals - Type explorer —
:SageFsTypeExplorerfor assembly→namespace→type→members drill-down, or:SageFsTypeExplorerFlatfor fuzzy pick - Session management — Create, switch, stop, reset sessions via picker
- Hot reload controls — Per-file toggle, watch-all, unwatch-all
- Daemon lifecycle —
:SageFsStart/:SageFsStopto manage the daemon from Neovim - Status dashboard —
:SageFsStatuswith daemon, session, tests, coverage, and config - History browser —
:SageFsHistorywith preview of past evaluations - Export to .fsx —
:SageFsExportexports session history as executable F# script - Call graph —
:SageFsCallers/:SageFsCalleesfor symbol dependency navigation - Code completion — Omnifunc-based completions via SageFs completion endpoint
- Combined statusline —
require("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:
- Edit code — agent writes to
.fsfiles (via editor or filesystem tools) - Tests auto-run — SageFs's file watcher detects the change, runs affected tests automatically
- Poll status — agent calls
get_live_test_statusto 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
| Feature | TUI | Raylib GUI | Web Dashboard | VS Code | Visual Studio | Neovim |
|---|---|---|---|---|---|---|
| Eval code | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Eval file | n/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 | ✅ | ✅ | ✅ | ✅ | —² | ✅ |
| CodeLens | n/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 Testing | SageFs Live Testing | |
|---|---|---|
| Speed | 5-30 seconds (full MSBuild rebuild) | 200-500ms (FSI hot reload, no build) |
| Scope | Rebuilds all impacted projects | Scope-level: just the function being edited |
| Broken code | Must compile to instrument | Tree-sitter works on broken/incomplete code |
| Frameworks | MSTest, xUnit, NUnit only | + Expecto, TUnit, extensible provider model |
| Coverage method | IL instrumentation (heavy) | Dual: FCS typed AST symbol graph (lightweight) + IL branch-level probes |
| Editors | Visual Studio only | VS Code, Neovim, TUI, GUI, Visual Studio, web dashboard |
| Cost | ~$250/month | Free, MIT licensed |
Three-speed feedback pipeline:
- ~50ms — Tree-sitter detects test attributes in broken/incomplete code → immediate gutter markers
- ~350ms — F# Compiler Service type-checks → namespace disambiguation, dependency graph, reachability annotations
- ~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.scmquery for instant detection in broken code - FCS
SymbolGraphBuilderfor dependency maps,SymbolDifffor change detection PipelineDebouncewith per-stage cancellation,AdaptiveDebouncefor FCS backoffSourceMappingbridges 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_batchevents withJsonFSharpConverter - 2900+ tests including FsCheck property-based state machine tests
🔥 Hot Reload
This is the headline feature. Save a .fs file and SageFs:
- Detects the change (~500ms debounce)
- Sends
#loadto FSI (~100ms) - Harmony patches method pointers at runtime — no restart
- Connected browsers auto-refresh via SSE (add
SageFs.DevReloadMiddlewareto 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.
| Tool | What it does |
|---|---|
send_fsharp_code | Execute F# code (each ;; is a transaction — failures are isolated) |
check_fsharp_code | Type-check without executing (pre-validate before committing) |
get_completions | Code completions at cursor position |
explore_type | Browse members of any .NET type |
explore_namespace | Browse types in a namespace |
cancel_eval | Cancel a running evaluation (recover from infinite loops) |
create_session | Spin up a new isolated FSI session |
hard_reset_fsi_session | Rebuild and reload (after source file changes) |
get_live_test_status | Query live test state with optional file filter |
run_tests | Run tests on demand with pattern/category filters |
set_run_policy | Control which test categories auto-run and when |
set_live_testing | Enable/disable the live testing pipeline |
get_pipeline_trace | Debug the three-speed pipeline waterfall |
Security scope: The MCP server binds to
localhostonly 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.
use → let 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,
usecalls.Dispose()at scope exit. In FSI (after rewrite), it's justlet— 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
usebindings 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;;thenlet x = 2;;doesn't error — it shadows the previousx. 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 runmain— it evaluates definitions and expressions interactively. - Assembly loading is session-scoped.
#rdirectives 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
| Tool | Description |
|---|---|
send_fsharp_code | Execute F# code. Each ;; marks a transaction boundary. |
check_fsharp_code | Type-check without executing. Returns diagnostics. |
get_completions | Code completions at a cursor position. |
cancel_eval | Cancel a running evaluation. |
load_fsharp_script | Load an .fsx file with partial progress. |
get_recent_fsi_events | Recent evals, errors, and loads with timestamps. |
get_fsi_status | Session health, loaded projects, statistics, affordances. |
get_startup_info | Projects, features, CLI arguments. |
get_available_projects | Discover .fsproj/.sln/.slnx in working directory. |
explore_namespace | Browse types and functions in a .NET namespace. |
explore_type | Browse members and properties of a .NET type. |
get_elm_state | Current UI render state (editor, output, diagnostics). |
reset_fsi_session | Soft reset — clear definitions, keep DLL locks. |
hard_reset_fsi_session | Full reset — rebuild, reload, fresh session. |
create_session | Create a new isolated FSI session. |
list_sessions | List all active sessions. |
stop_session | Stop a session by ID. |
switch_session | Switch active session by ID. |
set_live_testing | Enable/disable live unit testing. |
get_live_test_status | Current test state: summary, per-test status, timing. Filter by file. |
run_tests | Run tests explicitly — optionally filter by name pattern or category. |
set_run_policy | Set auto-run policy per test category (every/save/demand/disabled). |
get_pipeline_trace | Pipeline 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
- FsiX — The original F# Interactive experience that inspired SageFs
- sagefs.nvim — Neovim plugin for SageFs (separate repo)
- Falco & Falco.Datastar — Dashboard framework
- Harmony — Runtime method patching for hot reload
- Ionide.ProjInfo — Project file parsing
- Marten — Event sourcing on PostgreSQL
- Raylib-cs — GPU-rendered GUI
- Fable — F# to JavaScript compiler (powers the VS Code extension)
- ModelContextProtocol — AI integration standard