Skip to content

pac copilot mcp --run writes "Loading FeatureFlags from file: ..." to stdout — breaks MCP JSON-RPC transport #1393

Description

@jt-thrivara

Summary

`pac copilot mcp --run` (and the `dnx Microsoft.PowerApps.CLI.Tool --yes copilot mcp --run` equivalent) writes one non-JSON line to stdout before entering MCP protocol mode:

```
Loading FeatureFlags from file: C:\Users<user>.nuget\packages\microsoft.powerapps.cli.tool\2.7.4\tools\net10.0\any\featureflags.json
```

The MCP stdio transport requires stdout to be exclusively JSON-RPC. Any non-JSON line on stdout breaks the host parser. Claude Desktop surfaces this as a toast:

MCP pac-cli: Unexpected token 'L', "Loading Fe"... is not valid JSON

The server then fails to register and its tools are unavailable to the host.

Repro

```powershell
$tmpOut = New-TemporaryFile
$tmpErr = New-TemporaryFile
$p = Start-Process -FilePath "dnx" `
-ArgumentList "Microsoft.PowerApps.CLI.Tool","--yes","copilot","mcp","--run" `
-NoNewWindow -PassThru -RedirectStandardOutput $tmpOut -RedirectStandardError $tmpErr
Start-Sleep -Seconds 6
Stop-Process -Id $p.Id -Force
Get-Content $tmpOut # <-- contains the offending FeatureFlags line
Get-Content $tmpErr | Select-Object -First 6 # <-- clean info: lines (correct behavior)
```

Captured stdout (the problem):
```
Loading FeatureFlags from file: C:\Users<user>.nuget\packages\microsoft.powerapps.cli.tool\2.7.4\tools\net10.0\any\featureflags.json
```

Captured stderr (correctly routed, MCP ignores stderr):
```
info: ModelContextProtocol.Server.StdioServerTransport[857250842]
Server (stream) (Power Platform Management MCP Server) transport reading messages.
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
```

Expected

All bootstrap logging (FeatureFlags loader, lifecycle messages, anything diagnostic) should write to stderr. Stdout must be reserved exclusively for JSON-RPC frames. This is the documented requirement of the MCP stdio transport.

Actual

Exactly one line — the FeatureFlags-loader log — goes to stdout, breaking every MCP host that connects via stdio.

Workaround

A Node stdio proxy that filters non-JSON lines off stdout works:

```js
const { spawn } = require('child_process');
const readline = require('readline');
const isWin = process.platform === 'win32';
const command = isWin ? 'cmd.exe' : 'dnx';
const args = isWin
? ['/c', 'dnx', 'Microsoft.PowerApps.CLI.Tool', '--yes', 'copilot', 'mcp', '--run']
: ['Microsoft.PowerApps.CLI.Tool', '--yes', 'copilot', 'mcp', '--run'];
const child = spawn(command, args, { stdio: ['pipe', 'pipe', 'inherit'] });
process.stdin.pipe(child.stdin);
readline.createInterface({ input: child.stdout })
.on('line', line => { if (line.startsWith('{')) process.stdout.write(line + '\n'); });
child.on('exit', code => process.exit(code ?? 0));
```

But every user running pac MCP via Claude Desktop, GitHub Copilot, VS Code, etc. has to either deploy a wrapper like this or disable pac MCP entirely.

Suggested fix

Route the FeatureFlags loader's diagnostic write to `Console.Error` (or the configured stderr logger) instead of stdout. The lifecycle logger already routes correctly to stderr — only the FeatureFlags path is misrouted.

Environment

  • pac CLI 2.7.4 (resolved via `dnx Microsoft.PowerApps.CLI.Tool --yes` on 2026-05-29)
  • .NET 10.0.7
  • Windows 11 (Build 10.0.26200)
  • MCP host: Claude Desktop (latest)

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions