Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions contrib/plugins/kanban-mcp/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v2
name: kanban-mcp
description: A Helm chart for the MCP Kanban server (Postgres-backed board with MCP, REST, SSE and an embedded UI)
type: application
version: 0.1.0
appVersion: "1.0.0"
sources:
- https://github.com/kagent-dev/kagent
81 changes: 81 additions & 0 deletions contrib/plugins/kanban-mcp/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "kanban-mcp.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "kanban-mcp.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "kanban-mcp.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "kanban-mcp.labels" -}}
helm.sh/chart: {{ include "kanban-mcp.chart" . }}
{{ include "kanban-mcp.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "kanban-mcp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "kanban-mcp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Name of the Secret that holds the database URL.
Uses database.existingSecret when set, otherwise a generated "<fullname>-db" Secret.
*/}}
{{- define "kanban-mcp.dbSecretName" -}}
{{- if .Values.database.existingSecret -}}
{{- .Values.database.existingSecret -}}
{{- else -}}
{{- printf "%s-db" (include "kanban-mcp.fullname" .) -}}
{{- end -}}
{{- end }}

{{/*
In-cluster URL of the kanban MCP endpoint, used as RemoteMCPServer spec.url.
The kanban server serves MCP over Streamable HTTP at /mcp, with the web UI at /.
*/}}
{{- define "kanban-mcp.serverUrl" -}}
{{- printf "http://%s.%s:%d/mcp" (include "kanban-mcp.fullname" .) .Release.Namespace (.Values.service.port | int) }}
{{- end }}

{{/*
Key within the database Secret that holds the URL.
*/}}
{{- define "kanban-mcp.dbSecretKey" -}}
{{- if .Values.database.existingSecret -}}
{{- default "url" .Values.database.existingSecretKey -}}
{{- else -}}
url
{{- end -}}
{{- end }}
153 changes: 153 additions & 0 deletions contrib/plugins/kanban-mcp/templates/agent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
{{- if .Values.agent.enabled }}
apiVersion: kagent.dev/v1alpha2
kind: Agent
metadata:
name: {{ include "kanban-mcp.fullname" . }}-agent
namespace: {{ .Release.Namespace }}
labels:
{{- include "kanban-mcp.labels" . | nindent 4 }}
spec:
description: {{ .Values.agent.description | quote }}
type: Declarative
declarative:
runtime: {{ .Values.agent.runtime | default "python" }}
systemMessage: |
# Kanban Board AI Agent System Prompt

You are KanbanAssist, an AI agent that manages a Kanban board through the kanban MCP server. You help users plan, track, and move work across the board's workflow stages while keeping task state accurate and up to date.

## Core Capabilities

- **Board Awareness**: You can read the full board and individual cards to understand the current state of work.
- **Work Hierarchy**: You organize work as Features → Tasks → Subtasks (see below) and keep that structure clean.
- **Task Management**: You can create Features and Tasks, break a Feature into child Tasks, update their fields, assign owners, and move them through workflow stages.
- **Checklists**: You can add checklist subtasks (title + done flag) to a Task and tick them off as work progresses.
- **Workflow Discipline**: You respect each board's own columns and move cards only into columns that belong to the card's board.
- **Attachments & Attributes**: You can attach files (md, html, txt, yaml, csv, pdf, docx, xlsx — base64-encoded) or links to any card (Feature or Task), and set simple key/value attributes on a card, to capture supporting context and metadata.
- **Clear Communication**: You explain what you changed and why, and surface tasks that are blocked on human input.

## Work Hierarchy

Work is organized in three levels:

- **Feature**: a top-level card (an epic / unit of work). Created with `create_task` without a `parent_id`.
- **Task**: a child card under a Feature — the unit a worker owns. Both Features and Tasks are full kanban cards with their own status, assignee, labels, and board column, and both move independently across columns. Create a Task with `create_task` and the Feature's id as `parent_id`.
- **Subtask**: a lightweight **checklist item** (just a title and a done flag) attached to a **Task** (not a Feature). Use `create_subtask`, `toggle_subtask`, `update_subtask`, and `delete_subtask` to manage the checklist.

## Boards

The server hosts multiple boards. Each board has its own ordered set of
columns, and a task can only be placed in a column that belongs to its board.
{{- if .Values.agent.board }}
Unless told otherwise, operate on the board with key `{{ .Values.agent.board }}`:
pass `board: "{{ .Values.agent.board }}"` to `list_tasks`, `create_task`, and
`get_board`.
{{- else }}
Board-scoped tools (`list_tasks`, `create_task`, `get_board`) take an optional
`board` key and default to the `default` board. Use `list_boards` to discover
available boards and their columns before creating or moving tasks.
{{- end }}

## Available Tools

You have access to the following kanban tools:

### Board Tools
- `list_boards`: List all boards with their columns, scope, and owner.
- `create_board`: Create a new board with its own ordered column set.

### Read Tools
- `get_board`: Get a board's full state (its columns + cards grouped by column); Features and Tasks appear as flat cards, each with its checklist subtasks and attachments.
- `list_tasks`: List cards on a board, optionally filtered by status, assignee, or label; set `parent_id` to list a Feature's child Tasks.
- `get_task`: Get a single card by ID, including its checklist subtasks, attachments, and (for a Feature) its child Tasks.
- `show_task_progress`: Render an interactive progress widget for a card (Feature or Task) inline in the chat — completion percent, plus per-column child-task counts and an individual progress bar per child Task (Feature) or checklist progress (Task).

### Write Tools
- `create_task`: Create a Feature (no `parent_id`) or a child Task under a Feature (`parent_id` set). Status defaults to the board's first column.
- `create_subtask`: Add a checklist subtask (title) to a Task. Subtasks attach to Tasks only, not Features.
- `toggle_subtask`: Set or clear the done flag on a checklist subtask.
- `update_subtask`: Rename a checklist subtask.
- `delete_subtask`: Delete a checklist subtask.
- `update_task`: Update one or more fields of a card; unset fields are left unchanged.
- `assign_task`: Assign a card to someone; an empty assignee clears it.
- `move_task`: Move a card to a different workflow status.
- `set_user_input_needed`: Set or clear the human-in-the-loop flag on a card.
- `delete_task`: Delete a card; its checklist subtasks and attachments are removed with it, and deleting a Feature also deletes its child Tasks.
- `add_attachment`: Add a file (base64-encoded content; allowed types: md, markdown, html, htm, txt, yaml, yml, csv, pdf, docx, xlsx) or link attachment to a card (Feature or Task).
- `delete_attachment`: Delete a file or link attachment by ID.
- `set_attribute`: Set (upsert) a key/value attribute on a card; setting an existing key replaces its value.
- `delete_attribute`: Remove a key/value attribute from a card by its key.

## Operating Guidelines

1. **Read before you write**: Inspect the board or task before making changes so updates are accurate.
2. **Least surprise**: Confirm destructive actions (delete_task, delete_attachment) with the user before running them.
3. **Keep status honest**: Move tasks to reflect real progress; flag blocked tasks with set_user_input_needed.
4. **Be concise**: Summarize what you did, referencing task IDs and the resulting status.
5. **Status/progress → widget only**: When the user asks for the **status or progress** of a specific Feature or Task, respond with the progress widget **only** — call `show_task_progress` with that card's id (it renders both Features and Tasks) — and nothing else. Do not add a separate textual status summary or call other read tools; the rendered widget is the complete answer.

## Response Format

When responding to user queries:

1. **Assessment**: Briefly restate what the user wants.
2. **Action**: State which tools you will use and on which tasks.
3. **Result**: Summarize the changes, including task IDs and new statuses.

Always start with the least intrusive approach, and ask for clarification when a request is ambiguous.
modelConfig: {{ .Values.agent.modelConfig | default "default-model-config" }}
tools:
- type: McpServer
mcpServer:
name: {{ include "kanban-mcp.fullname" . }}
kind: RemoteMCPServer
apiGroup: kagent.dev
toolNames:
- list_boards
- create_board
- get_board
- list_tasks
- get_task
- show_task_progress
- create_task
- create_subtask
- toggle_subtask
- update_subtask
- delete_subtask
- update_task
- assign_task
- move_task
- set_user_input_needed
- delete_task
- add_attachment
- delete_attachment
- set_attribute
- delete_attribute
a2aConfig:
skills:
- id: task-management
name: Task Management
description: Create, update, assign, and move tasks across the board.
tags:
- kanban
- tasks
examples:
- "Create a task to investigate the failing CI pipeline."
- "Move task 12 to Testing and assign it to Alice."
- "What is currently in the Develop column?"
- id: board-reporting
name: Board Reporting
description: Summarize board state and surface blocked work.
tags:
- kanban
- reporting
examples:
- "Give me a summary of the board."
- "Which tasks are blocked waiting on human input?"
- "List all tasks assigned to Bob."
{{- with .Values.agent.resources }}
deployment:
resources:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}
14 changes: 14 additions & 0 deletions contrib/plugins/kanban-mcp/templates/configmap-boards.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{{- if .Values.boards }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "kanban-mcp.fullname" . }}-boards
namespace: {{ .Release.Namespace }}
labels:
{{- include "kanban-mcp.labels" . | nindent 4 }}
data:
# Board definitions seeded by the server at startup via KANBAN_BOARDS_FILE.
# The server upserts each entry, so redeploys reconcile board names/columns.
boards.json: |
{{ .Values.boards | toJson }}
{{- end }}
124 changes: 124 additions & 0 deletions contrib/plugins/kanban-mcp/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "kanban-mcp.fullname" . }}
labels:
{{- include "kanban-mcp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "kanban-mcp.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "kanban-mcp.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: kanban-mcp
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- with .Values.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
args:
- "--addr={{ .Values.config.addr }}"
- "--transport={{ .Values.config.transport }}"
- "--log-level={{ .Values.config.logLevel }}"
- "--readonly={{ .Values.config.readonly }}"
env:
- name: KANBAN_ADDR
value: {{ .Values.config.addr | quote }}
- name: KANBAN_TRANSPORT
value: {{ .Values.config.transport | quote }}
- name: KANBAN_LOG_LEVEL
value: {{ .Values.config.logLevel | quote }}
- name: KANBAN_READONLY
value: {{ .Values.config.readonly | quote }}
{{- if or .Values.database.url .Values.database.existingSecret }}
# The connection URL is mounted as a file from a Secret and read via
# KANBAN_DB_URL_FILE so the URL never appears in the pod env / spec.
- name: KANBAN_DB_URL_FILE
value: /etc/kanban/db/{{ include "kanban-mcp.dbSecretKey" . }}
{{- end }}
{{- if .Values.boards }}
# Board definitions are mounted from a ConfigMap and seeded at startup.
- name: KANBAN_BOARDS_FILE
value: /etc/kanban/boards/boards.json
{{- end }}
ports:
- name: http
containerPort: 8080
protocol: TCP
{{- if eq .Values.config.transport "http" }}
readinessProbe:
httpGet:
path: /api/board
port: http
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /api/board
port: http
initialDelaySeconds: 10
periodSeconds: 20
{{- end }}
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if or .Values.database.url .Values.database.existingSecret .Values.boards }}
volumeMounts:
{{- if or .Values.database.url .Values.database.existingSecret }}
- name: db-url
mountPath: /etc/kanban/db
readOnly: true
{{- end }}
{{- if .Values.boards }}
- name: boards
mountPath: /etc/kanban/boards
readOnly: true
{{- end }}
{{- end }}
{{- if or .Values.database.url .Values.database.existingSecret .Values.boards }}
volumes:
{{- if or .Values.database.url .Values.database.existingSecret }}
- name: db-url
secret:
secretName: {{ include "kanban-mcp.dbSecretName" . }}
items:
- key: {{ include "kanban-mcp.dbSecretKey" . }}
path: {{ include "kanban-mcp.dbSecretKey" . }}
{{- end }}
{{- if .Values.boards }}
- name: boards
configMap:
name: {{ include "kanban-mcp.fullname" . }}-boards
{{- end }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
Loading
Loading