Can you connect Minut to Claude? Yes. Minut has no official MCP server as of July 2026, but its public REST API at api.minut.com uses standard OAuth 2.0 client credentials, so a small read-only MCP server takes about fifteen minutes to build. Once it's running, Claude can query every home you monitor: noise and disturbance events, crowd and smoking detections, decibel history down to the minute, and which sensors are offline or low on battery.

MCP (the Model Context Protocol) is an open standard from Anthropic that lets AI assistants talk to outside systems. Minut is the privacy-safe sensor in this stack: according to Minut's help center, the device measures sound levels in decibels only and does not record audio, so everything Claude sees through this server is numbers and event metadata, never recordings.

What you can ask once it's connected

Noise patterns
  • "Pull the noise events for the lake house over the last 30 days. Which nights went from noise_detected to noise_ongoing, and how long until noise_quieted?"
  • "Chart the sound level for the living-room sensor last Saturday from 10pm to 4am in 5-minute buckets, with min and max per bucket."
Party risk
  • "Which homes had a disturbance_first_notice escalate to a second or third notice this quarter? Rank them by how often it happens."
  • "How many crowd_detected and smoking_detected events did we get this month, and at which homes?"
Device health
  • "List every sensor that's offline or low on battery right now, grouped by home."
  • "Any tamper_removed or device_offline events in the last two weeks? Which homes should the field team visit?"

What this data actually is. According to Minut's help center, "Minut does not record audio": the sensor measures sound levels in decibels, the way a sound meter does, and raises events when levels stay above a threshold (75 dB by default in the daytime, 70 dB during optional quiet hours). Connecting it to Claude exposes decibel values, timestamps, and event names. There is no audio anywhere in the API for Claude to access.

Before you start

Three things: a Minut account with API access (Minut's developer docs point you to the Developer tools page in the web app; see the FAQ below if you don't have that page), Node.js 18 or newer (check with node --version), and either Claude Desktop or Claude Code.

Step 1

Create a Minut OAuth client

Log in to the Minut web app and open the Developer tools page at web.minut.com/settings/api-clients. Create a new client and copy the client ID and client secret. You're only accessing your own data with the client_credentials grant, so per Minut's docs you can ignore the redirect URI.

That's the whole step. Minut's authentication uses OAuth 2.0 (RFC 6749): the server you build next exchanges those two values for a bearer token at https://api.minut.com/v8/oauth/token and refreshes it automatically before it expires.

Step 2

Scaffold the project

Two dependencies: the official MCP SDK and zod (used to describe each tool's inputs). No build step.

terminal
mkdir minut-mcp && cd minut-mcp
npm init -y
npm pkg set type="module"
npm install @modelcontextprotocol/sdk zod
Step 3

Write the server

Save this as index.js. Minut access tokens are short-lived (the docs' example shows expires_in: 3600, one hour), and Minut's guidance is to schedule a refresh rather than wait for 401s, so the server re-mints a few minutes early. It exposes six read-only tools.

index.js
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const BASE = "https://api.minut.com/v8";
const CLIENT_ID = process.env.MINUT_CLIENT_ID;
const CLIENT_SECRET = process.env.MINUT_CLIENT_SECRET;

if (!CLIENT_ID || !CLIENT_SECRET) {
  console.error("Set MINUT_CLIENT_ID and MINUT_CLIENT_SECRET first.");
  process.exit(1);
}

// Minut tokens are short-lived (~1h). Re-mint 5 minutes before expiry,
// per Minut's "schedule a refresh rather than waiting for 401s" guidance.
let token = null;
let tokenExp = 0;

async function getToken() {
  const now = Date.now();
  if (token && now < tokenExp) return token;
  const res = await fetch(`${BASE}/oauth/token`, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization: "Basic " +
        Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64"),
    },
    body: "grant_type=client_credentials&response_type=token",
  });
  if (!res.ok) throw new Error(`Minut token request failed (${res.status})`);
  const data = await res.json();
  token = data.access_token;
  tokenExp = now + (data.expires_in - 300) * 1000;
  return token;
}

async function get(path, params = {}) {
  const url = new URL(`${BASE}${path}`);
  for (const [k, v] of Object.entries(params)) {
    if (v !== undefined && v !== null && v !== "") url.searchParams.set(k, String(v));
  }
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${await getToken()}` },
  });
  if (!res.ok) throw new Error(`Minut ${res.status}: ${await res.text()}`);
  return res.json();
}

const json = (d) => ({ content: [{ type: "text", text: JSON.stringify(d, null, 2) }] });

const server = new McpServer({ name: "minut", version: "1.0.0" });

server.registerTool(
  "list_organizations",
  {
    description: "List the Minut organizations this account belongs to. Call first to get the organization_id other tools need.",
    inputSchema: {},
  },
  async () => json(await get("/organizations"))
);

server.registerTool(
  "list_homes",
  {
    description: "List all homes (monitored properties) in an organization.",
    inputSchema: {
      organization_id: z.string().describe("Minut organization id (from list_organizations)"),
    },
  },
  async ({ organization_id }) =>
    json(await get(`/organizations/${organization_id}/homes`))
);

server.registerTool(
  "list_devices",
  {
    description: "List sensors in an organization. Filter to offline or low-battery devices for a health check.",
    inputSchema: {
      organization_id: z.string().describe("Minut organization id"),
      offline: z.boolean().optional().describe("true = only devices currently offline"),
      low_battery: z.boolean().optional().describe("true = only devices low on battery"),
    },
  },
  async ({ organization_id, offline, low_battery }) =>
    json(await get(`/organizations/${organization_id}/devices`, { offline, low_battery }))
);

server.registerTool(
  "get_device",
  {
    description: "Get one device, including latest_sensor_values (sound, temperature, humidity). Devices sync at least once per hour, so these are near-real-time, not live.",
    inputSchema: { device_id: z.string().describe("Minut device id") },
  },
  async ({ device_id }) => json(await get(`/devices/${device_id}`))
);

server.registerTool(
  "get_home_events",
  {
    description: "List events for a home: noise_detected, noise_ongoing, noise_quieted, disturbance_first_notice through disturbance_third_notice, crowd_detected, smoking_detected, tamper_removed, battery_low, device_offline, and more. Page with limit/offset.",
    inputSchema: {
      home_id: z.string().describe("Minut home id (from list_homes)"),
      event_type: z.string().optional().describe("Comma-separated event types to include"),
      start_at: z.string().optional().describe("ISO-8601 UTC start, e.g. 2026-06-01T00:00:00Z"),
      end_at: z.string().optional().describe("ISO-8601 UTC end"),
      limit: z.number().optional().describe("Page size, default 50"),
      offset: z.number().optional().describe("Rows to skip, for paging"),
    },
  },
  async ({ home_id, ...params }) =>
    json(await get(`/homes/${home_id}/events`, { limit: 50, ...params }))
);

server.registerTool(
  "get_sound_levels",
  {
    description: "Decibel time series for a device. Defaults to the last 4 hours if no start_at. Use time_resolution (seconds) to bucket, include_min_max for peaks.",
    inputSchema: {
      device_id: z.string().describe("Minut device id"),
      start_at: z.string().optional().describe("ISO-8601 start (default: 4 hours ago)"),
      end_at: z.string().optional().describe("ISO-8601 end"),
      time_resolution: z.number().optional().describe("Bucket size in seconds, e.g. 300"),
      include_min_max: z.boolean().optional().describe("Include min/max per bucket"),
    },
  },
  async ({ device_id, ...params }) =>
    json(await get(`/devices/${device_id}/sound_level`, { include_min_max: true, ...params }))
);

const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Minut MCP server running on stdio");
Mind the rate limits. Per Minut's API information page, the API allows 5 requests per second (burstable to 50, with delays after 20) and 120 per minute; beyond that you get 429 Too many requests. Claude's tool calls are naturally spaced out, so you'll rarely hit this, but avoid asking it to sweep decibel history for dozens of devices in one breath.
Step 4

Connect it to Claude

Point Claude at the server and pass your two credentials.

Claude Desktop

Add a minut entry to your config file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %AppData%\Claude\claude_desktop_config.json on Windows), then fully quit and reopen the app.

claude_desktop_config.json
{
  "mcpServers": {
    "minut": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/minut-mcp/index.js"],
      "env": {
        "MINUT_CLIENT_ID": "your-client-id",
        "MINUT_CLIENT_SECRET": "your-client-secret"
      }
    }
  }
}
Claude Code

One command from your terminal. Everything after -- is what launches the server.

terminal
claude mcp add \
  --env MINUT_CLIENT_ID=your-client-id \
  --env MINUT_CLIENT_SECRET=your-client-secret \
  --transport stdio minut \
  -- node /ABSOLUTE/PATH/TO/minut-mcp/index.js

Confirm with claude mcp list. Add --scope user to make it available in every project.

Step 5

Ask away

Start a new conversation. Claude asks permission the first time it calls a tool, approve it, and you're set. Have it call list_organizations then list_homes first so it learns your ids.

try these
List our homes and devices, and flag anything offline or low on battery.

Pull noise and disturbance events for every home over the last 30 days
and rank homes by number of disturbance notices.

Get the sound levels for the loudest home's device last weekend in
10-minute buckets and tell me when it peaked.

The event types worth knowing

These are the event names you'll filter on with get_home_events, drawn from Minut's v8 API Reference and developer docs. Minut's noise flow escalates: a detection becomes an "ongoing" disturbance, and guest notices step up from first to third.

Event typeWhat it means
noise_detectedSound crossed the home's threshold.
noise_ongoingNoise kept going after detection.
noise_quietedLevels dropped back under the threshold.
disturbance_first_notice
disturbance_second_notice
disturbance_third_notice
The escalation ladder. Repeated third notices are your party-risk signal.
disturbance_ended / disturbance_dismissedHow the incident resolved.
crowd_detectedOccupancy above the home's limit (Crowd Detect, beta in the v8 reference).
smoking_detectedMinut's cigarette-smoke detection fired.
tamper_removed / tamper_mountedSomeone took the sensor off its mount, or put it back.
battery_low / device_offlineDevice health. Catch these before a party weekend, not after.

Minut's docs note the authoritative event catalog lives in the API Reference at api.minut.com/latest/docs, and that webhook payloads carry a reduced version of these events.

Prefer to skip the typing?

Paste this into Claude Code (or any coding agent) and it will build, install, and wire up the server for you. Then come back to step 1 for your credentials.

prompt for Claude Code
Build a local MCP server (Node.js, ESM, @modelcontextprotocol/sdk and zod)
that wraps the Minut API (base https://api.minut.com/v8). Read
MINUT_CLIENT_ID and MINUT_CLIENT_SECRET from the environment. Get an access
token by POSTing to https://api.minut.com/v8/oauth/token with
Content-Type application/x-www-form-urlencoded, an Authorization: Basic
header of base64(client_id:client_secret), and body
grant_type=client_credentials&response_type=token. Tokens are short-lived
(expires_in seconds, ~1h), so cache in memory and re-mint 5 minutes before
expiry. Add an authenticated GET helper (Bearer token) that builds query
strings from a params object, skipping empty values. Register these
read-only tools: list_organizations() -> /organizations,
list_homes(organization_id) -> /organizations/{id}/homes,
list_devices(organization_id, offline?, low_battery?) ->
/organizations/{id}/devices, get_device(device_id) -> /devices/{id},
get_home_events(home_id, event_type?, start_at?, end_at?, limit?, offset?)
-> /homes/{id}/events, get_sound_levels(device_id, start_at?, end_at?,
time_resolution?, include_min_max?) -> /devices/{id}/sound_level.
Use StdioServerTransport; log startup to stderr. Then make a package.json
(type: module), install the deps, and show me the claude mcp add command,
the claude_desktop_config.json block, and three prompts to try.

Troubleshooting

401 Unauthorized on every call

Check the client ID and secret in your Claude config, and that the token request body is exactly grant_type=client_credentials&response_type=token with the Basic auth header, per Minut's user authentication docs. If it worked and then stopped mid-session, the token expired; the server above re-mints automatically, but only 5 minutes before expiry, so a machine that slept through the window mints a fresh one on the next call.

I don't see the API clients / Developer tools page in Minut

API access is account-dependent. Minut's developer docs list "a Minut account with API access" as the prerequisite, and Minut's help center has listed the Minut API among Enterprise-tier integrations. If web.minut.com/settings/api-clients isn't available to you, contact Minut (the help center points to hello@minut.com) about enabling it on your plan.

404 on /devices/{id}/sound_level

Minut's sensor-data guide uses /devices/<DEVICE_ID>/sound_level in its example requests, while the v8 API Reference lists the endpoint as /devices/{device_id}/sound_level_dba. If one path 404s on your account, swap in the other in index.js; same parameters either way.

Claude doesn't see any Minut tools

Claude Desktop only reads its config at startup: fully quit (Cmd+Q on macOS) and reopen. Still nothing? Check the config JSON is valid, the path to index.js is absolute, and node is on your PATH. In Claude Code, run claude mcp list to confirm the server registered.

Sound values look stale

Expected. Per Minut's docs, latest_sensor_values update when the device connects to the backend, "at least once per hour", not in real time. Events (noise, disturbance) are the timely signal; for genuine push delivery, use Minut's webhooks alongside this server.

429 Too many requests

You hit Minut's throttle: 5 requests per second (burstable to 50, delays after 20) or 120 per minute. Narrow the question, or tell Claude to batch by home instead of by device.

The sensor hears the party. The photos show what it cost you. Minut tells you a disturbance happened Saturday night; your turnover photos tell you what it left behind. RapidEye reads the inspection photos your cleaners already capture and flags damage and missed cleaning before the next check-in, and its API connects to Claude the same way this guide does.

FAQ

Does Minut have an official MCP server?

No, not as of July 2026. Minut's developer documentation describes a REST API and webhooks, with no MCP server mentioned, and no first-party Minut server appears in the common MCP directories. That's why this guide exists: the API's standard OAuth and clean JSON make a do-it-yourself server a fifteen-minute job.

Is this safe? Can Claude change my alarm settings?

Not with this server. Every tool is a GET; Claude can read but not write. The Minut API does expose write endpoints (alarm modes, noise thresholds, monitoring profiles), so if you extend the server later, add write tools deliberately and one at a time. Your credentials stay in environment variables on your own machine, and the server runs locally over stdio.

Can Claude get real-time noise alerts?

MCP is pull-based: Claude queries when you ask. For push, Minut recommends webhooks: POST /webhooks with a public HTTPS endpoint and the event types you want. Note Minut's delivery is at-most-once with no retry queue, so Minut itself recommends reconciling against the events endpoint, which is exactly what this server queries.

I already have Minut connected to my PMS. Does this conflict?

No. Minut's integrations with PMSs like Guesty for Pros and Hostaway keep doing their job (syncing properties, auto-messaging guests on noise). This server is a separate read-only client on the same API. If your stack runs through a device aggregator instead, our Seam guide reaches Minut through Seam's unified API.

What sensor data can Claude actually see?

What the API exposes: decibel time series (with min/max per bucket), temperature, humidity, motion-derived events, occupancy detections, and device status like battery and connectivity. Per Minut's help center, no audio is recorded, so there are no recordings anywhere in the system.

Sources

Independent integration guide. RapidEye is not affiliated with or endorsed by Minut or Anthropic. API behavior, plan gating, and dashboard menus can change; confirm specifics against the official docs above. Last reviewed July 19, 2026.