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
- 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
99 lines
3.9 KiB
TypeScript
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.` }],
|
|
};
|
|
},
|
|
);
|
|
}
|