✨ feat(connector): Connectors system — API-level tool permissions with plugin fallback#15463
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Sorry @ONLY-yours, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d369fae37b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## canary #15463 +/- ##
===========================================
- Coverage 88.56% 69.48% -19.09%
===========================================
Files 819 3201 +2382
Lines 91994 306673 +214679
Branches 7682 32167 +24485
===========================================
+ Hits 81476 213081 +131605
- Misses 10336 93410 +83074
Partials 182 182
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
…r, and inferCrudType util (LOBE-9984, LOBE-9985) - packages/database/src/models/connector.ts: ConnectorModel with create/delete/query/queryByIdentifiers/findById/update/updateStatus - packages/database/src/models/connectorTool.ts: ConnectorToolModel with upsertMany (preserves user permission on sync), updatePermission, queryByConnector, queryByConnectorIds - src/libs/mcp/utils.ts: inferCrudType() — name-based CRUD type inference (delete > update > read > write) - src/server/routers/lambda/connector.ts: tRPC router with list/create/update/delete/syncTools/updateToolPermission - src/server/routers/lambda/index.ts: register connectorRouter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ion with plugin fallback (LOBE-9986)
- src/libs/mcp/buildConnectorManifests.ts: converts user_connector_tools rows into LobeToolManifest entries; maps permission → humanIntervention ('needs_approval' → 'required', 'disabled' → excluded)
- src/server/services/aiAgent/index.ts:
- queryByIdentifiers(agentPlugins) to find matching connectors first
- filter installedPlugins to exclude connector-covered identifiers
- inject connectorManifests as additionalManifests into createServerAgentToolsEngine
- add connector stdio tools to client executor map
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- src/store/tool/slices/connector/: new slice with ConnectorState, ConnectorAction, connectorSelectors - fetchConnectors, createConnector, deleteConnector, syncConnectorTools, disconnectConnector - updateToolPermission with optimistic update + rollback - connectorToolsGrouped selector splits tools into read / write groups - Wired into ToolStore (initialState + store.ts) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…mission editor (LOBE-9988) - src/features/Connectors/: new feature with two-panel layout (list + detail) - ConnectorList: groups connectors by Connected / Not connected, Add button - ConnectorDetail: sync button, disconnect, tool permission groups (read/write) - ToolPermissionGroup: collapsible with batch set (auto/approval/disable all) - ToolPermissionRow: three-state toggle auto(✓) / needs_approval(✋) / disabled(🚫) - AddConnectorModal: name + MCP URL input via @lobehub/ui/base-ui Modal Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…OBE-9989) - src/store/global/initialState.ts: add ChatSettingsTabs.Connector = 'connector' - src/features/AgentSetting/AgentCategory/useCategory.tsx: add Connectors tab with LinkIcon - src/features/AgentSetting/AgentConnectors/: new component listing user connectors with toggle - toggle calls toggleAgentPlugin(connector.identifier) — reuses agents.plugins[] field - shows per-connector tool count - src/features/AgentSetting/AgentSettingsContent.tsx: render AgentConnectors for Connector tab Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- src/store/global/initialState.ts: add SettingsTabs.Connector = 'connector' - src/routes/(main)/settings/hooks/useCategory.tsx: add Connectors item (LinkIcon) after Skills in AI config group - src/routes/(main)/settings/features/componentMap.ts: map SettingsTabs.Connector → '../connector' - src/routes/(main)/settings/features/SettingsContent.tsx: render Connector tab full-width (no SettingContainer), same as Provider - src/routes/(main)/settings/connector/index.tsx: route page rendering the Connectors feature Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ot function call) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tool manager ## Backend - connector.ts: add syncBuiltinTool — bootstraps user_connectors from builtin manifest api[] - connector.ts: add syncPluginTools — bootstraps user_connectors from user_installed_plugins manifest - connector.ts: upsertConnectorEntry helper + resolveDefaultPermission (maps humanIntervention → permission) - connectorTool.ts: SyncToolInput.defaultPermission — per-tool default for new rows, existing rows preserved ## Store - connector/selectors.ts: add connectorByIdentifier, connectorToolsGroupedByIdentifier, isSyncingByIdentifier - connector/action.ts: add syncBuiltinTool, syncPluginTools (idempotent — safe to call on panel open) ## /settings/skill refactor - index.tsx: two-panel master-detail layout (left: 300px skill list, right: detail + permissions) - SkillList: add onSelect + selectedIdentifier props, pass through to builtin/mcp items - BuiltinSkillItem: add onSelect + isSelected (selection highlight, click triggers right panel) - McpSkillItem: add onSelect + isSelected - SkillDetail (new): auto-syncs connector entry on mount, then renders ConnectorDetail permission editor - SettingsContent: Skill tab now renders full-width (same as Provider/Connector) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…m, KlavisSkillItem + error handling in SkillDetail Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ot hook; use string concat instead of cx() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…when onSelect provided All 5 item types (Builtin/Mcp/Lobehub/Klavis/AgentSkill): - When onSelect is provided (list mode): entire row is clickable, action buttons hidden - When onSelect is not provided (other usages): original behavior preserved - Added onSelect/isSelected to AgentSkillItem + wired in SkillList for all agent skill types - SkillDetail: show friendly message instead of error when skill has no tool permissions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…kill UI ConnectorDetail: - builtin → Reset (syncBuiltinTool from local manifest, resets permissions to defaults) - marketplace → Refresh (syncPluginTools from installed plugin manifest) - custom MCP → Sync (syncTools via remote MCP server, existing behavior) - Hide Disconnect button for builtin/marketplace (only MCP connectors can disconnect) - Show 'No tool permissions' message when connector has 0 tools - Fix hooks-rules violation: move useCallback before early return SkillDetail: - Catch sync failure cleanly — shows graceful 'no tool permissions' panel - Show skill identifier as title even when no tools available Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…torDetail layout SkillDetail: - Add 'agent-skill' ToolDetailType — renders AgentSkillDetail inline (no modal, no connector sync) - All hooks called before conditional returns (fixes rules-of-hooks) SkillList: - Pass type='agent-skill' for market/user agent skills (UUID identifiers, not plugin identifiers) ConnectorDetail: - Remove 'Tool permissions / Choose when AI...' subheader — tool groups render directly - Cleaner layout: name → sync/disconnect buttons → tool groups Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…detail panel Backend (connector.ts): - syncBuiltinTool: store manifest meta.description + meta.avatar in connector.metadata - syncPluginTools: same for plugin manifest meta - upsertConnectorEntry: always update metadata on re-sync (keeps description fresh) ConnectorDetail: - Show connector.metadata.description below name in header SkillDetail: - Add 'builtin-skill' ToolDetailType for builtinSkills (Artifacts, Task, AgentBrowser) → Shows avatar + name + description panel; no connector sync needed (prompt-based) - Add 'builtin-skill' type: reads from store builtinSkills array by identifier SkillList: - builtinAgent items → pass type='builtin-skill' (not 'builtin') to SkillDetail Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t items + categorized groups inferCrudType (utils.ts): - Fix: use prefix ^ anchoring instead of \b word boundary - getReactions/listPins/searchMessages now correctly → 'read' (not 'write') - \b fails on camelCase: 'getreactions' has no boundary after 'get' (both \w chars) SkillDetail: - builtin-skill type: render builtinSkill.content via <Markdown variant='chat'> - Artifacts/Task/LobeHub skills now show their full markdown content in right panel style.ts: - Compact skill items: icon 48→36px, padding-block 12→6px SkillList: - Remove old flat renderIntegrations() + Divider - Add categorized sections with headers: LobeHub 内置 Tools | 内置 Skill | 社区 Skill | 社区 Tools | 自定义 - Add sectionHeader style Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…mode
SkillList: remove text-transform: uppercase from sectionHeader
ConnectorDetail: split tools into 4 groups — Read / Create / Update / Delete
(maps to crudType: read / write / update / delete)
connectorToolsGrouped selector: return { readTools, createTools, updateTools, deleteTools }
All item components: remove SkillSourceTag in list mode (onSelect provided)
— tags are redundant when section headers already provide categorization
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…o auto connector router: resetPermissions endpoint — sets all connector's tools to 'auto' store: resetConnectorPermissions action ConnectorDetail: - Add 'Reset permissions' button — resets ALL tools back to auto (fully open) - Rename 'Reset'/'Refresh' button to 'Refresh' — clarifies it syncs tool list only - Two separate concerns: Refresh (tool list) vs Reset permissions (all → auto) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Type updates + add description to tool rows connectorTool.ts: - Use sql`excluded.crud_type` etc. instead of table.column refs in onConflictDoUpdate - table.column in set generates self-reference (no-op) in some Drizzle versions - Now correctly updates crudType when Refresh is clicked (read/update/delete groups will show correctly) ToolPermissionRow: - Add description below tool name: 11px, tertiary color, single-line truncate with ellipsis - Tooltip shows full description on hover (mouseEnterDelay: 0.5s) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n ConnectorItem Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… in /settings/skill - Remove src/routes/(main)/settings/connector/index.tsx - Remove SettingsTabs.Connector from enum and componentMap - Remove Connectors item from settings sidebar useCategory - Remove Connector from full-width list in SettingsContent - Remove unused LinkIcon import from useCategory ChatSettingsTabs.Connector (agent panel) is separate and unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ription + hard-block at callTool buildConnectorManifests: - Disabled tools are now INCLUDED in the manifest (not excluded) - Description replaced with: '[TOOL DISABLED] The user has disabled this tool and it cannot be executed...' - humanIntervention: 'required' set for disabled tools so AI is explicitly warned - AI can inform user the tool is disabled instead of silently not knowing it exists mcp.callTool: - Pre-call permission gate: query ConnectorModel + ConnectorToolModel by connector identifier - If tool.permission === 'disabled': return immediately with "disabled by user" message - MCP server is never called — the block is enforced server-side regardless of what AI attempts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… tools Gmail (and other Klavis-sourced connectors) use tools.klavis.callTool, not tools.mcp.callTool, so the previous MCP permission gate didn't apply. Fix: Add serverDatabase to klavisProcedure, extract connector identifier from toolName prefix, query user_connector_tools, hard-block if permission=disabled. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… flow TBD) Remove AddConnectorModal entry point from /settings/skill header. Custom HTTP MCP connectors require OAuth (Pre-registration / DCR) which is not yet fully implemented. Will be re-added in a future PR. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…al MCP endpoint
Root cause: Lobehub/Klavis OAuth skills are synced into user_connectors via
syncToolsFromClient with mcpServerUrl=null. buildConnectorManifests generates
mcpParams={url:''} for them. After humanIntervention approval, the runtime calls
tools.mcp.callTool({url:''}) → fails silently → empty result.
Fix: only use connectorsMcp (connectors with mcpServerUrl or stdio config) to
replace installedPlugins and build connector manifests. Connectors without a real
MCP endpoint (Lobehub/Klavis) fall back to their original plugin executor path,
preserving the Klavis callTool execution chain and fixing needs_approval flow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… execution paths connectorPermissionCheck.ts (new shared utility): - getConnectorToolPermission(): look up permission by identifier + toolName - buildBlockedToolResponse(): standardized "disabled by user" response - patchManifestWithPermissions(): patch manifest api[] with DB permissions ToolExecutionService.executeTool() — centralized disabled gate: - Queries DB at execution entry for ALL tool types (Lobehub skills, Klavis, MCP connectors, builtin plugins, and qstash/execAgent async path) - Hard-blocks 'disabled' tools before any executor runs - needs_approval handled by manifest humanIntervention (not blocked here) aiAgent/index.ts — manifest patching for Lobehub/Klavis: - After fetching lobehubSkillManifests + klavisManifests, query connector tools - Patch manifests: needs_approval → humanIntervention:'required' (pauses for approval) - Patch manifests: disabled → blocking description (AI informed, executor blocks) - humanIntervention system already handles headless auto-reject for qstash Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ayload.source is undefined Root cause: when a tool call is re-invoked after humanIntervention approval, the payload comes from the DB-stored message which does NOT persist the `source` field. `internal_transformToolCalls` sets source correctly but it only runs for LLM-generated tool calls, not for the approval re-invocation path. Fix: in `invokeBuiltinTool`, if `payload.source` is undefined, do a live lookup from the tool store (klavisAsLobeTools / lobehubSkillAsLobeTools) to determine the correct executor. Applies to Klavis (Gmail, etc) and LobeHub Skills alike. Also: remove all temporary [DEBUG] console.log statements. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- buildConnectorManifests: LobeToolManifest → ToolManifest (correct export name) - connectorPermissionCheck: cast permission string to ConnectorToolPermission - connector.ts model: guard encryptCredentials against null credentials - ConnectorDetail: String() cast for unknown metadata.description - AddConnectorModal: move loading to Modal.confirmLoading (correct prop) - connector/action.ts: break circular ToolStore type reference with Pick<Impl> - execAgent.disableTools.test.ts: mock ConnectorModel/ConnectorToolModel DB deps Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7c437c0 to
83ed774
Compare
P1 — real MCP disabled tools now appear in manifest:
- ConnectorToolModel.queryAllByConnectorIds: new method without disabled filter
- aiAgent.ts: uses queryAllByConnectorIds for manifest building so buildConnectorManifests
receives ALL tools (including disabled) and can emit blocking descriptions
- queryByConnectorIds (non-disabled filter) retained for runtime hot-path
P1 — Klavis gate works for hyphenated identifiers (google-calendar, etc):
- klavis.ts: replace split('_')[0] prefix hack with direct findByToolName DB lookup
- ConnectorToolModel.findByToolName: query user_connector_tools by userId + toolName
P3 — queryByConnector adds userId filter:
- Prevents leaking tool metadata to wrong user if connector UUID is known
Tests — mock ConnectorModel/ConnectorToolModel in all execAgent test files:
- execAgent.builtinRuntime.test.ts
- execAgent.deviceToolPipeline.test.ts
- execAgent.disableTools.test.ts (queryAllByConnectorIds added to mock)
TypeScript — ConnectorDetail metadata.description:
- Use typeof === 'string' type guard to narrow unknown → string for JSX render
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…led comments Klavis gate — identifier + toolName (precise, no same-name collision risk): - CallKlavisToolParams: add identifier? field - klavisExecutor: pass identifier to callKlavisTool - callKlavisTool store action: thread identifier through to tRPC mutate - klavis.callTool router: accept optional identifier in input schema - Permission gate: when identifier present, do queryByIdentifiers + queryByConnector + find by toolName for a precise 2-field lookup; fall back to findByToolName for legacy callers without identifier Comments updated to reflect current disabled behavior: - buildConnectorManifests.ts: disabled → injected with blocking description - connector.ts schema: same correction Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
# 🚀 LobeHub Release (20260610) **Release Date:** June 10, 2026 **Since v2.2.2:** 131 merged PRs · 13 contributors > This weekly release strengthens agent collaboration across cloud, desktop, CLI, and workspace flows, with steadier runtime behavior and a broader foundation for workspace-scoped data. --- ## ✨ Highlights - **Agent execution across devices** — Unifies per-device working directories, project skill discovery, and sub-agent suspend/resume behavior across server, QStash, and device RPC flows. (#15543, #15566, #15481, #15620, #15591) - **Connector and sandbox platform** — Expands connector permissions, custom OAuth MCP connector onboarding, sandbox provider support, and user-uploaded file sync into cloud sandbox runs. (#15463, #15546, #15184, #15550) - **Desktop and CLI reliability** — Fixes desktop cold-start, auto-update, Windows build, CLI skill discovery, and `lh connect` agent dispatch paths. (#15547, #15525, #15527, #15562, #15632, #15634) - **Pages and sharing** — Refreshes topic sharing, improves Page Editor layout behavior, and routes Page Agent tool execution through the server-side editor path. (#15581, #15556, #15588, #15023, #15610) - **Model availability and provider updates** — Adds user-scoped LobeHub model availability, Claude Fable 5, Qwen thinking preservation, and MiniMax M3 updates. (#15590, #15639, #13494, #15376) --- ## 🏗️ Core Product & Architecture ### Agent Runtime & Heterogeneous Agents - Improves sub-agent lifecycle handling, including async suspend/resume, queue-mode QStash resume delivery, and blocking nested sub-agent calls. (#15481, #15620, #15575) - Stabilizes heterogeneous agent ingestion and streaming with raw stream dumps, per-turn usage, image forwarding on regenerate, and duplicate-text fixes. (#15602, #15577, #15592, #15585) - Adds execution-device and working-directory controls across device RPC, legacy defaults, and remote-spawned Claude Code sessions. (#15543, #15566, #15591, #15572) - Improves runtime diagnostics and compatibility, including Gemini multimodal output capture, abort stream semantics, and trace quality analysis. (#15535, #13677, #15508) --- ## 📱 Platforms, Integrations & UX ### Connectors, Sandbox & Tools - Ships API-level connector tool permissions, custom OAuth MCP connector onboarding, and connector-first runtime execution. (#15463, #15546) - Adds sandbox provider support, cloud sandbox file sync, and safer external URL file input handling with SSRF validation. (#15184, #15550, #12657) - Improves tool visibility and execution with pinned app-fixed tools, ANSI output rendering, gateway-tunneled MCP calls, and automatic headless tool runs. (#15509, #15516, #15469, #15492) ### Desktop, CLI & Web UX - Restores desktop startup and reload behavior, preserves IPC error causes, and keeps the tab bar new-tab action visible across routes. (#15547, #15597, #15638) - Fixes desktop update and build stability for browser quit guards, macOS update signing, and Windows Visual Studio detection. (#15525, #15527, #15562) - Shows the plan-limit upgrade UI on desktop builds. (#15628) - Adds the Agent Run delivery checker and fixes CLI device dispatch plus skill list/search output. (#15489, #15634, #15632) - Refreshes onboarding, auth source preservation, topic UI states, referral/Fable campaign copy, and chat-input control bar behavior. (#15629, #15544, #15573, #15614, #15616, #15617, #15622, #15643) --- ## 🔒 Security, Reliability & Rollout Notes - External URL file input now includes SSRF validation for safer Google file handling. (#12657) - Database workspace-scope migrations are part of this release; self-hosted operators should run the normal migration path before serving the updated app. (#15446, #15465, #15468, #15472) - The release branch was re-cut from `canary` and includes the latest `main` release-version commit so `v2.2.2` is the verified compare base. --- ## 👥 Contributors @ONLY-yours, @sxjeru, @hardy-one, @xujingli, @hezhijie0327, @Coooolfan, @arvinxx, @tjx666, @Innei, @rivertwilight, @rdmclin2, @cy948, @AmAzing129 **Full Changelog**: v2.2.2...release/weekly-20260610-recut-3
💻 Change Type
🔗 Related Issue
Related to LOBE-9983 (parent) — LOBE-9984 through LOBE-9989
🔀 Description of Change
Implements the Connectors system as a unified tool permission management layer, extending it to cover ALL tool types (MCP, Klavis, Lobehub market skills, builtin tools).
Core Architecture
DB Layer (user_connectors + user_connector_tools)
ConnectorModel: create/delete/query/queryByIdentifiers/findById/update/updateStatusConnectorToolModel: upsertMany withdefaultPermission, updatePermission, queryByConnector/ByConnectorIdsinferCrudType(): prefix-based camelCase matching (getReactions → 'read', not 'write')tRPC Router (connector.*)
syncTools: fetch from remote MCP serversyncBuiltinTool/syncPluginTools/syncToolsFromClient: bootstrap connector entries from various manifest sourcesupdateToolPermission,resetPermissionsRuntime (aiAgent.ts)
queryByIdentifiers(agentPlugins)→ connector wins over pluginmcpServerUrlorstdio) replace plugins in manifest; Lobehub/Klavis OAuth skills keep their native executor pathbuildConnectorManifests: injects tools withhumanIntervention: 'required'forneeds_approval, and blocking description fordisabledPermission Enforcement (3-layer)
buildConnectorManifests+aiAgentmanifest patchneeds_approval→humanIntervention: 'required';disabled→ blocking descriptionToolExecutionService.executeTool()— covers ALL paths + qstashdisabledtools before any executor runsklavis.callTool,mcp.callToolExecution paths covered:
/settings/skill Refactor
Master-detail layout (left: skill list, right: permission editor)
builtin,builtin-skill,plugin,agent-skill,lobehub-connectorAgentSkillDetailinline for agent skills (no modal)🧪 How to Test
/settings/skill→ Connectors tab → click any builtin tool → right panel shows tool permissionsdisabled→ start chat → tool is blocked with "disabled by user" messageneeds_approval→ start chat → approval dialog appears; after Allow the tool executesdisabled→ ask AI to check email → blocked with messageReset permissions→ all tools reset toauto📝 Notes for reviewer
oidcConfigusesas any— OIDCConfig zod schema alignment is follow-up+button hiddensrc/locales/default/before mergebuildConnectorManifestsnow includesdisabledtools in manifest (with blocking description) — AI knows they exist but can inform user they're disabled