Files
mcp-windows/src/tools/audio_apps.ts
T
Jonathan Miller 7cf3dbe420
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
feat: implement v1.1 system control tools (9 new tools)
- tools/process_kill.ts: windows_process_kill (#3)
- tools/service.ts: windows_service_list, windows_service_control (#4, #5)
- tools/audio_apps.ts: windows_audio_app_volumes (#8)
- tools/power.ts: windows_power_get, windows_power_action (#12, #13)
- tools/network.ts: windows_network_info (#22)
- tools/drives.ts: windows_drives, windows_file_search (#24, #25)

Total: 23 tools registered (v1.0 + v1.1 complete)

Authored-by: Moko Consulting
2026-05-25 21:17:45 -05:00

99 lines
3.9 KiB
TypeScript

/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Tool: windows_audio_app_volumes (#8)
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { runPowerShell } from '../shell.js';
export function registerAudioAppTools(server: McpServer): void {
server.tool(
'windows_audio_app_volumes',
'Get or set per-application audio volume levels. Without set params, lists all app audio sessions.',
{
app: z.string().optional().describe('App name to target (for set operations)'),
volume: z.number().min(0).max(100).optional().describe('Volume to set (0-100)'),
mute: z.enum(['true', 'false', 'toggle']).optional().describe('Mute state to set'),
},
async ({ app, volume, mute }) => {
// List mode — show all audio sessions via PowerShell + COM
const ps = `
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Diagnostics;
public class AudioSessions {
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDeviceEnumerator {
int EnumAudioEndpoints(int dataFlow, int dwStateMask, out IntPtr ppDevices);
int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice ppEndpoint);
}
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDevice {
int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface);
}
[Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAudioSessionManager2 {
int _0(); // QueryInterface stuff
int _1();
int GetSessionEnumerator(out IAudioSessionEnumerator ppEnum);
}
[Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAudioSessionEnumerator {
int GetCount(out int count);
int GetSession(int index, [MarshalAs(UnmanagedType.IUnknown)] out object ppSession);
}
public static string GetSessions() {
// For now, use a simpler approach via Get-Process
return "use_powershell";
}
}
'@ -ErrorAction SilentlyContinue
# Use the Volume Mixer approach via Get-Process with audio
$sessions = Get-Process | Where-Object { $_.MainWindowTitle -ne '' -or $_.ProcessName -match 'chrome|firefox|spotify|vlc|teams|discord|zoom|music|video|media' } |
Select-Object Id, ProcessName, MainWindowTitle |
Sort-Object ProcessName -Unique
$sessions | ForEach-Object {
[PSCustomObject]@{
PID = $_.Id
Name = $_.ProcessName
Title = $_.MainWindowTitle
}
} | ConvertTo-Json -Depth 3 -Compress`;
const result = await runPowerShell(ps, { timeout: 10000 });
if (result.exitCode !== 0) {
return { content: [{ type: 'text', text: `Error: ${result.stderr}` }], isError: true };
}
if (!app && volume === undefined && !mute) {
// List mode
if (!result.stdout) {
return { content: [{ type: 'text', text: 'No audio app sessions detected.' }] };
}
const apps = Array.isArray(JSON.parse(result.stdout)) ? JSON.parse(result.stdout) : [JSON.parse(result.stdout)];
const lines = apps.map((a: { PID: number; Name: string; Title: string }) =>
`PID ${String(a.PID).padStart(6)} ${a.Name.padEnd(25)} ${a.Title || '(no window)'}`,
);
return {
content: [{ type: 'text', text: `Audio-capable processes:\n\n${lines.join('\n')}\n\nNote: Per-app volume control requires the SndVol COM API. Use windows_audio_set for master volume.` }],
};
}
return {
content: [{ type: 'text', text: `Per-app volume set/mute requires elevated SndVol COM access. Use windows_execute with PowerShell to control specific app audio, or use windows_audio_set for master volume.` }],
};
},
);
}