A custom MCP server written in C# that manages audio devices (Sonos) and provides system status checks
vanilla-mcp is a minimal HTTP server with 4 tools for Sonos device control. All tools are explicitly registered with names, descriptions, and input schemas visible in Program.cs. However, the server has significant gaps in definition quality: (1) Parameter descriptions are minimal or absent, only 'id' and 'volume' are described; parameters like those in get_system_status and get_devices lack any description. (2) Output schemas are completely undocumented, there is no specification of what structure get_devices, get_device_volume, or set_device_volume return, forcing LLMs to guess. (3) Error handling is absent, there are no error messages guiding LLM recovery, and invalid inputs (e.g., non-existent device ID) return only a text string with no recovery guidance. (4) Tool descriptions are minimal (10-50 chars), below the production baseline of 194 chars, and lack context on when to use each tool or what they return. (5) No input validation or constraints, e.g., volume accepts any integer but the description mentions '0 - 100'; no enum or min/max constraint is enforced. (6) Composition is weak: get_device_volume and set_device_volume both accept 'id', but there's no documented mechanism for discovering valid device IDs without calling get_devices first. Tools average 1.5 parameters with minimal documentation, well below production norms.
Retrieves the current volume level of a device by ID
Retrieves the list of sonos devices in the system
Checks the status of the system
Sets the volume level of a device by ID
Output schemas completely undocumented. Tools return content with type and text fields, but the structure and semantics of the 'text' field are never specified. get_devices returns a JSON string representation of an array, but this is not declared in any output schema. LLMs cannot plan downstream calls without knowing what fields to expect.
Parameter descriptions are missing or minimal. 'id' in get_device_volume is described as 'The ID of the device' but the tool does not explain how to discover valid IDs, what format they take, or that get_devices must be called first. 'volume' in set_device_volume mentions '0 - 100' in the description but this is not enforced as a JSON Schema constraint (no minimum/maximum). get_system_status and get_devices have no required parameters but the empty properties object is not documented, LLMs don't know if these tools take optional parameters.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | F | 46 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 46 | - | v1 |
No error handling or recovery guidance. When an invalid device ID is passed to get_device_volume or set_device_volume, the response is 'No device found with id {id}' (plain text). There is no structured error, no error classification (retryable vs fatal), and no guidance on what to do next. Per pattern:recovery-guide, errors should tell the LLM: can I retry? Should I ask the user? Or call search_users()? This response provides none of that.
Tool descriptions are far too short. 'Checks the status of the system' (31 chars) and 'Retrieves the list of sonos devices in the system' (50 chars) are vague. Production baseline is 194 chars. Descriptions should answer: What exactly does it do? When should I call it instead of a similar tool? What does it return? For example, get_system_status provides no detail on what 'status' means, is it a heartbeat? CPU load? Network connectivity? LLMs will guess wrong.
No input validation constraints. The 'volume' parameter in set_device_volume accepts any integer, but the description says '0 - 100'. There is no JSON Schema minimum/maximum constraint, so an LLM could pass 150 or -1 and the tool would silently accept it (the mock handler doesn't validate). Per pattern:constrained-input, numeric ranges must be declared in the schema, not just in description text.
Composition gap: no discovery hint. The 'id' parameter in get_device_volume and set_device_volume has no guidance on where to obtain a valid ID. A production tool would document: 'Call get_devices() first to obtain a list of valid device IDs.' This prevents wasted calls and guides multi-step planning.
set_device_volume lacks side-effect documentation. The tool modifies state (sets volume) but the description does not explicitly say 'Sets the volume level...' with no qualifier about whether this is idempotent, logged, or reversible. Per pattern:command-tool, write tools must declare their side effects so LLMs know which calls are safe to retry.