geoiq-taskflow-mcp
v1.0.4
Published
TaskFlow MCP server — lets Claude Code, Cursor, and other MCP clients read and update TaskFlow issues
Readme
TaskFlow MCP Server
Exposes TaskFlow as an MCP (Model Context Protocol) server, so AI coding agents — Claude Code, Cursor, Windsurf, and any MCP-compatible client — can read and update your project issues, activity, and wiki directly.
Published on npm as geoiq-taskflow-mcp. Runs over stdio; no server to host.
Quick start
Point your client at the package with npx — no clone, no local path, no global install:
claude mcp add taskflow --env TASKFLOW_URL=https://taskflow.geoiq.ai -- npx -y geoiq-taskflow-mcp@latestThen, inside your agent, authenticate once:
loginThat opens a browser OAuth flow, saves a 30-day token to ~/.taskflow.json, and lists your projects. Set a working project and you're going:
set_project { "project_key": "GEOIQ" }
list_issues { "all": true }Already a CLI user? If you've run
taskflow login, the MCP server reuses the same~/.taskflow.json—loginis unnecessary.
Configuring your client
A running TaskFlow instance serves a ready-made config block at /mcp (e.g. https://taskflow.geoiq.ai/mcp), so you can copy it rather than hand-writing one.
Claude Code
claude mcp add taskflow --env TASKFLOW_URL=https://taskflow.geoiq.ai -- npx -y geoiq-taskflow-mcp@latestStored in ~/.claude.json. Verify with claude mcp list.
Cursor / Windsurf / generic MCP clients
Add to the client's MCP config file (Cursor: ~/.cursor/mcp.json; Windsurf: ~/.codeium/windsurf/mcp_config.json):
{
"mcpServers": {
"taskflow": {
"command": "npx",
"args": ["-y", "geoiq-taskflow-mcp@latest"],
"env": { "TASKFLOW_URL": "https://taskflow.geoiq.ai" }
}
}
}Running from a checkout instead
For local development against the source in this repo:
claude mcp add taskflow -- node /absolute/path/to/artifacts/mcp-server/src/index.jsConfiguration
| Setting | Where | Purpose |
|---|---|---|
| TASKFLOW_URL | env var | Base URL of your TaskFlow instance. Optional — defaults to https://taskflow.geoiq.ai. Used only until a token is saved. |
| url | ~/.taskflow.json | Base URL, once login has run. Takes precedence over TASKFLOW_URL. |
| accessToken | ~/.taskflow.json | 30-day OAuth token, written by login (file mode 0600). |
| defaultProject | ~/.taskflow.json | Project key used when a tool call omits project. |
{
"url": "https://taskflow.geoiq.ai",
"accessToken": "...",
"defaultProject": "GEOIQ"
}The base URL is the origin TaskFlow is served from — the server appends /api/cli itself. It defaults to https://taskflow.geoiq.ai, so TASKFLOW_URL is only needed to point somewhere else. For local development set it to the Vite dev server (http://localhost:5173), which proxies /api to the API server — not port 8080 directly.
Config is re-read on every API call, so a login or taskflow use <KEY> mid-session takes effect immediately with no client restart.
Project selection
Every issue, activity, and wiki tool needs a project. Resolution order:
projectparameter on the tool calldefaultProjectin~/.taskflow.json(set viaset_project, ortaskflow usein the CLI)- Error — the project is required
Set it once, or override per call:
set_project { "project_key": "GEOIQ" }
list_issues { "project": "BACKEND", "all": true }
create_issue { "project": "BACKEND", "title": "Fix memory leak" }Tools
Auth & setup
| Tool | Description |
|------|-------------|
| login | Authenticate via browser OAuth. Pass url on first login to set the server address. |
| whoami | Show the authenticated user and active project. |
| list_projects | List all projects you can access, with keys and roles. |
| set_project | Set the default project for subsequent calls. Persists to ~/.taskflow.json. |
Issues
| Tool | Description |
|------|-------------|
| list_issues | List issues, filtered by status, assignee, priority, labels, or date range. |
| get_issue | Full issue detail — description, comments, activity, sub-issues. |
| create_issue | Create an issue with title, type, priority, description. |
| create_sub_issue | Create a sub-issue under a parent issue key. |
| update_issue | Update status, priority, title, description, assignee, story points, due date, labels. |
| claim_issue | Atomically assign to yourself and move to in_progress. Returns 409 if already claimed. |
| close_issue | Mark an issue done, optionally with a closing comment. Shorthand for update_issue with status=done. |
| delete_issue | Permanently delete an issue. Requires admin/owner on the project; cannot be undone. |
| add_comment | Add a comment to an issue. |
AI (Gemini)
| Tool | Description |
|------|-------------|
| query_issues | Ask a plain-English question about issues; returns the matching issues and relevant fields. |
| analyze_meeting_notes | Send meeting notes; returns a summary plus proposed create/update/comment actions. |
| apply_meeting_notes | Execute the actions returned by analyze_meeting_notes. |
Activity
| Tool | Description |
|------|-------------|
| list_activity | Recent project activity, filtered by type, user, or date range. |
Wiki
| Tool | Description |
|------|-------------|
| list_wiki_docs | List wiki pages with title, folder, tags, last-updated. |
| get_wiki_doc | Get a wiki page's full HTML content by ID. |
| create_wiki_doc | Create a page with title, content, folder, icon, tags. |
| update_wiki_doc | Update an existing page. |
| delete_wiki_doc | Permanently delete a wiki page; cannot be undone. |
| import_confluence_page | Import a Confluence page, stripping Confluence XML markup. |
| import_obsidian_page | Import one Obsidian note from raw Markdown — strips YAML frontmatter and #tags, flattens [[WikiLinks]], notes ![[embeds]] inline, and converts Markdown (headings, lists, code, tables, links) to HTML. |
| bulk_import_obsidian_pages | Import up to 50 Obsidian notes in one call — use this rather than looping import_obsidian_page. |
| bulk_create_wiki_docs | Create up to 50 pages in one call — for Confluence migrations. |
| list_wiki_spaces | List wiki spaces (top-level containers). |
| create_wiki_space | Create a space. |
| update_wiki_space | Rename a space or change its icon. |
| delete_wiki_space | Delete a space; docs inside are kept. |
Examples
A typical session
list_projects
set_project { "project_key": "GEOIQ" }
list_issues { "all": true }
get_issue { "key": "GEOIQ-12" }
update_issue { "key": "GEOIQ-12", "status": "in_review" }Ask questions instead of writing filters
query_issues { "question": "what is the status of the ARR dashboard?" }
query_issues { "question": "which issues are overdue?" }
query_issues { "question": "who is working on the pipeline view?" }Meeting notes → issues
analyze_meeting_notes {
"notes": "Alice: finished the deal stage fix. Bob: new bug in invoice export."
}
# review the proposed actions, then:
apply_meeting_notes { "actions": [ ... ], "summary": "..." }Create an issue
create_issue {
"title": "Fix payment timeout in checkout",
"type": "bug",
"priority": "high",
"description": "Users on slow connections hit a 30s timeout..."
}Troubleshooting
| Symptom | Cause / fix |
|---|---|
| npm error 404 geoiq-taskflow-mcp | The package isn't published yet — see Releasing. Until then, use the from-a-checkout config above. |
| Tools missing, or every call says unauthenticated | Run login. Check ~/.taskflow.json has an accessToken. |
| Calls hit the wrong instance | url in ~/.taskflow.json overrides TASKFLOW_URL. Delete the file and re-run login, or pass login { "url": "..." }. |
| Connection refused in local dev | Point at the Vite dev server (http://localhost:5173), not the API server (8080). |
| Token stopped working after ~30 days | Tokens are 30-day. Run login again. |
Releasing
The /mcp discovery endpoint advertises npx -y geoiq-taskflow-mcp@latest, so that config only works once this package is on npm.
Prerequisites: an npm account with publish rights on this package name. It is unscoped, so no npm organization is required — but the name is first-come, and npm can reject a new name it considers too similar to an existing one (that check only runs at publish time).
cd artifacts/mcp-server
npm pack --dry-run # confirm contents: package.json, README.md, src/index.js
npm login
npm publish # publishConfig.access=public is already set in package.json
npm view geoiq-taskflow-mcp version # confirmNotes:
publishConfig.access: "public"is retained inpackage.jsoneven though unscoped packages are public by default — it costs nothing and keeps publishing correct if this ever moves to a@scope/name, where scoped packages otherwise default to a private publish and fail without a paid plan.- Never add
catalog:dependency specifiers here. That protocol is pnpm-workspace-only and breaks every npm consumer. Keep this package's deps as literal semver ranges. - Bump
versioninpackage.jsonfor each release, and keep theversionpassed tonew McpServer({...})insrc/index.jsin sync — clients surface it, and the two currently disagree. src/index.jsmust stay executable (mode755) for thetaskflow-mcpbin entry to work.
