A Windows system tray application that connects to Microsoft Flight Simulator 2024 via SimConnect and exposes flight data through the Model Context Protocol (MCP). This allows AI agents (Claude, etc.) to query real-time flight information for use cases like flight instruction, situational awareness, and learning.
Key Use Case: Flight instruction - an AI agent can see your current flight state and provide contextual guidance without controlling the aircraft.
┌─────────────────────────────────────────────────────────┐
│ AI Client (Claude, etc.) │
└─────────────────────┬───────────────────────────────────┘
│ HTTP/SSE (localhost:5000)
┌─────────────────────▼───────────────────────────────────┐
│ MCP Server (ASP.NET Core / Kestrel) │
│ ┌────────────────────────────────────────────────────┐ │
│ │ MCP Tools: get_position, get_autopilot, etc. │ │
│ └─────────────────────┬──────────────────────────────┘ │
│ │ C# method calls │
│ ┌─────────────────────▼──────────────────────────────┐ │
│ │ SimConnect Service │ │
│ │ - Manages connection to MSFS │ │
│ │ - On-demand data fetching │ │
│ │ - Request/response with timeout │ │
│ └─────────────────────┬──────────────────────────────┘ │
│ │ │
│ ┌─────────────────────▼──────────────────────────────┐ │
│ │ Web Dashboard (static HTML) │ │
│ │ - Connection status │ │
│ │ - Live flight data display │ │
│ │ - MCP call logging │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ System Tray Host (Windows Forms) │ │
│ │ - Windows message loop (required for SimConnect) │ │
│ │ - Tray icon with status and menu │ │
│ └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│ Windows Messages
┌─────────────────────▼───────────────────────────────────┐
│ Microsoft Flight Simulator 2024 │
└─────────────────────────────────────────────────────────┘
Decision: Fetch data from SimConnect only when an MCP tool is called, with a timeout.
Reasoning: Cached data can become contextually invalid without any signal:
- User exits to main menu - cache shows mid-flight data
- User loads a different flight - cache shows wrong aircraft/position
- User swaps aircraft - cache shows old fuel/weight/callsign
- MSFS crashes - cache shows healthy flight
With on-demand fetching:
- If MSFS is in-flight: Response in ~50ms
- If MSFS is on menu/loading: Timeout returns clear "unavailable" state
- If MSFS is not running: Connection error is explicit
The agent gets truth or a clear signal that truth isn't available. This is critical for flight instruction where wrong data leads to wrong advice.
Decision: Run an embedded Kestrel web server for MCP communication.
Reasoning:
- App is long-running (system tray) - not spawned per-request
- Enables the web dashboard on the same port
- Testable with browser/curl during development
- Multiple clients can connect simultaneously
- Claude Desktop supports HTTP MCP servers
Decision: Separate tools for each data category rather than one get_all_data tool.
Tools:
get_connection_status- Is MSFS running? Is a flight active?get_flight_position- Lat, lon, altitude, heading, ground speed, vertical speed, pitch, bankget_flight_instruments- Indicated altitude, IAS, TAS, Mach, heading indicator, altimeter settingget_engine_status- RPM, throttle, fuel flow, fuel quantity, temps, pressuresget_autopilot_status- AP master, modes engaged, target valuesget_aircraft_info- Aircraft type, callsign, weights
Reasoning:
- Agent fetches only what's needed for the question
- Smaller, focused responses use fewer tokens
- Better for instruction: "check your airspeed" only needs instruments
- Each tool has a single responsibility, easier to test
Decision: V1 is strictly read-only. No setting autopilot, tuning radios, or triggering events.
Reasoning:
- This is for flight instruction, not AI copilot
- Write operations need extensive validation and safety checks
- Simpler scope, faster to ship
- Can add write tools in V2 if needed
System Tray UI:
- Right-click menu:
- Connection status indicator
- MCP server status with port
- "Open Dashboard" link
- "Settings..." (future)
- "Exit"
- Double-click: Opens web dashboard in browser
- Tooltip on hover: Quick status (e.g., "Connected - N172SP @ 8,500ft")
Web Dashboard (served at same port as MCP):
- Real-time connection status
- Current aircraft info
- Live position/speed/altitude
- Log of recent MCP tool calls (for debugging)
Documentation: https://docs.flightsimulator.com/msfs2024/html/6_Programming_APIs/SimConnect/SimConnect_SDK.htm
Key Pages:
- Simulation Variables: https://docs.flightsimulator.com/msfs2024/html/6_Programming_APIs/SimVars/Simulation_Variables.htm
- API Reference: Search for
SimConnect_AddToDataDefinition,SimConnect_RequestDataOnSimObject
SDK Location: The MSFS 2024 SDK installs to a path stored in environment variable MSFS2024_SDK. The managed DLL is at:
$(MSFS2024_SDK)\SimConnect SDK\lib\managed\Microsoft.FlightSimulator.SimConnect.dll
The native DLL (must be distributed with app):
$(MSFS2024_SDK)\SimConnect SDK\lib\SimConnect.dll
SimConnect Pattern:
- Create
SimConnectinstance with a window handle (for message loop) - Call
AddToDataDefinition()to register which SimVars you want - Call
RegisterDataDefineStruct<T>()to map to a C# struct - Call
RequestDataOnSimObject()to request data - Data arrives asynchronously via
OnRecvSimobjectDatacallback - Must call
ReceiveMessage()in the Windows message loop to pump messages
NuGet Package: ModelContextProtocol (prerelease)
dotnet add package ModelContextProtocol --prerelease
dotnet add package ModelContextProtocol.AspNetCore --prerelease
dotnet add package Microsoft.Extensions.Hosting
GitHub: https://github.com/modelcontextprotocol/csharp-sdk
Official Docs: https://modelcontextprotocol.io/docs/develop/build-server
Key Pattern - Defining a tool:
[McpServerToolType]
public class MyTools
{
[McpServerTool, Description("Description for the AI")]
public static string MyTool([Description("Param description")] string param)
{
return "result";
}
}HTTP/SSE Setup requires ModelContextProtocol.AspNetCore package and configuring Kestrel endpoints.
Key Classes:
NotifyIcon- The tray iconContextMenuStrip- Right-click menuApplicationContext- For running without a visible form
Pattern: Create a class inheriting ApplicationContext, set up NotifyIcon in constructor, run with Application.Run(context).
No SimVars - just checks if SimConnect connection is alive and responsive.
| SimVar | Unit | Description |
|---|---|---|
| PLANE LATITUDE | degrees | Current latitude |
| PLANE LONGITUDE | degrees | Current longitude |
| PLANE ALTITUDE | feet | Altitude above mean sea level |
| PLANE HEADING DEGREES TRUE | degrees | True heading |
| GROUND VELOCITY | knots | Ground speed |
| VERTICAL SPEED | feet per minute | Rate of climb/descent |
| PLANE PITCH DEGREES | degrees | Pitch attitude |
| PLANE BANK DEGREES | degrees | Bank angle |
| SimVar | Unit | Description |
|---|---|---|
| INDICATED ALTITUDE | feet | Altimeter reading |
| AIRSPEED INDICATED | knots | IAS |
| AIRSPEED TRUE | knots | TAS |
| AIRSPEED MACH | mach | Mach number |
| HEADING INDICATOR | degrees | Heading indicator/HSI |
| KOHLSMAN SETTING HG | inHg | Altimeter setting |
| ATTITUDE INDICATOR PITCH DEGREES | degrees | AI pitch |
| ATTITUDE INDICATOR BANK DEGREES | degrees | AI bank |
| SimVar | Unit | Description |
|---|---|---|
| NUMBER OF ENGINES | number | Engine count |
| GENERAL ENG RPM:1 | rpm | Engine 1 RPM |
| GENERAL ENG THROTTLE LEVER POSITION:1 | percent | Throttle position |
| ENG FUEL FLOW GPH:1 | gallons per hour | Fuel flow |
| FUEL TOTAL QUANTITY | gallons | Total fuel remaining |
| ENG EXHAUST GAS TEMPERATURE:1 | celsius | EGT |
| ENG OIL PRESSURE:1 | psi | Oil pressure |
| ENG OIL TEMPERATURE:1 | celsius | Oil temp |
| SimVar | Unit | Description |
|---|---|---|
| AUTOPILOT MASTER | bool | AP on/off |
| AUTOPILOT HEADING LOCK | bool | Heading mode |
| AUTOPILOT HEADING LOCK DIR | degrees | Target heading |
| AUTOPILOT ALTITUDE LOCK | bool | Altitude hold mode |
| AUTOPILOT ALTITUDE LOCK VAR | feet | Target altitude |
| AUTOPILOT AIRSPEED HOLD | bool | Speed hold mode |
| AUTOPILOT AIRSPEED HOLD VAR | knots | Target speed |
| AUTOPILOT VERTICAL HOLD | bool | VS mode |
| AUTOPILOT VERTICAL HOLD VAR | feet per minute | Target VS |
| AUTOPILOT NAV1 LOCK | bool | NAV mode |
| AUTOPILOT APPROACH HOLD | bool | Approach mode |
| SimVar | Unit | Description |
|---|---|---|
| TITLE | string | Aircraft name |
| ATC ID | string | Tail number |
| ATC AIRLINE | string | Airline name |
| TOTAL WEIGHT | pounds | Current weight |
| MAX GROSS WEIGHT | pounds | Max weight |
| EMPTY WEIGHT | pounds | Empty weight |
Format: ZIP file containing:
MsfsMcpServer.exe- Main application (self-contained .NET 8)SimConnect.dll- Native SimConnect library
Target Framework: .NET 8.0 Windows (net8.0-windows)
Build Command:
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true
Then zip the exe with SimConnect.dll.
Goal: Get a building solution with all dependencies configured. Nothing runs yet, but the foundation is solid.
Prerequisites: None - this is the starting phase.
Key Context:
- Target framework is
net8.0-windows(Windows Forms for system tray) - SimConnect DLL comes from MSFS SDK at path in
MSFS2024_SDKenvironment variable - MCP packages are prerelease, use
--prereleaseflag
Create the Visual Studio solution with proper project structure:
MsfsMcpServer/
├── MsfsMcpServer.sln
├── src/
│ └── MsfsMcpServer/
│ ├── MsfsMcpServer.csproj
│ ├── Program.cs
│ ├── Services/
│ ├── Tools/
│ ├── Models/
│ └── UI/
└── tests/
└── MsfsMcpServer.Tests/
└── MsfsMcpServer.Tests.csproj
Acceptance Criteria:
- Solution opens in Visual Studio / Rider
- Projects build successfully
- Test project references main project
Add required packages to the main project:
ModelContextProtocol(prerelease)ModelContextProtocol.AspNetCore(prerelease)Microsoft.Extensions.HostingMicrosoft.Extensions.Logging
Add test packages:
xunitxunit.runner.visualstudioMoqFluentAssertions
Acceptance Criteria:
dotnet restoresucceeds- No package version conflicts
Set up the reference to the managed SimConnect DLL:
- Reference
Microsoft.FlightSimulator.SimConnect.dllfrom SDK - Add post-build step to copy
SimConnect.dllto output - Document the
MSFS2024_SDKenvironment variable requirement
Acceptance Criteria:
- Project builds with SimConnect reference
- SimConnect.dll appears in output directory
- Clear error message if SDK path not found
Goal: Create a testable abstraction over SimConnect before implementing the real connection. This lets us build and test tools without MSFS running.
Prerequisites: Phase 0 complete (solution builds)
Key Context:
ISimConnectServiceis the interface all tools will depend on- Data structs must use
[StructLayout(LayoutKind.Sequential)]for SimConnect marshalling - Mock implementation enables unit testing without MSFS
Create an interface that abstracts SimConnect operations:
public interface ISimConnectService
{
bool IsConnected { get; }
Task<bool> ConnectAsync(CancellationToken ct = default);
void Disconnect();
Task<T?> RequestDataAsync<T>(CancellationToken ct = default) where T : struct;
}Acceptance Criteria:
- Interface defined in
Services/folder - Supports async operations with cancellation
- Generic method for requesting typed data structs
Create C# structs/records for each data category that map to SimConnect data definitions:
FlightPositionDataFlightInstrumentsDataEngineDataAutopilotDataAircraftInfoConnectionStatus
Use [StructLayout(LayoutKind.Sequential)] for SimConnect compatibility.
Acceptance Criteria:
- All models defined in
Models/folder - Structs have proper layout for SimConnect marshalling
- Properties have XML documentation
Implement MockSimConnectService : ISimConnectService for testing:
- Returns configurable fake data
- Can simulate connection failures
- Can simulate timeouts
Acceptance Criteria:
- Mock in test project
- Configurable responses
- Can inject into tools for testing
Goal: Build two tools perfectly with full error handling, logging, and tests. These become the template for all other tools. This is the most important phase - get it right here and the rest is copy-paste.
Prerequisites: Phase 1 complete (ISimConnectService interface and models defined, mock available)
Key Context:
- Tools use
[McpServerToolType]on class and[McpServerTool]on methods - Inject
ISimConnectServicevia constructor DI - Always return response objects, never throw exceptions
- Handle: not connected, timeout, null data, unexpected errors
- See AGENTS.md for the exact code pattern to follow
Template Tools:
get_connection_status- Simplest possible tool, tests infrastructureget_flight_position- First real data tool, establishes async request pattern
The simplest tool - checks if SimConnect is connected and responsive.
Returns:
{
"connected": true,
"simulator": "Microsoft Flight Simulator 2024",
"error": null
}Or on failure:
{
"connected": false,
"simulator": null,
"error": "SimConnect not available. Is MSFS running?"
}Implementation Requirements:
- Use
[McpServerToolType]and[McpServerTool]attributes - Inject
ISimConnectServicevia DI - Full XML documentation on class and method
- Proper error handling with user-friendly messages
- Logging at appropriate levels
Acceptance Criteria:
- Tool discoverable by MCP client
- Returns correct status when connected
- Returns helpful error when disconnected
- Unit tests cover success, failure, and timeout cases
The first "real" data tool - fetches position data from SimConnect.
Returns:
{
"latitude": 39.8561,
"longitude": -104.6737,
"altitude_msl_ft": 8500,
"heading_true": 270.5,
"ground_speed_kts": 120,
"vertical_speed_fpm": 500,
"pitch_deg": 5.2,
"bank_deg": -2.1,
"timestamp": "2024-01-15T10:30:00Z"
}Or on failure:
{
"error": "Unable to retrieve flight data. Ensure you are in an active flight."
}Implementation Requirements:
- Same patterns as Task 2.1
- Request data from SimConnect with timeout (2 seconds)
- Handle timeout gracefully with clear error message
- Include timestamp in response
- Round numeric values appropriately (e.g., 1 decimal for degrees)
Acceptance Criteria:
- Returns accurate position data when in flight
- Times out gracefully when on main menu
- Unit tests with mock service
- Integration test with actual SimConnect (manual)
Create thorough test coverage:
- Unit tests with mocked SimConnect
- Test success cases
- Test timeout handling
- Test disconnection handling
- Test malformed data handling
- Test cancellation token respect
Acceptance Criteria:
-
90% code coverage on template tools
- Tests run in CI without MSFS
- Clear test names describing scenarios
Create TOOL_IMPLEMENTATION_GUIDE.md:
- Step-by-step guide for adding new tools
- Code snippets and patterns
- Error handling checklist
- Testing checklist
Acceptance Criteria:
- Another developer (or AI) can follow guide to add tools
- Includes all necessary boilerplate
- References the template tools as examples
Goal: Implement the real SimConnect service that talks to MSFS. Replace the mock with actual flight sim data.
Prerequisites: Phase 2 complete (template tools working with mock)
Key Context:
- SimConnect requires a Windows message loop (HWND) to receive callbacks
- Data flow:
RequestDataOnSimObject()→ callback via Windows message →TaskCompletionSourcecompletes - Must call
ReceiveMessage()in message pump to process SimConnect messages - 2-second timeout on all data requests
- Connection can drop if MSFS exits - handle gracefully
SimConnect Pattern:
SimConnect.Open()with window handleAddToDataDefinition()for each SimVarRegisterDataDefineStruct<T>()to map to C# structRequestDataOnSimObject()to request (returns immediately)OnRecvSimobjectDatacallback fires with data- Complete the waiting
TaskCompletionSource
Create the Windows Forms infrastructure:
- Hidden form or
NativeWindowto receive SimConnect messages - Message pump integration
- Thread-safe access from async code
Acceptance Criteria:
- SimConnect messages are received
- No UI is shown for this component
- Works alongside the system tray
Full implementation of ISimConnectService:
- Connection management with retry logic
- Data definition registration for all data types
- Async request/response with
TaskCompletionSource - Timeout handling (2 second default)
- Proper resource cleanup on disconnect
Acceptance Criteria:
- Can connect to running MSFS instance
- Requests return data within expected time
- Timeouts are handled gracefully
- No resource leaks on connect/disconnect cycles
Test against real MSFS:
- Test connection when MSFS running
- Test connection when MSFS not running
- Test data retrieval in flight
- Test behavior on main menu
- Test behavior during loading screens
- Test aircraft swap
- Test MSFS shutdown while connected
Acceptance Criteria:
- All scenarios documented with results
- Known issues logged
- Error messages are helpful for each scenario
Goal: Implement all remaining MCP tools following the established pattern from Phase 2.
Prerequisites: Phase 3 complete (real SimConnect working), Phase 2 template tools as reference
Key Context:
- Copy the pattern exactly from
FlightPositionTool - Each tool gets its own file in
Tools/folder - Each tool has a corresponding response class in
Models/ - Each tool needs unit tests in
Tests/Tools/ - Refer to PROJECT_SPEC.md "SimVar Definitions by Tool" section for which SimVars each tool needs
Tools to implement:
get_flight_instruments- Altimeter, airspeed, attitude indicator readingsget_engine_status- RPM, throttle, fuel, temperaturesget_autopilot_status- AP modes and target valuesget_aircraft_info- Aircraft type, callsign, weights
Follow pattern from Task 2.2. Returns altimeter, airspeed, attitude data.
Follow pattern from Task 2.2. Returns engine RPM, fuel, temps.
Follow pattern from Task 2.2. Returns AP modes and targets.
Follow pattern from Task 2.2. Returns aircraft type, callsign, weights.
Acceptance Criteria for all:
- Follows established patterns exactly
- Unit tests match template tool coverage
- Manual verification against MSFS
Goal: Configure the MCP server with HTTP/SSE transport so external clients (Claude Desktop, etc.) can connect.
Prerequisites: Phase 4 complete (all tools implemented)
Key Context:
- Using
ModelContextProtocol.AspNetCorepackage for HTTP transport - Server runs on
localhost:5000by default - SSE (Server-Sent Events) transport for streaming
- Tools are discovered via assembly scanning with
WithToolsFromAssembly() - Same Kestrel instance will later serve the web dashboard
MCP C# SDK Docs: https://github.com/modelcontextprotocol/csharp-sdk
Set up ASP.NET Core hosting:
- Configure Kestrel to listen on
localhost:5000 - Register MCP server with SSE transport
- Register all tools via assembly scanning
- Configure logging
Acceptance Criteria:
- Server starts and listens on configured port
- MCP inspector can connect and see tools
- Tools are callable via MCP protocol
Configure CORS for local development/testing:
- Allow localhost origins
- Allow required MCP headers
Acceptance Criteria:
- Browser-based clients can connect
- No CORS errors in console
Configure Claude Desktop to use the MCP server:
{
"mcpServers": {
"msfs": {
"url": "http://localhost:5000/mcp"
}
}
}Acceptance Criteria:
- Claude Desktop discovers all tools
- Tools return correct data
- Errors display appropriately
Goal: Create the Windows system tray host that ties everything together. App runs minimized to tray with status and controls.
Prerequisites: Phase 5 complete (MCP server working)
Key Context:
- Use
ApplicationContextsubclass for tray-only app (no main form) NotifyIconfor tray icon,ContextMenuStripfor right-click menu- Must maintain Windows message loop for SimConnect
- Double-click opens web dashboard in default browser
- Icon should reflect connection state (gray=disconnected, color=connected)
Windows Forms Classes:
NotifyIcon- The tray icon itselfContextMenuStrip- Right-click menuToolStripMenuItem- Menu itemsApplicationContext- Run app without visible form
Implement the tray icon and menu:
- Tray icon with airplane graphic
- Right-click context menu:
- Status line (connected/disconnected)
- MCP server status with port
- Separator
- "Open Dashboard" menu item
- Separator
- "Exit" menu item
- Double-click opens dashboard
- Tooltip shows quick status
Acceptance Criteria:
- App starts minimized to tray
- Menu items work correctly
- Icon reflects connection status
- Tooltip updates with flight info
Wire together in Program.cs:
- Start system tray
- Start MCP server on background thread
- Start SimConnect service
- Handle graceful shutdown
Acceptance Criteria:
- Single exe starts everything
- Exit menu item shuts down cleanly
- No orphan processes
Update tray icon and tooltip based on state:
- Gray icon: Not connected
- Colored icon: Connected
- Tooltip: "MSFS MCP Server - Connected - N172SP @ 8,500ft"
Acceptance Criteria:
- Visual feedback matches actual state
- Updates within 2 seconds of state change
Goal: Create a simple web dashboard for monitoring status and debugging MCP interactions. Served from the same Kestrel instance as MCP.
Prerequisites: Phase 6 complete (system tray working)
Key Context:
- Static HTML + vanilla JavaScript (no build step, no framework)
- Served from
wwwroot/folder via Kestrel static files - Dashboard at
http://localhost:5000/ - Can call the same endpoints MCP uses, or add simple REST endpoints
- Auto-refresh data every 2 seconds
- Shows recent MCP tool calls for debugging agent interactions
Single-page dashboard showing:
- Connection status (green/red indicator)
- Current aircraft info
- Live position/speed/altitude (auto-refresh)
- Recent MCP tool call log
Implementation:
- Static HTML + vanilla JS
- Fetch from MCP endpoints or add simple REST endpoints
- Auto-refresh every 2 seconds
Acceptance Criteria:
- Accessible at
http://localhost:5000/ - Shows live data when connected
- Shows clear offline state when disconnected
Log recent MCP tool invocations:
- Tool name
- Timestamp
- Success/failure
- Response time
Display last 20 calls in dashboard.
Acceptance Criteria:
- Calls appear in log within 1 second
- Log doesn't grow unbounded
- Helps debug agent interactions
Goal: Final polish, error handling review, and create distributable package.
Prerequisites: Phase 7 complete (full app working end-to-end)
Key Context:
- Distribution is a ZIP containing exe + SimConnect.dll
- Use
dotnet publishwith self-contained and single-file options - README should enable a new user to get running without other docs
- Test the full flow: extract zip → run exe → connect Claude Desktop → query flight data
Build Command:
dotnet publish src/MsfsMcpServer -c Release -r win-x64 --self-contained true -p:PublishSingleFile=trueCreate/source an appropriate icon:
- Airplane or flight-related
- Looks good at 16x16, 32x32, 256x256
- Set as application icon and tray icon
Acceptance Criteria:
- Icon visible in tray
- Icon visible in taskbar/alt-tab
- Icon in exe properties
Review all error paths:
- Startup errors (port in use, SimConnect not found)
- Runtime errors (MSFS crash, network issues)
- User-friendly messages for all
Acceptance Criteria:
- No unhandled exceptions
- All errors logged
- User sees helpful messages
PowerShell script to:
- Build release version
- Publish self-contained exe
- Copy SimConnect.dll
- Create zip file with version number
Acceptance Criteria:
- Single script produces distributable zip
- Zip contains only needed files
- Versioned filename (e.g.,
MsfsMcpServer-1.0.0.zip)
Documentation covering:
- What this is
- Requirements (MSFS 2024, Windows)
- Installation (extract zip, run exe)
- Configuration (Claude Desktop setup)
- Troubleshooting common issues
- Available tools and what they return
Acceptance Criteria:
- New user can get running from README alone
- Troubleshooting covers common issues
- Tool documentation is accurate
A task is complete when:
- Code is implemented and compiles without warnings
- Unit tests pass with >80% coverage on new code
- Manual testing completed where applicable
- Code follows established patterns
- XML documentation on public members
- No TODO comments left unresolved
Not in scope for V1, but worth noting:
- Write operations (set autopilot, tune radios)
- Nearby airport/navaid queries
- Flight plan information
- Weather data
- Multiple simultaneous MSFS connections
- Settings UI for port configuration
- Auto-start with Windows option
- Installer (MSI/MSIX)