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.
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
- Node.js >= 18.0.0 (comes with Claude Desktop)
- Atlassian Cloud account with Goals access
- Atlassian API token
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(replaceyour-companywith your subdomain) and copy thecloudIdvalue - Site URL: Your Atlassian site URL (e.g.,
https://your-company.atlassian.net)
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):
{
"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"
}
}
}
}Restart Claude Desktop to load the MCP server.
That's it! You can now ask Claude about your Atlassian Goals.
If you want to modify or contribute to this server:
- Clone and build:
git clone https://github.com/expel-io/atlassian-goals-mcp.git
cd atlassian-goals-mcp
npm install
npm run build- Update your Claude Desktop config to use the local build:
{
"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"
}
}
}
}- Optionally create a
.envfile for testing:
cp .env.example .env
# Edit .env with your credentialsTools 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.
List goals with optional filtering by status, name, or tags. Supports cursor-based pagination.
Parameters:
limit(optional): 1–100, default 20status(optional): one ofNOT_STARTED,IN_PROGRESS,COMPLETED,CANCELLEDsearchTerm(optional): partial-match filter on goal nametags(optional): array of tag names; multiple tags AND togethercursor(optional): pagination cursor from a previous response
Fetch a single goal with description, owner, metrics, parent/sub-goal relationships, tags, and recent updates.
Parameters:
goalId(required): goal ARI
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 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_tracklimit(optional): 1–100, default 20cursor(optional): pagination cursor
List projects with optional name filtering and cursor pagination.
Parameters:
limit(optional): 1–100, default 20searchTerm(optional): partial-match filter on project namecursor(optional): pagination cursor
Fetch a single project with description, owner, members, linked goals, and recent updates.
Parameters:
projectId(required): project ARI (ari:cloud:townsquare:{cloudId}:project/{uuid})
Batch-fetch multiple projects (1–20 per call).
Parameters:
projectIds(required): array of project ARIs
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 20cursor(optional): pagination cursor
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 ARIsummary(required): the visible headline, max 280 charsdetails(optional): longer-form "More detail" body (no length limit)status(optional): see abovescore(optional): integer 1–100targetDate(optional):{ date: "YYYY-MM-DD", confidence?: "EXACT"|"QUARTER"|"HALF"|"YEAR" }metricUpdates(optional):[{ targetId, value }]—targetIdis the metric target ID fromget_goaldryRun(optional): return the resolved mutation payload without submitting
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 ARIgoalUpdateId(required): update ARI (ari:cloud:townsquare:{cloudId}:goal-update/{uuid})summary,details,status,score,targetDate(all optional): same aspost_goal_updatemetricUpdates(optional):[{ metricId, value }]dryRun(optional)
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 goaldryRun(optional)
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 ARIname(optional): new goal namedescription(optional): markdownownerId(optional): Atlassian account ID (the valueget_goalreturns asowner.accountId)targetDate(optional):{ date, confidence? }startDate(optional):YYYY-MM-DDarchived(optional): booleandryRun(optional)
At least one editable field is required.
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 ARItagNames(required): array of tag names (≥1)dryRun(optional)
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 ARItagNames(optional): array of tag names — looked up against the goal's current tags; errors if a name is not currently attachedtagIds(optional): array of tag ARIs — provide eithertagNamesortagIdsdryRun(optional)
Verify API connectivity, authentication, and performance.
Parameters:
verbose(optional): include diagnostic details (default false)
npm run buildFor development with automatic rebuilding:
npm run watchatlassian-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
This project uses Vitest for testing with separate unit and integration test suites.
# 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:uiUnit 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 parsingutils/tql.test.ts— TQL escaping/injection preventionutils/goal-update-status.test.ts— score/status resolution for the update toolsatlassian/client.test.ts— rate limiting and error handlingatlassian/mutation-result.test.ts— write-mutation payload-error unwrappingtools/*.test.ts— schema validation for each tool
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:
- Set up your
.envfile with valid Atlassian credentials - Optionally set
TEST_OWNER_ACCOUNT_IDto a valid account ID from your workspace to test owner filtering. To find your account ID, look at theowner.accountIdfield on any goal returned byget_goal. - Run
npm run test:integration
Integration test suites:
connection.test.ts— API connectivity and configurationhealth-check.test.ts— health-check tool behaviorlist-goals.test.ts— goal listingget-goal.test.ts— single goal detailget-goals.test.ts— batch goal fetchingsearch-goals.test.ts— TQL goal searchprojects.test.ts— project list/get/searchupdates.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 below.
# 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 variationsSeveral more one-off scripts live in scripts/ and are run directly with node:
# 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.jsThese 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
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
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
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
If npx fails to install or run the server, you may encounter authentication or network errors from your package manager:
Symptoms:
npm ERR! code E401ornpm ERR! 401 UnauthorizedETIMEDOUTorENOTFOUNDerrors- 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:
-
Check npm registry configuration:
npm config get registry # Should show https://registry.npmjs.org/ for public packages -
Check for authentication requirements:
npm config get //registry.npmjs.org/:_authToken # If set, your npm may require authentication for public packages -
View all npm configuration:
npm config list # Look for proxy, registry, or cert settings -
Test registry access:
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:
npm config set registry https://registry.npmjs.org/ - Local installation: Clone the repository and use the local development setup instead of npx
The server includes structured logging and performance profiling capabilities.
Environment Variables:
# 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=trueLog Levels:
ERROR: Errors and failuresWARN: Warnings and potential issuesINFO: Tool calls, completions, and important eventsDEBUG: 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
Enable debug logging by adding to your environment variables:
DEBUG=trueThis server uses the Atlassian Goals GraphQL API. For more information:
MIT License - see LICENSE file for details
Contributions are welcome! Please feel free to submit issues or pull requests.
For issues related to:
- This MCP server: Open an issue in this repository
- Atlassian Goals API: Visit Atlassian Developer Community
- Claude Desktop: Visit Anthropic Support