A content-agnostic MCP server that exposes intimate hardware control to AI agents by wrapping Buttplug/Intiface.
Tactus demonstrates solid foundational quality with 11 well-structured tools, explicit parameter schemas via Zod, and clear descriptions. However, it falls short of A-grade (80+) primarily due to: (1) missing output schema documentation, tool responses return structured data but the schema is not formally declared in registration; (2) limited error guidance, errors are returned as text but lack recovery hints or categorization; (3) no tool annotations (readOnlyHint/destructiveHint) despite clear risk distinctions in the data. The safety layer and device validation logic are well-engineered, and descriptions generally exceed the 20-char minimum, but the lack of formal output documentation and error recovery patterns prevents a higher score. All 11 tools are explicitly registered with Zod schemas and descriptions; none are inferred.
Read battery level (0.0-1.0) for a device that reports it.
[Hardware control] Forwards a command to physical hardware. Move a stroker to a position over a duration.
List currently connected devices and their actuators.
[Hardware control] Forwards a command to physical hardware. Set oscillation intensity on a device.
[Hardware control] Forwards a command to physical hardware. Set rotation speed (and direction) on a device.
Scan for new devices for a duration, then return any newly found.
Report connection status to Intiface and device count.
Output schemas not formally documented in tool registration. Tools return structured objects (devices, status, battery, etc.) but the response schema is not declared in the server.registerTool() call. LLMs cannot plan downstream tool chains or extract fields reliably without knowing the output structure.
Error responses lack recovery guidance. The fail() helper returns a plain error message with isError: true, but provides no hint about what the agent should do next (retry, lookup device, check connection status, etc.). Errors are not categorized as retryable vs. fatal.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 59 | 2025-06-18+ | v2 |
Immediately stop every connected device. Emergency stop.
Stop all actuators on one device.
[Hardware control] Forwards a command to physical hardware. Set vibration intensity on a device.
[Hardware control] Forwards a command to physical hardware. Play a timed multi-step vibration pattern; interruptible by any stop.
Tool annotations missing. Five tools (vibrate, oscillate, rotate, linear, vibrate_pattern) perform hardware writes (destructive/state-changing), while six tools read-only. The registration does not use destructiveHint or readOnlyHint annotations to signal this to the MCP client. This forces clients to infer risk from the description text instead of structured metadata.
Irreversible operations (vibrate, oscillate, rotate, linear, stop_device, stop_all) lack dry-run or confirmation mechanism. An agent could accidentally drive hardware at full intensity or stop all devices mid-operation without a chance to review. Sensitive hardware control should support a confirmation_required or dry_run pattern.
Parameter constraints in descriptions could be stricter. For example, intensity and speed parameters accept 0.0-1.0, but the descriptions don't specify what happens if an LLM accidentally passes 1.5 or -0.5. Zod validation will clamp/reject, but error messages to the agent should be explicit about valid ranges and what was clamped.
device_id parameter accepts any non-negative integer, but descriptions don't hint that invalid device IDs will fail. When an LLM passes a device_id that doesn't exist, the error should suggest calling list_devices first to discover valid IDs.