Skip to main content
Glama
expel-io

Atlassian Goals MCP Server

by expel-io
README.md
# Atlassian Goals MCP Server

A Model Context Protocol (MCP) server for the Atlassian Goals API. Enables AI assistants like Claude to query, search, and update Atlassian Goals and Projects.

## Features

**Goals — Read**
- List, get (single or batch), and search goals via TQL (Townsquare Query Language)

**Projects — Read**
- List, get (single or batch), and search projects via TQL

**Goals — Write**
- Post weekly status updates (with `summary`, `More detail`, status/score, target date, and metric values)
- Edit or delete the most recent update
- Update goal metadata (name, description, owner, target/start date, archive flag)
- Add or remove goal tags by name

**Operational**
- Health check for API connectivity and authentication
- Security: TQL injection prevention, input validation, request timeouts, retry with exponential backoff
- Performance: rate limiting, automatic throttling, structured logging
- Robustness: enhanced ADF parser supporting 15+ node types; markdown→ADF conversion for write fields

## Prerequisites

- Node.js >= 18.0.0 (comes with Claude Desktop)
- Atlassian Cloud account with Goals access
- Atlassian API token

## Quick Start

### 1. Get Your Atlassian Credentials

You'll need these four values:

- **Email**: Your Atlassian account email
- **API Token**: Generate at https://id.atlassian.com/manage-profile/security/api-tokens
- **Cloud ID**: Visit `https://your-company.atlassian.net/_edge/tenant_info` (replace `your-company` with your subdomain) and copy the `cloudId` value
- **Site URL**: Your Atlassian site URL (e.g., `https://your-company.atlassian.net`)

### 2. Add to Claude Desktop

Open your Claude Desktop config file:
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`

Add this configuration (replace the placeholder values with your credentials from step 1):

```json
{
  "mcpServers": {
    "atlassian-goals": {
      "command": "npx",
      "args": ["-y", "github:expel-io/atlassian-goals-mcp"],
      "env": {
        "ATLASSIAN_EMAIL": "your-email@example.com",
        "ATLASSIAN_API_TOKEN": "your-api-token",
        "ATLASSIAN_CLOUD_ID": "your-cloud-id",
        "ATLASSIAN_SITE_URL": "https://your-subdomain.atlassian.net"
      }
    }
  }
}
```

### 3. Restart Claude Desktop

Restart Claude Desktop to load the MCP server.

That's it! You can now ask Claude about your Atlassian Goals.

## Local Development

If you want to modify or contribute to this server:

1. Clone and build:
```bash
git clone https://github.com/expel-io/atlassian-goals-mcp.git
cd atlassian-goals-mcp
npm install
npm run build
```

2. Update your Claude Desktop config to use the local build:
```json
{
  "mcpServers": {
    "atlassian-goals": {
      "command": "node",
      "args": ["/absolute/path/to/atlassian-goals-mcp/build/index.js"],
      "env": {
        "ATLASSIAN_EMAIL": "your-email@example.com",
        "ATLASSIAN_API_TOKEN": "your-api-token",
        "ATLASSIAN_CLOUD_ID": "your-cloud-id",
        "ATLASSIAN_SITE_URL": "https://your-subdomain.atlassian.net"
      }
    }
  }
}
```

3. Optionally create a `.env` file for testing:
```bash
cp .env.example .env
# Edit .env with your credentials
```

## Available Tools

Tools are grouped by surface area. All goal/update IDs are Atlassian ARIs (e.g. `ari:cloud:townsquare:{cloudId}:goal/{uuid}`); the formats are validated by each tool's input schema.

### Goals — Read

#### `list_goals`

List goals with optional filtering by status, name, or tags. Supports cursor-based pagination.

**Parameters:**
- `limit` (optional): 1–100, default 20
- `status` (optional): one of `NOT_STARTED`, `IN_PROGRESS`, `COMPLETED`, `CANCELLED`
- `searchTerm` (optional): partial-match filter on goal name
- `tags` (optional): array of tag names; multiple tags AND together
- `cursor` (optional): pagination cursor from a previous response

#### `get_goal`

Fetch a single goal with description, owner, metrics, parent/sub-goal relationships, tags, and recent updates.

**Parameters:**
- `goalId` (required): goal ARI

#### `get_goals`

Batch-fetch multiple goals in one request. More efficient than calling `get_goal` repeatedly.

**Parameters:**
- `goalIds` (required): array of goal ARIs (1–20)

#### `search_goals`

Search goals using TQL (Townsquare Query Language).

**Operators:** `LIKE` (partial, supports `_` as a single-character wildcard), `=` (exact), `AND`, `OR`.
**Fields:** `name`, `status` (`pending` / `on_track` / `at_risk` / `off_track` / `done` / `cancelled`), `owner` (account ID), `tag`.

**Parameters:**
- `searchString` (required): a TQL query, e.g. `name LIKE "Q4" AND status = on_track`
- `limit` (optional): 1–100, default 20
- `cursor` (optional): pagination cursor

### Projects — Read

#### `list_projects`

List projects with optional name filtering and cursor pagination.

**Parameters:**
- `limit` (optional): 1–100, default 20
- `searchTerm` (optional): partial-match filter on project name
- `cursor` (optional): pagination cursor

#### `get_project`

Fetch a single project with description, owner, members, linked goals, and recent updates.

**Parameters:**
- `projectId` (required): project ARI (`ari:cloud:townsquare:{cloudId}:project/{uuid}`)

#### `get_projects`

Batch-fetch multiple projects (1–20 per call).

**Parameters:**
- `projectIds` (required): array of project ARIs

#### `search_projects`

Search projects using TQL. Currently supports `LIKE` on `name` with `OR`.

**Parameters:**
- `searchString` (required): TQL query, e.g. `name LIKE "Integration"`
- `limit` (optional): 1–100, default 20
- `cursor` (optional): pagination cursor

### Goals — Write

#### `post_goal_update`

Post a weekly status update — the entry that appears in the goal's Updates tab.

Status/score model: `on_track`/`at_risk`/`off_track` are derived from a 1–100 integer score (the Atlassian UI labels these as decimals like "0.8", but the API stores integers). Bands: 10–30 = off_track, 40–60 = at_risk, 70–100 = on_track. Status alone fills the band midpoint; score alone infers status; combined values are validated. `pending`/`paused`/`done`/`cancelled`/`archived` take only status — no score.

Markdown is supported in both `summary` and `details` and is converted to ADF before send.

**Parameters:**
- `goalId` (required): goal ARI
- `summary` (required): the visible headline, max 280 chars
- `details` (optional): longer-form "More detail" body (no length limit)
- `status` (optional): see above
- `score` (optional): integer 1–100
- `targetDate` (optional): `{ date: "YYYY-MM-DD", confidence?: "EXACT"|"QUARTER"|"HALF"|"YEAR" }`
- `metricUpdates` (optional): `[{ targetId, value }]` — `targetId` is the metric target ID from `get_goal`
- `dryRun` (optional): return the resolved mutation payload without submitting

#### `edit_goal_update`

Edit a previously-posted update. Partial fields are supported; at least one editable field is required.

Both `goalId` and `goalUpdateId` are required so the tool can pre-query existing update notes and pass `updateNoteId` when replacing `details` — without it, Townsquare appends a second note that the UI doesn't render.

Note the metric-input asymmetry with `post_goal_update`: `metricUpdates` takes `metricId` (the metric itself), not `targetId` (the metric target).

**Parameters:**
- `goalId` (required): goal ARI
- `goalUpdateId` (required): update ARI (`ari:cloud:townsquare:{cloudId}:goal-update/{uuid}`)
- `summary`, `details`, `status`, `score`, `targetDate` (all optional): same as `post_goal_update`
- `metricUpdates` (optional): `[{ metricId, value }]`
- `dryRun` (optional)

#### `delete_latest_goal_update`

Remove the most recent update on a goal. The Townsquare API only deletes the latest update per goal; earlier updates cannot be removed this way.

**Parameters:**
- `goalUpdateId` (required): update ARI; must be the latest on its goal
- `dryRun` (optional)

#### `update_goal`

Edit a goal's metadata. Status changes are NOT done here — use `post_goal_update`. Tags are also separate — use `add_goal_tags` / `remove_goal_tags`.

`description` accepts markdown and is converted to ADF. `archived: true` archives the goal (destructive in effect — confirm with the user before flipping on a real goal).

**Parameters:**
- `goalId` (required): goal ARI
- `name` (optional): new goal name
- `description` (optional): markdown
- `ownerId` (optional): Atlassian account ID (the value `get_goal` returns as `owner.accountId`)
- `targetDate` (optional): `{ date, confidence? }`
- `startDate` (optional): `YYYY-MM-DD`
- `archived` (optional): boolean
- `dryRun` (optional)

At least one editable field is required.

#### `add_goal_tags`

Attach tags to a goal by name. Tag names that don't yet exist at the workspace level are auto-created — the Townsquare API does not expose a delete-tag mutation, so prefer reusing existing names over inventing new ones.

**Parameters:**
- `goalId` (required): goal ARI
- `tagNames` (required): array of tag names (≥1)
- `dryRun` (optional)

#### `remove_goal_tags`

Detach tags from a goal. Prefer `tagNames` — the tool looks up matching tag IDs from the goal's current tag list. `tagIds` is supported for callers that already have them.

**Parameters:**
- `goalId` (required): goal ARI
- `tagNames` (optional): array of tag names — looked up against the goal's current tags; errors if a name is not currently attached
- `tagIds` (optional): array of tag ARIs — provide either `tagNames` or `tagIds`
- `dryRun` (optional)

### Operational

#### `health_check`

Verify API connectivity, authentication, and performance.

**Parameters:**
- `verbose` (optional): include diagnostic details (default false)

## Development

### Build

```bash
npm run build
```

### Watch Mode

For development with automatic rebuilding:

```bash
npm run watch
```

### Project Structure

```
atlassian-goals-mcp/
├── src/
│   ├── index.ts                          # Entry point
│   ├── server.ts                         # MCP server setup
│   ├── config.ts                         # Configuration management
│   ├── atlassian/
│   │   ├── client.ts                     # GraphQL client
│   │   ├── queries.ts                    # GraphQL query + mutation definitions
│   │   ├── mutation-result.ts            # Shared payload-error unwrap for write mutations
│   │   └── types.ts                      # TypeScript interfaces
│   ├── tools/
│   │   ├── index.ts                      # Tool registry + executeTool dispatch
│   │   ├── list-goals.ts                 # Goal tools — read
│   │   ├── get-goal.ts
│   │   ├── get-goals.ts
│   │   ├── search-goals.ts
│   │   ├── list-projects.ts              # Project tools — read
│   │   ├── get-project.ts
│   │   ├── get-projects.ts
│   │   ├── search-projects.ts
│   │   ├── post-goal-update.ts           # Goal tools — write
│   │   ├── edit-goal-update.ts
│   │   ├── delete-latest-goal-update.ts
│   │   ├── update-goal.ts
│   │   ├── add-goal-tags.ts
│   │   ├── remove-goal-tags.ts
│   │   └── health-check.ts
│   └── utils/
│       ├── logger.ts                     # Logging utility
│       ├── errors.ts                     # Error handling
│       ├── adf-parser.ts                 # ADF → text
│       ├── markdown-to-adf.ts            # Markdown → ADF JSON (write fields)
│       ├── goal-update-status.ts         # Score/status resolution shared by post/edit
│       ├── tql.ts                        # TQL escaping/builders
│       ├── goal-formatter.ts             # Read-tool formatting
│       └── project-formatter.ts          # Read-tool formatting
├── tests/
│   ├── unit/                             # Unit tests
│   ├── integration/                      # Integration tests
│   └── helpers/                          # Test utilities
├── scripts/                              # Introspection + live verification scripts
├── build/                                # Compiled output
├── package.json
├── tsconfig.json
├── vitest.config.ts                      # Test configuration
└── README.md
```

## Testing

This project uses [Vitest](https://vitest.dev/) for testing with separate unit and integration test suites.

### Running Tests

```bash
# Run all tests
npm test

# Run unit tests only (fast, no credentials needed)
npm run test:unit

# Run integration tests (requires .env configuration)
npm run test:integration

# Watch mode for development
npm run test:watch

# Generate coverage report
npm run test:coverage

# Interactive UI
npm run test:ui
```

### Unit Tests

Unit tests are located in `tests/unit/` and test individual functions and modules in isolation. They don't require API credentials and should run quickly.

**Coverage:**
- `utils/adf-parser.test.ts` — Atlassian Document Format parsing
- `utils/tql.test.ts` — TQL escaping/injection prevention
- `utils/goal-update-status.test.ts` — score/status resolution for the update tools
- `atlassian/client.test.ts` — rate limiting and error handling
- `atlassian/mutation-result.test.ts` — write-mutation payload-error unwrapping
- `tools/*.test.ts` — schema validation for each tool

### Integration Tests

Integration tests are located in `tests/integration/` and test against the real Atlassian Goals API. They require valid credentials in your `.env` file.

**Note**: Integration tests are automatically skipped if credentials are not available. This allows unit tests to run in CI environments without requiring API access.

To run integration tests locally:
1. Set up your `.env` file with valid Atlassian credentials
2. Optionally set `TEST_OWNER_ACCOUNT_ID` to a valid account ID from your workspace to test owner filtering. To find your account ID, look at the `owner.accountId` field on any goal returned by `get_goal`.
3. Run `npm run test:integration`

**Integration test suites:**
- `connection.test.ts` — API connectivity and configuration
- `health-check.test.ts` — health-check tool behavior
- `list-goals.test.ts` — goal listing
- `get-goal.test.ts` — single goal detail
- `get-goals.test.ts` — batch goal fetching
- `search-goals.test.ts` — TQL goal search
- `projects.test.ts` — project list/get/search
- `updates.test.ts` — goal update data structures

Write tools (`post_goal_update`, `edit_goal_update`, `delete_latest_goal_update`, `update_goal`, `add_goal_tags`, `remove_goal_tags`) are exercised by the `scripts/test-*.js` live-verification scripts rather than the integration test suite — they need a known-safe test goal to write against. See [Development Scripts](#development-scripts) below.

### Development Scripts

```bash
# GraphQL schema introspection (npm-aliased)
npm run introspect:goal      # Inspect TownsquareGoal type
npm run introspect:types     # Inspect available types
npm run introspect:updates   # Inspect update types
npm run introspect:metric    # Inspect metric types

# Diagnostic tools
npm run diagnose:ari         # Test ARI format variations
```

Several more one-off scripts live in `scripts/` and are run directly with `node`:

```bash
# Type introspection (used when adding write tools)
node scripts/introspect-write-inputs.js
node scripts/introspect-edit-delete.js
node scripts/introspect-tag-mutations.js
node scripts/introspect-mutations.js

# Live-verification harnesses for the write tools — each one posts/edits/
# deletes test data on a "Jeffrey Test" goal and reverts when possible.
# These are NOT in the integration test suite because they need a known-safe
# test goal to write against.
node scripts/test-post-goal-update.js
node scripts/test-edit-delete-goal-update.js
node scripts/test-update-goal.js
node scripts/test-tag-tools.js
node scripts/verify-update-notes-behavior.js
```

These scripts are useful for:
- Exploring available GraphQL fields
- Verifying write-tool behavior end-to-end against the live API
- Troubleshooting API connectivity
- Understanding the data schema

## Troubleshooting

### Authentication Errors

If you see authentication errors:
- Verify your API token is correct and hasn't expired
- Ensure your email matches the Atlassian account
- Check that your Cloud ID is correct

### Connection Errors

If the server can't connect:
- Verify your site URL is correct
- Check your network connection
- Ensure you have access to the Atlassian Goals API

### Configuration Errors

If you see "Invalid configuration" errors:
- Check all required environment variables are set
- Verify the format of your site URL (must be a valid URL)
- Ensure your email is in valid email format

### Package Installation Errors

If `npx` fails to install or run the server, you may encounter authentication or network errors from your package manager:

**Symptoms:**
- `npm ERR! code E401` or `npm ERR! 401 Unauthorized`
- `ETIMEDOUT` or `ENOTFOUND` errors
- Proxy authentication failures
- Certificate verification errors

**Common Causes:**
- Corporate package registry proxies (Artifactory, Nexus, Verdaccio)
- npm registry authentication required
- Network proxy configuration
- SSL/TLS certificate issues

**Debugging Steps:**

1. **Check npm registry configuration:**
   ```bash
   npm config get registry
   # Should show https://registry.npmjs.org/ for public packages
   ```

2. **Check for authentication requirements:**
   ```bash
   npm config get //registry.npmjs.org/:_authToken
   # If set, your npm may require authentication for public packages
   ```

3. **View all npm configuration:**
   ```bash
   npm config list
   # Look for proxy, registry, or cert settings
   ```

4. **Test registry access:**
   ```bash
   npm view @modelcontextprotocol/sdk version
   # Should return a version number if registry is accessible
   ```

**Solutions:**

- **Corporate registry:** Contact your IT team about accessing public npm packages
- **Proxy settings:** Configure npm proxy settings or use VPN
- **Direct registry:** Temporarily use the official npm registry:
  ```bash
  npm config set registry https://registry.npmjs.org/
  ```
- **Local installation:** Clone the repository and use the local development setup instead of npx

### Logging and Performance Monitoring

The server includes structured logging and performance profiling capabilities.

**Environment Variables:**

```env
# Enable debug logging (logs INFO level and above)
DEBUG=true

# Enable trace logging (very verbose, includes timing details)
TRACE=true

# Show performance metrics in tool responses
SHOW_PERFORMANCE=true
```

**Log Levels:**
- `ERROR`: Errors and failures
- `WARN`: Warnings and potential issues
- `INFO`: Tool calls, completions, and important events
- `DEBUG`: Detailed diagnostic information (requires DEBUG=true)
- `TRACE`: Very detailed tracing including timers (requires DEBUG=true and TRACE=true)

**Log Format:**
```
[2025-01-06T12:34:56.789Z] [INFO] Tool called: list_goals {"tool":"list_goals","operation":"start","args":"{\"limit\":20}"}
[2025-01-06T12:34:57.123Z] [INFO] Tool completed: list_goals (334ms) {"tool":"list_goals","operation":"complete","duration":334,"resultSize":1250}
```

**Performance Profiling:**
- Automatic timing for all tool calls
- Duration logged in milliseconds
- Result size tracking
- Optional performance metrics in responses (with SHOW_PERFORMANCE=true)

**Viewing Logs:**
- Logs are written to stderr (stdout is reserved for MCP JSON-RPC)
- In Claude Desktop: View logs in developer console
- In terminal: Redirect stderr to file: `node build/index.js 2> logs.txt`

### Debug Mode

Enable debug logging by adding to your environment variables:

```env
DEBUG=true
```

## API Reference

This server uses the Atlassian Goals GraphQL API. For more information:
- [Atlassian Goals API Documentation](https://developer.atlassian.com/platform/goals/goals-graphql-api/introduction/)
- [Atlassian API Authentication](https://developer.atlassian.com/cloud/jira/platform/basic-auth-for-rest-apis/)

## License

MIT License - see [LICENSE](LICENSE) file for details

## Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

## Support

For issues related to:
- This MCP server: Open an issue in this repository
- Atlassian Goals API: Visit [Atlassian Developer Community](https://community.developer.atlassian.com/)
- Claude Desktop: Visit [Anthropic Support](https://support.anthropic.com/)

TDQS

A4.1/5.0

Scored across 15 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: list versus search for retrieval, batch versus single get, update versus post/edit for modifications, and add/remove for tags. The only potential overlap is between list_goals and search_goals, but their descriptions clarify the use cases.

Naming Consistency4/5

Tool names follow a consistent snake_case verb_noun pattern, with plural forms for batch operations (get_goals vs get_goal). There is minor inconsistency with update_goal versus edit_goal_update, but the pattern remains predictable overall.

Tool Count5/5

15 tools is at the upper end of the ideal range and each tool has a clear role. The count is well-scoped for managing goals and projects, with no unnecessary tools.

Completeness2/5

The tool set lacks create_goal and delete_goal, which are core lifecycle operations. While it covers reading, searching, updating, and posting updates, the inability to create or delete goals is a significant gap that will block common workflows.

Maintenance

ActivityInactive
ResponsivenessSyncing