README
¶
MCP Taskfile Server
A Model Context Protocol (MCP) server that dynamically exposes Taskfile.yml tasks as individual MCP tools, allowing AI assistants to discover and execute any task defined in your Taskfile.
Built using the official Go MCP SDK for MCP protocol implementation and the go-task library for native Taskfile.yml parsing and execution.
Why
- Standard practices for building, linting, etc. are already defined in a Taskfile. Allow the assistant to execute these tasks directly.
- Parity between local, CI and AI.
- Seemed like a fun idea.
Features
- Dynamic Task Discovery: Automatically discovers all tasks from Taskfile.yml at runtime
- Individual Task Tools: Each task becomes its own MCP tool with proper schema
- Variable Schema Generation: Automatically extracts task variables for proper parameter validation
- Native Task Execution: Uses go-task library directly (no subprocess execution)
- Multi-Root Support: Discovers roots via the MCP Roots capability, loading Taskfiles from each root directory
- Auto Reload: Watches Taskfile.yml (and included Taskfiles) for changes and automatically re-exposes updated tools to connected clients
- MCP Protocol Compliance: Uses the official Go MCP SDK for full specification compliance
Requirements
- Go 1.19 or later
Installation
go get github.com/rsclarke/mcp-taskfile-server
Usage
Running the Server
The server communicates via JSON-RPC over stdin/stdout:
mcp-taskfile-server
Root Discovery
After the client handshake completes, the server calls roots/list to discover which directories to load Taskfiles from. Clients that support the MCP Roots capability can provide one or more file:// root URIs. If the client does not support roots (returns JSON-RPC -32601), the server falls back to the current working directory.
When roots change at runtime (notifications/roots/list_changed), the server automatically diffs the root set, tears down removed roots (stopping their file watchers and unregistering their tools), and loads any newly added roots.
Dynamic Tool Discovery
The server automatically discovers all tasks in each root's Taskfile.yml and exposes each as an individual MCP tool.
Each tool automatically includes:
- Task-specific variables: Extracted from the task definition with proper defaults
- Proper descriptions: Uses task descriptions from Taskfile.yml
Tool Name Mapping
Taskfile task names can contain characters (:, *) that are invalid in MCP tool names. The server automatically sanitizes task names to conform to the MCP tool name specification ([a-zA-Z0-9_.-]).
Naming Rules
| Transformation | Example task name | MCP tool name |
|---|---|---|
| Colons → underscores | db:migrate |
db_migrate |
| Namespace (includes) | docs:serve |
docs_serve |
| Deep namespacing | uv:run:dev:lint-imports |
uv_run_dev_lint-imports |
| Leading dot preserved | uv:.venv |
uv_.venv |
| Single wildcard | start:* |
start |
| Multiple wildcards | deploy:*:* |
deploy |
| Mixed namespace + wildcard | uv:add:* |
uv_add |
When the tool name differs from the original task name, the original is included in the tool description for discoverability.
Multi-Root Prefixing
With a single root, tool names are unprefixed (as shown above). When the client provides multiple roots, each tool name is prefixed with a sanitized form of the root directory's basename to avoid collisions:
| Root directory | Task | MCP tool name |
|---|---|---|
/home/user/frontend |
build |
frontend_build |
/home/user/backend |
build |
backend_build |
/home/user/frontend |
lint:* |
frontend_lint |
The prefix is derived from the directory basename with non-alphanumeric characters (except _, -, .) replaced by underscores. If a root is added or removed such that the count crosses the 1↔N boundary, all tools are re-registered with or without prefixes accordingly.
Wildcard Tasks
Taskfile wildcard tasks (e.g. start:*) are exposed as tools with a required MATCH parameter. The server reconstructs the full task name at invocation time.
For a task defined as start:*, calling the tool:
{"name": "start", "arguments": {"MATCH": "web"}}
executes task start:web.
For tasks with multiple wildcards (e.g. deploy:*:*), provide comma-separated values:
{"name": "deploy", "arguments": {"MATCH": "api,production"}}
executes task deploy:api:production.
MCP Integration
This server implements the Model Context Protocol and can be used with any MCP-compatible client or AI assistant. The server:
- Requests roots from the client after handshake; falls back to the working directory if unsupported
- Dynamically discovers all tasks from each root's Taskfile.yml
- Sanitizes task names into valid MCP tool names for strict client compatibility
- Exposes each task as an individual MCP tool with proper JSON schema
- Automatically extracts task variables for parameter validation
- Reacts to root changes by adding/removing roots and re-syncing tools at runtime
- Executes tasks natively using the go-task library (no subprocess calls)
- Provides comprehensive error handling and feedback
Auto Reload
The server watches your Taskfile.yml and any included Taskfiles for changes using fsnotify. When a file is modified, added, or removed, the server automatically:
- Reloads and re-parses the Taskfile
- Diffs the updated task set against currently registered tools
- Adds new tools and removes stale ones via the MCP SDK
- Notifies connected clients of the change (
notifications/tools/list_changed)
File system events are debounced (~200 ms) to avoid redundant reloads during rapid edits. The watcher runs for the lifetime of the server and is cleaned up on shutdown.
Error Handling
The server handles various error conditions:
- Missing Taskfile.yml
- Invalid task names
- Task execution failures
- Invalid MCP requests
All errors are returned following MCP error response format.
Security Considerations
This server executes arbitrary commands defined in your Taskfile. Only use it in trusted environments and ensure your Taskfile doesn't contain malicious commands.
Development
To modify or extend the server:
- Server Setup: The MCP server is created using
mcp.NewServer()withInitializedHandlerandRootsListChangedHandler - Root Loading:
handleInitialized()callsListRootsto discover directories;handleRootsChanged()diffs and updates the root set - Dynamic Discovery: Tasks are discovered and built into a tool set via
buildToolSet() - Tool Generation: Each task becomes an MCP tool via
createToolForTask() - Variable Extraction: Task variables are automatically extracted for schema generation
- Handler Creation: Each task gets its own handler via
createTaskHandler() - Tool Sync:
syncTools()diffs and updates registered tools;watchTaskfiles()triggers reloads on file changes - Native Execution: Tasks are executed using
executor.Run()from go-task library
Key Components
NewTaskfileServer(): Creates an empty server; roots are loaded after the client handshakehandleInitialized(): Requests roots from the client, loads each root's Taskfile, syncs tools, and starts file watchershandleRootsChanged(): Diffs the current root set against the client's updated list, adding/removing roots and re-syncing toolsloadRoot()/unloadRoot(): Creates or tears down a per-root executor and file watcherbuildToolSet(): Discovers tasks across all roots and builds the set of MCP tools and handlerscreateToolForTask(): Generates MCP tool schema from task definitioncreateTaskHandler(): Creates execution handler for each tasksyncTools(): Diffs current tasks against registered tools and adds/removes as neededwatchTaskfiles(): Watches Taskfile.yml and included files for changes with debounced reload
Key Dependencies
- Go MCP SDK: Official MCP protocol implementation
- go-task: Native Taskfile.yml parsing and execution
- fsnotify: Cross-platform file system notifications for auto reload
The server uses the go-task library's native API for both parsing and execution, ensuring maximum compatibility with Taskfile.yml features.