A collection of tools for working with a specific Asana project via the Asana REST API. The repo contains one production-ready MCP server (asana_mcp_server.py) and two prototype conversational interfaces — a terminal chatbot and an SDK-backed REPL copilot — both housed in prototypes/, plus a credential validation helper and a set of early exploration scripts.
https://www.loom.com/share/70620a8a683448c6b0cb7883f977769a
| Workspace GID | 1214958615680522 |
| Project GID | 1214982995972383 |
asana_challenge/
├── asana_mcp_server.py # FastMCP stdio server (4 tools)
├── preflight_checker.py # Credential / API validation helper
├── prototypes/
│ ├── asana_basic_REPL_agent.py # Terminal chatbot (requests + rich)
│ ├── asana_full_REPL_copilot.py # REPL copilot (official Asana SDK)
│ ├── a_asana_1.py # Workspace membership lookup (SDK)
│ ├── b_asana_1.py # Token verification (raw requests)
│ └── c_asana_1.py # User info + memberships (SDK)
├── tests/
│ ├── test_asana_agent.py
│ └── test_asana_mcp_server.py
├── serviceNOW_challenge_Dave-Brace.pdf
├── serviceNOW_memo_2.pdf
├── .env.example
├── .mcp.json # MCP server wiring for Claude Code
└── pyproject.toml
Requires Python 3.13+ and uv.
cp .env.example .env
# fill in ASANA_TOKEN in .env
uv sync| Variable | Required | Default | Notes |
|---|---|---|---|
ASANA_TOKEN |
Yes | — | Asana Personal Access Token |
ASANA_WORKSPACE_GID |
Yes | — | Workspace GID |
ASANA_PROJECT_GID |
Yes | — | Project GID |
ASANA_API_BASE |
No | https://app.asana.com/api/1.0 |
Override for proxies |
ASANA_IMPLEMENTED_SECTION_NAME |
No | Implemented |
Section name to exclude from active tasks |
If ASANA_TOKEN is not set when running the prototype scripts, they prompt for it interactively without saving it.
My final solution to the Take Home Challenge was to build a smart Asana MCP server (asana_mcp_server.py).
- clone the repo to yoyur linux system
- note:
- (Its developed in python, so it will work in a Windows python env also)
- I have only tested it in a Linux env
- Install the MCP server into Claude Code or OpenAI Codex
- Simply ask Claude (or Codex) to install the asana_mcp_server.py for you...
- Check that 4 new MCP tools are now available... (you may need to exit/restart or force a Refresh)
- You can now conversationally interact with Asana project tasks. The new MCP tools will do all the work for you.
- You will need to add the ASANA env vars to the .env file
- A temp Asana Token is provided in the serviceNOW_challenge_1 PDF doc. - Its points to my account. (I will nuke that info later)
asana_mcp_server.py is a FastMCP stdio MCP server that exposes 4 tools for use with Claude Code, OpenAI Codex, or any MCP-compatible AI agent.
| Tool | Parameters | Description |
|---|---|---|
create_asana_task |
description, due_date |
Create a task in the configured project |
list_asana_tasks |
— | List active tasks; returns a table with Due Date column |
close_asana_task |
task_ref |
Mark one or more tasks complete |
modify_asana_task |
task_ref, description?, due_date? |
Update a task's title and/or due date |
task_ref accepts row ID, ordinal, GID, partial title, or top N.
list_asana_tasks and close_asana_task cache the most recently fetched task list in module state so consecutive calls within a session avoid redundant network round-trips.
The repo ships with .mcp.json MCP config file pre-configured:
{
"mcpServers": {
"asana": {
"command": "uv",
"args": ["run", "python", "asana_mcp_server.py"],
"cwd": "/home/dbrace/code/asana_challenge"
}
}
}Claude Code picks this up automatically. The four tools appear as mcp__asana__create_asana_task, mcp__asana__list_asana_tasks, mcp__asana__close_asana_task, and mcp__asana__modify_asana_task.
Runs three sequential validation checks against the Asana API to confirm credentials and connectivity before using the main tools.
| Check | Method | What it verifies |
|---|---|---|
| 1 | Asana SDK WorkspaceMembershipsApi |
Workspace membership is accessible |
| 2 | Raw requests to /users/me |
PAT is valid; lists user name, email, and workspaces |
| 3 | Asana SDK UsersApi + WorkspaceMembershipsApi |
Full user record and workspace membership list |
uv run python preflight_checker.pyOutput is colored via rich.
Prototype and exploratory scripts kept for reference.
An interactive terminal chatbot built on requests and rich. Authenticates at startup, then accepts free-text commands in a REPL loop.
uv run python prototypes/asana_basic_REPL_agent.py| Command examples | Action |
|---|---|
create a task |
Prompts for description and due date, then creates the task |
show active tasks, list, display open tasks |
Lists active tasks as a rich table |
close the first task, close 002, complete 1214982880426742 |
Marks one or more tasks complete |
mark done top 3 |
Closes the first three tasks in the list |
goodbye, quit, exit |
Exits |
Tasks are displayed in a table with columns: Row ID, Asana Task id, Task title.
today tomorrow next week
3 days from now
05/22/2026 5/22/26 2026-05-22
May 22, 2026 May 22 2026
Tasks can be identified by:
- Row ID:
001,2,003 - Ordinal:
first,second,third…tenth - Positional suffix:
1st,2nd,3rd - Asana task GID: full numeric GID
- Title: exact or partial match (disambiguated interactively if ambiguous)
- Batch:
top 3,top 5
A conversational terminal REPL built on the official Asana Python SDK (asana>=5.2.4). Uses a layered architecture — a storage-agnostic Copilot conversation layer over an AsanaStore data layer — and runs in interactive mode.
- 50% Hand-coded.
- I built this as a prototype to evaluate how easy/difficult it would be to fully emulate a REPL without leveraging an agentic framework/harness.
uv run python prototypes/asana_full_REPL_copilot.pyRequires ASANA_TOKEN and ASANA_PROJECT_GID in the environment (or .env).
- Multi-turn task creation: inline title extraction from
create a task called X, or step-by-step prompting if no title is found. - Due-date parsing:
today,tomorrow,tmrw,next week,YYYY-MM-DD. - List open tasks: fetches all non-completed tasks and prints GID, name, and due date.
- Close a task: keyword-matches the user's message against open task titles.
- Clean SDK shutdown: explicitly closes the Asana SDK thread pool to prevent hang on exit.
| File | What it does |
|---|---|
a_asana_1.py |
Workspace membership lookup via the Asana SDK |
b_asana_1.py |
Token verification via raw requests to /users/me |
c_asana_1.py |
Authenticated user info and workspace memberships via the Asana SDK |
These are standalone scripts; run them with ASANA_TOKEN set in the environment.
Automated tests cover date parsing, intent detection, active-task filtering, task selection/resolution, API client pagination and error handling, and all four MCP tool functions. No live Asana writes are made.
uv run python -m unittest discover -s testsOr with standard Python:
python -m unittest discover -s tests| Test file | Covers |
|---|---|
tests/test_asana_agent.py |
parse_due_date, detect_intent, is_active_task, summarize_active_tasks, select_tasks, strip_close_words, format_prompt |
tests/test_asana_mcp_server.py |
load_config, parse_due_date, is_active_task, summarize_active_tasks, format_task_table, resolve_task_reference, AsanaClient (create, list with pagination, error surface, modify), and all four MCP tool functions via mocked AsanaClient |
Declared in pyproject.toml (Python 3.13+):
| Package | Purpose |
|---|---|
asana>=5.2.4 |
Official Asana SDK (used by asana_full_REPL_copilot.py and preflight_checker.py) |
fastmcp>=2.13.0 |
MCP server framework |
python-dotenv>=1.2.1 |
.env file loading |
requests>=2.34.2 |
HTTP client for asana_basic_REPL_agent.py and asana_mcp_server.py |
rich>=15.0.0 |
Colored terminal output |