npm package: @s-tier-building-automation/stier-mcp-server
Description: MCP server for STier Web API - Niagara 4 building automation
Author: S-Tier Building Automation
Latest version: 1.12.0

# STier MCP Server

A Model Context Protocol (MCP) server that provides tools for interacting with the STier Web API on Niagara 4 building automation stations.

## Features

### Read Operations
- **list_histories** - List all available history IDs
- **get_history** - Query history trend data with time range and pagination
- **list_alarms** - List all alarm source names
- **get_alarms** - Query alarms with filtering
- **get_point** - Get control point details (by history ID for STierAPI, by path for OBIX)
- **get_stats** - Get station statistics and system info
- **list_wiresheets** - List wire sheets (component containers) in the station
- **get_wiresheet** - Get detailed wire sheet with components and connections
- **get_component_slots** - Get available slots for a component
- **list_component_types** - List available component types by category
- **get_component_tree** - Browse the component hierarchy
- **search_components** - Search for components by name or type
- **execute_bql** - Execute BQL (Baja Query Language) queries

### Write Operations
- **create_wiresheet** - Create a new wire sheet with optional components and connections
- **create_component** - Add a component to an existing wire sheet
- **create_connection** - Create wiring connections between components
- **delete_wiresheet** - Delete a wire sheet or component
- **delete_component** - Delete a component
- **set_value** - Set a value on a control point
- **bulk_read** - Read multiple points in one call
- **bulk_write** - Write multiple points in one call

### Multi-Station Operations
- **list_stations** - List all configured stations (multi-station mode)
- **test_connection** - Test connectivity to a station
- **obix_info** - Get OBIX server information
- **obix_browse** - Browse OBIX object hierarchy

### Authentication & Connection

- Supports both Basic and Digest authentication (auto-detection)
- Automatic HTTP-to-HTTPS redirect handling
- Self-signed certificate support for development environments

### Protocol Support

The server supports two protocols for connecting to Niagara stations:

- **STierAPI** - Custom module with extended features (recommended when available)
- **OBIX** - Standard Niagara protocol (works with any Niagara station)

## Prerequisites

- Node.js 18 or later
- A Niagara 4 station with the STier API module installed
- Valid credentials for the station

### Required Station Connection Type: TLS WebSocket

The STier API module is served by the station's **HTTPS web service** (port 443 by default). The `STIER_STATION_URL` must point to this HTTPS port.

> ⚠️ **Fox ports are not supported.** Niagara's Fox protocol ports (1911 for Fox, 4911 for FoxS) use a binary protocol that does not expose HTTP endpoints. The STier API and OBIX endpoints are only accessible via the HTTPS web service.

In Niagara Workbench, this corresponds to the **TLS WebSocket** connection type. If you see your station configured with Fox or FoxS only, ensure the `HTTPSWebService` is also enabled on the station and use its port in `STIER_STATION_URL`.

## Setup

### Option 1: Install via npx (Recommended)

No authentication required — the package is published to the public npm registry.

#### Cursor

Add to `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "stier-api": {
      "command": "npx",
      "args": ["-y", "@s-tier-building-automation/stier-mcp-server"],
      "env": {
        "STIER_STATION_URL": "https://192.168.1.100",
        "STIER_USERNAME": "your-username",
        "STIER_PASSWORD": "your-password",
        "STIER_REJECT_UNAUTHORIZED": "false"
      }
    }
  }
}
```

#### Claude Code

```bash
claude mcp add stier-api -- npx -y @s-tier-building-automation/stier-mcp-server
```

Then set environment variables in your shell or `.env` file:

```bash
export STIER_STATION_URL="https://192.168.1.100"
export STIER_USERNAME="your-username"
export STIER_PASSWORD="your-password"
export STIER_REJECT_UNAUTHORIZED="false"
```

#### VS Code (Copilot)

Add to `.vscode/mcp.json` in your project:

```json
{
  "servers": {
    "stier-api": {
      "command": "npx",
      "args": ["-y", "@s-tier-building-automation/stier-mcp-server"],
      "env": {
        "STIER_STATION_URL": "https://192.168.1.100",
        "STIER_USERNAME": "your-username",
        "STIER_PASSWORD": "your-password",
        "STIER_REJECT_UNAUTHORIZED": "false"
      }
    }
  }
}
```

#### Windsurf

Add to `~/.codeium/windsurf/mcp_config.json`:

```json
{
  "mcpServers": {
    "stier-api": {
      "command": "npx",
      "args": ["-y", "@s-tier-building-automation/stier-mcp-server"],
      "env": {
        "STIER_STATION_URL": "https://192.168.1.100",
        "STIER_USERNAME": "your-username",
        "STIER_PASSWORD": "your-password",
        "STIER_REJECT_UNAUTHORIZED": "false"
      }
    }
  }
}
```

### Option 2: Local Installation

If you have the source code:

```bash
cd mcp-server
npm install
npm run build
```

Then configure in your MCP client using `node` instead of `npx`:

```json
{
  "command": "node",
  "args": ["<path-to-repo>/mcp-server/dist/index.js"],
  "env": {
    "STIER_STATION_URL": "https://192.168.1.100",
    "STIER_USERNAME": "your-username",
    "STIER_PASSWORD": "your-password",
    "STIER_REJECT_UNAUTHORIZED": "false"
  }
}
```

> **Note**:
> - The server handles HTTP-to-HTTPS redirects automatically
> - Restart your MCP client or toggle the server after configuration changes
> - Use `-y` flag with npx to skip the install confirmation prompt

### Environment Variables

The server supports two modes: **Legacy Mode** (single station) and **Multi-Station Mode**.

#### Legacy Mode (Single Station)

| Variable | Required | Description |
|----------|----------|-------------|
| `STIER_STATION_URL` | Yes | URL to the Niagara station (e.g., `http://localhost:8080` or `https://192.168.1.100`) |
| `STIER_USERNAME` | Yes | Username for station authentication |
| `STIER_PASSWORD` | Yes | Password for station authentication |
| `STIER_REJECT_UNAUTHORIZED` | No | Set to `true` to reject self-signed certificates (default: `false`) |

#### Multi-Station Mode

Multi-station mode uses a PostgreSQL database shared with the stier-chat web app. Stations are managed through the web UI and credentials are encrypted.

| Variable | Required | Description |
|----------|----------|-------------|
| `DATABASE_URL` | Yes | PostgreSQL connection string |
| `ENCRYPTION_KEY` | Yes | 32-character key for decrypting station credentials (must match stier-chat) |
| `STIER_ENABLE_WRITES` | No | Set to `true` to enable write operations (default: `false`) |
| `STIER_RATE_LIMIT` | No | Requests per second per station (default: `5`) |
| `STIER_TIMEOUT_READ_MS` | No | Read operation timeout in ms (default: `5000`) |
| `STIER_TIMEOUT_HISTORY_MS` | No | History query timeout in ms (default: `30000`) |
| `STIER_TIMEOUT_BULK_MS` | No | Bulk operation timeout in ms (default: `60000`) |

> **Note**: Multi-station mode requires running from the monorepo context where Prisma has been generated for stier-chat. See `.env.example` for a complete template.

## Available Tools

### list_histories

List all available history IDs from the station.

**Parameters:**
- `station` (optional): Filter by station name

**Example:**
```
list_histories()
list_histories(station: "MyStation")
```

### get_history

Query history trend data with time range and pagination.

**Parameters:**
- `id` (required): History ID to query
- `station` (optional): Station name
- `start` (optional): Start time in ISO 8601 format
- `end` (optional): End time in ISO 8601 format
- `page` (optional): Page number (default: 1)
- `pageSize` (optional): Records per page (default: 500)

**Example:**
```
get_history(id: "AHU1_SupplyTemp")
get_history(id: "AHU1_SupplyTemp", start: "2025-01-01T00:00:00Z", end: "2025-01-02T00:00:00Z")
```

### list_alarms

List all alarm source names from the station.

**Parameters:**
- `station` (optional): Filter by station name

### get_alarms

Query alarms with filtering options.

**Parameters:**
- `id` (optional): Filter by alarm source name
- `ackState` (optional): Filter by acknowledgment state
- `alarmState` (optional): Filter by alarm state
- `page` (optional): Page number (default: 1)
- `pageSize` (optional): Records per page (default: 500)

**Example:**
```
get_alarms()
get_alarms(ackState: "unacked")
get_alarms(id: "AHU1_HighTemp", alarmState: "offnormal")
```

### get_point

Get control point details.

**Parameters:**
- `path` (required): Point path or history ID (e.g., "/AHU1/SupplyTemp" or "AHU1_SupplyTemp")
- `station` (optional): Station name (for multi-station mode)

**Protocol-Specific Behavior:**
- **STierAPI stations**: The `path` can be a history ID from `list_histories` or a slot path. The API resolves the point via history configuration.
- **OBIX stations**: The `path` is treated as a point path (e.g., `/config/AHU1/SupplyTemp`). The API reads the point directly.

**Returns:** Point information including name, path, units, precision, and range facets.

### get_stats

Get station statistics and system information.

**Parameters:** None

**Returns:** Object with memory usage, OS info, Java version, timezone, and other system details.

### list_wiresheets

List wire sheets (component containers with control logic) in the station.

**Parameters:**
- `pathPrefix` (optional): Filter by path prefix (e.g., "/Drivers" to list only driver wiresheets)
- `filterByType` (optional): Filter by component type (e.g., "program", "control", "folder")

**Example:**
```
list_wiresheets()
list_wiresheets(pathPrefix: "/Drivers")
list_wiresheets(filterByType: "folder")
```

**Returns:** Object with `success`, `count`, and `wiresheets` array containing path and type for each wire sheet.

### get_wiresheet

Get detailed wire sheet information including all components, their properties, positions, and wiring connections.

**Parameters:**
- `path` (required): Full path to the wire sheet container (e.g., "/Logic/AHU1")

**Example:**
```
get_wiresheet(path: "/Drivers/NiagaraNetwork/Controller")
get_wiresheet(path: "/Logic/TemperatureControl")
```

**Returns:** Object containing:
- `exists`: Whether the wire sheet was found
- `path`: The wire sheet path
- `type`: Component type
- `components`: Array of components with name, path, type, properties, position, and children
- `connections`: Array of connections with fromComponent, fromSlot, toComponent, toSlot

### create_wiresheet

Create a new wire sheet with optional components and connections.

**Parameters:**
- `stationPath` (required): Path where the wiresheet should be created (e.g., "/Logic")
- `wiresheetName` (required): Name for the new wiresheet
- `wiresheetType` (optional): Type of wiresheet to create (default: "baja:Folder")
- `components` (optional): Array of components to create
- `connections` (optional): Array of connections to create between components

**Example:**
```
create_wiresheet(
  stationPath: "/Logic",
  wiresheetName: "AHU_Control",
  wiresheetType: "baja:Folder",
  components: [
    { name: "SetPoint", type: "control:NumericPoint", position: { x: 10, y: 10 } },
    { name: "Adder", type: "kitControl:Add", position: { x: 30, y: 10 } }
  ],
  connections: [
    { fromComponent: "SetPoint", fromSlot: "out", toComponent: "Adder", toSlot: "in" }
  ]
)
```

**Returns:** Object with `success`, `wiresheetPath`, `componentsCreated`, and any errors.

### create_component

Add a single component to an existing wire sheet.

**Parameters:**
- `wiresheetPath` (required): Path to the parent wiresheet
- `name` (required): Name for the new component
- `type` (required): Component type (e.g., "control:NumericPoint", "kitControl:Add")
- `position` (optional): Visual position `{ x, y, width, height }`
- `properties` (optional): Component properties to set

**Example:**
```
create_component(
  wiresheetPath: "/Logic/AHU_Control",
  name: "TempSensor",
  type: "control:NumericPoint",
  position: { x: 20, y: 30, width: 8, height: 0 },
  properties: { out: 72.5 }
)
```

**Returns:** Object with `success`, `componentPath`, and `message`.

### create_connection

Create a wiring connection between two components.

**Parameters:**
- `wiresheetPath` (required): Path to the wiresheet containing both components
- `fromComponent` (required): Name of the source component
- `fromSlot` (optional): Output slot name (default: "out")
- `toComponent` (required): Name of the target component
- `toSlot` (optional): Input slot name (default: "in")

**Example:**
```
create_connection(
  wiresheetPath: "/Logic/AHU_Control",
  fromComponent: "SetPoint",
  fromSlot: "out",
  toComponent: "Controller",
  toSlot: "in1"
)
```

**Returns:** Object with `success`, `message`, `linkName`, and available slots if errors occur.

### delete_wiresheet

Delete a wire sheet or component at the specified path.

**Parameters:**
- `path` (required): Full path to the wiresheet/component to delete

**Example:**
```
delete_wiresheet(path: "/Logic/OldController")
```

**Returns:** Object with `success` and `message`.

### set_value

Set a value on a control point.

**Parameters:**
- `path` (required): Path to the control point
- `value` (required): Value to set
- `station` (optional): Station name (for multi-station mode)

**Example:**
```
set_value(path: "/Drivers/AHU1/SupplyTempSP", value: 72.5)
```

### list_stations

List all configured stations (multi-station mode only).

**Returns:** Array of stations with id, name, url, protocol, and isActive status.

### test_connection

Test connectivity to a station.

**Parameters:**
- `station` (required): Station name or ID

**Returns:** Connection status and any error details.

### obix_info

Get OBIX server information (for OBIX stations).

**Parameters:**
- `station` (optional): Station name

**Returns:** OBIX server version and capabilities.

### obix_browse

Browse the OBIX object hierarchy.

**Parameters:**
- `path` (optional): Path to browse (default: root)
- `station` (optional): Station name

**Returns:** List of child objects with their types and values.

### bulk_read

Read multiple points in a single call.

**Parameters:**
- `paths` (required): Array of point paths to read
- `station` (optional): Station name

**Returns:** Object with values for each path.

### bulk_write

Write multiple points in a single call. Requires `STIER_ENABLE_WRITES=true`.

**Parameters:**
- `values` (required): Object mapping paths to values
- `station` (optional): Station name

**Returns:** Object with success status for each write.

## Development

### Building

```bash
npm run build
```

### Running in Development

```bash
npm run dev
```

## Troubleshooting

### Empty Response or JSON Parse Error

This usually indicates a connection or authentication issue:

1. **Test with Postman/curl first** - Verify the API works with your credentials
2. **Check the URL** - Try both HTTP and HTTPS; the server follows redirects automatically
3. **Verify credentials** - Ensure username/password are correct

### Connection Refused

Ensure:
1. The station URL and port are correct — use the **HTTPS web service port** (typically 443), not a Fox port
   - ❌ `https://192.168.1.100:1911` — Fox port (binary protocol, HTTP not supported)
   - ❌ `https://192.168.1.100:4911` — FoxS port (binary protocol, HTTP not supported)
   - ✅ `https://192.168.1.100:443` — HTTPS web service (TLS WebSocket in Workbench)
2. The station is running
3. The STier API servlet is enabled in Niagara Workbench
4. The `HTTPSWebService` is enabled on the station (not just `FoxService`)
5. Your firewall allows the connection

### SSL Certificate Errors

For self-signed certificates (common in development):
- Set `STIER_REJECT_UNAUTHORIZED` to `"false"` in your config

### Authentication Failed (Login Page Returned)

If you receive an HTML login page instead of JSON:
1. Username and password are incorrect
2. The user doesn't have API access permissions
3. The STier service is not licensed

### MCP Server Not Starting

1. Ensure you've run `npm run build` after any code changes
2. Verify the path in your MCP config points to `dist/index.js` (not `src/index.ts`)
3. Check Cursor's MCP logs for error details

## License

MIT