Skip to content

feature/kagent-mcp-app-plugins#1447

Draft
dimetron wants to merge 98 commits intokagent-dev:mainfrom
dimetron:feature/kanban-mcp
Draft

feature/kagent-mcp-app-plugins#1447
dimetron wants to merge 98 commits intokagent-dev:mainfrom
dimetron:feature/kanban-mcp

Conversation

@dimetron
Copy link
Contributor

@dimetron dimetron commented Mar 5, 2026

WIP - for reference only

dimetron and others added 30 commits February 25, 2026 14:34
Create go/cmd/kanban-mcp/ binary with flag-based config loading
and KANBAN_* environment variable fallback. Uses flag.NewFlagSet
for testability. Builds and prints usage on --help.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- internal/db/models.go: Task GORM model, TaskStatus enum (8 statuses),
  StatusWorkflow ordered slice, ValidStatus() helper
- internal/db/manager.go: Manager with NewManager(cfg), Initialize() AutoMigrate,
  DB() accessor; mirrors go/internal/database/manager.go pattern
- internal/db/db_test.go: table-driven TestValidStatus, TestNewManager_Sqlite,
  TestNewManager_InvalidType
- main.go: wire DB init, log "database initialized"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add table-driven tests for TaskService covering all 8 test requirements
from the spec: CreateTask defaults, CreateTask with status, GetTask not
found (wrapped sentinel), MoveTask valid/invalid, ListTasks filter,
DeleteTask simple, and Broadcast called on every mutation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add AssignTask (clears on empty string) and CreateSubtask (one-level
nesting only, rejects grandchildren) to TaskService. DeleteTask cascade
and GetTask subtask preload were already implemented in Step 3.

Add 7 table-driven tests: TestAssignTask, TestListTasks_AssigneeFilter,
TestCreateSubtask_Valid, TestCreateSubtask_ParentNotFound,
TestCreateSubtask_NestedRejection, TestDeleteTask_Cascade,
TestGetTask_WithSubtasks (all pass, 15/15 total).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- internal/sse/hub.go: Hub with Subscribe/Unsubscribe/Broadcast/ServeSSE
- Broadcast wraps data in Event{Type:"board_update"} and stores lastJSON snapshot
- ServeSSE sends snapshot to new clients then streams subsequent events
- Non-blocking delivery: slow subscribers are skipped (select default)
- internal/sse/hub_test.go: 4 tests all pass (subscribe/unsubscribe, non-blocking,
  50 concurrent subscribers, SSE integration via httptest.NewServer)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added internal/mcp/tools.go: NewServer() registers all 10 tools
  (list_tasks, get_task, create_task, create_subtask, assign_task,
  move_task, update_task, set_user_input_needed, delete_task, get_board)
- Added internal/mcp/tools_test.go: 6 tests using InMemoryTransports
  (all pass): CreateTask, MoveTask_Invalid, CreateSubtask, AssignTask,
  DeleteTask_Cascade, GetBoard
- Updated main.go: wires SSE hub + TaskService, runs stdio transport
  via mcpServer.Run(ctx, &StdioTransport{})

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- server.go: NewHTTPServer() wires all 4 surfaces on one mux
  (MCP Streamable HTTP, SSE /events, REST /api/*, SPA /)
- internal/api/handlers.go: 501/404 stubs for REST routes (Step 8)
- internal/ui/embed.go + index.html: embedded SPA stub (Step 9)
- main.go: HTTP mode calls NewHTTPServer + ListenAndServe
- server_test.go: 4 tests (MCP, SSE, NotFound, CORS) all pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implement full REST API replacing 501/404 stubs:
- GET/POST /api/tasks (list with status/assignee filter, create)
- GET/PUT/DELETE /api/tasks/{id}
- GET/POST /api/tasks/{id}/subtasks
- GET /api/board (8-column board grouped by StatusWorkflow)

Error mapping: gorm.ErrRecordNotFound→404, invalid-status/nesting→400, else→500
Board/Column types exported for test use.

9/9 tests pass: CreateTask, GetTask(404), UpdateTask, ListTasks_Filter,
Subtasks_Create, Subtasks_List, DeleteTask_Cascade, Board, SSE_AfterMutation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full single-page Kanban board in internal/ui/index.html:
- 8-column board layout rendered from /api/board on load
- EventSource(/events) for live board_update SSE re-render
- renderCard: assignee badge (blue), HITL badge (amber), subtask count
- Collapsible subtask details with status pills
- Prev/Next nav buttons call PUT /api/tasks/:id for status moves
- HITL toggle button calls PUT /api/tasks/:id
- Add-task form in Inbox column (POST /api/tasks)
- No build step, no npm — vanilla JS + inline CSS

Added internal/ui/embed_test.go: TestUI_Embedded + TestUI_Handler.
All 8 kanban-mcp packages pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add TestPostgres_Integration (skipped unless KANBAN_TEST_POSTGRES_URL set):
  full CRUD + subtask + assign workflow against real Postgres
- Add go/cmd/kanban-mcp/Dockerfile: multi-stage build (golang:1.23-alpine)
- Add contrib/tools/kanban-mcp/ Helm chart: Deployment, Service, PVC,
  _helpers.tpl, Chart.yaml, values.yaml
- helm lint passes; helm template produces valid K8s manifests
Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Add NamespaceProvider context (namespace string + setter), wire into
root layout wrapping AgentsProvider, and add unit tests verifying
hook behavior inside/outside provider.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Build sidebar navigation with 4 sections (OVERVIEW, AGENTS, RESOURCES,
ADMIN) and 12 nav items using existing shadcn sidebar primitives.
Active route detection via usePathname. AppSidebar wraps nav with
KAgent logo header, placeholder footer, and SidebarRail for collapse.
6 unit tests verify section labels, item count, and active state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…Footer)

Wire AppSidebar into app/layout.tsx with SidebarProvider + SidebarInset,
replacing the Header and Footer components. Body className changed from
flex-col to flex (sidebar handles horizontal layout). 5 unit tests verify
sidebar presence, children rendering, and absence of Header/Footer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create NamespaceSelector component wrapping existing NamespaceCombobox
logic for sidebar display. Compact trigger button in expanded mode,
icon-only with tooltip in collapsed mode. Wired into AppSidebar header
with useNamespace() context. 6 unit tests, all 128 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove inner SidebarProvider from chat layout (global one sufficient).
SessionsSidebar moved to side="right", AgentDetailsSidebar converted
to Sheet with open/onClose props. ChatLayoutUI gains info trigger
button and flex wrapper for proper horizontal layout.

- chat/layout.tsx: removed SidebarProvider wrapper and CSS overrides
- SessionsSidebar: side="left" → side="right"
- AgentDetailsSidebar: Sidebar → Sheet with open/onClose props
- ChatLayoutUI: added agentDetailsOpen state, Info trigger button,
  flex container wrapping content + sessions sidebar
- 12 new unit tests (6 AgentDetailsSidebar, 6 ChatLayoutUI)
- All 140 tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add MobileTopBar component (SidebarTrigger + KAgent logo) hidden on lg:
breakpoint via Tailwind. Wired into SidebarInset in root layout.
5 new unit tests + 1 layout integration test (146 total pass).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create StatusIndicator component (green dot + text, collapsed: dot + tooltip)
- Wire StatusIndicator + ThemeToggle into AppSidebar footer
- Add aria-label="Main navigation" on Sidebar
- Add role="group" + aria-labelledby on SidebarGroups
- Add aria-current="page" on active SidebarMenuButton
- 4 new StatusIndicator tests + 4 new accessibility tests (154 total pass)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create 7 "Coming Soon" stub pages so all sidebar nav items resolve
without 404: /feed, /workflows, /cronjobs, /kanban, /git,
/admin/org, /admin/gateways. Each page shows the matching lucide
icon, title, and "Coming soon" text. 28 new tests (182 total pass).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Add full attachment and label support across all layers of the kanban-mcp server:

- DB: Attachment model (file/link types), Labels field (StringSlice) on Task,
  AutoMigrate updated to include Attachment table
- Service: AddAttachment, DeleteAttachment methods with validation
  (top-level only, type-specific field requirements), label deduplication,
  cascade delete includes attachments, Preload("Attachments") on GetTask/ListTasks
- MCP: 2 new tools (add_attachment, delete_attachment) bringing total to 12,
  labels support added to create_task, create_subtask, update_task, list_tasks
- REST: POST /api/tasks/:id/attachments, DELETE /api/attachments/:id routes,
  labels support in task CRUD endpoints, label query filter
- Tests: 13 new service tests covering attachment CRUD, validation,
  cascade delete with attachments, label filtering, label deduplication

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Define AgentCronJob CRD in kagent.dev/v1alpha2 with spec fields
(schedule, prompt, agentRef) and status fields (lastRunTime,
nextRunTime, lastRunResult, lastRunMessage, lastSessionID).
Includes generated deepcopy, CRD YAML, Helm chart template,
and example manifest.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements the Kubernetes controller for AgentCronJob CRD that:
- Parses cron schedules using robfig/cron/v3 (5-field standard format)
- Uses RequeueAfter for scheduling (no in-memory scheduler)
- Triggers agent runs via HTTP API (session creation + A2A message)
- Updates status with lastRunTime, nextRunTime, lastRunResult, lastSessionID
- Sets Accepted/Ready conditions for schedule validation feedback
- Handles restart recovery without retroactive execution of missed runs

Includes unit tests for: invalid schedule, not-yet-due scheduling,
agent-not-found, HTTP failure, restart recovery, and cron parsing
table-driven tests (10 cases).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add REST API at /api/cronjobs for managing AgentCronJob CRs:
- GET /api/cronjobs (list all)
- GET /api/cronjobs/{namespace}/{name} (get one)
- POST /api/cronjobs (create)
- PUT /api/cronjobs/{namespace}/{name} (update)
- DELETE /api/cronjobs/{namespace}/{name} (delete)

Follows existing agent handler patterns with K8s client proxy,
auth middleware, and StandardResponse format. Includes 13 unit
tests covering all CRUD operations and error cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the cronjobs placeholder page with a full list view following
the models page pattern. Includes expandable rows showing schedule,
agent ref, prompt, and status details (last/next run times, result,
session ID). Server actions provide getCronJobs, getCronJob,
createCronJob, updateCronJob, and deleteCronJob with proper error
handling. Delete uses a confirmation dialog.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create /cronjobs/new page with form fields for name, namespace,
schedule (cron expression), agent (dropdown from API), and prompt.
Supports edit mode via ?edit=true&name=X&namespace=Y query params.
Includes field validation and follows existing models/new pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add go/test/e2e/agentcronjob_test.go with 4 test functions covering the
full AgentCronJob lifecycle:

- TestE2EAgentCronJobCRUD: HTTP API CRUD (create, duplicate conflict,
  get, list, update, delete, get-after-delete 404)
- TestE2EAgentCronJobStatusAccepted: valid schedule → Accepted=True,
  NextRunTime populated, Ready=True
- TestE2EAgentCronJobInvalidSchedule: bad cron → Accepted=False with
  InvalidSchedule reason
- TestE2EAgentCronJobInvalidAgentRef: missing agent → LastRunResult=Failed
  after cron tick (120s timeout for */1 schedule)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dimetron and others added 9 commits March 6, 2026 12:20
Add comprehensive E2E tests covering the full Temporal + NATS integration:
- Infrastructure verification (Temporal server + NATS deployments)
- CRD translation (env vars TEMPORAL_HOST_ADDR, NATS_ADDR in agent pods)
- Workflow execution via A2A (sync and streaming invocations)
- Temporal UI plugin proxy routing
- Fallback path (agent without temporal spec uses sync execution)
- Crash recovery (pod deletion + restart + re-invocation)
- Custom timeout/retry policy from TemporalSpec

Tests gated by TEMPORAL_ENABLED=1 env var to skip when Temporal
infrastructure is not deployed. Makefile target: `make -C go e2e-temporal`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The plugin routing had browser URLs and proxy URLs on the same path
(/plugins/), causing nginx to send browser requests directly to the Go
backend instead of Next.js. This broke the sidebar+iframe wrapping.

Now: browser /plugins/{name} -> Next.js (sidebar + iframe shell)
     iframe /_p/{name}/      -> nginx /_p/ -> Go backend -> upstream

Changes:
- nginx.conf: location /plugins/ -> location /_p/
- server.go: PathPrefix("/plugins/{name}") -> PathPrefix("/_p/{name}")
- pluginproxy.go: strip /_p/{name} prefix instead of /plugins/{name}
- page.tsx: iframe src uses /_p/ instead of /plugins/
- Tests and E2E updated to match new paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…gins

- Add pluginsLoading/pluginsError state to AppSidebarNav
- Show Loader2 spinner while /api/plugins fetch is in-flight
- Show error indicator with retry button on fetch failure
- Check response.ok before parsing JSON (was silent .catch)
- Retry button increments fetchKey to re-trigger useEffect
- Add 5 new tests: loading state, hide after load, error on
  network failure, error on HTTP error, retry re-fetches
- Update existing test mocks to include ok: true

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add loading/error/retry states to the plugin iframe page:
- Loader2 spinner with "Loading plugin…" shown while iframe loads
- "Plugin unavailable" error fallback with AlertCircle icon and Retry button
- Retry increments key to force iframe re-creation
- Event listeners attached directly on iframe element (React doesn't
  support onError for iframes via synthetic events)
- 6 unit tests covering all states

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…update

Tests verify reconcilePluginUI correctly:
- Creates plugin with all explicit fields and derives upstream URL
- Falls back to defaults (name→pathPrefix, name→displayName, puzzle→icon, PLUGINS→section)
- Deletes plugin when ui.enabled=false
- Deletes plugin when ui is nil
- Updates plugin metadata on re-reconcile

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dd Makefile test targets

- check-plugins-api.sh: add --wait mode that polls until plugin appears (configurable timeout/interval)
- check-plugins-api.sh: add --proxy flag to verify /_p/{name}/ reverse proxy returns non-404
- Makefile: add test-e2e-plugins, test-e2e-go, test-e2e-all targets under new Testing section

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7 test scenarios covering sidebar plugin items, navigation, hard refresh,
postMessage theme sync, badge updates, loading states, and error/retry.
Uses cy.intercept() to mock /api/plugins and /_p/ proxy responses.
Adds test-e2e-browser Makefile target and mock fixtures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Charlesthebird
Copy link
Contributor

I looked through the proposal, and I like the idea of being able to configure the site. But this plan seems much more complicated than it should be.

I would think:

For iframes in new sidebar items, this needs:

  • A label for the sidebar item label
  • A url for the iframe

For iframes to show up inline in the chat

  • This should be a new artifact type or task type (I forget the exact the term), that renders a custom component (iframe), with the loaded URL.
    • The data in this chat response would need
      • url for the iframe
      • page-height (optional) for the pixel height of the iframe, to render it well, and this could be optional with some default value.

For the postMessage idea, we could a standard set of messages that can be posted to/from the iframe, reused for both the inline chat, and the sidebar page iframes.


What I'd request for this feature is:

To please remove all or almost all backend changes, so that adding support for this includes:

  • A way to configure the UI sidebar pages (could be a JSON object with the properties I mentioned above)
  • A postMessage TypeScript type that defines a shared contract between all custom iframes and the UI.

To add the inline-iframe chat feature in a follow-up PR, since both features are fairly involved and it will be easier to identify what is needed for each feature this way.

Thank you!

dimetron and others added 12 commits March 6, 2026 23:17
…apper

Add Go module at go/plugins/temporal-mcp/ with:
- Config loading (CLI flags + TEMPORAL_* env vars)
- Temporal client wrapper (list, describe, cancel, signal workflows)
- Workflow ID parsing (agent-{name}-{session} pattern)
- Types for WorkflowSummary, WorkflowDetail, ActivityInfo
- Unit tests for config and workflow ID parsing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 4 MCP tools: list_workflows, get_workflow, cancel_workflow, signal_workflow
- REST API: GET /api/workflows, GET /api/workflows/{id}, POST .../cancel, POST .../signal
- SSE hub with Temporal polling at configurable interval, snapshot on connect
- WorkflowClient interface for testability
- All 20+ tests pass with mocked Temporal client

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…mcp plugin

- internal/ui/embed.go + index.html: single-file SPA with workflow list,
  status filters, expandable detail panel with activities, cancel/signal
  actions, SSE live updates, dark/light theme via kagent bridge
- server.go: HTTP mux wiring /mcp, /events, /api/workflows, /api/workflows/*, /
- main.go: entry point with Temporal client, SSE hub, stdio/http transport
- Tests: embed_test.go (embed + handler), server_test.go (UI, API, MCP, SSE)
- All 7 packages pass (30+ tests total), binary builds cleanly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create helm/tools/temporal-mcp/ chart (deployment, service, configmap,
  RemoteMCPServer CRD with section=PLUGINS, pathPrefix=temporal-workflows)
- Update stock Temporal UI CRD: section PLUGINS→AGENTS, displayName→Workflows
- Remove hardcoded Workflows nav entry from AppSidebarNav (now dynamic via CRD)
- Delete stub ui/src/app/workflows/page.tsx
- Update tests to reflect 11 static nav items (was 12)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- temporal workflow ui
- git repo search - rfx
- fix cron jobs
- dashboard

Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
…nJob

- Add PVC template for persistent storage of cloned repos and SQLite DB
- Add liveness/readiness probes targeting /health endpoint
- Add optional CronJob template for periodic repo sync (disabled by default)
- Set proper default config values (GITREPO_ADDR, GITREPO_DATA_DIR)
- Default args to run `serve` subcommand
- Mount PVC at /data when persistence is enabled

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 10 tests across 4 suites covering page loading, error states,
form validation, and navigation. All tests pass without a backend.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add search bar with client-side filtering by server name and tool
names/descriptions. Servers whose tools match auto-expand. Matching
text is highlighted. Server list wrapped in ScrollArea for viewport-
filling layout. Empty state shown when search yields no results.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- temporal workflow ui
- git repo search - rfx
- fix cron jobs
- dashboard

Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
@github-actions github-actions bot added the enhancement-proposal Indicates that this PR is for an enhancement proposal label Mar 8, 2026
Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
@dimetron
Copy link
Contributor Author

dimetron commented Mar 8, 2026

Thanks @Charlesthebird - I think this is the direction it goes
see design/EP-2004-dynamic-mcp-ui-routing.md

Implementation Details

  • Discovery: GET /api/plugins returns registered plugins for sidebar
  • UI: ui/src/app/plugins/[name]/[[...path]]/page.tsx — iframe host with postMessage bridge

Inspired by MCP-APP implementation https://modelcontextprotocol.io/extensions/apps/overview

dimetron added 5 commits March 9, 2026 17:38
Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement-proposal Indicates that this PR is for an enhancement proposal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants