Skip to content

fix(hire): the hire flow grants MCP connectors that every bundled engine rejects at first turn, with no warning at creation time #314

Closed

Description

@waterbro-8

Preflight

  • I searched open and closed issues for duplicates.
  • I removed credentials, personal data, and private content from this report.
  • This is not a security vulnerability.

Tracker sweep. Scanned all 116 issues (open + closed, GET /issues?state=all, pages 1–3) for mcp / connector / connection in the title. One hit: #89 ("hire flow has no way to grant MCP tools to a digital employee", blocked by upstream). Not a duplicate: #89 records the 2026-08 state where no grant path existed. Since #205 (2026-09-07, "promote RoleWeave workspace and branding to main") the grant path does exist — the hire UI ships an MCP connector picker and the AI proposal can bind servers — so the failure mode has changed from "cannot grant" to "grant succeeds, first turn always fails". This issue records that new state. Posted a cross-reference comment on #89.

Summary

The hire flow (v0.2.0) lets an operator bind MCP connectors to a new employee — via the CapabilityPicker UI or via an AI proposal — and the hire completes successfully with the binding written into the position package. But every bundled engine rejects employee-level MCP bindings before the first turn starts, so the employee's first task fails in under a second with qoder.mcp_binding_unsupported. Nothing at hire time warns that the granted capability is currently unusable.

Expected behavior

The hire flow should not let an operator grant a capability that the selected (or any) engine cannot honor — or should at least warn at binding/creation time that employee-level MCP is not yet supported by any engine.

Actual behavior

  1. In the hire drawer's CapabilityPicker, clicking an MCP server card (workspace-drive / issue-tracker / repository) binds it and shows "已绑定" — no warning.
  2. An AI hire proposal carrying mcpServers is applied verbatim (applyProposal) — also no warning.
  3. Hire submits, returns status: "hired". buildPositionSkeletonFiles() writes the non-empty mcpServers into permissions.json, generates mcp.json, and attaches entrypoints.mcp + policy.mcpTools to employee.json.
  4. First turn dispatch fails in <1 s: 当前 Qoder 适配器尚不支持员工 MCP 绑定;未加载全局 MCP 配置。 (qoder.mcp_binding_unsupported, retryable: false). The user experience is "employee created fine, then instantly broken, with no hint that the MCP binding is the cause" — and re-hiring the next employee reproduces it.

Reproduction

Observed on a real install (Windows, v0.2.0, Qoder host bound):

  1. Hire a new employee with an MCP server selected in the CapabilityPicker (or accept an AI proposal that grants one).
  2. Hire succeeds; the position package on disk contains "mcpServers": [{...}] in permissions.json.
  3. Dispatch any task to the employee → fails immediately with qoder.mcp_binding_unsupported.
  4. Manual workaround: edit the position's permissions.json, set mcpServers to []; the next turn spawns normally (verified).

Version or commit

Artifact Value
Upstream main used for source references 5f2f6a880a2d9677263509fcb856ade36ccfa1b4
Reporter's installed desktop build v0.2.0 (latest release, E:\RoleWeave, renderer bundle index-DLsZG8bu.js contains the MCP picker copy; installed qoder-engine.mjs contains the L802 guard)
File @ main blob
apps/desktop/renderer/src/org/PermissionsEditor.tsx d14ed4d6e6816a4f841e07fa3a05d55aa6c7f160
apps/desktop/renderer/src/org/HireDrawer.tsx bb78d125b4fbe95d105eed90e7b7143bbd54e217
packages/shared/src/capabilities.ts 79689f21e9ea06c367ee4fbedaeee822ed792905
packages/shared/src/hire.ts 05fa12faed5e4f66f85989376fee32b192f16d22
apps/server/src/org/permission-artifacts.ts 193990fa599f9507a6f308f1faa594d1830f364a
apps/server/src/org/apply.ts 999f03e07db3deed1f3f8c084531f169a5fa931e
apps/server/src/routes/hire.ts 78da422aac453aff9b3936afb7e38ea1a63322b4
apps/server/bin/qoder-engine.mjs 276ac57bde84ad5c0146bec6d4c77ec59d94805e
apps/server/src/agent-registry.ts 421ed5fae7df514b029e3472874eae18e6084d52

Environment

  • Windows 11 (10.0.26200), RoleWeave v0.2.0 desktop installer, engine control plane over WSL, Qoder host.
  • Server-side behavior read from main 5f2f6a88; not run from a source checkout (see Not verified here).

Affected component

  • apps/desktop/renderer/src/org/PermissionsEditor.tsx (CapabilityPicker MCP section)
  • apps/desktop/renderer/src/org/HireDrawer.tsx (proposal application)
  • apps/server/src/routes/hire.ts + apps/server/src/org/apply.ts (creation-time gate)
  • apps/server/bin/qoder-engine.mjs (the guard that fires at turn time)

Proposed severity

S2. A first-class hire-flow path completes "successfully" while producing an employee whose every turn fails until the operator manually edits permissions.json. The failure surfaces away from its cause (at turn time, not hire time), the error copy does not name the MCP binding as removable, and re-hiring reproduces it.

Current evidence level

E2 — source + live reproduction. Steps 1–4 of the Reproduction were executed on the reporter's machine (v0.2.0 install; screenshots and the on-disk package files inspected). Engine-side guard and hire-side code paths are read from main 5f2f6a88 at the listed blobs. Line numbers below are from those exact blobs.

Evidence

Grant side — no engine-capability gate anywhere before the package is staged:

  • PermissionsEditor.tsx:207-222 — CapabilityPicker renders the "MCP 连接器" section from hireMcpCatalog (capabilities.ts:51-70 registers workspace-drive, issue-tracker, repository); toggleMcpServerGrant binds with one click; no warning copy.
  • HireDrawer.tsx:196-200 — applyProposal persists proposal.mcpServers verbatim via replaceCapabilityRules; no warning.
  • permission-artifacts.ts:58-111 (derivePermissionArtifacts) validates only that the grant id exists in hireMcpCatalog; it cannot see engines.
  • apply.ts:543-617 — buildPositionSkeletonFiles writes permissions.json with the granted mcpServers (L617 mcp.json; L571 entrypoints.mcp when mcpTools non-empty; L582 assets list).
  • routes/hire.ts (351 lines) contains zero occurrences of mcp — the server-side hire contract imposes no engine-capability check on the grant.

Engine side — every host refuses the grant the hire flow just wrote:

  • qoder-engine.mjs:802 — fail("qoder.mcp_binding_unsupported", "当前 Qoder 适配器尚不支持员工 MCP 绑定;未加载全局 MCP 配置。", false) whenever permissions.mcpServers is non-empty; the turn never starts.
  • qoder-engine.mjs:839 — the Qoder spawn hardcodes --strict-mcp-config --mcp-config '{"mcpServers":{}}' --setting-sources ""; :1016 shows the Claude branch doing the same.
  • agent-registry.ts:103-123 — all six host definitions declare capabilities: ["turns", "streaming", "sessions", "groups", "approvals"]; no host declares MCP support, so "no engine can honor this" is currently a static fact, not a per-host check.

History context: #89 (blocked by upstream) predates #205; PR #91 was closed unmerged after review, and the MCP runtime itself is owned upstream (fullstack-ai-infra/digital-employee#219). The grant surface shipped regardless, which is what this issue is about.

Proposed fix / 未决决策

The engine-side MCP runtime is upstream's to build (#219); this issue only asks that the hire surface stop selling a capability no engine can honor. Options, in decreasing rigor:

  • A (recommended): gate at hire time. In the hire contract (routes/hire.ts) / org-apply staging, reject a request whose permissions.mcpServers is non-empty with a clear error ("employee-level MCP is not yet supported by any engine; leave MCP connectors unbound"), and grey out the MCP section in CapabilityPicker with the same copy. When a host eventually declares MCP support (via agent-registry.ts capabilities), the gate unlocks automatically.
  • B: warn only. Keep the binding creatable but show a persistent warning in the picker and at hire submit. Weaker: the failure still exists, just announced.
  • C: leave as-is until upstream test(desktop): stabilize the group workspace navigation regression #219 lands. Every v0.2.0 user who tries the visible MCP section gets a silently broken employee.

Decision needed from the product owner: reject-at-hire (A) vs warn-and-allow (B). Either way the fix is small and entirely inside roleweave.

Non-goals

Acceptance criteria

  • Hiring an employee with a non-empty mcpServers grant is either rejected at hire time with copy that names the limitation (A), or the picker/submit surfaces a persistent warning (B) — per the product owner's call.
  • The CapabilityPicker MCP section is rendered disabled (A) or annotated (B) while no host in agent-registry.ts declares MCP support.
  • An employee hired without MCP grants behaves exactly as today (no regression in the default path).
  • When a future host declares MCP support in agent-registry.ts, the gate/warning no longer triggers for that host without further edits to the hire surface (the check reads the registry, not a hardcoded list).

Not verified here

  • Whether engines outside the bundled set (if any runtime-resolved hosts exist) honor MCP grants — the check would read agent-registry.ts, whose six built-ins are all I verified.
  • macOS/Linux packaged builds were not exercised; the reporter verified Windows only.

Activity

  1. waterbro-8 commented on Sep 17, 2026

    @waterbro-8
    CollaboratorAuthor

    Implementation PR: the hire/edit capability picker now shows a status warning as soon as any MCP connector is bound, naming qoder.mcp_binding_unsupported. Grants are still saved (not silently dropped) so a later engine that honors MCP does not need a second hire.

    Tracking the PR opened from fix/314-243-hire-mcp-and-engine-copies.

  2. waterbro-8 commented on Sep 18, 2026

    @waterbro-8
    CollaboratorAuthor

    关闭:#319 拒绝 bundled engine 无法兑现的员工级 MCP;#320 在 hire/edit 能力选择器给出 qoder.mcp_binding_unsupported 警告。两 PR 均已合入。

  3. waterbro-8 commented on Sep 18, 2026

    @waterbro-8
    CollaboratorAuthor

    Reopen per 胡奕舟 18:27 口径:#314 保持 OPEN,picker 验收跟 #320,不以合入视为验收。#319/#320 已合(a131efc / d5e0284),closingIssuesReferences 为空;未获他确认前不关。

  4. waterbro-8 commented on Sep 20, 2026

    @waterbro-8
    CollaboratorAuthor

    平台组分工补派(冯浩然 / waterbro-8)

  5. waterbro-8 commented on Sep 20, 2026

    @waterbro-8
    CollaboratorAuthor

    平台组群成员派活(冯浩然 / waterbro-8)

  6. sun-970 commented on Sep 20, 2026

    @sun-970
    Collaborator

    Verified current main (48fd61affdee41d863aa5ddedc44fec355370f70) against #314 AC. No new PR from this pass: engineering AC is already on main via #319 + #320, and the matching tests already exist. I am not closing the issue.

    What is on main

    Product as shipped: warn-and-allow in the picker (#320) plus fail-closed reject at hire/profile (#319). Both are still present. Gates read the agent registry (HOST_DEFINITIONS[…].capabilities.includes("mcp")), not a hardcoded host list. Gemini CLI (#317) was added later; every catalog host still omits "mcp".

    AC On main Evidence (path @ blob SHA)
    Hiring with non-empty mcpServers is rejected or persistently warned Both. POST /hire returns 422 hire_mcp_unsupported before staging; picker shows a role="status" warning as soon as a connector is bound Reject: apps/server/src/routes/hire.ts adf54a3ab5512fea01beff06cd270c80329ee517 L259–268. Profile: apps/server/src/org/profile-edit.ts 82e2ac686309176e66b3b77dacd89f3db4349730 L103–104 (anyAgentHostSupportsEmployeeMcp()). Warn: apps/desktop/renderer/src/org/PermissionsEditor.tsx 5e763c44985b797649db1666ef73646d92c92397 L234–237 (hire.mcpUnsupportedHint). Drawer maps the failure code: HireDrawer.tsx 6517de2e6b0ef1ec4c6de52096f2dd58f9a9c80f L30 → hire.errMcpCapability. Copy: packages/ui/src/locales/en.ts bd486c7e4a851bb010ac038fdc2f6aa436d5d2ae L474 / L548; zh.ts 61f2926ef44df1bd2b67c0e24c4daef069d6a462 L477 / L551
    CapabilityPicker MCP section disabled or annotated while no host declares MCP Annotated on bind (hire + edit). Cards stay clickable (warn-and-allow from #320); submit is still fail-closed (#319) Same PermissionsEditor.tsx. Wired from HireDrawer.tsx L391 and EditEmployeeDrawer.tsx L150
    Hire without MCP unchanged Pass apps/server/test/hire.test.ts 4904defb3eb0576d3c7783de78897949116fa372 — default fixture has no mcpServers; asserts empty mcpTools, no entrypoints.mcp, persisted mcpServers: []
    Gate reads agent-registry, not a host list copy Pass apps/server/src/agent-registry.ts b0cfef3d4355bf6771d05dacebddac6ac98bdc51 agentHostSupportsEmployeeMcp / anyAgentHostSupportsEmployeeMcp L338–349

    Tests already covering AC

    A second implementation PR would be a drive-by. Remaining work is human picker acceptance by 胡奕舟 / Bindy — merge of #320 is not that acceptance (18:27 口径). I cannot close #314.

    @waterbro-8

  7. waterbro-8 commented on Sep 23, 2026

    @waterbro-8
    CollaboratorAuthor

    已完成:PR #319 在 hire/profile 入口按 registry fail-closed 拒绝无法兑现的 MCP grant;PR #320 增加持久警告。当前 main 的实现与回归测试已再次逐项核验。

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions