Skip to main content
Glama
asafkiv

AppDynamics MCP Server

by asafkiv
README.md
# AppDynamics MCP Server

A [Model Context Protocol](https://modelcontextprotocol.io/) server that gives LLM clients (Cursor, Claude Desktop, etc.) full access to your AppDynamics monitoring data — plus the ability to create and manage dashboards and health rules.

## Features

**30 tools** across 8 categories:

- **Discovery**: List and search applications by name
- **Health Monitoring**: Full health rule CRUD, violations, and anomaly detection
- **Application Performance**: Business transactions, service endpoints, and their metrics
- **Infrastructure**: Tiers, nodes, and backend/remote service dependencies
- **Diagnostics**: Transaction snapshots and error events
- **Root Cause Analysis**: Automated composite diagnosis across all signal types
- **Metrics**: Browse the metric tree and query any metric with rollup support
- **Dashboards**: Full CRUD — list, view, create, update, add widgets, clone, delete, export, import, auto-build, per-rule health status widgets

### Key capabilities

- **Natural language friendly**: Accept application names, not just IDs
- **Historical time ranges**: Query any past window, not just "the last N minutes" — see [Time Ranges](#time-ranges)
- **Metric tree browser**: Discover available metrics interactively, including custom/machine-agent metrics
- **Rollup control**: Per-widget rollup for time-series vs. aggregate metric views
- **Dashboard auto-builder**: Create full multi-section dashboards from a single prompt
- **HealthListWidget scoping**: Each widget can be pinned to a specific health rule (not "all rules")
- **Health rule CRUD**: Create, update, enable/disable, and delete health rules — including custom metrics scoped to a specific tier or node
- **Smart defaults**: Sensible time ranges and result limits out of the box

## Time Ranges

Every time-aware tool accepts `durationInMins`, `startTime`, and `endTime`. Timestamps
may be **ISO 8601** (`2026-08-03T14:00:00Z`) or **epoch milliseconds**; bare epoch-seconds
values are detected and converted.

| Arguments supplied | Window queried | AppDynamics range type |
|---|---|---|
| *(none)* | The tool's default lookback, ending now | `BEFORE_NOW` |
| `durationInMins` | Last N minutes | `BEFORE_NOW` |
| `startTime` + `endTime` | Exactly that window | `BETWEEN_TIMES` |
| `endTime` + `durationInMins` | N minutes **before** that point | `BEFORE_TIME` |
| `startTime` + `durationInMins` | N minutes **after** that point | `AFTER_TIME` |

```jsonc
// "What did response time look like during yesterday's incident?"
{
  "application": "Checkout",
  "metricPath": "Overall Application Performance|Average Response Time (ms)",
  "startTime": "2026-08-02T12:00:00Z",
  "endTime":   "2026-08-02T15:00:00Z"
}
```

Contradictory input is rejected with an actionable message rather than silently
guessing — supplying `durationInMins` together with both endpoints, an inverted
window (`endTime` before `startTime`), or an unparseable timestamp all return an error.

**Tools supporting time ranges**: `appd_get_metric_data`, `appd_get_health_violations`,
`appd_get_anomalies`, `appd_get_errors`, `appd_get_snapshots`, `appd_get_bt_performance`,
`appd_get_service_endpoint_performance`, `appd_diagnose_issue`.

> Note: AppDynamics rolls older data into coarser buckets. A narrow window far in the
> past may return nothing even though a wider window over the same period returns an
> aggregate — this is controller-side retention granularity, not a query error.

## Quick Start

### 1. Install dependencies

```bash
npm install
```

### 2. Configure environment

Copy `.env.example` to `.env` and fill in your credentials:

```bash
cp .env.example .env
```

Required variables:

| Variable | Description |
|---|---|
| `APPD_URL` | Controller base URL (e.g., `https://mycompany.saas.appdynamics.com`) |
| `APPD_CLIENT_NAME` | OAuth client name or API key |
| `APPD_CLIENT_SECRET` | OAuth client secret |
| `APPD_ACCOUNT_NAME` | Account name (for `clientName@accountName` format) |

### 3. Add to your MCP client

**Cursor** (`~/.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "appdynamics": {
      "command": "npx",
      "args": ["tsx", "src/index.ts"],
      "cwd": "/path/to/appdynamics-mcp-server",
      "env": {
        "APPD_URL": "https://your-controller.saas.appdynamics.com",
        "APPD_CLIENT_NAME": "your-client-name",
        "APPD_CLIENT_SECRET": "your-client-secret",
        "APPD_ACCOUNT_NAME": "your-account-name"
      }
    }
  }
}
```

**Claude Desktop** (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "appdynamics": {
      "command": "npx",
      "args": ["tsx", "/path/to/appdynamics-mcp-server/src/index.ts"],
      "env": {
        "APPD_URL": "https://your-controller.saas.appdynamics.com",
        "APPD_CLIENT_NAME": "your-client-name",
        "APPD_CLIENT_SECRET": "your-client-secret",
        "APPD_ACCOUNT_NAME": "your-account-name"
      }
    }
  }
}
```

## Tools Reference

### Discovery

| Tool | Description |
|---|---|
| `appd_get_applications` | List all monitored applications (with optional name filter) |

### Health Monitoring

| Tool | Description |
|---|---|
| `appd_get_health_rules` | List health rules or get details of a specific rule |
| `appd_create_health_rule` | Create a new health rule with warning and/or critical conditions. Supports `OVERALL_APPLICATION_PERFORMANCE`, `BUSINESS_TRANSACTION_PERFORMANCE`, `TIER_NODE_HEALTH`, and `CUSTOM` entity types. Use `affectedTier` or `affectedNode` to scope rules to a specific tier/node for custom metrics. |
| `appd_update_health_rule` | Update an existing health rule's name, conditions, thresholds, or scope |
| `appd_delete_health_rule` | Permanently delete a health rule |
| `appd_enable_health_rule` | Enable or disable a health rule |
| `appd_get_health_violations` | Get health rule violations for one or all apps |
| `appd_get_anomalies` | Get anomaly events (open-only by default) |

### Application Performance

| Tool | Description |
|---|---|
| `appd_get_business_transactions` | List BTs for an application |
| `appd_get_bt_performance` | Get response time, throughput, errors for a BT |
| `appd_get_service_endpoints` | List service endpoints (API-level granularity), discovered via the metric tree |
| `appd_get_service_endpoint_performance` | Get performance metrics for a service endpoint (by **name**, plus optional `tier`) |

### Infrastructure

| Tool | Description |
|---|---|
| `appd_get_tiers_and_nodes` | Get tiers with their nodes (agents, machines, IPs) |
| `appd_get_backends` | List backend dependencies (databases, APIs, caches, queues) |

### Diagnostics

| Tool | Description |
|---|---|
| `appd_get_snapshots` | Get transaction snapshots (deep diagnostic captures) |
| `appd_get_errors` | Get error and exception events |

### Root Cause Analysis

| Tool | Description |
|---|---|
| `appd_diagnose_issue` | Automated root cause analysis — fetches violations, anomalies, error events, and snapshots in parallel, then returns ranked candidates, a merged timeline, error breakdown, and investigation steps. Accepts a historical window; the baseline automatically becomes the equivalent window immediately before it |

### Metrics

| Tool | Description |
|---|---|
| `appd_browse_metric_tree` | Browse the metric hierarchy to discover available metrics, including custom machine-agent metrics |
| `appd_get_metric_data` | Query any metric by path, over any [time range](#time-ranges). Supports `rollup` control: `true` returns a single aggregated value, `false` returns individual time-series data points |

### Dashboards

| Tool | Description |
|---|---|
| `appd_get_dashboards` | List all custom dashboards |
| `appd_get_dashboard` | Get full dashboard definition with widgets |
| `appd_create_dashboard` | Create a new dashboard with optional widgets |
| `appd_update_dashboard` | Update dashboard properties and/or widgets |
| `appd_add_widget_to_dashboard` | Add a single widget without replacing existing ones |
| `appd_clone_dashboard` | Clone a dashboard with a new name |
| `appd_delete_dashboard` | Delete a dashboard (permanent) |
| `appd_export_dashboard` | Export dashboard as portable JSON |
| `appd_import_dashboard` | Create a new dashboard from a saved JSON definition |
| `appd_save_dashboard_file` | Build a complete dashboard JSON file locally without creating anything in AppDynamics — ready to edit and import |
| `appd_auto_build_dashboard` | Auto-discover tiers, BTs, and health rules, then create a complete multi-section dashboard in one shot |

#### Dashboard widget types

| Widget type | Description |
|---|---|
| `TIMESERIES_GRAPH` | Time-series line chart for one or more metrics |
| `METRIC_VALUE` | Single aggregated number (gauge tile) — stored as `METRIC_LABEL` in AppDynamics |
| `GAUGE` | Gauge dial |
| `PIE` | Pie chart |
| `HEALTH_LIST` | Health rule status (green/yellow/red circles). Set `healthRuleIds: [id]` to pin a widget to specific health rules; omit to show all rules for the application. Display options: `showPie: true` renders a pie chart, add `innerRadius: 30-40` for a donut, `showList: false` hides the rule list under the chart. |
| `TEXT` | Static text / label — stored as `LABEL` in AppDynamics |

Widgets are saved first, then their metric bindings are attached in a second pass using
the server-assigned widget ids — this is what makes graphs actually render data.
Metric paths are bound in one of three forms (each verified against the dashboard
rendering API): Business Transaction metrics with `btIds` use BT criteria; app-scope
metrics (Overall Application Performance, Backends, Service Endpoints, …) use the
application-aggregate criteria, optionally scoped per tier via `tierIds`; node metrics
(JVM, Hardware Resources, or `Application Infrastructure Performance|Tier|…` paths) use
node-level criteria with the node-relative path.

## Example Conversations

**"What's the health status of my production apps?"**
→ Uses `appd_get_applications` + `appd_get_health_violations` + `appd_get_anomalies`

**"Show me the slowest business transactions for the Orders app"**
→ Uses `appd_get_business_transactions` + `appd_get_bt_performance`

**"What databases does the Payment service connect to?"**
→ Uses `appd_get_backends` with typeFilter="JDBC"

**"Create a health rule that fires when Custom Metrics|RequestCount > 1000 on the WebTier"**
→ Uses `appd_create_health_rule` with affectedEntityType="TIER_NODE_HEALTH", affectedTier="WebTier", metricPath="Custom Metrics|RequestCount"

**"Create a dashboard for the Checkout app with response time and error rate"**
→ Uses `appd_get_applications` → `appd_browse_metric_tree` → `appd_create_dashboard`

**"Build me a full monitoring dashboard for the Orders app"**
→ Uses `appd_auto_build_dashboard` — auto-discovers all tiers, BTs, and health rules, creates a complete dashboard in one shot

**"Create one health widget per URL Monitor service, each scoped to its own rule"**
→ Uses `appd_create_health_rule` (one per service) + `appd_create_dashboard` with `healthRuleIds` on each `HEALTH_LIST` widget

**"Clone the production monitoring dashboard for staging"**
→ Uses `appd_get_dashboards` → `appd_clone_dashboard`

**"Why is my Payment app slow? Diagnose the last hour"**
→ Uses `appd_diagnose_issue` with application="Payment", durationInMins=60 — returns ranked root cause candidates, merged event timeline, error class breakdown, and step-by-step investigation guide

**"Are there any errors spiking in the Orders app right now?"**
→ Uses `appd_diagnose_issue` with application="Orders", focus="errors"

**"What went wrong during yesterday's outage between 14:00 and 15:00?"**
→ Uses `appd_diagnose_issue` with startTime/endTime — degradation is measured against
the 14:00-preceding hour, so the report reflects that incident rather than current state

**"Pull the snapshots captured during last Tuesday's slowdown"**
→ Uses `appd_get_snapshots` with startTime/endTime

## Health Rules — Custom Metrics

Custom metrics reported by machine agents are stored per-node under:
```
Application Infrastructure Performance|{Tier}|Individual Nodes|{Node}|Custom Metrics|{MetricName}
```

When creating health rules for custom metrics, use `affectedEntityType=TIER_NODE_HEALTH` and provide the **relative** metric path (not the full absolute path):

```
affectedEntityType: "TIER_NODE_HEALTH"
affectedNode: "my-server-hostname"          # scope to specific node
metricPath: "Custom Metrics|MyMetric"       # relative path only
```

## Architecture

```
src/
├── index.ts              # Entry point, registers all tools
├── types.ts              # TypeScript interfaces
├── constants.ts          # Shared constants
├── services/
│   ├── auth.ts           # OAuth2 token management
│   └── api-client.ts     # Authenticated HTTP client
├── utils/
│   ├── error-handler.ts  # Error → MCP response
│   ├── app-resolver.ts   # App name → ID resolution
│   ├── time-range.ts     # Shared time-range parsing → AppD query params
│   ├── concurrency.ts    # Bounded fan-out across apps/tiers
│   └── formatting.ts     # Response formatting
└── tools/                # One file per tool domain
    ├── applications.ts
    ├── health-rules.ts        # CRUD + enable/disable
    ├── health-violations.ts
    ├── anomalies.ts
    ├── business-transactions.ts
    ├── bt-performance.ts
    ├── service-endpoints.ts
    ├── service-endpoint-paths.ts # pure SEP metric-tree paths + matching
    ├── tiers-nodes.ts
    ├── backends.ts
    ├── snapshots.ts
    ├── errors.ts
    ├── metrics.ts             # browse + query with rollup
    ├── dashboards.ts          # full CRUD + auto-build + HealthListWidget scoping
    ├── root-cause.ts
    └── root-cause-analysis.ts # pure correlation, scoring, narration
```

## Development

```bash
# Run in dev mode (auto-reload)
npm run dev

# Build TypeScript
npm run build

# Run built version
npm start

# Unit tests — payload builders and root-cause analysis vs. ground-truth
# fixtures, plus SEP metric paths, application resolution, time-range
# resolution, response truncation, token caching, bounded concurrency,
# and error handling
npm test

# End-to-end verification: drives the MCP server against the live controller,
# creates test dashboards, and asserts the persisted widget shapes.
# Requires APPD_* env vars.
node scripts/verify-dashboard-fixes.mjs
```

CI runs the typecheck, the unit tests, and a server-startup smoke check on every
push and pull request, plus a gitleaks secret scan over the full history.
See `.github/workflows/ci.yml`.


## Authentication

The server supports two authentication modes:

1. **OAuth2 Client Credentials** (recommended): Set `APPD_CLIENT_NAME`, `APPD_CLIENT_SECRET`, and optionally `APPD_ACCOUNT_NAME`. The server acquires and caches tokens automatically.

2. **API Key**: Set only `APPD_CLIENT_NAME` (as the API key). No secret needed.

Token handling details:

- Tokens are cached until 5 minutes before expiry.
- Concurrent callers share a single in-flight token request, so a cold start
  issues one OAuth exchange rather than one per parallel tool call.
- A `401` invalidates the cached token and retries the request once with a fresh
  one — a token revoked before its advertised expiry recovers automatically
  instead of failing every call until the cache times out.
- Credentials are never written to logs or error messages; OAuth failures log
  only the HTTP status code.

**Never commit credentials.** Keep them in `.env` (gitignored) or your MCP client's
`env` block. If a secret is ever committed, rotate it in the AppDynamics controller —
removing it from git history alone does not invalidate it.

## Reliability

- **Bounded fan-out**: tools that sweep every application or tier cap simultaneous
  requests (`MAX_CONCURRENT_REQUESTS`, default 6) instead of firing one request per
  entity at once, which trips controller rate limiting on large accounts.
- **Budgeted truncation**: oversized responses are trimmed to the largest prefix that
  fits `CHARACTER_LIMIT`, and the note reports how many of the total items were kept.
- **Capped error bodies**: upstream error payloads (including HTML error pages) are
  collapsed to a single line and truncated before being surfaced.

## License

ISC

TDQS

A4.1/5.0

Scored across 30 tools

Disambiguation4/5

Most tools have clear distinct purposes, but there is some overlap between appd_add_widget_to_dashboard, appd_create_dashboard, and appd_update_dashboard regarding widget management. Descriptions help differentiate them, so only minor ambiguity remains.

Naming Consistency5/5

All tools follow a consistent 'appd_verb_noun' pattern (e.g., appd_get_applications, appd_create_dashboard). There are no deviations or mixed conventions.

Tool Count3/5

With 30 tools, the server is on the heavy side. While each tool serves a specific purpose for a comprehensive monitoring platform, the count exceeds the typical 3-15 range and may feel overwhelming.

Completeness4/5

The tool surface covers core CRUD operations for dashboards and health rules, plus extensive querying capabilities. Minor gaps exist (e.g., no tools to create/delete applications or modify business transactions), but major workflows are well-supported.

Maintenance

ActivitySlowing
ResponsivenessUnresponsive