Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 4 additions & 16 deletions .github/workflows/release-mcp-server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,26 +96,14 @@ jobs:
dotnet restore src/ExcelMcp.McpServer/ExcelMcp.McpServer.csproj
dotnet restore src/ExcelMcp.CLI/ExcelMcp.CLI.csproj

- name: Inject Application Insights Connection String
run: |
$telemetryFile = "src/ExcelMcp.McpServer/Telemetry/ExcelMcpTelemetry.cs"
$connectionString = "${{ secrets.APPINSIGHTS_CONNECTION_STRING }}"

if ([string]::IsNullOrWhiteSpace($connectionString)) {
Write-Output "⚠️ APPINSIGHTS_CONNECTION_STRING secret not configured - telemetry will be disabled"
} else {
Write-Output "Injecting Application Insights connection string..."
$content = Get-Content $telemetryFile -Raw
$content = $content -replace '__APPINSIGHTS_CONNECTION_STRING__', $connectionString
Set-Content $telemetryFile $content
Write-Output "✅ Telemetry connection string injected"
}
shell: pwsh

- name: Build MCP Server & CLI
run: |
dotnet build src/ExcelMcp.McpServer/ExcelMcp.McpServer.csproj --configuration Release --no-restore
dotnet build src/ExcelMcp.CLI/ExcelMcp.CLI.csproj --configuration Release --no-restore
env:
# Application Insights connection string is embedded at build time by MSBuild
# See ExcelMcp.McpServer.csproj for the GenerateTelemetryConfig target
APPINSIGHTS_CONNECTION_STRING: ${{ secrets.APPINSIGHTS_CONNECTION_STRING }}

- name: Skip Tests (require Excel)
run: |
Expand Down
23 changes: 7 additions & 16 deletions .github/workflows/release-vscode-extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,28 +75,19 @@ jobs:
cd vscode-extension
npm install

- name: Inject Application Insights Connection String
run: |
$telemetryFile = "src/ExcelMcp.McpServer/Telemetry/ExcelMcpTelemetry.cs"
$connectionString = "${{ secrets.APPINSIGHTS_CONNECTION_STRING }}"

if ([string]::IsNullOrWhiteSpace($connectionString)) {
Write-Output "⚠️ APPINSIGHTS_CONNECTION_STRING secret not configured - telemetry will be disabled"
} else {
Write-Output "Injecting Application Insights connection string..."
$content = Get-Content $telemetryFile -Raw
$content = $content -replace '__APPINSIGHTS_CONNECTION_STRING__', $connectionString
Set-Content $telemetryFile $content
Write-Output "✅ Telemetry connection string injected"
}
shell: pwsh

- name: Build and Package Extension
run: |
cd vscode-extension
# Run the package script which does: build:mcp-server + vsce package
npm run package
env:
# Application Insights connection string is embedded at build time by MSBuild
# See ExcelMcp.McpServer.csproj for the GenerateTelemetryConfig target
APPINSIGHTS_CONNECTION_STRING: ${{ secrets.APPINSIGHTS_CONNECTION_STRING }}

- name: Prepare VSIX
run: |
cd vscode-extension
$version = "${{ env.PACKAGE_VERSION }}"

# vsce creates filename based on package.json name (excel-mcp)
Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,11 @@ gh-pages/_site/*

codeql-db/*
infrastructure/azure/appinsights.secrets.local

# Local environment files (secrets, connection strings)
.env
.env.local
.env.*.local

# Local MSBuild properties (secrets, connection strings)
Directory.Build.props.user
3 changes: 3 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,7 @@
</PropertyGroup>


<!-- Import user-specific properties (gitignored, for local secrets) -->
<Import Project="Directory.Build.props.user" Condition="Exists('Directory.Build.props.user')" />

</Project>
17 changes: 17 additions & 0 deletions Directory.Build.props.user.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!--
Local developer settings (COPY to Directory.Build.props.user)

This file is a template. To use:
1. Copy this file to Directory.Build.props.user
2. Fill in your actual values
3. Directory.Build.props.user is gitignored and won't be committed

The values here are picked up by MSBuild at build time and embedded into the compiled assembly.
-->
<Project>
<PropertyGroup>
<!-- Application Insights connection string for telemetry -->
<!-- Get from Azure Portal: Application Insights > Overview > Connection String -->
<AppInsightsConnectionString>YOUR_CONNECTION_STRING_HERE</AppInsightsConnectionString>
</PropertyGroup>
</Project>
7 changes: 5 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.10" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
<!-- Application Insights SDK for telemetry (Users, Sessions, Funnels, User Flows) -->
<!-- Application Insights Worker Service SDK for telemetry (Users, Sessions, Funnels, User Flows) -->
<!-- Worker Service SDK provides proper DI integration, auto-collection modules, and host lifetime awareness -->
<PackageVersion Include="Microsoft.ApplicationInsights.WorkerService" Version="2.23.0" />
<!-- Base SDK needed for tests that directly use TelemetryClient -->
<PackageVersion Include="Microsoft.ApplicationInsights" Version="2.23.0" />
<PackageVersion Include="Microsoft.Extensions.Resilience" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.10" />
Expand All @@ -37,4 +40,4 @@
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.5" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.100" />
</ItemGroup>
</Project>
</Project>
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,4 @@ This means you get:
### SEO & Discovery

`Excel Automation` • `Automate Excel with AI` • `MCP Server` • `Model Context Protocol` • `GitHub Copilot Excel` • `AI Excel Assistant` • `Power Query Automation` • `Power Query M Code` • `Power Pivot Automation` • `DAX Measures` • `DAX Automation` • `Data Model Automation` • `PivotTable Automation` • `VBA Automation` • `Excel Tables Automation` • `Excel AI Integration` • `COM Interop` • `Windows Excel Automation` • `Excel Development Tools` • `Excel Productivity` • `Excel Scripting` • `Conversational Excel` • `Natural Language Excel`
# test
149 changes: 57 additions & 92 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,13 +373,23 @@ dotnet build -c Release

## 📊 **Application Insights / Telemetry Setup**

ExcelMcp uses Azure Application Insights for anonymous usage telemetry and crash reporting. Telemetry is **opt-out** (enabled by default in release builds).
ExcelMcp uses Azure Application Insights (Classic SDK with WorkerService integration) for anonymous usage telemetry and crash reporting. Telemetry is **opt-out** (enabled by default in release builds).

### **How It Works**

The Application Insights connection string is **embedded at build time** via MSBuild - there is no runtime environment variable lookup.

**Build-time flow:**
1. MSBuild reads `AppInsightsConnectionString` property (from `Directory.Build.props.user` or env var)
2. Generates `TelemetryConfig.g.cs` with the connection string as a `const string`
3. Compiled assembly contains the embedded connection string

### **What is Tracked**

- **Tool invocations**: Tool name, action, duration (ms), success/failure
- **Unhandled exceptions**: Exception type and redacted stack trace
- **Session ID**: Random GUID per process (no user identification)
- **User ID**: SHA256 hash of machine identity (anonymous, 16 chars)
- **Session ID**: Random GUID per process (8 chars)

### **What is NOT Tracked**

Expand All @@ -396,6 +406,30 @@ All telemetry passes through `SensitiveDataRedactingProcessor` which removes:
- Connection string secrets (`Password=...` → `[REDACTED_CREDENTIAL]`)
- Email addresses → `[REDACTED_EMAIL]`

### **Local Development with Telemetry**

To enable telemetry in local builds:

```powershell
# 1. Copy the template file
Copy-Item "Directory.Build.props.user.template" "Directory.Build.props.user"

# 2. Edit Directory.Build.props.user and add your connection string
# <AppInsightsConnectionString>InstrumentationKey=xxx;IngestionEndpoint=...</AppInsightsConnectionString>

# 3. Build - connection string is embedded at compile time
dotnet build src/ExcelMcp.McpServer/ExcelMcp.McpServer.csproj

# 4. Run - telemetry is automatically sent to Azure
dotnet run --project src/ExcelMcp.McpServer/ExcelMcp.McpServer.csproj
```

**Note:** `Directory.Build.props.user` is gitignored - your connection string won't be committed.

### **Local Development without Telemetry**

If you don't create `Directory.Build.props.user`, builds will have an empty connection string and telemetry will be disabled. This is the default for local development.

### **Azure Resources Setup (Maintainers Only)**

To deploy the Application Insights infrastructure:
Expand All @@ -419,106 +453,37 @@ After deploying Azure resources:
2. Add new secret: `APPINSIGHTS_CONNECTION_STRING`
3. Paste the connection string from deployment output

The release workflow automatically injects this at build time.

### **Local Development**

During local development, telemetry is **disabled by default** because the placeholder connection string is not replaced. This is intentional - no telemetry data is sent from dev builds.

#### **Debug Mode: Console Output**

To test telemetry locally without Azure, enable debug mode which logs to stderr:

```powershell
# Enable debug telemetry (logs to console instead of Azure)
$env:EXCELMCP_DEBUG_TELEMETRY = "true"

# Build and run the MCP server
dotnet build src/ExcelMcp.McpServer/ExcelMcp.McpServer.csproj
dotnet run --project src/ExcelMcp.McpServer/ExcelMcp.McpServer.csproj

# You'll see telemetry output like:
# [Telemetry] Debug mode enabled - logging to stderr
# Activity.TraceId: abc123...
# Activity.DisplayName: ToolInvocation
# Activity.Tags:
# tool.name: excel_file
# tool.action: list
# tool.duration_ms: 42
# tool.success: true
```

#### **Testing with Real Azure Resources**

To test with actual Application Insights:

```powershell
# 1. Deploy Azure resources
.\infrastructure\azure\deploy-appinsights.ps1 -SubscriptionId "<your-sub-id>"

# 2. Temporarily inject connection string (DON'T COMMIT!)
$connStr = "InstrumentationKey=xxx;IngestionEndpoint=https://..."
(Get-Content "src/ExcelMcp.McpServer/Telemetry/ExcelMcpTelemetry.cs") -replace `
'__APPINSIGHTS_CONNECTION_STRING__', $connStr | `
Set-Content "src/ExcelMcp.McpServer/Telemetry/ExcelMcpTelemetry.cs"

# 3. Build and run
dotnet build src/ExcelMcp.McpServer/ExcelMcp.McpServer.csproj
dotnet run --project src/ExcelMcp.McpServer/ExcelMcp.McpServer.csproj

# 4. Check Azure Portal → Application Insights → Transaction search

# 5. IMPORTANT: Revert the file (don't commit connection string!)
git checkout src/ExcelMcp.McpServer/Telemetry/ExcelMcpTelemetry.cs
```

To verify telemetry state:
```csharp
// ExcelMcpTelemetry.IsEnabled returns false when:
// - Connection string is placeholder "__APPINSIGHTS_CONNECTION_STRING__"
// - User has opted out via EXCELMCP_TELEMETRY_OPTOUT=true

// ExcelMcpTelemetry.IsEnabled returns true when:
// - EXCELMCP_DEBUG_TELEMETRY=true (console output mode)
// - Connection string is real (injected at build time)
```

### **User Opt-Out**

Users can disable telemetry by setting an environment variable:

```powershell
# Windows
$env:EXCELMCP_TELEMETRY_OPTOUT = "true"

# Or permanently via System Properties → Environment Variables
```
The release workflow sets this as an environment variable, and MSBuild embeds it at build time.

### **Telemetry Architecture**

```
MCP Tool Invocation
ExcelToolsBase.ExecuteToolAction()
│ (tracks: tool, action, duration, success)
ExcelMcpTelemetry.TrackToolInvocation()
SensitiveDataRedactingProcessor
│ (removes: paths, credentials, emails)
Azure Monitor Exporter → Application Insights
```text
Build Time:
MSBuild → reads AppInsightsConnectionString → generates TelemetryConfig.g.cs

Runtime:
MCP Tool Invocation
ExcelMcpTelemetry.TrackToolInvocation()
│ (tracks: tool, action, duration, success)
SensitiveDataRedactingProcessor
│ (removes: paths, credentials, emails)
TelemetryClient → Application Insights
```

### **Files Overview**

| File | Purpose |
|------|---------|
| `Telemetry/ExcelMcpTelemetry.cs` | Static helper for tracking |
| `Telemetry/ExcelMcpTelemetry.cs` | Static helper for tracking events |
| `Telemetry/ExcelMcpTelemetryInitializer.cs` | Sets User.Id and Session.Id on telemetry |
| `Telemetry/SensitiveDataRedactingProcessor.cs` | Redacts PII before transmission |
| `Program.cs` | OpenTelemetry configuration |
| `Program.cs` | Application Insights WorkerService configuration |
| `ExcelMcp.McpServer.csproj` | MSBuild target that generates TelemetryConfig.g.cs |
| `Directory.Build.props.user.template` | Template for local dev connection string |
| `infrastructure/azure/appinsights.bicep` | Azure resource definitions |
| `infrastructure/azure/deploy-appinsights.ps1` | Deployment script |

Expand Down
Loading
Loading