Atlassian Goals MCP Server
# 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
Scored across 15 tools
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.
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.
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.
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.