Compact discovery prompt
apps/backend/config/prompts/kandev-context.md ↗The template now marks the list as selected guidance and tells agents how to discover missing tools.
Template header
KANDEV MCP TOOLS — Selected tools from the "kandev" server are available here.
Names ending in `_kandev` are canonical MCP protocol names. A client-specific alias may be server-qualified; use the exact callable name and schema exposed by the client.
Discovery section
MCP DISCOVERY:
These instructions list selected Kandev tools, not the complete MCP catalog.
For Kandev operations, use tools exposed by the "kandev" server. If the needed tool is already callable, use its current schema; otherwise use your client's native tool search or discovery when available.
Search for "kandev" plus the operation or known canonical tool name. If search is unavailable, inspect the MCP tools available in your client.
Use the exact callable name and schema exposed by the client. An omitted entry here does not mean that the tool is unavailable. If discovery cannot find a required tool, report that limitation before substituting another result.
Essential workflow
ESSENTIAL WORKFLOW:
Preserve task/session identity and the system marker, question barriers, title ownership, completion gates, autopilot behavior, delegation boundaries, final-action rules, and user edits in task plans. Use create/get/update plan tools. For data requests that need a chart, preview, or metric, call show_rich_output_kandev with the schema returned by discovery.
Capability-gated sysprompt
apps/backend/internal/sysprompt/sysprompt.go ↗Canvas guidance is now an explicit option that the caller sets from the resolved MCP profile.
Canvas guidance constant
const canvasGuidanceSection = `CANVAS AUTHORING:
Use these tools only when the user explicitly asks for a Kandev canvas: create_canvas_kandev, read_canvas_authoring_skill_kandev, and publish_canvas_kandev. Create it in Kandev before writing app files. Read the skill once, edit only in its returned source directory, then publish through MCP. Report publication status, including failures. Local files or a successful build do not publish a canvas.
`
Compact rich output
const richOutputSection = `- show_rich_output_kandev: For a chart, graph, plot, file preview, KPI, or metrics request with data, call this now. Do not implement it as ASCII, SVG, HTML, or another app. Otherwise use prose or a small Markdown table. Get the schema and examples from tool discovery. Paths are workspace-relative; Kandev owns layout, axes, legends, and tooltips. Label series with units.
`
Options struct
type KandevContextOptions struct {
RequiresCompletionSignal bool
IncludeCoordinatorTaskControls bool
IncludeTaskTitleTool bool
IncludeCanvasGuidance bool
Autopilot bool
IncludeUserQuestionTool bool
IncludeParentQuestionTool bool
}
Conditional injection
canvasGuidance := ""
if options.IncludeCanvasGuidance {
canvasGuidance = canvasGuidanceSection
}
return Resolve("kandev-context", map[string]string{
"task_id": taskID,
"session_id": sessionID,
"step_complete_section": section,
"task_title_section": taskTitle,
"coordinator_task_control_section": coordinatorControls,
"canvas_guidance_section": canvasGuidance,
"rich_output_section": richOutputSection,
"autopilot_section": autopilot,
"question_tool_section": questionTool,
})
MCP profile owns canvas capability
apps/backend/internal/orchestrator/executor/executor_execute.go ↗The executor adds CapabilityCanvas only for kanban tasks when the global canvases flag is on, and exposes the profile for prompt producers.
Profile resolver
func (e *Executor) resolveTaskSessionMCPProfile(ctx context.Context, taskID string, session *models.TaskSession, allowTitleTool bool) (mcpprofile.Context, error) {
if isConfigModeSession(session) {
capabilities := []mcpprofile.Capability{mcpprofile.CapabilityUserQuestion}
if session.IsPassthrough {
capabilities = nil
}
return e.withCanvasCapability(mcpprofile.New(mcpprofile.SurfaceConfiguration, capabilities, nil)), nil
}
// ... kanban / office / automation branches ...
return e.withCanvasCapability(mcpprofile.New(surface, capabilities, nil)), nil
}
Canvas gate
func (e *Executor) withCanvasCapability(profile mcpprofile.Context) mcpprofile.Context {
if e != nil && e.canvasesEnabled && profile.Surface == mcpprofile.SurfaceKanbanTask {
return profile.WithCapability(mcpprofile.CapabilityCanvas)
}
return profile
}
Read-only query
func (e *Executor) ResolveTaskSessionMCPProfile(ctx context.Context, taskID string, session *models.TaskSession, allowTitleTool bool) (mcpprofile.Context, error) {
return e.resolveTaskSessionMCPProfile(ctx, taskID, session, allowTitleTool)
}
Prompt producers follow the profile
apps/backend/internal/orchestrator/task_operations.go ↗Every launch path resolves canvas guidance from the same profile and passes the same value to both the stored and dispatched prompt.
Created session launch
includeCanvasGuidance := false
if (effectivePrompt != "" || len(attachments) > 0) && !isOfficeTask && !session.IsPassthrough && !configMode {
if options.canvasGuidanceResolved {
includeCanvasGuidance = options.includeCanvasGuidance
} else {
includeCanvasGuidance, err = s.taskSessionCanvasGuidanceEnabled(ctx, taskID, session, true)
if err != nil {
return nil, fmt.Errorf("failed to resolve canvas prompt capability: %w", err)
}
}
}
if effectivePrompt != "" || len(attachments) > 0 {
effectivePrompt = s.wrapCreatedSessionPrompt(ctx, effectivePrompt, taskID, sessionID, session, dbTask, isOfficeTask, configMode, titleOwner, includeCanvasGuidance, references, promptReferenceContext)
}
Workflow auto-start
func (s *Service) resolveAutoStartPromptContext(ctx context.Context, taskID string, session *models.TaskSession) (bool, *models.Task, bool, bool, error) {
// ... office / task load ...
includeCanvasGuidance, err = s.taskSessionCanvasGuidanceEnabled(ctx, taskID, session, true)
// ... return includeCanvasGuidance as fourth value ...
}
Injection
return sysprompt.InjectKandevContextWithOptions(taskID, sessionID, prompt, sysprompt.KandevContextOptions{
RequiresCompletionSignal: s.WorkflowStepRequiresCompletionSignal(ctx, dbTask.WorkflowStepID),
IncludeCoordinatorTaskControls: !configMode,
IncludeTaskTitleTool: !configMode && titleOwner,
IncludeCanvasGuidance: includeCanvasGuidance,
Autopilot: dbTask.Autopilot,
IncludeUserQuestionTool: !dbTask.Autopilot && !session.IsPassthrough,
IncludeParentQuestionTool: dbTask.Autopilot && dbTask.ParentID != "",
}, referenceContext, promptReferenceContext, pullRequestTargetContext)
Direct message preserves the decision
apps/backend/internal/task/handlers/message_handlers.go ↗The WebSocket handler resolves canvas guidance once at admission and carries the projection to the launch, so the DB row and the agent prompt agree.
Resolve at admission
func (h *MessageHandlers) resolveCanvasGuidance(ctx context.Context, taskID, sessionID string) (bool, error) {
resolver, ok := h.orchestrator.(taskCanvasGuidanceResolver)
if !ok {
return false, nil
}
return resolver.TaskSessionCanvasGuidanceEnabled(ctx, taskID, sessionID)
}
Admission path
includeCanvasGuidance := false
canvasGuidanceResolved := false
if task != nil && !task.IsFromOffice && !sessionResp.Session.IsPassthrough && !configMode {
canvasGuidanceResolved = true
includeCanvasGuidance, resolveErr = h.resolveCanvasGuidance(ctx, req.TaskID, req.TaskSessionID)
}
storedContent = h.injectMessageContext(ctx, req, sessionResp, task, configMode, startCreatedSession, titleOwner, includeCanvasGuidance, storedContent, trustedPromptContext)
req.canvasGuidanceResolved = canvasGuidanceResolved
req.includeCanvasGuidance = includeCanvasGuidance
Forward with projection
if starter, ok := h.orchestrator.(orchestrator.DirectPromptStarterWithCanvasGuidance); ok && len(canvasGuidance) > 0 {
_, err = starter.StartCreatedSessionWithPromptContextAndCanvasGuidance(ctx, taskID, sessionID, agentProfileID, content, true, planMode, false, attachments, references, trustedPromptContext, projection.resolved, projection.include)
}
Localized canvas creation preset
apps/web/src/locales/en/canvases.json ↗The preset now teaches discovery and the full create-read-publish workflow with exact tool names in every locale.
Preset prompt
"createCanvasTaskPrompt": "Create an interactive canvas inside Kandev for the application I describe. If the application goal is missing, ask what the canvas must show or do. Find the Kandev canvas MCP tools before writing application files. If they are not callable, use native tool search for \"kandev canvas\" or inspect the available MCP catalog. Create the draft in Kandev before writing application files. Call `create_canvas_kandev` to create the draft and obtain its source directory. Read `read_canvas_authoring_skill_kandev` once without a path. Build inside the returned directory and use authorized live Kandev data for domain views. Call `publish_canvas_kandev` and address any validation errors. Report the canvas identity and whether its release is active, awaits permission review, or was unsuccessful. If publication is unsuccessful, report the failure and do not claim that the canvas is published. If workspace access requires promotion, explain the user action that is still required. A local build alone does not publish a canvas inside Kandev. If the tools remain unavailable, report the limitation instead of claiming that workspace files are a Kandev canvas."