Sitelet https://github.com/simstudioai/sim/pull/7070
Skip to content

feat(mothership): add editable skills and tools to the resource panel - #7070

Open
BillLeoutsakosvl346 wants to merge 7 commits into
stagingfrom
feat/mothership-resource-panel-tools
Open

feat(mothership): add editable skills and tools to the resource panel#7070
BillLeoutsakosvl346 wants to merge 7 commits into
stagingfrom
feat/mothership-resource-panel-tools

Conversation

@BillLeoutsakosvl346

@BillLeoutsakosvl346 BillLeoutsakosvl346 commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds Skills, Custom Tools, and connected MCP servers to Sim Chat's persisted resource panel. Canonical resource persistence now drives attachment validation, while the existing chat lifecycle remains the single owner of resource add, remove, and reorder operations. The panel reuses the existing editors, routes, permissions, and query hooks; embedded drafts are protected across panel transitions, browser navigation, supported workspace navigation, and active-chat deletion.

Fixes #N/A

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

  • Ran the complete root test suite with bun run test after rebasing onto the latest staging (19 workspace tasks; 2,305 app test files and 32,533 app tests passed).
  • Ran bun run lint:check, all-workspace type-check, the relevant React Query, client-boundary, and import audits, the production app build, and git diff --check.
  • Verified the resource picker, persisted tabs and deep links, existing full-editor routes, Skill and Custom Tool saves, built-in Skill read-only behavior, MCP discovered tools and editing, Skill/MCP chat-context focus, panel-only composer exclusion, guarded browser Back and command-palette navigation, dirty MCP behavior, first-message URL replacement, and guarded active-chat deletion in the local app. Deleted all disposable browser fixtures afterward.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

Evidence is attached in the first PR comment

@vercel

vercel Bot commented Aug 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 25, 2026 10:08pm

Request Review

@BillLeoutsakosvl346

Copy link
Copy Markdown
Contributor Author

Resource panel evidence

Panel picker — Skills, Custom Tools, and MCP Servers
01-picker

Editable Skill
02-skill-editable

Built-in Skill remains read-only
03-skill-builtin-readonly

Custom Tool editor
04-custom-tool

MCP server with discovered child tools
05-mcp-server-tools

Mothership edit with the affected MCP tab focused
06-mothership-focus

@BillLeoutsakosvl346
BillLeoutsakosvl346 force-pushed the feat/mothership-resource-panel-tools branch from 078dc25 to a66a2fd Compare August 25, 2026 22:03
@BillLeoutsakosvl346 BillLeoutsakosvl346 changed the title feat(mothership): add skills and tools to the resource panel feat(mothership): add editable skills and tools to the resource panel Aug 25, 2026
@BillLeoutsakosvl346
BillLeoutsakosvl346 marked this pull request as ready for review August 25, 2026 22:05
@BillLeoutsakosvl346

Copy link
Copy Markdown
Contributor Author

@cursor bugbot
@greptile

@cursor

cursor Bot commented Aug 25, 2026

Copy link
Copy Markdown

You need to increase your spend limit or enable usage-based billing to run background agents. Go to Cursor

@greptile-apps

greptile-apps Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds persisted, editable Skill, Custom Tool, and MCP server resources to Sim Chat’s resource panel while centralizing resource mutations and unsaved-draft navigation protection.

  • Extends resource discovery, rendering, naming, persistence, extraction, and management-tool handling for the three new resource families.
  • Reuses existing editors and permission checks in embedded panel views, with dirty-state reporting and deletion handling.
  • Adds a shared transition guard covering tab operations, browser navigation, workspace navigation, and active-chat lifecycle changes.
  • Keeps panel-only resources out of prompt attachments while allowing them to persist with chats.

Confidence Score: 5/5

The PR appears safe to merge; no concrete blocking or independently actionable non-blocking defects were identified.

The new resource families are handled consistently across discovery, persistence, rendering, permissions, prompt exclusion, and management extraction, while dirty embedded drafts are protected through centralized transition and lifecycle handling.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/home/hooks/use-resource-transition-guard.ts Introduces centralized dirty-resource tracking, history-sentinel handling, navigation interception, and deferred transition replay.
apps/sim/app/workspace/[workspaceId]/home/home.tsx Moves resource persistence ownership into the chat lifecycle and integrates the shared transition guard with resource and chat state.
apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx Embeds Skill, Custom Tool, and MCP editors with existing permission checks, dirty reporting, deletion handling, and full-editor actions.
apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx Adds live names for the new resource types and routes tab additions, selections, and closures through centralized ownership and transition protection.
apps/sim/lib/copilot/resources/types.ts Extends the canonical resource policy so Skills, Custom Tools, and MCP servers are persisted and sanitized consistently.
apps/sim/lib/copilot/resources/extraction.ts Extends management-tool result extraction to add and remove the new persisted panel resources.
apps/sim/app/workspace/[workspaceId]/skills/[skillId]/skill-detail.tsx Makes the Skill editor reusable in the panel while preserving drafts across background data updates and reporting dirty state.
apps/sim/app/workspace/[workspaceId]/settings/components/custom-tools/components/custom-tool-detail/custom-tool-detail.tsx Adds embedded operation, cache-aware draft baselines, host-owned navigation protection, and deletion callbacks to the Custom Tool editor.
apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx Supports directly opening an MCP server in the embedded settings surface and forwarding modal dirty state to the panel guard.
apps/sim/lib/copilot/chat/post.ts Uses canonical resource persistence during first-message chat creation so the persisted panel state drives subsequent validation.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Picker[Resource picker or management tool] --> Canonical[Canonical resource policy and sanitization]
  Canonical --> ChatState[Chat-owned resource state]
  ChatState --> Persistence[Persisted chat resources]
  ChatState --> Panel[Resource panel tabs]
  Panel --> Skill[Embedded Skill editor]
  Panel --> Tool[Embedded Custom Tool editor]
  Panel --> MCP[Embedded MCP server editor]
  Skill --> Dirty[Shared dirty-resource guard]
  Tool --> Dirty
  MCP --> Dirty
  Dirty -->|clean| Transition[Add, remove, reorder, navigate]
  Dirty -->|dirty| Confirm[Discard confirmation]
  Confirm -->|discard| Transition
  Confirm -->|cancel| Panel
  Panel --> ContextMap[Prompt context mapping]
  ContextMap -->|panel-only types| Excluded[No prompt attachment]
Loading

Reviews (2): Last reviewed commit: "fix(mothership): use canonical sentinel ..." | Re-trigger Greptile

Comment thread apps/sim/app/workspace/[workspaceId]/home/hooks/use-resource-transition-guard.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found and verified against the latest diff

Confidence score: 2/5

  • use-resource-transition-guard.ts and home.tsx can let Back navigation or agent focus bypass a dirty editor, potentially losing unsaved work; preserve the sentinel before routing and pass the effective active resource ID to the guard.
  • custom-tool-detail.tsx and skill-detail.tsx treat optimistic rollback as a clean server update, erasing edits and clearing the dirty guard after a failed save; ignore optimistic update/rollback transitions when reseeding drafts.
  • add-resource-dropdown.tsx can show stale placeholder rows from the previous workspace while hydration reports complete, leading users to select incorrect resources; track placeholder state for every query or suppress those results during workspace changes.
  • Lower-severity fallback issues remain in resource-tabs.tsx, mcp.tsx, and post.ts: empty server names can blank tabs, transient refetch errors can hide resolvable details, and Integration can block later attachment titles; apply the shared name fallback, gate errors on unresolved IDs, and include Integration in the placeholder set.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/app/workspace/[workspaceId]/settings/components/custom-tools/components/custom-tool-detail/custom-tool-detail.tsx">

<violation number="1" location="apps/sim/app/workspace/[workspaceId]/settings/components/custom-tools/components/custom-tool-detail/custom-tool-detail.tsx:114">
P2: When an update fails after the optimistic cache update, this treats the optimistic tool as the new baseline. The rollback then replaces the user's edits with the old server value and clears the embedded dirty guard; compare the draft with `previousToolSource` before reseeding.</violation>
</file>

<file name="apps/sim/app/workspace/[workspaceId]/home/hooks/use-resource-transition-guard.ts">

<violation number="1" location="apps/sim/app/workspace/[workspaceId]/home/hooks/use-resource-transition-guard.ts:57">
P1: When first-message routing replaces the sentinel while a draft is still dirty, browser Back has already reached the previous home entry before this handler re-seeds a sentinel. Confirming discard then calls `history.back()` again and skips that destination; preserve whether the original pop already performed the requested navigation and avoid replaying Back in that case.</violation>
</file>

<file name="apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx">

<violation number="1" location="apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx:180">
P2: When an MCP server has an empty name, this lookup replaces the resource's existing title with an empty string, so its tab renders blank. Use the same `Unnamed server` fallback as the resource picker.</violation>
</file>

<file name="apps/sim/app/workspace/[workspaceId]/skills/[skillId]/skill-detail.tsx">

<violation number="1" location="apps/sim/app/workspace/[workspaceId]/skills/[skillId]/skill-detail.tsx:126">
P2: When an update fails after the optimistic cache update, the rollback is treated as a clean server change and `seedDrafts(skill)` erases the edits the user needs to retry. Ignore optimistic update/rollback transitions when synchronizing drafts so a failed save preserves the local values.</violation>
</file>

<file name="apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx">

<violation number="1" location="apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx:457">
P2: When a background server-list refetch fails after cached data loaded, this branch hides a still-resolvable MCP server detail. Show the load error only when `serverId` cannot be resolved from `servers`.</violation>
</file>

<file name="apps/sim/lib/copilot/chat/post.ts">

<violation number="1" location="apps/sim/lib/copilot/chat/post.ts:123">
P2: When an integration attachment omits `title`, this fallback stores `Integration`, but later specific titles cannot replace it because the shared placeholder set does not recognize that value. Add `Integration` to `GENERIC_RESOURCE_TITLES`.</violation>
</file>

<file name="apps/sim/app/workspace/[workspaceId]/home/home.tsx">

<violation number="1" location="apps/sim/app/workspace/[workspaceId]/home/home.tsx:271">
P1: When the visible tab is the fallback resource after the URL selection is cleared, this guard sees no active resource and allows agent focus to switch away from a dirty editor. Pass the effective active resource ID to the automatic-focus guard, or keep the URL-selection ref synchronized with the rendered fallback.</violation>
</file>

<file name="apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx">

<violation number="1" location="apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx:189">
P2: When the picker remains mounted across a workspace switch, these hooks serve the previous workspace's placeholder rows while `isHydrating` stays false. Track `isPlaceholderData` for all three queries or suppress placeholder rows before allowing selection, otherwise the panel can attach a resource from the prior workspace.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic


const retireHistorySentinel = useCallback((afterRetirement?: () => void) => {
const sentinel = historySentinelRef.current
historySentinelRef.current = null

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: When first-message routing replaces the sentinel while a draft is still dirty, browser Back has already reached the previous home entry before this handler re-seeds a sentinel. Confirming discard then calls history.back() again and skips that destination; preserve whether the original pop already performed the requested navigation and avoid replaying Back in that case.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/app/workspace/[workspaceId]/home/hooks/use-resource-transition-guard.ts, line 57:

<comment>When first-message routing replaces the sentinel while a draft is still dirty, browser Back has already reached the previous home entry before this handler re-seeds a sentinel. Confirming discard then calls `history.back()` again and skips that destination; preserve whether the original pop already performed the requested navigation and avoid replaying Back in that case.</comment>

<file context>
@@ -0,0 +1,220 @@
+
+  const retireHistorySentinel = useCallback((afterRetirement?: () => void) => {
+    const sentinel = historySentinelRef.current
+    historySentinelRef.current = null
+    const currentState = window.history.state
+    const ownsCurrentEntry =
</file context>

markAttention()
return
}
routeAutomaticResourceFocus(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: When the visible tab is the fallback resource after the URL selection is cleared, this guard sees no active resource and allows agent focus to switch away from a dirty editor. Pass the effective active resource ID to the automatic-focus guard, or keep the URL-selection ref synchronized with the rendered fallback.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/app/workspace/[workspaceId]/home/home.tsx, line 271:

<comment>When the visible tab is the fallback resource after the URL selection is cleared, this guard sees no active resource and allows agent focus to switch away from a dirty editor. Pass the effective active resource ID to the automatic-focus guard, or keep the URL-selection ref synchronized with the rendered fallback.</comment>

<file context>
@@ -220,20 +261,30 @@ export function Home({ chatId, userName, userId, tableViewsEnabled }: HomeProps)
+      markAttention()
+      return
     }
+    routeAutomaticResourceFocus(
+      activeResourceId,
+      resourceId,
</file context>

previousToolSource.code !== nextSource.code)

if (switchedTool || sourceChanged) {
const hadLocalDraft = jsonSchema !== seededSchema || functionCode !== seededCode

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When an update fails after the optimistic cache update, this treats the optimistic tool as the new baseline. The rollback then replaces the user's edits with the old server value and clears the embedded dirty guard; compare the draft with previousToolSource before reseeding.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/app/workspace/[workspaceId]/settings/components/custom-tools/components/custom-tool-detail/custom-tool-detail.tsx, line 114:

<comment>When an update fails after the optimistic cache update, this treats the optimistic tool as the new baseline. The rollback then replaces the user's edits with the old server value and clears the embedded dirty guard; compare the draft with `previousToolSource` before reseeding.</comment>

<file context>
@@ -75,10 +84,46 @@ export function CustomToolDetail({
+        previousToolSource.code !== nextSource.code)
+
+    if (switchedTool || sourceChanged) {
+      const hadLocalDraft = jsonSchema !== seededSchema || functionCode !== seededCode
+      setPreviousToolSource(nextSource)
+      setSeededSchema(nextSource.schema)
</file context>
Suggested change
const hadLocalDraft = jsonSchema !== seededSchema || functionCode !== seededCode
const hadLocalDraft =
previousToolSource !== null &&
(jsonSchema !== previousToolSource.schema || functionCode !== previousToolSource.code)

const skillsEnabled = enabled && !excludeTypes?.includes('skill')
const customToolsEnabled = enabled && !excludeTypes?.includes('custom_tool')
const mcpServersEnabled = enabled && !excludeTypes?.includes('mcp_server')
const { data: skills, isPending: skillsPending } = useSkills(workspaceId, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When the picker remains mounted across a workspace switch, these hooks serve the previous workspace's placeholder rows while isHydrating stays false. Track isPlaceholderData for all three queries or suppress placeholder rows before allowing selection, otherwise the panel can attach a resource from the prior workspace.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx, line 189:

<comment>When the picker remains mounted across a workspace switch, these hooks serve the previous workspace's placeholder rows while `isHydrating` stays false. Track `isPlaceholderData` for all three queries or suppress placeholder rows before allowing selection, otherwise the panel can attach a resource from the prior workspace.</comment>

<file context>
@@ -180,6 +183,18 @@ export function useAvailableResources(
+  const skillsEnabled = enabled && !excludeTypes?.includes('skill')
+  const customToolsEnabled = enabled && !excludeTypes?.includes('custom_tool')
+  const mcpServersEnabled = enabled && !excludeTypes?.includes('mcp_server')
+  const { data: skills, isPending: skillsPending } = useSkills(workspaceId, {
+    enabled: skillsEnabled,
+  })
</file context>

for (const folder of folders ?? []) map.set(`folder:${folder.id}`, folder.name)
for (const skill of skills ?? []) map.set(`skill:${skill.id}`, skill.name)
for (const tool of customTools ?? []) map.set(`custom_tool:${tool.id}`, tool.title)
for (const server of mcpServers ?? []) map.set(`mcp_server:${server.id}`, server.name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When an MCP server has an empty name, this lookup replaces the resource's existing title with an empty string, so its tab renders blank. Use the same Unnamed server fallback as the resource picker.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx, line 180:

<comment>When an MCP server has an empty name, this lookup replaces the resource's existing title with an empty string, so its tab renders blank. Use the same `Unnamed server` fallback as the resource picker.</comment>

<file context>
@@ -141,36 +138,48 @@ const PREVIEW_MODE_LABELS: Record<PreviewMode, string> = {
     for (const folder of folders ?? []) map.set(`folder:${folder.id}`, folder.name)
+    for (const skill of skills ?? []) map.set(`skill:${skill.id}`, skill.name)
+    for (const tool of customTools ?? []) map.set(`custom_tool:${tool.id}`, tool.title)
+    for (const server of mcpServers ?? []) map.set(`mcp_server:${server.id}`, server.name)
     return map
-  }, [enabled, workflows, tables, files, knowledgeBases, folders])
</file context>
Suggested change
for (const server of mcpServers ?? []) map.set(`mcp_server:${server.id}`, server.name)
for (const server of mcpServers ?? []) {
map.set(`mcp_server:${server.id}`, server.name || 'Unnamed server')
}

descriptionDraft === previousSkillSource.description &&
contentDraft === previousSkillSource.content)
setPreviousSkillSource(nextSource)
if (shouldReseed) seedDrafts(skill)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When an update fails after the optimistic cache update, the rollback is treated as a clean server change and seedDrafts(skill) erases the edits the user needs to retry. Ignore optimistic update/rollback transitions when synchronizing drafts so a failed save preserves the local values.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/app/workspace/[workspaceId]/skills/[skillId]/skill-detail.tsx, line 126:

<comment>When an update fails after the optimistic cache update, the rollback is treated as a clean server change and `seedDrafts(skill)` erases the edits the user needs to retry. Ignore optimistic update/rollback transitions when synchronizing drafts so a failed save preserves the local values.</comment>

<file context>
@@ -82,11 +98,33 @@ export function SkillDetail({ workspaceId, skillId }: SkillDetailProps) {
+          descriptionDraft === previousSkillSource.description &&
+          contentDraft === previousSkillSource.content)
+      setPreviousSkillSource(nextSource)
+      if (shouldReseed) seedDrafts(skill)
+    }
   }
</file context>

)
}

if (serverId && serversError) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When a background server-list refetch fails after cached data loaded, this branch hides a still-resolvable MCP server detail. Show the load error only when serverId cannot be resolved from servers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx, line 457:

<comment>When a background server-list refetch fails after cached data loaded, this branch hides a still-resolvable MCP server detail. Show the load error only when `serverId` cannot be resolved from `servers`.</comment>

<file context>
@@ -425,6 +443,30 @@ export function MCP() {
+    )
+  }
+
+  if (serverId && serversError) {
+    return (
+      <SettingsPanel
</file context>
Suggested change
if (serverId && serversError) {
if (serverId && serversError && !selectedServer) {

filefolder: 'File Folder',
task: 'Task',
log: 'Log',
integration: 'Integration',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When an integration attachment omits title, this fallback stores Integration, but later specific titles cannot replace it because the shared placeholder set does not recognize that value. Add Integration to GENERIC_RESOURCE_TITLES.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/lib/copilot/chat/post.ts, line 123:

<comment>When an integration attachment omits `title`, this fallback stores `Integration`, but later specific titles cannot replace it because the shared placeholder set does not recognize that value. Add `Integration` to `GENERIC_RESOURCE_TITLES`.</comment>

<file context>
@@ -134,6 +120,10 @@ const GENERIC_RESOURCE_TITLE: Record<z.infer<typeof ResourceAttachmentSchema>['t
   filefolder: 'File Folder',
   task: 'Task',
   log: 'Log',
+  integration: 'Integration',
+  skill: 'Skill',
+  custom_tool: 'Custom Tool',
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant