@myrialabs/clopen 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +6 -0
- package/.github/workflows/release.yml +60 -0
- package/.github/workflows/test.yml +40 -0
- package/CONTRIBUTING.md +499 -0
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/backend/index.ts +156 -0
- package/backend/lib/chat/helpers.ts +42 -0
- package/backend/lib/chat/index.ts +2 -0
- package/backend/lib/chat/stream-manager.ts +1126 -0
- package/backend/lib/database/README.md +77 -0
- package/backend/lib/database/index.ts +119 -0
- package/backend/lib/database/migrations/001_create_projects_table.ts +31 -0
- package/backend/lib/database/migrations/002_create_chat_sessions_table.ts +33 -0
- package/backend/lib/database/migrations/003_create_messages_table.ts +32 -0
- package/backend/lib/database/migrations/004_create_prompt_templates_table.ts +34 -0
- package/backend/lib/database/migrations/005_create_settings_table.ts +24 -0
- package/backend/lib/database/migrations/006_add_user_to_messages.ts +58 -0
- package/backend/lib/database/migrations/007_create_stream_states_table.ts +41 -0
- package/backend/lib/database/migrations/008_create_message_snapshots_table.ts +62 -0
- package/backend/lib/database/migrations/009_add_delta_snapshot_fields.ts +41 -0
- package/backend/lib/database/migrations/010_add_soft_delete_and_branch_support.ts +70 -0
- package/backend/lib/database/migrations/011_git_like_commit_graph.ts +156 -0
- package/backend/lib/database/migrations/012_add_file_change_statistics.ts +41 -0
- package/backend/lib/database/migrations/013_checkpoint_tree_state.ts +118 -0
- package/backend/lib/database/migrations/014_add_engine_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/015_add_model_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/016_create_user_projects_table.ts +34 -0
- package/backend/lib/database/migrations/017_add_current_session_to_user_projects.ts +32 -0
- package/backend/lib/database/migrations/018_create_claude_accounts_table.ts +24 -0
- package/backend/lib/database/migrations/019_add_claude_account_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/020_add_snapshot_tree_hash.ts +32 -0
- package/backend/lib/database/migrations/021_drop_prompt_templates_table.ts +33 -0
- package/backend/lib/database/migrations/index.ts +154 -0
- package/backend/lib/database/queries/checkpoint-queries.ts +87 -0
- package/backend/lib/database/queries/engine-queries.ts +75 -0
- package/backend/lib/database/queries/index.ts +9 -0
- package/backend/lib/database/queries/message-queries.ts +472 -0
- package/backend/lib/database/queries/project-queries.ts +117 -0
- package/backend/lib/database/queries/session-queries.ts +271 -0
- package/backend/lib/database/queries/settings-queries.ts +34 -0
- package/backend/lib/database/queries/snapshot-queries.ts +326 -0
- package/backend/lib/database/queries/utils-queries.ts +59 -0
- package/backend/lib/database/seeders/index.ts +13 -0
- package/backend/lib/database/seeders/settings_seeder.ts +84 -0
- package/backend/lib/database/utils/connection.ts +174 -0
- package/backend/lib/database/utils/index.ts +4 -0
- package/backend/lib/database/utils/migration-runner.ts +118 -0
- package/backend/lib/database/utils/seeder-runner.ts +121 -0
- package/backend/lib/engine/adapters/claude/environment.ts +164 -0
- package/backend/lib/engine/adapters/claude/error-handler.ts +60 -0
- package/backend/lib/engine/adapters/claude/index.ts +1 -0
- package/backend/lib/engine/adapters/claude/path-utils.ts +38 -0
- package/backend/lib/engine/adapters/claude/stream.ts +177 -0
- package/backend/lib/engine/adapters/opencode/index.ts +2 -0
- package/backend/lib/engine/adapters/opencode/message-converter.ts +862 -0
- package/backend/lib/engine/adapters/opencode/server.ts +104 -0
- package/backend/lib/engine/adapters/opencode/stream.ts +755 -0
- package/backend/lib/engine/index.ts +196 -0
- package/backend/lib/engine/types.ts +58 -0
- package/backend/lib/files/file-operations.ts +478 -0
- package/backend/lib/files/file-reading.ts +308 -0
- package/backend/lib/files/file-watcher.ts +383 -0
- package/backend/lib/files/path-browsing.ts +382 -0
- package/backend/lib/git/git-executor.ts +88 -0
- package/backend/lib/git/git-parser.ts +411 -0
- package/backend/lib/git/git-service.ts +505 -0
- package/backend/lib/mcp/README.md +1144 -0
- package/backend/lib/mcp/config.ts +316 -0
- package/backend/lib/mcp/index.ts +35 -0
- package/backend/lib/mcp/project-context.ts +236 -0
- package/backend/lib/mcp/servers/browser-automation/actions.ts +156 -0
- package/backend/lib/mcp/servers/browser-automation/browser.ts +419 -0
- package/backend/lib/mcp/servers/browser-automation/index.ts +791 -0
- package/backend/lib/mcp/servers/browser-automation/inspection.ts +501 -0
- package/backend/lib/mcp/servers/helper.ts +143 -0
- package/backend/lib/mcp/servers/index.ts +45 -0
- package/backend/lib/mcp/servers/weather/get-temperature.ts +56 -0
- package/backend/lib/mcp/servers/weather/index.ts +31 -0
- package/backend/lib/mcp/stdio-server.ts +103 -0
- package/backend/lib/mcp/types.ts +65 -0
- package/backend/lib/preview/browser/browser-audio-capture.ts +86 -0
- package/backend/lib/preview/browser/browser-console-manager.ts +263 -0
- package/backend/lib/preview/browser/browser-dialog-handler.ts +222 -0
- package/backend/lib/preview/browser/browser-interaction-handler.ts +421 -0
- package/backend/lib/preview/browser/browser-mcp-control.ts +415 -0
- package/backend/lib/preview/browser/browser-native-ui-handler.ts +512 -0
- package/backend/lib/preview/browser/browser-navigation-tracker.ts +104 -0
- package/backend/lib/preview/browser/browser-pool.ts +357 -0
- package/backend/lib/preview/browser/browser-preview-service.ts +882 -0
- package/backend/lib/preview/browser/browser-tab-manager.ts +935 -0
- package/backend/lib/preview/browser/browser-video-capture.ts +695 -0
- package/backend/lib/preview/browser/scripts/audio-stream.ts +292 -0
- package/backend/lib/preview/browser/scripts/cursor-tracking.ts +85 -0
- package/backend/lib/preview/browser/scripts/video-stream.ts +438 -0
- package/backend/lib/preview/browser/types.ts +359 -0
- package/backend/lib/preview/index.ts +24 -0
- package/backend/lib/project/index.ts +2 -0
- package/backend/lib/project/status-manager.ts +182 -0
- package/backend/lib/shared/index.ts +2 -0
- package/backend/lib/shared/port-utils.ts +25 -0
- package/backend/lib/shared/process-manager.ts +281 -0
- package/backend/lib/snapshot/blob-store.ts +227 -0
- package/backend/lib/snapshot/gitignore.ts +307 -0
- package/backend/lib/snapshot/helpers.ts +397 -0
- package/backend/lib/snapshot/snapshot-service.ts +483 -0
- package/backend/lib/terminal/helpers.ts +14 -0
- package/backend/lib/terminal/index.ts +8 -0
- package/backend/lib/terminal/pty-manager.ts +4 -0
- package/backend/lib/terminal/pty-session-manager.ts +387 -0
- package/backend/lib/terminal/shell-utils.ts +313 -0
- package/backend/lib/terminal/stream-manager.ts +293 -0
- package/backend/lib/tunnel/global-tunnel-manager.ts +243 -0
- package/backend/lib/tunnel/project-tunnel-manager.ts +311 -0
- package/backend/lib/user/helpers.ts +87 -0
- package/backend/lib/utils/ws.ts +944 -0
- package/backend/lib/vite-dev.ts +353 -0
- package/backend/middleware/cors.ts +15 -0
- package/backend/middleware/error-handler.ts +49 -0
- package/backend/middleware/logger.ts +9 -0
- package/backend/types/api.ts +24 -0
- package/backend/ws/README.md +1505 -0
- package/backend/ws/chat/background.ts +198 -0
- package/backend/ws/chat/index.ts +21 -0
- package/backend/ws/chat/stream.ts +707 -0
- package/backend/ws/engine/claude/accounts.ts +401 -0
- package/backend/ws/engine/claude/index.ts +13 -0
- package/backend/ws/engine/claude/status.ts +43 -0
- package/backend/ws/engine/index.ts +14 -0
- package/backend/ws/engine/opencode/index.ts +11 -0
- package/backend/ws/engine/opencode/status.ts +30 -0
- package/backend/ws/engine/utils.ts +36 -0
- package/backend/ws/files/index.ts +30 -0
- package/backend/ws/files/read.ts +189 -0
- package/backend/ws/files/search.ts +453 -0
- package/backend/ws/files/watch.ts +124 -0
- package/backend/ws/files/write.ts +143 -0
- package/backend/ws/git/branch.ts +106 -0
- package/backend/ws/git/commit.ts +39 -0
- package/backend/ws/git/conflict.ts +68 -0
- package/backend/ws/git/diff.ts +69 -0
- package/backend/ws/git/index.ts +24 -0
- package/backend/ws/git/log.ts +41 -0
- package/backend/ws/git/remote.ts +214 -0
- package/backend/ws/git/staging.ts +84 -0
- package/backend/ws/git/status.ts +90 -0
- package/backend/ws/index.ts +69 -0
- package/backend/ws/mcp/index.ts +61 -0
- package/backend/ws/messages/crud.ts +74 -0
- package/backend/ws/messages/index.ts +14 -0
- package/backend/ws/preview/browser/cleanup.ts +129 -0
- package/backend/ws/preview/browser/console.ts +114 -0
- package/backend/ws/preview/browser/interact.ts +513 -0
- package/backend/ws/preview/browser/mcp.ts +129 -0
- package/backend/ws/preview/browser/native-ui.ts +235 -0
- package/backend/ws/preview/browser/stats.ts +55 -0
- package/backend/ws/preview/browser/tab-info.ts +126 -0
- package/backend/ws/preview/browser/tab.ts +166 -0
- package/backend/ws/preview/browser/webcodecs.ts +293 -0
- package/backend/ws/preview/index.ts +146 -0
- package/backend/ws/projects/crud.ts +113 -0
- package/backend/ws/projects/index.ts +25 -0
- package/backend/ws/projects/presence.ts +46 -0
- package/backend/ws/projects/status.ts +116 -0
- package/backend/ws/sessions/crud.ts +327 -0
- package/backend/ws/sessions/index.ts +33 -0
- package/backend/ws/settings/crud.ts +112 -0
- package/backend/ws/settings/index.ts +14 -0
- package/backend/ws/snapshot/index.ts +17 -0
- package/backend/ws/snapshot/restore.ts +173 -0
- package/backend/ws/snapshot/timeline.ts +141 -0
- package/backend/ws/system/index.ts +14 -0
- package/backend/ws/system/operations.ts +49 -0
- package/backend/ws/terminal/index.ts +40 -0
- package/backend/ws/terminal/persistence.ts +153 -0
- package/backend/ws/terminal/session.ts +382 -0
- package/backend/ws/terminal/stream.ts +79 -0
- package/backend/ws/tunnel/index.ts +14 -0
- package/backend/ws/tunnel/operations.ts +91 -0
- package/backend/ws/types.ts +20 -0
- package/backend/ws/user/crud.ts +156 -0
- package/backend/ws/user/index.ts +14 -0
- package/bin/clopen.ts +307 -0
- package/bun.lock +1352 -0
- package/frontend/App.svelte +34 -0
- package/frontend/app.css +313 -0
- package/frontend/lib/app-environment.ts +10 -0
- package/frontend/lib/components/chat/ChatInterface.svelte +407 -0
- package/frontend/lib/components/chat/formatters/ErrorMessage.svelte +57 -0
- package/frontend/lib/components/chat/formatters/MessageFormatter.svelte +224 -0
- package/frontend/lib/components/chat/formatters/TextMessage.svelte +395 -0
- package/frontend/lib/components/chat/formatters/Tools.svelte +70 -0
- package/frontend/lib/components/chat/formatters/index.ts +3 -0
- package/frontend/lib/components/chat/input/ChatInput.svelte +421 -0
- package/frontend/lib/components/chat/input/components/ChatInputActions.svelte +78 -0
- package/frontend/lib/components/chat/input/components/DragDropOverlay.svelte +30 -0
- package/frontend/lib/components/chat/input/components/EditModeIndicator.svelte +33 -0
- package/frontend/lib/components/chat/input/components/EngineModelPicker.svelte +619 -0
- package/frontend/lib/components/chat/input/components/FileAttachmentPreview.svelte +48 -0
- package/frontend/lib/components/chat/input/components/LoadingIndicator.svelte +31 -0
- package/frontend/lib/components/chat/input/composables/use-animations.svelte.ts +201 -0
- package/frontend/lib/components/chat/input/composables/use-chat-actions.svelte.ts +148 -0
- package/frontend/lib/components/chat/input/composables/use-file-handling.svelte.ts +216 -0
- package/frontend/lib/components/chat/input/composables/use-input-state.svelte.ts +357 -0
- package/frontend/lib/components/chat/input/composables/use-textarea-resize.svelte.ts +57 -0
- package/frontend/lib/components/chat/message/ChatMessage.svelte +478 -0
- package/frontend/lib/components/chat/message/ChatMessages.svelte +541 -0
- package/frontend/lib/components/chat/message/DateSeparator.svelte +86 -0
- package/frontend/lib/components/chat/message/MessageBubble.svelte +86 -0
- package/frontend/lib/components/chat/message/MessageHeader.svelte +157 -0
- package/frontend/lib/components/chat/modal/DebugModal.svelte +59 -0
- package/frontend/lib/components/chat/modal/TokenUsageModal.svelte +124 -0
- package/frontend/lib/components/chat/shared/index.ts +2 -0
- package/frontend/lib/components/chat/shared/utils.ts +116 -0
- package/frontend/lib/components/chat/tools/BashOutputTool.svelte +35 -0
- package/frontend/lib/components/chat/tools/BashTool.svelte +46 -0
- package/frontend/lib/components/chat/tools/CustomMcpTool.svelte +139 -0
- package/frontend/lib/components/chat/tools/EditTool.svelte +48 -0
- package/frontend/lib/components/chat/tools/ExitPlanModeTool.svelte +32 -0
- package/frontend/lib/components/chat/tools/GlobTool.svelte +51 -0
- package/frontend/lib/components/chat/tools/GrepTool.svelte +90 -0
- package/frontend/lib/components/chat/tools/KillShellTool.svelte +26 -0
- package/frontend/lib/components/chat/tools/ListMcpResourcesTool.svelte +31 -0
- package/frontend/lib/components/chat/tools/NotebookEditTool.svelte +38 -0
- package/frontend/lib/components/chat/tools/ReadMcpResourceTool.svelte +34 -0
- package/frontend/lib/components/chat/tools/ReadTool.svelte +41 -0
- package/frontend/lib/components/chat/tools/TaskTool.svelte +64 -0
- package/frontend/lib/components/chat/tools/TodoWriteTool.svelte +75 -0
- package/frontend/lib/components/chat/tools/WebFetchTool.svelte +35 -0
- package/frontend/lib/components/chat/tools/WebSearchTool.svelte +84 -0
- package/frontend/lib/components/chat/tools/WriteTool.svelte +33 -0
- package/frontend/lib/components/chat/tools/components/CodeBlock.svelte +79 -0
- package/frontend/lib/components/chat/tools/components/DiffBlock.svelte +408 -0
- package/frontend/lib/components/chat/tools/components/FileHeader.svelte +45 -0
- package/frontend/lib/components/chat/tools/components/InfoLine.svelte +19 -0
- package/frontend/lib/components/chat/tools/components/StatsBadges.svelte +27 -0
- package/frontend/lib/components/chat/tools/components/TerminalCommand.svelte +54 -0
- package/frontend/lib/components/chat/tools/components/index.ts +7 -0
- package/frontend/lib/components/chat/tools/index.ts +26 -0
- package/frontend/lib/components/chat/widgets/FloatingTodoList.svelte +249 -0
- package/frontend/lib/components/chat/widgets/TokenUsage.svelte +78 -0
- package/frontend/lib/components/checkpoint/TimelineModal.svelte +391 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineEdge.svelte +26 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineGraph.svelte +87 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineNode.svelte +108 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineVersionGroup.svelte +59 -0
- package/frontend/lib/components/checkpoint/timeline/animation.ts +168 -0
- package/frontend/lib/components/checkpoint/timeline/config.ts +44 -0
- package/frontend/lib/components/checkpoint/timeline/graph-builder.ts +304 -0
- package/frontend/lib/components/checkpoint/timeline/types.ts +65 -0
- package/frontend/lib/components/checkpoint/timeline/utils.ts +53 -0
- package/frontend/lib/components/common/Alert.svelte +139 -0
- package/frontend/lib/components/common/AvatarBubble.svelte +56 -0
- package/frontend/lib/components/common/Button.svelte +71 -0
- package/frontend/lib/components/common/Card.svelte +102 -0
- package/frontend/lib/components/common/Checkbox.svelte +48 -0
- package/frontend/lib/components/common/Dialog.svelte +249 -0
- package/frontend/lib/components/common/FolderBrowser.svelte +843 -0
- package/frontend/lib/components/common/Icon.svelte +58 -0
- package/frontend/lib/components/common/Input.svelte +72 -0
- package/frontend/lib/components/common/Lightbox.svelte +233 -0
- package/frontend/lib/components/common/LoadingScreen.svelte +52 -0
- package/frontend/lib/components/common/LoadingSpinner.svelte +48 -0
- package/frontend/lib/components/common/Modal.svelte +177 -0
- package/frontend/lib/components/common/ModalProvider.svelte +28 -0
- package/frontend/lib/components/common/ModelSelector.svelte +110 -0
- package/frontend/lib/components/common/MonacoEditor.svelte +569 -0
- package/frontend/lib/components/common/NotificationToast.svelte +113 -0
- package/frontend/lib/components/common/PageTemplate.svelte +76 -0
- package/frontend/lib/components/common/ProjectUserAvatars.svelte +79 -0
- package/frontend/lib/components/common/Select.svelte +98 -0
- package/frontend/lib/components/common/Textarea.svelte +80 -0
- package/frontend/lib/components/common/ThemeToggle.svelte +44 -0
- package/frontend/lib/components/common/lucide-icons.ts +1642 -0
- package/frontend/lib/components/common/material-icons.ts +1082 -0
- package/frontend/lib/components/common/xterm/XTerm.svelte +796 -0
- package/frontend/lib/components/common/xterm/index.ts +16 -0
- package/frontend/lib/components/common/xterm/terminal-config.ts +68 -0
- package/frontend/lib/components/common/xterm/types.ts +31 -0
- package/frontend/lib/components/common/xterm/xterm-service.ts +353 -0
- package/frontend/lib/components/files/FileNode.svelte +384 -0
- package/frontend/lib/components/files/FileTree.svelte +681 -0
- package/frontend/lib/components/files/FileViewer.svelte +728 -0
- package/frontend/lib/components/files/SearchResults.svelte +303 -0
- package/frontend/lib/components/git/BranchManager.svelte +458 -0
- package/frontend/lib/components/git/ChangesSection.svelte +107 -0
- package/frontend/lib/components/git/CommitForm.svelte +76 -0
- package/frontend/lib/components/git/ConflictResolver.svelte +158 -0
- package/frontend/lib/components/git/DiffViewer.svelte +364 -0
- package/frontend/lib/components/git/FileChangeItem.svelte +97 -0
- package/frontend/lib/components/git/GitButton.svelte +33 -0
- package/frontend/lib/components/git/GitLog.svelte +361 -0
- package/frontend/lib/components/git/GitModal.svelte +80 -0
- package/frontend/lib/components/history/HistoryModal.svelte +563 -0
- package/frontend/lib/components/history/HistoryView.svelte +615 -0
- package/frontend/lib/components/index.ts +34 -0
- package/frontend/lib/components/preview/browser/BrowserPreview.svelte +549 -0
- package/frontend/lib/components/preview/browser/components/Canvas.svelte +1058 -0
- package/frontend/lib/components/preview/browser/components/ConsolePanel.svelte +757 -0
- package/frontend/lib/components/preview/browser/components/Container.svelte +450 -0
- package/frontend/lib/components/preview/browser/components/ContextMenu.svelte +236 -0
- package/frontend/lib/components/preview/browser/components/SelectDropdown.svelte +224 -0
- package/frontend/lib/components/preview/browser/components/Toolbar.svelte +339 -0
- package/frontend/lib/components/preview/browser/components/VirtualCursor.svelte +36 -0
- package/frontend/lib/components/preview/browser/core/cleanup.svelte.ts +155 -0
- package/frontend/lib/components/preview/browser/core/coordinator.svelte.ts +837 -0
- package/frontend/lib/components/preview/browser/core/interactions.svelte.ts +113 -0
- package/frontend/lib/components/preview/browser/core/mcp-handlers.svelte.ts +296 -0
- package/frontend/lib/components/preview/browser/core/native-ui-handlers.svelte.ts +391 -0
- package/frontend/lib/components/preview/browser/core/stream-handler.svelte.ts +231 -0
- package/frontend/lib/components/preview/browser/core/tab-manager.svelte.ts +210 -0
- package/frontend/lib/components/preview/browser/core/tab-operations.svelte.ts +239 -0
- package/frontend/lib/components/preview/index.ts +2 -0
- package/frontend/lib/components/settings/SettingsModal.svelte +235 -0
- package/frontend/lib/components/settings/SettingsView.svelte +36 -0
- package/frontend/lib/components/settings/appearance/AppearanceSettings.svelte +51 -0
- package/frontend/lib/components/settings/appearance/LayoutPresetSettings.svelte +160 -0
- package/frontend/lib/components/settings/appearance/LayoutPreview.svelte +76 -0
- package/frontend/lib/components/settings/engines/AIEnginesSettings.svelte +917 -0
- package/frontend/lib/components/settings/general/AdvancedSettings.svelte +187 -0
- package/frontend/lib/components/settings/general/DataManagementSettings.svelte +203 -0
- package/frontend/lib/components/settings/general/GeneralSettings.svelte +10 -0
- package/frontend/lib/components/settings/model/ModelSettings.svelte +357 -0
- package/frontend/lib/components/settings/notifications/NotificationSettings.svelte +205 -0
- package/frontend/lib/components/settings/user/UserSettings.svelte +197 -0
- package/frontend/lib/components/terminal/Terminal.svelte +368 -0
- package/frontend/lib/components/terminal/TerminalTabs.svelte +87 -0
- package/frontend/lib/components/terminal/TerminalView.svelte +55 -0
- package/frontend/lib/components/tunnel/TunnelActive.svelte +142 -0
- package/frontend/lib/components/tunnel/TunnelButton.svelte +54 -0
- package/frontend/lib/components/tunnel/TunnelInactive.svelte +284 -0
- package/frontend/lib/components/tunnel/TunnelModal.svelte +47 -0
- package/frontend/lib/components/tunnel/TunnelQRCode.svelte +49 -0
- package/frontend/lib/components/workspace/DesktopNavigator.svelte +382 -0
- package/frontend/lib/components/workspace/MobileNavigator.svelte +403 -0
- package/frontend/lib/components/workspace/PanelContainer.svelte +100 -0
- package/frontend/lib/components/workspace/PanelHeader.svelte +505 -0
- package/frontend/lib/components/workspace/ViewMenu.svelte +162 -0
- package/frontend/lib/components/workspace/WorkspaceLayout.svelte +169 -0
- package/frontend/lib/components/workspace/layout/DesktopLayout.svelte +15 -0
- package/frontend/lib/components/workspace/layout/MobileLayout.svelte +17 -0
- package/frontend/lib/components/workspace/layout/split-pane/Container.svelte +42 -0
- package/frontend/lib/components/workspace/layout/split-pane/Handle.svelte +85 -0
- package/frontend/lib/components/workspace/layout/split-pane/Layout.svelte +37 -0
- package/frontend/lib/components/workspace/panels/ChatPanel.svelte +274 -0
- package/frontend/lib/components/workspace/panels/FilesPanel.svelte +1261 -0
- package/frontend/lib/components/workspace/panels/GitPanel.svelte +1560 -0
- package/frontend/lib/components/workspace/panels/PreviewPanel.svelte +150 -0
- package/frontend/lib/components/workspace/panels/TerminalPanel.svelte +73 -0
- package/frontend/lib/constants/preview.ts +45 -0
- package/frontend/lib/services/chat/chat.service.ts +704 -0
- package/frontend/lib/services/chat/index.ts +7 -0
- package/frontend/lib/services/notification/global-stream-monitor.ts +86 -0
- package/frontend/lib/services/notification/index.ts +8 -0
- package/frontend/lib/services/notification/push.service.ts +144 -0
- package/frontend/lib/services/notification/sound.service.ts +127 -0
- package/frontend/lib/services/preview/browser/browser-console.service.ts +61 -0
- package/frontend/lib/services/preview/browser/browser-webcodecs.service.ts +1499 -0
- package/frontend/lib/services/preview/browser/mcp-integration.svelte.ts +67 -0
- package/frontend/lib/services/preview/index.ts +23 -0
- package/frontend/lib/services/project/index.ts +8 -0
- package/frontend/lib/services/project/status.service.ts +159 -0
- package/frontend/lib/services/snapshot/snapshot.service.ts +47 -0
- package/frontend/lib/services/terminal/background/index.ts +130 -0
- package/frontend/lib/services/terminal/background/session-restore.ts +274 -0
- package/frontend/lib/services/terminal/background/stream-manager.ts +286 -0
- package/frontend/lib/services/terminal/index.ts +14 -0
- package/frontend/lib/services/terminal/persistence.service.ts +260 -0
- package/frontend/lib/services/terminal/project.service.ts +953 -0
- package/frontend/lib/services/terminal/session.service.ts +364 -0
- package/frontend/lib/services/terminal/terminal.service.ts +369 -0
- package/frontend/lib/stores/core/app.svelte.ts +117 -0
- package/frontend/lib/stores/core/files.svelte.ts +73 -0
- package/frontend/lib/stores/core/presence.svelte.ts +48 -0
- package/frontend/lib/stores/core/projects.svelte.ts +317 -0
- package/frontend/lib/stores/core/sessions.svelte.ts +383 -0
- package/frontend/lib/stores/features/claude-accounts.svelte.ts +58 -0
- package/frontend/lib/stores/features/models.svelte.ts +89 -0
- package/frontend/lib/stores/features/settings.svelte.ts +87 -0
- package/frontend/lib/stores/features/terminal.svelte.ts +701 -0
- package/frontend/lib/stores/features/tunnel.svelte.ts +161 -0
- package/frontend/lib/stores/features/user.svelte.ts +96 -0
- package/frontend/lib/stores/ui/chat-input.svelte.ts +57 -0
- package/frontend/lib/stores/ui/chat-model.svelte.ts +61 -0
- package/frontend/lib/stores/ui/dialog.svelte.ts +59 -0
- package/frontend/lib/stores/ui/edit-mode.svelte.ts +214 -0
- package/frontend/lib/stores/ui/notification.svelte.ts +166 -0
- package/frontend/lib/stores/ui/settings-modal.svelte.ts +88 -0
- package/frontend/lib/stores/ui/theme.svelte.ts +179 -0
- package/frontend/lib/stores/ui/workspace.svelte.ts +754 -0
- package/frontend/lib/types/native-ui.ts +73 -0
- package/frontend/lib/utils/chat/date-separator.ts +39 -0
- package/frontend/lib/utils/chat/message-grouper.ts +219 -0
- package/frontend/lib/utils/chat/message-processor.ts +135 -0
- package/frontend/lib/utils/chat/tool-handler.ts +161 -0
- package/frontend/lib/utils/chat/virtual-scroll.svelte.ts +142 -0
- package/frontend/lib/utils/click-outside.ts +20 -0
- package/frontend/lib/utils/context-manager.ts +257 -0
- package/frontend/lib/utils/file-icon-mappings.ts +769 -0
- package/frontend/lib/utils/folder-icon-mappings.ts +1030 -0
- package/frontend/lib/utils/git-status.ts +68 -0
- package/frontend/lib/utils/platform.ts +113 -0
- package/frontend/lib/utils/port-check.ts +65 -0
- package/frontend/lib/utils/terminalFormatter.ts +207 -0
- package/frontend/lib/utils/theme.ts +6 -0
- package/frontend/lib/utils/tree-visualizer.ts +320 -0
- package/frontend/lib/utils/ws.ts +44 -0
- package/frontend/main.ts +13 -0
- package/index.html +70 -0
- package/package.json +111 -0
- package/scripts/generate-icons.ts +87 -0
- package/scripts/pre-publish-check.sh +142 -0
- package/scripts/setup-hooks.sh +134 -0
- package/scripts/validate-branch-name.sh +47 -0
- package/scripts/validate-commit-msg.sh +42 -0
- package/shared/constants/engines.ts +134 -0
- package/shared/types/database/connection.ts +16 -0
- package/shared/types/database/index.ts +6 -0
- package/shared/types/database/schema.ts +141 -0
- package/shared/types/engine/index.ts +45 -0
- package/shared/types/filesystem/index.ts +22 -0
- package/shared/types/git.ts +171 -0
- package/shared/types/messaging/index.ts +239 -0
- package/shared/types/messaging/tool.ts +526 -0
- package/shared/types/network/api.ts +18 -0
- package/shared/types/network/index.ts +5 -0
- package/shared/types/stores/app.ts +23 -0
- package/shared/types/stores/dialog.ts +21 -0
- package/shared/types/stores/index.ts +3 -0
- package/shared/types/stores/settings.ts +15 -0
- package/shared/types/terminal/index.ts +44 -0
- package/shared/types/ui/components.ts +61 -0
- package/shared/types/ui/icons.ts +23 -0
- package/shared/types/ui/index.ts +22 -0
- package/shared/types/ui/notifications.ts +14 -0
- package/shared/types/ui/theme.ts +12 -0
- package/shared/types/websocket/index.ts +43 -0
- package/shared/types/window.d.ts +13 -0
- package/shared/utils/anonymous-user.ts +168 -0
- package/shared/utils/async.ts +10 -0
- package/shared/utils/diff-calculator.ts +184 -0
- package/shared/utils/file-type-detection.ts +166 -0
- package/shared/utils/logger.ts +158 -0
- package/shared/utils/message-formatter.ts +79 -0
- package/shared/utils/path.ts +47 -0
- package/shared/utils/ws-client.ts +768 -0
- package/shared/utils/ws-server.ts +660 -0
- package/static/audio/notification.ogg +0 -0
- package/static/favicon.svg +8 -0
- package/static/fonts/dm-sans/dm-sans-italic-latin-ext.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-italic-latin.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-normal-latin-ext.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-normal-latin.woff2 +0 -0
- package/static/fonts/dm-sans.css +96 -0
- package/svelte.config.js +20 -0
- package/vite.config.ts +33 -0
|
@@ -0,0 +1,1144 @@
|
|
|
1
|
+
# Custom MCP Tools
|
|
2
|
+
|
|
3
|
+
Custom MCP (Model Context Protocol) tools for adding specialized functionality to both **Claude Code** and **Open Code** engines. Servers are defined once and shared across both engines via a single-source-of-truth architecture.
|
|
4
|
+
|
|
5
|
+
## 📚 Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Architecture](#architecture)
|
|
10
|
+
- [Creating Custom Tools](#creating-custom-tools)
|
|
11
|
+
- [Configuration](#configuration)
|
|
12
|
+
- [API Reference](#api-reference)
|
|
13
|
+
- [Examples](#examples)
|
|
14
|
+
- [Best Practices](#best-practices)
|
|
15
|
+
- [Troubleshooting](#troubleshooting)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
**What is Custom MCP Tools?**
|
|
22
|
+
System for adding custom tools to AI engines with type-safe TypeScript definitions. Tools are defined once using `defineServer()` and automatically available to both Claude Code (in-process) and Open Code (stdio subprocess).
|
|
23
|
+
|
|
24
|
+
**Features:**
|
|
25
|
+
- Single source of truth — define tools once, use in both engines
|
|
26
|
+
- In-process execution for Claude Code via `createSdkMcpServer`
|
|
27
|
+
- Stdio subprocess for Open Code via `@modelcontextprotocol/sdk`
|
|
28
|
+
- WebSocket bridge for tool handlers that need in-process access
|
|
29
|
+
- Type-safe with TypeScript
|
|
30
|
+
- Auto metadata extraction and registration
|
|
31
|
+
- Configuration-based enable/disable
|
|
32
|
+
- Zod validation
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
### 1. Create a New Server
|
|
39
|
+
|
|
40
|
+
Create a new folder in `./servers/` (e.g., `calculator/`) and create an `index.ts` file using the `defineServer` helper:
|
|
41
|
+
|
|
42
|
+
**File: `./servers/calculator/index.ts`**
|
|
43
|
+
```typescript
|
|
44
|
+
import { z } from "zod";
|
|
45
|
+
import { defineServer } from "../helper";
|
|
46
|
+
|
|
47
|
+
export default defineServer({
|
|
48
|
+
name: "calculator",
|
|
49
|
+
version: "1.0.0",
|
|
50
|
+
tools: {
|
|
51
|
+
"calculate": {
|
|
52
|
+
description: "Perform mathematical calculations",
|
|
53
|
+
schema: {
|
|
54
|
+
expression: z.string().describe("Mathematical expression to evaluate"),
|
|
55
|
+
precision: z.number().optional().default(2).describe("Decimal precision")
|
|
56
|
+
},
|
|
57
|
+
handler: async (args) => {
|
|
58
|
+
try {
|
|
59
|
+
// IMPORTANT: Use a safe math evaluation library in production!
|
|
60
|
+
// This is just an example - eval() is dangerous!
|
|
61
|
+
const result = eval(args.expression);
|
|
62
|
+
const formatted = Number(result).toFixed(args.precision);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
content: [{
|
|
66
|
+
type: "text",
|
|
67
|
+
text: `${args.expression} = ${formatted}`
|
|
68
|
+
}]
|
|
69
|
+
};
|
|
70
|
+
} catch (error) {
|
|
71
|
+
return {
|
|
72
|
+
content: [{
|
|
73
|
+
type: "text",
|
|
74
|
+
text: `Error: Invalid expression - ${error.message}`
|
|
75
|
+
}],
|
|
76
|
+
isError: true
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 2. Register the Server
|
|
86
|
+
|
|
87
|
+
Add to `./servers/index.ts` to auto-build registries:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import weather from './weather';
|
|
91
|
+
import calculator from './calculator';
|
|
92
|
+
import { buildServerRegistries } from './helper';
|
|
93
|
+
|
|
94
|
+
const allServers = [
|
|
95
|
+
weather,
|
|
96
|
+
calculator, // Simply add your server here!
|
|
97
|
+
// Add more servers...
|
|
98
|
+
] as const;
|
|
99
|
+
|
|
100
|
+
const { metadata, registry } = buildServerRegistries(allServers);
|
|
101
|
+
|
|
102
|
+
export const serverMetadata = metadata;
|
|
103
|
+
export const serverRegistry = registry;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 3. Configure the Server
|
|
107
|
+
|
|
108
|
+
Add to `./config.ts` (only specify `enabled` and `tools`):
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const mcpServersConfig: Record<ServerName, ServerConfig> = {
|
|
112
|
+
"weather-service": {
|
|
113
|
+
enabled: true,
|
|
114
|
+
tools: ["get_temperature"]
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
// Add your new server config
|
|
118
|
+
"calculator": {
|
|
119
|
+
enabled: true,
|
|
120
|
+
tools: ["calculate"] // Type-safe! Only valid tool names allowed
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 4. Done!
|
|
126
|
+
|
|
127
|
+
Tool available to Claude as: `mcp__calculator__calculate`
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Architecture
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
backend/lib/mcp/
|
|
135
|
+
├── types.ts # TypeScript type definitions (auto-inferred from metadata)
|
|
136
|
+
├── config.ts # User configuration (enabled, tools) + auto-merge with registry
|
|
137
|
+
│ # + resolveOpenCodeToolName() & getOpenCodeMcpConfig()
|
|
138
|
+
├── index.ts # Main export point
|
|
139
|
+
├── stdio-server.ts # Standalone MCP stdio server for Open Code (subprocess)
|
|
140
|
+
├── servers/ # Server implementations (single source of truth)
|
|
141
|
+
│ ├── index.ts # Auto-build registries from server array
|
|
142
|
+
│ ├── helper.ts # defineServer & buildServerRegistries functions
|
|
143
|
+
│ ├── weather/ # Example: Weather service
|
|
144
|
+
│ │ ├── index.ts # Server definition using defineServer
|
|
145
|
+
│ │ └── get-temperature.ts # Tool handler implementation
|
|
146
|
+
│ └── browser-automation/ # Example: Browser automation service
|
|
147
|
+
│ ├── index.ts # Server definition
|
|
148
|
+
│ ├── session.ts # Session management handlers
|
|
149
|
+
│ ├── navigation.ts # Navigation handlers
|
|
150
|
+
│ └── ... # Other tool handlers
|
|
151
|
+
└── README.md # This file
|
|
152
|
+
|
|
153
|
+
backend/ws/mcp/ # WebSocket bridge route (lives in ws/ per convention)
|
|
154
|
+
└── index.ts # WS .http() route for stdio server → main server
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Server Organization
|
|
158
|
+
|
|
159
|
+
For simple servers with one or two tools, you can keep all logic in `index.ts`:
|
|
160
|
+
```
|
|
161
|
+
servers/
|
|
162
|
+
└── simple-server/
|
|
163
|
+
└── index.ts # All tools defined here
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
For complex servers with many tools, split handlers into separate files:
|
|
167
|
+
```
|
|
168
|
+
servers/
|
|
169
|
+
└── complex-server/
|
|
170
|
+
├── index.ts # Server definition using defineServer
|
|
171
|
+
├── tool-a.ts # Handler for tool A
|
|
172
|
+
├── tool-b.ts # Handler for tool B
|
|
173
|
+
└── utils.ts # Shared utilities
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Example structure from `browser-automation`:
|
|
177
|
+
```
|
|
178
|
+
servers/browser-automation/
|
|
179
|
+
├── index.ts # Main server definition with all tools
|
|
180
|
+
├── session.ts # Session management handlers
|
|
181
|
+
├── navigation.ts # Navigation handlers
|
|
182
|
+
├── actions.ts # Browser action handlers
|
|
183
|
+
├── inspection.ts # Page inspection handlers
|
|
184
|
+
└── ... # Other organized handler files
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Data Flow
|
|
188
|
+
|
|
189
|
+
**Claude Code (in-process):**
|
|
190
|
+
```
|
|
191
|
+
1. Server Definition (servers/weather/index.ts)
|
|
192
|
+
└─> defineServer() extracts metadata automatically
|
|
193
|
+
↓
|
|
194
|
+
2. Registry Building (servers/index.ts)
|
|
195
|
+
└─> buildServerRegistries() creates metadata + registry
|
|
196
|
+
↓
|
|
197
|
+
3. Configuration (config.ts)
|
|
198
|
+
└─> User config merged with registry automatically
|
|
199
|
+
↓
|
|
200
|
+
4. Claude Agent SDK (stream.ts)
|
|
201
|
+
└─> Uses getEnabledMcpServers()
|
|
202
|
+
↓
|
|
203
|
+
5. Claude uses the tool (in-process handler execution)
|
|
204
|
+
↓
|
|
205
|
+
6. UI displays result (CustomMcpTool.svelte)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Open Code (stdio subprocess + WS bridge):**
|
|
209
|
+
```
|
|
210
|
+
1. Server Definition (servers/weather/index.ts)
|
|
211
|
+
└─> Same defineServer() — single source of truth
|
|
212
|
+
↓
|
|
213
|
+
2. Open Code engine (opencode/stream.ts)
|
|
214
|
+
└─> Uses getOpenCodeMcpConfig() → spawns stdio-server.ts
|
|
215
|
+
↓
|
|
216
|
+
3. stdio-server.ts (subprocess)
|
|
217
|
+
└─> Reads serverMetadata for tool schemas/descriptions
|
|
218
|
+
└─> Registers tools via @modelcontextprotocol/sdk
|
|
219
|
+
↓
|
|
220
|
+
4. Open Code calls a tool → stdio-server receives JSON-RPC
|
|
221
|
+
↓
|
|
222
|
+
5. stdio-server proxies via WSClient.http('mcp:execute', ...)
|
|
223
|
+
↓
|
|
224
|
+
6. WS bridge route (backend/ws/mcp/)
|
|
225
|
+
└─> Looks up handler from serverMetadata.toolDefs
|
|
226
|
+
└─> Executes handler in-process (same context as main server)
|
|
227
|
+
↓
|
|
228
|
+
7. Response flows back: bridge → WSClient → stdio → Open Code
|
|
229
|
+
↓
|
|
230
|
+
8. UI displays result (CustomMcpTool.svelte)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Key Components
|
|
234
|
+
|
|
235
|
+
**`defineServer`**
|
|
236
|
+
Helper function to define MCP server with automatic metadata extraction.
|
|
237
|
+
Stores both Claude SDK server instance AND raw tool definitions (`toolDefs`)
|
|
238
|
+
for reuse by other transports (stdio server, bridge).
|
|
239
|
+
|
|
240
|
+
**`buildServerRegistries`**
|
|
241
|
+
Function to build server registries from server array.
|
|
242
|
+
|
|
243
|
+
**`mcpServers`**
|
|
244
|
+
Final configuration combining user config with server instances.
|
|
245
|
+
|
|
246
|
+
**`stdio-server.ts`**
|
|
247
|
+
Standalone MCP stdio server subprocess spawned by Open Code. Reads tool
|
|
248
|
+
definitions from `serverMetadata` and proxies all calls to the main server
|
|
249
|
+
via `WSClient.http()`. Uses `@modelcontextprotocol/sdk` for MCP protocol.
|
|
250
|
+
|
|
251
|
+
**`backend/ws/mcp/`**
|
|
252
|
+
WebSocket `.http()` route that receives tool calls from the stdio server
|
|
253
|
+
and executes handlers in-process. Lives in `backend/ws/` per the WS
|
|
254
|
+
module convention (see `backend/ws/README.md`).
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Creating Custom Tools
|
|
259
|
+
|
|
260
|
+
### Folder Structure
|
|
261
|
+
|
|
262
|
+
Each MCP server should be in its own folder under `./servers/`:
|
|
263
|
+
|
|
264
|
+
1. **Create a folder**: `./servers/your-server-name/`
|
|
265
|
+
2. **Create index.ts**: Main server definition file
|
|
266
|
+
3. **Optional**: Create separate files for tool handlers (e.g., `tool-name.ts`)
|
|
267
|
+
|
|
268
|
+
Example:
|
|
269
|
+
```
|
|
270
|
+
servers/
|
|
271
|
+
└── your-server-name/
|
|
272
|
+
├── index.ts # Server definition
|
|
273
|
+
├── handler-1.ts # Optional: Separate handler file
|
|
274
|
+
└── handler-2.ts # Optional: Another handler file
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Tool Definition Format
|
|
278
|
+
|
|
279
|
+
Tools are defined as an object. Each tool has three components:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
{
|
|
283
|
+
"tool_name": {
|
|
284
|
+
description: string, // Tool description for Claude
|
|
285
|
+
schema: Record<string, ZodType>, // Zod schema (plain object, not wrapped)
|
|
286
|
+
handler: async (args) => Promise<ToolResult> // Handler function
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Input Schema (Zod)
|
|
292
|
+
|
|
293
|
+
Define schema as a plain object of Zod types:
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
schema: {
|
|
297
|
+
// Required string
|
|
298
|
+
name: z.string().describe("User's name"),
|
|
299
|
+
|
|
300
|
+
// Required number with constraints
|
|
301
|
+
age: z.number().min(0).max(150).describe("User's age"),
|
|
302
|
+
|
|
303
|
+
// Optional with default
|
|
304
|
+
format: z.enum(["json", "csv"]).default("json").describe("Output format"),
|
|
305
|
+
|
|
306
|
+
// Optional field
|
|
307
|
+
email: z.string().email().optional().describe("Email address"),
|
|
308
|
+
|
|
309
|
+
// Array
|
|
310
|
+
tags: z.array(z.string()).describe("List of tags"),
|
|
311
|
+
|
|
312
|
+
// Nested object
|
|
313
|
+
address: z.object({
|
|
314
|
+
street: z.string(),
|
|
315
|
+
city: z.string(),
|
|
316
|
+
zipCode: z.string()
|
|
317
|
+
}).describe("User address")
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Note:** The schema is automatically wrapped in `z.object()` by `defineServer`.
|
|
322
|
+
|
|
323
|
+
### Handler Function
|
|
324
|
+
|
|
325
|
+
The handler receives validated arguments and returns a result:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
async (args) => {
|
|
329
|
+
try {
|
|
330
|
+
// Do your work here
|
|
331
|
+
const result = await someAsyncOperation(args);
|
|
332
|
+
|
|
333
|
+
// Return success
|
|
334
|
+
return {
|
|
335
|
+
content: [{
|
|
336
|
+
type: "text",
|
|
337
|
+
text: `Result: ${result}`
|
|
338
|
+
}]
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
} catch (error) {
|
|
342
|
+
// Return error
|
|
343
|
+
return {
|
|
344
|
+
content: [{
|
|
345
|
+
type: "text",
|
|
346
|
+
text: `Error: ${error.message}`
|
|
347
|
+
}],
|
|
348
|
+
isError: true
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Return Format
|
|
355
|
+
|
|
356
|
+
Tools must return an object with this structure:
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
{
|
|
360
|
+
content: Array<{
|
|
361
|
+
type: "text" | "image" | "resource",
|
|
362
|
+
text?: string, // For type: "text"
|
|
363
|
+
// Additional fields for other types
|
|
364
|
+
}>,
|
|
365
|
+
isError?: boolean // Mark as error result
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Configuration
|
|
372
|
+
|
|
373
|
+
### Server Configuration
|
|
374
|
+
|
|
375
|
+
Configuration is split into two parts:
|
|
376
|
+
|
|
377
|
+
**1. User Configuration (`mcpServersConfig` in `config.ts`):**
|
|
378
|
+
```typescript
|
|
379
|
+
const mcpServersConfig: Record<ServerName, ServerConfig> = {
|
|
380
|
+
"weather-service": {
|
|
381
|
+
enabled: boolean, // Whether server is active
|
|
382
|
+
tools: readonly string[] // Array of enabled tool names (type-safe!)
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**2. Auto-Merged with Registry:**
|
|
388
|
+
Server instances from `serverRegistry` are automatically merged to create the final `mcpServers` object:
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
// Final structure (after merge):
|
|
392
|
+
{
|
|
393
|
+
instance: McpSdkServerConfigWithInstance, // From registry
|
|
394
|
+
enabled: boolean, // From user config
|
|
395
|
+
tools: readonly string[] // From user config (type-validated)
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Environment Variables & Secrets
|
|
400
|
+
|
|
401
|
+
For tools that require API keys or secrets:
|
|
402
|
+
|
|
403
|
+
1. **Never hardcode secrets** in the code
|
|
404
|
+
2. Use environment variables:
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
async (args) => {
|
|
408
|
+
// Get API key from environment
|
|
409
|
+
const apiKey = process.env.MY_API_KEY;
|
|
410
|
+
|
|
411
|
+
if (!apiKey) {
|
|
412
|
+
return {
|
|
413
|
+
content: [{
|
|
414
|
+
type: "text",
|
|
415
|
+
text: "Error: MY_API_KEY environment variable not set"
|
|
416
|
+
}],
|
|
417
|
+
isError: true
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Use the API key
|
|
422
|
+
const response = await fetch(url, {
|
|
423
|
+
headers: {
|
|
424
|
+
'Authorization': `Bearer ${apiKey}`
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// ... rest of implementation
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
3. Add to `.env` file:
|
|
433
|
+
```bash
|
|
434
|
+
MY_API_KEY=your-secret-key-here
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## API Reference
|
|
440
|
+
|
|
441
|
+
### Main Exports
|
|
442
|
+
|
|
443
|
+
#### Type Definitions
|
|
444
|
+
```typescript
|
|
445
|
+
import type {
|
|
446
|
+
ServerName, // Union of all server names (from metadata)
|
|
447
|
+
ToolsForServer, // Tool names for a specific server (from metadata)
|
|
448
|
+
ServerConfig, // User config structure
|
|
449
|
+
McpServerConfigWithInstance, // Config + instance structure
|
|
450
|
+
ParsedMcpToolName, // Parsed tool name components
|
|
451
|
+
McpServerStatus // Server status from SDK
|
|
452
|
+
} from '$backend/lib/mcp';
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
#### Main Configuration
|
|
456
|
+
|
|
457
|
+
**`mcpServers`** - Final merged configuration:
|
|
458
|
+
```typescript
|
|
459
|
+
import { mcpServers } from '$backend/lib/mcp';
|
|
460
|
+
|
|
461
|
+
// Access server configuration
|
|
462
|
+
const weatherConfig = mcpServers["weather-service"];
|
|
463
|
+
// {
|
|
464
|
+
// instance: McpSdkServerConfigWithInstance,
|
|
465
|
+
// enabled: true,
|
|
466
|
+
// tools: ["get_temperature"]
|
|
467
|
+
// }
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
#### Server Registries
|
|
471
|
+
|
|
472
|
+
**`serverMetadata`** - Metadata for type inference:
|
|
473
|
+
```typescript
|
|
474
|
+
import { serverMetadata } from '$backend/lib/mcp/servers';
|
|
475
|
+
|
|
476
|
+
// Access metadata
|
|
477
|
+
const weatherMeta = serverMetadata["weather-service"];
|
|
478
|
+
// { name: "weather-service", tools: ["get_temperature"] }
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**`serverRegistry`** - Server instances:
|
|
482
|
+
```typescript
|
|
483
|
+
import { serverRegistry } from '$backend/lib/mcp/servers';
|
|
484
|
+
|
|
485
|
+
// Access server instance
|
|
486
|
+
const weatherServer = serverRegistry["weather-service"];
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Main Functions
|
|
490
|
+
|
|
491
|
+
#### `getEnabledMcpServers()`
|
|
492
|
+
Returns all enabled MCP servers for use with Claude SDK.
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
import { getEnabledMcpServers } from '$backend/lib/mcp';
|
|
496
|
+
|
|
497
|
+
const servers = getEnabledMcpServers();
|
|
498
|
+
// Returns: Record<string, McpServerConfig>
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
#### `getAllowedMcpTools()`
|
|
502
|
+
Returns array of allowed tool names (formatted for Claude SDK).
|
|
503
|
+
|
|
504
|
+
```typescript
|
|
505
|
+
import { getAllowedMcpTools } from '$backend/lib/mcp';
|
|
506
|
+
|
|
507
|
+
const tools = getAllowedMcpTools();
|
|
508
|
+
// Returns: ["mcp__weather-service__get_temperature", ...]
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
#### `parseMcpToolName(fullName)`
|
|
512
|
+
Parse MCP tool name into components.
|
|
513
|
+
|
|
514
|
+
```typescript
|
|
515
|
+
import { parseMcpToolName } from '$backend/lib/mcp';
|
|
516
|
+
|
|
517
|
+
const parsed = parseMcpToolName("mcp__weather-service__get_temperature");
|
|
518
|
+
// Returns: { server: "weather-service", tool: "get_temperature", fullName: "..." }
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
#### `isMcpTool(toolName)`
|
|
522
|
+
Check if a tool name is a custom MCP tool.
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
import { isMcpTool } from '$backend/lib/mcp';
|
|
526
|
+
|
|
527
|
+
isMcpTool("mcp__weather-service__get_temperature"); // true
|
|
528
|
+
isMcpTool("Bash"); // false
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
#### `getOpenCodeMcpConfig()`
|
|
532
|
+
Returns MCP configuration for Open Code engine (spawns stdio subprocess).
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
import { getOpenCodeMcpConfig } from '$backend/lib/mcp';
|
|
536
|
+
|
|
537
|
+
const mcpConfig = getOpenCodeMcpConfig();
|
|
538
|
+
// Returns: { 'clopen-mcp': { type: 'local', command: ['bun', 'run', '...'], ... } }
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
#### `resolveOpenCodeToolName(toolName)`
|
|
542
|
+
Resolve an Open Code tool name to `mcp__server__tool` format (single source of truth).
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
import { resolveOpenCodeToolName } from '$backend/lib/mcp';
|
|
546
|
+
|
|
547
|
+
resolveOpenCodeToolName("clopen-mcp_get_temperature");
|
|
548
|
+
// Returns: "mcp__weather-service__get_temperature"
|
|
549
|
+
|
|
550
|
+
resolveOpenCodeToolName("get_temperature");
|
|
551
|
+
// Returns: "mcp__weather-service__get_temperature"
|
|
552
|
+
|
|
553
|
+
resolveOpenCodeToolName("unknown_tool");
|
|
554
|
+
// Returns: null
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Helper Functions
|
|
558
|
+
|
|
559
|
+
```typescript
|
|
560
|
+
import {
|
|
561
|
+
getServerConfig,
|
|
562
|
+
getToolConfig,
|
|
563
|
+
isServerEnabled,
|
|
564
|
+
isToolEnabled,
|
|
565
|
+
getEnabledServerNames,
|
|
566
|
+
getEnabledToolsForServer,
|
|
567
|
+
getMcpStats
|
|
568
|
+
} from '$backend/lib/mcp';
|
|
569
|
+
|
|
570
|
+
// Get server configuration (includes instance)
|
|
571
|
+
const config = getServerConfig("weather-service");
|
|
572
|
+
|
|
573
|
+
// Get tool configuration
|
|
574
|
+
const hasTemperature = getToolConfig("weather-service", "get_temperature");
|
|
575
|
+
|
|
576
|
+
// Check if server/tool enabled
|
|
577
|
+
const serverEnabled = isServerEnabled("weather-service");
|
|
578
|
+
const toolEnabled = isToolEnabled("weather-service", "get_temperature");
|
|
579
|
+
|
|
580
|
+
// Get enabled server names
|
|
581
|
+
const enabledServers = getEnabledServerNames();
|
|
582
|
+
// Returns: ["weather-service", ...]
|
|
583
|
+
|
|
584
|
+
// Get enabled tools for a server
|
|
585
|
+
const tools = getEnabledToolsForServer("weather-service");
|
|
586
|
+
// Returns: ["mcp__weather-service__get_temperature", ...]
|
|
587
|
+
|
|
588
|
+
// Get statistics
|
|
589
|
+
const stats = getMcpStats();
|
|
590
|
+
// Returns: {
|
|
591
|
+
// totalServers: number,
|
|
592
|
+
// enabledServers: number,
|
|
593
|
+
// totalTools: number,
|
|
594
|
+
// serverNames: string[],
|
|
595
|
+
// toolNames: string[]
|
|
596
|
+
// }
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
---
|
|
600
|
+
|
|
601
|
+
## Examples
|
|
602
|
+
|
|
603
|
+
### Example 1: Weather Service (Included)
|
|
604
|
+
|
|
605
|
+
**Simple approach** - All logic in `index.ts`:
|
|
606
|
+
|
|
607
|
+
**File: `servers/weather/index.ts`**
|
|
608
|
+
```typescript
|
|
609
|
+
import { z } from "zod";
|
|
610
|
+
import { defineServer } from "../helper";
|
|
611
|
+
|
|
612
|
+
export default defineServer({
|
|
613
|
+
name: "weather-service",
|
|
614
|
+
version: "1.0.0",
|
|
615
|
+
tools: {
|
|
616
|
+
"get_temperature": {
|
|
617
|
+
description: "Get current temperature for a location using coordinates. Returns temperature in Fahrenheit.",
|
|
618
|
+
schema: {
|
|
619
|
+
latitude: z.number().min(-90).max(90).describe("Latitude coordinate (-90 to 90)"),
|
|
620
|
+
longitude: z.number().min(-180).max(180).describe("Longitude coordinate (-180 to 180)")
|
|
621
|
+
},
|
|
622
|
+
handler: async (args) => {
|
|
623
|
+
try {
|
|
624
|
+
const url = `https://api.open-meteo.com/v1/forecast?latitude=${args.latitude}&longitude=${args.longitude}¤t=temperature_2m&temperature_unit=fahrenheit`;
|
|
625
|
+
const response = await fetch(url);
|
|
626
|
+
|
|
627
|
+
if (!response.ok) {
|
|
628
|
+
return {
|
|
629
|
+
content: [{
|
|
630
|
+
type: "text",
|
|
631
|
+
text: `Failed to fetch weather data: ${response.status} ${response.statusText}`
|
|
632
|
+
}],
|
|
633
|
+
isError: true
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const data = await response.json();
|
|
638
|
+
const temperature = data.current.temperature_2m;
|
|
639
|
+
const unit = data.current_units?.temperature_2m || "°F";
|
|
640
|
+
|
|
641
|
+
return {
|
|
642
|
+
content: [{
|
|
643
|
+
type: "text",
|
|
644
|
+
text: `Temperature: ${temperature}${unit}`
|
|
645
|
+
}]
|
|
646
|
+
};
|
|
647
|
+
} catch (error) {
|
|
648
|
+
return {
|
|
649
|
+
content: [{
|
|
650
|
+
type: "text",
|
|
651
|
+
text: `Error fetching temperature: ${error.message}`
|
|
652
|
+
}],
|
|
653
|
+
isError: true
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
**Organized approach** - Separate handler file:
|
|
663
|
+
|
|
664
|
+
**File: `servers/weather/get-temperature.ts`**
|
|
665
|
+
```typescript
|
|
666
|
+
export async function getTemperatureHandler(args: { latitude: number; longitude: number }) {
|
|
667
|
+
try {
|
|
668
|
+
const url = `https://api.open-meteo.com/v1/forecast?latitude=${args.latitude}&longitude=${args.longitude}¤t=temperature_2m&temperature_unit=fahrenheit`;
|
|
669
|
+
const response = await fetch(url);
|
|
670
|
+
|
|
671
|
+
if (!response.ok) {
|
|
672
|
+
return {
|
|
673
|
+
content: [{
|
|
674
|
+
type: "text",
|
|
675
|
+
text: `Failed to fetch weather data: ${response.status} ${response.statusText}`
|
|
676
|
+
}],
|
|
677
|
+
isError: true
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const data = await response.json();
|
|
682
|
+
const temperature = data.current.temperature_2m;
|
|
683
|
+
const unit = data.current_units?.temperature_2m || "°F";
|
|
684
|
+
|
|
685
|
+
return {
|
|
686
|
+
content: [{
|
|
687
|
+
type: "text",
|
|
688
|
+
text: `Temperature: ${temperature}${unit}`
|
|
689
|
+
}]
|
|
690
|
+
};
|
|
691
|
+
} catch (error) {
|
|
692
|
+
return {
|
|
693
|
+
content: [{
|
|
694
|
+
type: "text",
|
|
695
|
+
text: `Error fetching temperature: ${error.message}`
|
|
696
|
+
}],
|
|
697
|
+
isError: true
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
**File: `servers/weather/index.ts`**
|
|
704
|
+
```typescript
|
|
705
|
+
import { z } from "zod";
|
|
706
|
+
import { defineServer } from "../helper";
|
|
707
|
+
import { getTemperatureHandler } from "./get-temperature";
|
|
708
|
+
|
|
709
|
+
export default defineServer({
|
|
710
|
+
name: "weather-service",
|
|
711
|
+
version: "1.0.0",
|
|
712
|
+
tools: {
|
|
713
|
+
"get_temperature": {
|
|
714
|
+
description: "Get current temperature for a location using coordinates. Returns temperature in Fahrenheit.",
|
|
715
|
+
schema: {
|
|
716
|
+
latitude: z.number().min(-90).max(90).describe("Latitude coordinate (-90 to 90)"),
|
|
717
|
+
longitude: z.number().min(-180).max(180).describe("Longitude coordinate (-180 to 180)")
|
|
718
|
+
},
|
|
719
|
+
handler: getTemperatureHandler
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
### Example 2: Database Query
|
|
726
|
+
|
|
727
|
+
Execute database queries with connection pooling:
|
|
728
|
+
|
|
729
|
+
```typescript
|
|
730
|
+
import { z } from "zod";
|
|
731
|
+
import { defineServer } from "../helper";
|
|
732
|
+
import { Pool } from 'pg'; // PostgreSQL client
|
|
733
|
+
|
|
734
|
+
// Create connection pool (outside defineServer)
|
|
735
|
+
const pool = new Pool({
|
|
736
|
+
connectionString: process.env.DATABASE_URL
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
export default defineServer({
|
|
740
|
+
name: "database",
|
|
741
|
+
version: "1.0.0",
|
|
742
|
+
tools: {
|
|
743
|
+
"query_database": {
|
|
744
|
+
description: "Execute a read-only database query",
|
|
745
|
+
schema: {
|
|
746
|
+
query: z.string().describe("SQL query to execute (SELECT only)"),
|
|
747
|
+
params: z.array(z.any()).optional().describe("Query parameters")
|
|
748
|
+
},
|
|
749
|
+
handler: async (args) => {
|
|
750
|
+
try {
|
|
751
|
+
// Validate query is SELECT only
|
|
752
|
+
if (!args.query.trim().toLowerCase().startsWith('select')) {
|
|
753
|
+
return {
|
|
754
|
+
content: [{
|
|
755
|
+
type: "text",
|
|
756
|
+
text: "Error: Only SELECT queries are allowed"
|
|
757
|
+
}],
|
|
758
|
+
isError: true
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
const result = await pool.query(args.query, args.params || []);
|
|
763
|
+
|
|
764
|
+
return {
|
|
765
|
+
content: [{
|
|
766
|
+
type: "text",
|
|
767
|
+
text: `Found ${result.rowCount} rows:\n${JSON.stringify(result.rows, null, 2)}`
|
|
768
|
+
}]
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
} catch (error) {
|
|
772
|
+
return {
|
|
773
|
+
content: [{
|
|
774
|
+
type: "text",
|
|
775
|
+
text: `Database error: ${error.message}`
|
|
776
|
+
}],
|
|
777
|
+
isError: true
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
### Example 3: API Gateway
|
|
787
|
+
|
|
788
|
+
Make authenticated requests to external APIs:
|
|
789
|
+
|
|
790
|
+
```typescript
|
|
791
|
+
import { z } from "zod";
|
|
792
|
+
import { defineServer } from "../helper";
|
|
793
|
+
|
|
794
|
+
// Service configurations (outside defineServer)
|
|
795
|
+
const configs = {
|
|
796
|
+
github: {
|
|
797
|
+
baseUrl: "https://api.github.com",
|
|
798
|
+
token: process.env.GITHUB_TOKEN
|
|
799
|
+
},
|
|
800
|
+
slack: {
|
|
801
|
+
baseUrl: "https://slack.com/api",
|
|
802
|
+
token: process.env.SLACK_TOKEN
|
|
803
|
+
},
|
|
804
|
+
stripe: {
|
|
805
|
+
baseUrl: "https://api.stripe.com/v1",
|
|
806
|
+
token: process.env.STRIPE_KEY
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
export default defineServer({
|
|
811
|
+
name: "api-gateway",
|
|
812
|
+
version: "1.0.0",
|
|
813
|
+
tools: {
|
|
814
|
+
"api_request": {
|
|
815
|
+
description: "Make authenticated API requests to external services",
|
|
816
|
+
schema: {
|
|
817
|
+
service: z.enum(["github", "slack", "stripe"]).describe("Service to call"),
|
|
818
|
+
endpoint: z.string().describe("API endpoint path"),
|
|
819
|
+
method: z.enum(["GET", "POST", "PUT", "DELETE"]).describe("HTTP method"),
|
|
820
|
+
body: z.record(z.any()).optional().describe("Request body")
|
|
821
|
+
},
|
|
822
|
+
handler: async (args) => {
|
|
823
|
+
const config = configs[args.service];
|
|
824
|
+
const url = `${config.baseUrl}${args.endpoint}`;
|
|
825
|
+
|
|
826
|
+
const response = await fetch(url, {
|
|
827
|
+
method: args.method,
|
|
828
|
+
headers: {
|
|
829
|
+
'Authorization': `Bearer ${config.token}`,
|
|
830
|
+
'Content-Type': 'application/json'
|
|
831
|
+
},
|
|
832
|
+
body: args.body ? JSON.stringify(args.body) : undefined
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
const data = await response.json();
|
|
836
|
+
|
|
837
|
+
return {
|
|
838
|
+
content: [{
|
|
839
|
+
type: "text",
|
|
840
|
+
text: JSON.stringify(data, null, 2)
|
|
841
|
+
}]
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
### Example 4: File Operations
|
|
850
|
+
|
|
851
|
+
Read and process files from the filesystem:
|
|
852
|
+
|
|
853
|
+
```typescript
|
|
854
|
+
import { z } from "zod";
|
|
855
|
+
import { defineServer } from "../helper";
|
|
856
|
+
|
|
857
|
+
export default defineServer({
|
|
858
|
+
name: "file-utils",
|
|
859
|
+
version: "1.0.0",
|
|
860
|
+
tools: {
|
|
861
|
+
"count_lines": {
|
|
862
|
+
description: "Count lines in a file",
|
|
863
|
+
schema: {
|
|
864
|
+
filePath: z.string().describe("Path to the file")
|
|
865
|
+
},
|
|
866
|
+
handler: async (args) => {
|
|
867
|
+
try {
|
|
868
|
+
const content = await Bun.file(args.filePath).text();
|
|
869
|
+
const lines = content.split('\n').length;
|
|
870
|
+
|
|
871
|
+
return {
|
|
872
|
+
content: [{
|
|
873
|
+
type: "text",
|
|
874
|
+
text: `File has ${lines} lines`
|
|
875
|
+
}]
|
|
876
|
+
};
|
|
877
|
+
} catch (error) {
|
|
878
|
+
return {
|
|
879
|
+
content: [{
|
|
880
|
+
type: "text",
|
|
881
|
+
text: `Error reading file: ${error.message}`
|
|
882
|
+
}],
|
|
883
|
+
isError: true
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
---
|
|
893
|
+
|
|
894
|
+
## Best Practices
|
|
895
|
+
|
|
896
|
+
### 1. Error Handling
|
|
897
|
+
|
|
898
|
+
Always wrap tool logic in try-catch and return meaningful errors:
|
|
899
|
+
|
|
900
|
+
```typescript
|
|
901
|
+
async (args) => {
|
|
902
|
+
try {
|
|
903
|
+
// Your logic here
|
|
904
|
+
return { content: [{ type: "text", text: result }] };
|
|
905
|
+
} catch (error) {
|
|
906
|
+
return {
|
|
907
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
908
|
+
isError: true
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
### 2. Input Validation
|
|
915
|
+
|
|
916
|
+
Use Zod constraints for robust validation:
|
|
917
|
+
|
|
918
|
+
```typescript
|
|
919
|
+
{
|
|
920
|
+
email: z.string().email().describe("Valid email address"),
|
|
921
|
+
age: z.number().min(0).max(150).describe("Age in years"),
|
|
922
|
+
url: z.string().url().describe("Valid URL")
|
|
923
|
+
}
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
### 3. Descriptive Messages
|
|
927
|
+
|
|
928
|
+
Provide clear descriptions for Claude to understand tool usage:
|
|
929
|
+
|
|
930
|
+
```typescript
|
|
931
|
+
{
|
|
932
|
+
"send_email": {
|
|
933
|
+
description: "Send an email to a recipient. Use this when the user explicitly asks to send an email.",
|
|
934
|
+
schema: { /* ... */ },
|
|
935
|
+
handler: async (args) => { /* ... */ }
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
### 4. Resource Management
|
|
941
|
+
|
|
942
|
+
Clean up resources properly:
|
|
943
|
+
|
|
944
|
+
```typescript
|
|
945
|
+
handler: async (args) => {
|
|
946
|
+
const connection = await createConnection();
|
|
947
|
+
|
|
948
|
+
try {
|
|
949
|
+
const result = await connection.query(args.query);
|
|
950
|
+
return { content: [{ type: "text", text: result }] };
|
|
951
|
+
} finally {
|
|
952
|
+
await connection.close(); // Always clean up
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
### 5. Security
|
|
958
|
+
|
|
959
|
+
```typescript
|
|
960
|
+
// Use environment variables for secrets
|
|
961
|
+
const apiKey = process.env.API_KEY;
|
|
962
|
+
if (!apiKey) {
|
|
963
|
+
return { content: [{ type: "text", text: "API key not configured" }], isError: true };
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// Validate user input with Zod
|
|
967
|
+
// Use read-only database connections
|
|
968
|
+
// Sanitize file paths
|
|
969
|
+
```
|
|
970
|
+
|
|
971
|
+
### 6. Performance
|
|
972
|
+
|
|
973
|
+
```typescript
|
|
974
|
+
// Use connection pool (create once, outside defineServer)
|
|
975
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
976
|
+
|
|
977
|
+
export default defineServer({
|
|
978
|
+
name: "database",
|
|
979
|
+
version: "1.0.0",
|
|
980
|
+
tools: {
|
|
981
|
+
"query": {
|
|
982
|
+
description: "Execute a query",
|
|
983
|
+
schema: { query: z.string() },
|
|
984
|
+
handler: async (args) => {
|
|
985
|
+
const result = await pool.query(args.query);
|
|
986
|
+
// ...
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
---
|
|
994
|
+
|
|
995
|
+
## Troubleshooting
|
|
996
|
+
|
|
997
|
+
### Tool Not Appearing
|
|
998
|
+
|
|
999
|
+
**Problem:** My custom tool doesn't appear in Claude's available tools.
|
|
1000
|
+
|
|
1001
|
+
**Solutions:**
|
|
1002
|
+
1. Verify server folder exists in `./servers/` with an `index.ts` file
|
|
1003
|
+
2. Verify server is defined using `defineServer` and exported as default (`export default defineServer(...)`)
|
|
1004
|
+
3. Check server is imported and added to `allServers` array in `servers/index.ts`
|
|
1005
|
+
4. Check that server is enabled in `mcpServersConfig` in `config.ts`
|
|
1006
|
+
5. Check that tool is listed in `tools` array in `config.ts`
|
|
1007
|
+
6. Verify tool name format: `mcp__{server}__{tool}`
|
|
1008
|
+
7. Check console for MCP initialization errors or TypeScript errors
|
|
1009
|
+
|
|
1010
|
+
### Connection Errors
|
|
1011
|
+
|
|
1012
|
+
**Problem:** MCP server fails to connect.
|
|
1013
|
+
|
|
1014
|
+
**Check:**
|
|
1015
|
+
- Server uses `defineServer` and exports as default (`export default defineServer(...)`)
|
|
1016
|
+
- Server is imported in `servers/index.ts` and added to `allServers` array
|
|
1017
|
+
- No syntax errors in server file
|
|
1018
|
+
- All dependencies are installed (`bun install`)
|
|
1019
|
+
- Console logs show initialization
|
|
1020
|
+
- Run `bun run check` to catch TypeScript errors
|
|
1021
|
+
|
|
1022
|
+
### Tool Execution Fails
|
|
1023
|
+
|
|
1024
|
+
**Problem:** Tool executes but returns errors.
|
|
1025
|
+
|
|
1026
|
+
**Debug:**
|
|
1027
|
+
1. Check error message in Claude's response
|
|
1028
|
+
2. Look at server logs/console output
|
|
1029
|
+
3. Verify input schema matches what Claude is sending
|
|
1030
|
+
4. Test tool handler independently
|
|
1031
|
+
5. Check for missing environment variables
|
|
1032
|
+
|
|
1033
|
+
### Environment Variables Not Working
|
|
1034
|
+
|
|
1035
|
+
**Problem:** `process.env.MY_KEY` returns undefined.
|
|
1036
|
+
|
|
1037
|
+
**Solutions:**
|
|
1038
|
+
1. Add to `.env` file in project root
|
|
1039
|
+
2. Restart the application (env vars are loaded at startup)
|
|
1040
|
+
3. Check that variable name matches exactly
|
|
1041
|
+
4. Verify `.env` file is not gitignored
|
|
1042
|
+
|
|
1043
|
+
### Type Errors
|
|
1044
|
+
|
|
1045
|
+
**Problem:** TypeScript errors in custom tool.
|
|
1046
|
+
|
|
1047
|
+
**Solutions:**
|
|
1048
|
+
1. Install dependencies: `bun install zod @anthropic-ai/claude-agent-sdk`
|
|
1049
|
+
2. Verify you're importing `defineServer` from `../helper`
|
|
1050
|
+
3. Check that server name matches between `defineServer` and `config.ts`
|
|
1051
|
+
4. Verify tool names in `config.ts` match tool keys in `defineServer`
|
|
1052
|
+
5. Ensure schema is a plain object, not wrapped in `z.object()`
|
|
1053
|
+
6. Run `bun run check` to see all errors
|
|
1054
|
+
|
|
1055
|
+
**Common Type Errors:**
|
|
1056
|
+
|
|
1057
|
+
```typescript
|
|
1058
|
+
// ❌ Wrong - wrapped in z.object()
|
|
1059
|
+
schema: z.object({
|
|
1060
|
+
name: z.string()
|
|
1061
|
+
})
|
|
1062
|
+
|
|
1063
|
+
// ✅ Correct - plain object
|
|
1064
|
+
schema: {
|
|
1065
|
+
name: z.string()
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// ❌ Wrong - invalid tool name in config
|
|
1069
|
+
"calculator": {
|
|
1070
|
+
enabled: true,
|
|
1071
|
+
tools: ["add", "multiply"] // "multiply" doesn't exist in defineServer
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// ✅ Correct - matches defineServer
|
|
1075
|
+
"calculator": {
|
|
1076
|
+
enabled: true,
|
|
1077
|
+
tools: ["add"] // Tool exists in defineServer
|
|
1078
|
+
}
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
---
|
|
1082
|
+
|
|
1083
|
+
## Open Code Integration
|
|
1084
|
+
|
|
1085
|
+
### How It Works
|
|
1086
|
+
|
|
1087
|
+
Open Code uses a **stdio subprocess** pattern for MCP tools. The integration has three parts:
|
|
1088
|
+
|
|
1089
|
+
1. **`stdio-server.ts`** — Standalone Bun subprocess that Open Code spawns. It registers tools from `serverMetadata` using `@modelcontextprotocol/sdk` and communicates with Open Code via stdin/stdout (JSON-RPC 2.0).
|
|
1090
|
+
|
|
1091
|
+
2. **`WSClient` bridge** — The stdio server uses `WSClient` from `shared/utils/ws-client.ts` to connect to the main Clopen server via WebSocket. Tool calls are proxied using the standard `.http()` request-response pattern.
|
|
1092
|
+
|
|
1093
|
+
3. **`backend/ws/mcp-bridge/`** — WS `.http()` route that receives bridge requests and executes tool handlers in-process. This is necessary because handlers like browser-automation need access to Puppeteer instances managed by the main server.
|
|
1094
|
+
|
|
1095
|
+
### Why a Bridge?
|
|
1096
|
+
|
|
1097
|
+
Tool handlers often need in-process access to resources managed by the main server (browser instances, project context, database connections). The stdio subprocess runs in a separate process and cannot access these directly. The WS bridge pattern solves this by:
|
|
1098
|
+
|
|
1099
|
+
- Keeping tool **definitions** (schema, description) in the subprocess for MCP protocol
|
|
1100
|
+
- Proxying tool **execution** to the main server where handlers run in-process
|
|
1101
|
+
- Using the standard WSClient protocol (same as frontend), not custom WebSocket code
|
|
1102
|
+
|
|
1103
|
+
### Adding Open Code Support to New Tools
|
|
1104
|
+
|
|
1105
|
+
No extra work needed! When you add a new tool via `defineServer()`, it's automatically available to both engines:
|
|
1106
|
+
|
|
1107
|
+
- **Claude Code**: Uses the in-process `createSdkMcpServer` instance
|
|
1108
|
+
- **Open Code**: `stdio-server.ts` reads from `serverMetadata` and `mcpServers` — the same registries
|
|
1109
|
+
|
|
1110
|
+
### File Locations
|
|
1111
|
+
|
|
1112
|
+
| File | Location | Purpose |
|
|
1113
|
+
|------|----------|---------|
|
|
1114
|
+
| Tool definitions | `backend/lib/mcp/servers/` | Single source of truth |
|
|
1115
|
+
| Stdio server | `backend/lib/mcp/stdio-server.ts` | Subprocess for Open Code |
|
|
1116
|
+
| WS bridge route | `backend/ws/mcp/index.ts` | Bridge handler (in ws/) |
|
|
1117
|
+
| Open Code config | `backend/lib/mcp/config.ts` | `getOpenCodeMcpConfig()` |
|
|
1118
|
+
| Tool name resolver | `backend/lib/mcp/config.ts` | `resolveOpenCodeToolName()` |
|
|
1119
|
+
|
|
1120
|
+
---
|
|
1121
|
+
|
|
1122
|
+
## Additional Resources
|
|
1123
|
+
|
|
1124
|
+
- [Agent SDK Reference](https://platform.claude.com/docs/en/agent-sdk/typescript.md)
|
|
1125
|
+
- [Custom Tools](https://platform.claude.com/docs/en/agent-sdk/custom-tools.md)
|
|
1126
|
+
- [MCP in the SDK](https://platform.claude.com/docs/en/agent-sdk/mcp.md)
|
|
1127
|
+
- [MCP SDK (@modelcontextprotocol/sdk)](https://github.com/modelcontextprotocol/typescript-sdk)
|
|
1128
|
+
- [WebSocket API Documentation](../../../backend/ws/README.md)
|
|
1129
|
+
- [Zod Documentation](https://zod.dev/)
|
|
1130
|
+
|
|
1131
|
+
---
|
|
1132
|
+
|
|
1133
|
+
## Support
|
|
1134
|
+
|
|
1135
|
+
For questions or issues:
|
|
1136
|
+
1. Check this README
|
|
1137
|
+
2. Review example implementations in `./servers/`
|
|
1138
|
+
3. Check console logs for error messages
|
|
1139
|
+
4. Review Claude Agent SDK documentation
|
|
1140
|
+
5. For WS bridge issues, see `backend/ws/README.md`
|
|
1141
|
+
|
|
1142
|
+
---
|
|
1143
|
+
|
|
1144
|
+
**Happy coding!**
|