The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Aha.io listing page.
A Model Context Protocol (MCP) server that provides seamless integration with Aha.io's product management platform. Features offline database synchronization, vector embeddings for semantic search, and comprehensive workflow automation.
This server is published to the official MCP Registry
as io.github.cedricziel/aha-mcp, so a client that browses the registry can install it
without any of the configuration below:
The registry holds metadata only. Its entry points at the three artifacts described below —
the npm package, the ghcr.io image and the .mcpb desktop extension — and a client picks
whichever it can run. Either way the server needs AHA_COMPANY and AHA_TOKEN.
Download aha-mcp-v<version>.mcpb from the latest release
and open it with Claude Desktop, which will prompt you for your Aha.io subdomain and API
token. No Node.js or Docker setup and no manual JSON editing required.
The extension exposes 50 tools that query Aha.io directly, so results are always current and nothing is stored locally. Cross-record search is served by Aha.io's own index — see Search.
To use this MCP server with Claude Desktop, add the following to your claude_desktop_config.json:
Using npx:
Using Docker:
Note: Replace
your-companyandyour-api-tokenwith your actual Aha.io subdomain and API token.
You can run the MCP server directly using npx without installing it globally:
You can also run the MCP server using Docker:
Install Bun if you haven't already:
Install dependencies:
Configure environment variables:
Start the server:
For development with auto-reload:
This MCP server provides hybrid integration with Aha.io through both live API access and offline database synchronization. The server automatically maintains a local SQLite database with your Aha.io data, enabling faster queries, offline access, and advanced semantic search capabilities.
The Aha.io integration can be configured using multiple methods, with the following priority order:
~/.aha-mcp-config.json)AHA_COMPANY: Your Aha.io subdomain (e.g., mycompany for mycompany.aha.io)AHA_TOKEN: Your Aha.io API token (for API token authentication)AHA_ACCESS_TOKEN: Your OAuth 2.0 access token (for OAuth authentication)MCP_TRANSPORT_MODE: Transport mode (stdio or streamable-http)MCP_PORT: Port number for HTTP-based modes (default: 3001)MCP_HOST: Host address for HTTP-based modes (default: 0.0.0.0)MCP_AUTH_TOKEN: Authentication token for HTTP-based modes (optional)MCP_TOOL_RATE_LIMIT_PER_MINUTE: Tool calls allowed per minute (default: 120, 0 disables)The server supports two transport modes:
Example usage:
Removed: the
ssetransport was deprecated in MCP spec 2025-03-26 and has been removed.MCP_TRANSPORT_MODE=sseand--mode ssenow fall back tostreamable-httpwith a warning, so existing configurations keep starting.
The streamable-http transport supports optional Bearer token authentication:
When authentication is enabled, clients must include a Bearer token in the Authorization
header. All MCP traffic goes to the single /mcp endpoint:
Most callers should use an MCP client library rather than raw HTTP; pass the token as an
Authorization header when constructing the transport.
MCP_AUTH_TOKEN is not set, all requests are allowed.The server provides three MCP tools for configuration management:
These tools allow you to manage configuration without restarting the server, making it easy to switch between different Aha.io accounts or update credentials.
The server maintains a local SQLite database with your Aha.io data for improved performance and offline access.
aha_sync_start: Start background synchronization of specified entity typesaha_sync_status: Check the status and progress of sync jobsaha_sync_stop: Stop a running sync jobaha_sync_pause: Pause a sync job (can be resumed later)aha_sync_resume: Resume a paused sync jobaha_sync_history: View detailed history of sync operationsaha_sync_health: Get overall sync service health statusaha_database_health: Check database connectivity and statisticsaha_database_cleanup: Clean up old sync jobs and optimize databaseupdatedSince filtering to sync only recent changesThe server includes advanced semantic search capabilities using vector embeddings.
aha_generate_embeddings: Generate vector embeddings for entity text contentaha_embedding_status: Check the status of embedding generation jobsaha_semantic_search: Search entities using natural language queriesaha_generate_entity_embedding: Generate embedding for a specific entityaha_find_similar: Find entities similar to a given entityaha_pause_embeddings: Pause embedding generation jobsaha_stop_embeddings: Stop embedding generation jobsBreaking change: many collection resources (slim index lists like aha_features, and a
few with a handful of scalar columns like aha_ideas) now return text/markdown - a link
list or a table, depending on the record type - instead of a JSON array. Anything that used
to parse those collections' contents as JSON needs to change. Collections with richer nested
data (aha_goals, aha_initiatives, comment resources, and similar) are unaffected and, like
every single-record resource (aha://feature/{id} and the like), still return
application/json.
aha_idea: Access individual ideas using aha://idea/{id}aha_feature: Access individual features using aha://feature/{id}aha_user: Access individual users using aha://user/{id}aha_epic: Access individual epics using aha://epic/{id}aha_product: Access individual products using aha://product/{id}aha_initiative: Access individual initiatives using aha://initiative/{id}aha_requirement: Access individual requirements using aha://requirement/{id}aha_competitor: Access individual competitors using aha://competitor/{id}aha_todo: Access individual todos using aha://todo/{id}aha_features: List features with optional filtering using aha://features?query=...&tag=...aha_users: List all users using aha://usersaha_epics: List epics for a product using aha://epics/{product_id}aha_products: List all products using aha://products?updatedSince=...aha_initiatives: List all initiatives using aha://initiatives?query=...&onlyActive=trueaha_ideas: List all ideas globally using aha://ideas?query=...&status=...&category=...aha_ideas_by_product: List ideas for a product using aha://ideas/{product_id}?query=...&spam=false&sort=recentaha_competitors: List competitors for a product using aha://competitors/{product_id}aha_product_releases: List releases for a product using aha://releases/{product_id}?query=...&status=...aha_initiative_epics: List epics for an initiative using aha://initiative/{initiative_id}/epicsaha_feature_comments: Access comments for a feature using aha://comments/feature/{feature_id}aha_epic_comments: Access comments for an epic using aha://comments/epic/{epic_id}aha_idea_comments: Access an idea's internal comments using aha://comments/idea/{idea_id}aha_idea_portal_comments: Access an idea's ideas-portal comments using aha://idea-comments/{idea_id} — different records from the above, including anything a customer wroteaha_initiative_comments: Access comments for an initiative using aha://comments/initiative/{initiative_id}aha_product_comments: Access comments for a product using aha://comments/product/{product_id}aha_goal_comments: Access comments for a goal using aha://comments/goal/{goal_id}aha_release_comments: Access comments for a release using aha://comments/release/{release_id}aha_release_phase_comments: Access comments for a release phase using aha://comments/release-phase/{release_phase_id}aha_requirement_comments: Access comments for a requirement using aha://comments/requirement/{requirement_id}aha_todo_comments: Access comments for a todo using aha://comments/todo/{todo_id}aha_goal: Access individual goals using aha://goal/{goal_id}aha_goals: List all goals using aha://goalsaha_goal_epics: Access epics associated with a goal using aha://goal/{goal_id}/epicsaha_goal_key_results: Access key results for a goal using aha://goal/{goal_id}/key_resultsaha_key_result: Access individual key results using aha://key_result/{id}aha_release: Access individual releases using aha://release/{release_id}aha_releases: List all releases using aha://releasesaha_release_features: Access features associated with a release using aha://release/{release_id}/featuresaha_release_epics: Access epics associated with a release using aha://release/{release_id}/epicsaha_release_phase: Access individual release phases using aha://release-phase/{release_phase_id}aha_release_phases: List all release phases using aha://release-phasesaha_custom_fields: List all custom field definitions using aha://custom-fieldsaha_custom_field_options: Access options for a custom field using aha://custom-field/{custom_field_id}/optionsNote: List operations are handled through MCP resources. Tools cover search, single-record reads, write operations and relationship management.
aha_get_feature: Read one feature, including workflow status, release, assignee, tags, score and custom field valuesaha_get_epic: Read one epicaha_get_idea: Read one ideaaha_get_initiative: Read one initiativeaha_get_release: Read one releaseaha_get_goal: Read one goal, including its time frame, progress source, success metric and key result summaryaha_get_key_result: Read one key result, including its status and starting, current and target metricsThese return the full record as structuredContent. They duplicate what
aha://feature/{id} and friends already serve, deliberately: a client is free to surface
resources to its model or not, and several do not — on those, every read here was
unreachable, leaving write tools with no way to see what they were about to replace.
aha_search is not a substitute, as it cannot return per-record fields.
aha_list_release_features: List the features assigned to a release, with a link per feature and Aha's total for the releaseaha_list_release_epics: List the epics assigned to a release, with a link per epic and Aha's total for the releaseaha_list_key_results: List a goal's key results, with status, progress and metricsaha_list_comments: List the comments on a record, both streams for an ideaThe two release tools are the only way to enumerate a release. aha_search is
relevance-ranked, returns no release membership on a hit and cannot be asked for every record
in a scope, so a release list assembled from search results is partial — and nothing in it says
so. Both types are listed because a release is not organised the same way in every workspace: a
release planned in epics is invisible to the features tool. Each asks for 200 records per page
(Aha's own default is 30 for features; on the epics route it is unmeasured, which is why the
tool never relies on it) and always returns Aha's pagination block, so a caller can tell a
complete list from the front of a longer one. Aha
returns identity fields only on these endpoints, so use aha_get_feature or aha_get_epic for
the state of any one record.
aha_create_feature_comment: Create a comment on a featureaha_create_initiative_in_product: Create an initiative within a specific productaha_create_feature: Create a feature within a specific releaseaha_update_feature: Update a featureaha_delete_feature: Delete a featureaha_update_feature_progress: Update a feature's progressaha_update_feature_score: Update a feature's scoreaha_update_feature_custom_fields: Update a feature's custom fieldsaha_update_epic: Update an epicaha_delete_epic: Delete an epicaha_create_epic_in_product: Create an epic within a specific productaha_create_epic_in_release: Create an epic within a specific releaseaha_create_idea: Create an idea in a productaha_create_idea_with_category: Create an idea with a categoryaha_create_idea_with_score: Create an idea with a scoreaha_delete_idea: Delete an ideaaha_create_goal: Create a goal (objective) in a workspaceaha_update_goal: Update a goal's name, description, success metric, status, time frame or progressaha_delete_goal: Delete a goal, and with it the key results it ownsaha_list_key_results: List a goal's key results, with status, progress and metricsaha_create_key_result: Create a key result under a goalaha_update_key_result: Update a key result — its status and starting, current or target metricaha_delete_key_result: Delete a key resultThree things about these differ from the rest of the API, all measured against a live account:
POST /products/{id}/goals and
DELETE /products/{id}/goals/{id} are the only routes Aha offers, so both tools require a
workspace id — aha_get_goal returns it as product_id. Updates do not need one.url. Unlike every other record type, the standalone record carries
neither url nor resource, so the aha://key_result/{id} resource link each tool returns
is the only pointer a client can follow.success_metric.workflow_status,
which is what the Aha UI shows as the goal's status.aha_create_competitor: Create a competitor in a productaha_update_competitor: Update a competitoraha_delete_competitor: Delete a competitoraha_create_idea_by_portal_user: Create an idea by a portal useraha_create_idea_with_portal_settings: Create an idea with enhanced portal settingsaha_associate_feature_with_epic: Associate a feature with an epicaha_move_feature_to_release: Move a feature to a different releaseaha_associate_feature_with_goals: Associate a feature with multiple goalsaha_update_feature_tags: Update tags for a featureNote: reads are offered through both interfaces, by design:
The overlap is deliberate. Resources are the richer read surface, but the MCP spec leaves it to each client whether to expose them to its model, and tool-only clients are common. Keeping reads tool-accessible for the types that have write tools is what stops an agent from changing a field it cannot see.
The MCP server now provides comprehensive lifecycle management for Aha.io entities with complete CRUD operations, portal integration, and advanced workflow features:
Feature Management (6 Tools)
aha_create_feature: Create features within releasesaha_update_feature: Update existing featuresaha_delete_feature: Delete featuresaha_update_feature_progress: Update feature progress (0-100%)aha_update_feature_score: Update feature scoresaha_update_feature_custom_fields: Update feature custom fieldsEpic Management (2 Tools)
aha_update_epic: Update existing epicsaha_delete_epic: Delete epicsIdea Management (4 Tools)
aha_create_idea: Create ideas in productsaha_create_idea_with_category: Create ideas with categoriesaha_create_idea_with_score: Create ideas with scoresaha_delete_idea: Delete ideasCompetitor Management (3 Tools)
aha_create_competitor: Create competitors in productsaha_update_competitor: Update existing competitorsaha_delete_competitor: Delete competitorsNote: Initiative data access is now handled through MCP resources (aha_initiative, aha_initiatives, aha_initiative_comments, aha_initiative_epics) for a cleaner separation between read and write operations.
Portal Integration
aha_create_idea_by_portal_user: Create ideas by portal usersaha_create_idea_with_portal_settings: Create ideas with portal settingsquery, updatedSince, assignedToUser, onlyActive parametersskip_portal and submitted_idea_portal_id settingsaha_search queries Aha.io's own search index through the GraphQL API
(POST /api/v2/graphql, searchDocuments). Nothing is cached locally, so results are
always current and no native dependencies or writable storage are required.
What it matches: record names and descriptions. Comment bodies match too, surfacing as
Comment hits that link to their parent record.
Query syntax: term* for prefix matching, AND / OR / NOT, and "quoted phrases".
There is no match-all query. A bare * is rejected: on its own Aha returns an arbitrary
subset for it, and combined with workspaceId it returns nothing at all — an empty result
that reads like an empty workspace. Search for a term, or enumerate a workspace through the
list resources (aha://features, aha://ideas/{product_id}) instead of searching it.
What a hit carries: name, type, reference_num, internal id, workspace, absolute URL and
updated_at. Idea hits also carry portal votes and endorsements, and scorable types their
Aha.io score, so ideas can be ranked by demand without a second call. Hits whose type is
readable as a resource — feature, epic, idea, initiative, goal, key result, release,
requirement, competitor — come with a resource_link; the rest are reachable by URL.
Quote a reference number in full. The workspace prefix is part of it: IDEASVOC-I-9930
identifies an idea, I-9930 identifies nothing, and Aha answers the truncated form with a 404
that reads like a missing record rather than a mistyped one.
What it does not return: workflow status, release membership, assignee or custom field values. Read the record itself for those:
Record types (recordTypes, omit to search all):
Paging: perPage accepts 10–200 and defaults to 20 — Aha raises anything below 10.
total_count stops counting at 10,000, reported as total_count_is_capped: true.
Use scripts/check-graphql.ts to confirm what your own account and token can reach:
Earlier versions synced Aha into SQLite and ranked results with a local "semantic search".
That has been removed. The embedding function hashed character codes through Math.sin(),
so it carried no semantic signal and its similarity scores were not interpretable. It also
required the native sqlite3 module and a writable data directory, which is what broke it
in packaged installs. Aha's server-side index is keyword-based but real, always current, and
free of all that machinery.
Aha also hosts its own MCP server at https://<yourcompany>.aha.io/api/v1/mcp.
When adding custom tools, resources, or prompts to your MCP server:
Use underscores (_) instead of hyphens (-) in all resource, tool, and prompt names
This naming convention ensures compatibility with Cursor and other AI tools that interact with your MCP server
The Aha MCP server is available as Docker images on GitHub Container Registry:
ghcr.io/cedricziel/aha-mcpTo persist configuration between runs:
The repository includes a docker-compose.yml file for easy setup:
Example .env file:
The Docker image supports all the same environment variables as the npm package:
| Variable | Description | Default |
|---|---|---|
AHA_COMPANY | Aha.io company subdomain | - |
AHA_TOKEN | Aha.io API token | - |
MCP_TRANSPORT_MODE | Transport mode (stdio or streamable-http) | stdio |
MCP_PORT | Port for streamable-http mode | 3001 |
MCP_HOST | Host for streamable-http mode | 0.0.0.0 |
MCP_AUTH_TOKEN | Bearer token for the streamable-http transport | - |
MCP_TOOL_RATE_LIMIT_PER_MINUTE | Tool calls allowed per minute (0 disables) | 120 |
MCP_CONFIG_DIR | Configuration directory | /home/mcp/.config |
The Docker image includes health checks for streamable-http mode:
To build the Docker image locally:
The Docker images are built for multiple architectures:
linux/amd64 (x86_64)linux/arm64 (Apple Silicon, ARM64)Docker will automatically pull the correct image for your platform.
The Docker image follows security best practices:
mcp)This project uses Conventional Commits to ensure consistent commit messages and enable automated versioning.
Commit Message Format:
Types:
feat: A new featurefix: A bug fixdocs: Documentation only changesstyle: Changes that do not affect the meaning of the coderefactor: A code change that neither fixes a bug nor adds a featureperf: A code change that improves performancetest: Adding missing tests or correcting existing testsbuild: Changes that affect the build system or external dependenciesci: Changes to CI configuration files and scriptschore: Other changes that don't modify src or test filesExamples:
Commit messages are validated using commitlint on every commit and in CI.
Run the test suite:
The Docker environment includes all necessary dependencies for testing:
The Docker environment includes:
The Docker environment supports the full test suite including:
To build for production:
This project uses release-please for automated versioning and publishing:
Make changes using Conventional Commits format:
feat: for new features (minor version bump)fix: for bug fixes (patch version bump)feat!: or fix!: for breaking changes (major version bump)Push to main - release-please will automatically:
ghcr.io and the
MCP RegistryThe registry job runs last, because the registry verifies ownership by reading the
already-published artifacts: mcpName in the npm package, the
io.modelcontextprotocol.server.name label on the image, and the SHA-256 of the .mcpb
asset attached to the release. It authenticates with GitHub OIDC, so no registry
credential is stored anywhere.
To publish the package manually:
Note: Make sure to set the NPM_TOKEN secret in your repository settings for automated publishing.
This project is licensed under the MIT License - see the LICENSE file for details.