- Clarify Secret retrieval (must tap "查看" on admin's phone, not copy)
- Update WeCom customer-service binding section to point to the
"接入链接" UI (copy link / generate QR code)
- Drop developer-only asides (wechatcomapp_secret / port collision
notes, internal sections about cursor persistence, channel runtime
differences, multi-kf-account support)
- Stop exposing `wechatcom_kf_cursor_dir` as a user config; cursor file
is now fixed under `tmp/`, which is an internal implementation detail.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Switch from the local `WechatComAppClient` (whose `fetch_access_token`
may return the raw response dict and whose background refresh loop
re-fetches every 60s) to the stock `wechatpy.enterprise.WeChatClient`.
- Use `client.access_token` (string property) when building sync_msg /
send_msg URLs; the previous `client.fetch_access_token()` call could
interpolate a dict into the URL and yield errcode 40014.
- Always skip historical messages on first start; drop the
`wechatcom_kf_skip_history_on_first_start` config — there is no real
case for replaying up to 14 days of history.
- Change default callback port from 9899 to 9888.
Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce a new channel that integrates with WeCom Customer Service
(微信客服), separate from the existing self-built WeCom app channel.
- Register channel type `wechatcom_kf` in factory, app loader and const
- Add config keys for token / secret / aes_key / port / cursor dir and
the first-start history-skip switch; also expose corresponding env vars
- Implement channel, message and cursor store under channel/wechatcom_kf/
Co-authored-by: Cursor <cursoragent@cursor.com>
Add embedding_provider config knob with native support for
openai / dashscope / doubao / zhipu / linkai, plus an in-chat
/memory status and /memory rebuild-index workflow for switching
vendors safely.
When reloading a conversation, failed tool calls incorrectly showed checkmark instead of X because the is_error field was lost in the history rendering pipeline. Propagate is_error from DB extraction through to the frontend rendering to match the live SSE behavior.
Browser sessions now reuse a Chromium user profile across runs by default
(`~/.cow/browser_profile`), so users only log in to a site once.
Three launch modes are selectable via `tools.browser` in config.json:
- persistent (default): Playwright Chromium with a persistent user_data_dir
- cdp: attach to an externally launched real Chrome via `cdp_endpoint`
(full fingerprints, ideal for sites with strict bot detection)
- fresh: clean context every run, set `persistent: false`
Also:
- Self-heal when the user closes the browser window mid-session: detect
closed page/context/browser via close listeners and exception scanning,
then transparently relaunch on the next request.
- Graceful CDP shutdown: disconnect only, never kill the user's Chrome.
- Friendly errors when the CDP endpoint is unreachable or the persistent
profile is locked, so the LLM can guide the user instead of looping.
- Fix tool config being silently overwritten by workspace config in
AgentInitializer; per-tool user settings (e.g. browser.cdp_endpoint)
are now merged instead of replaced.
- Update zh / en / ja docs with the new login-persistence section,
including the Chrome 137+ requirement to pair --remote-debugging-port
with a dedicated --user-data-dir.
Boot MCP servers (npx/uvx) on a background thread instead of blocking
agent init. Built-in tools serve traffic immediately while MCP comes
online; each new agent reads whatever is ready at creation time.
Idempotent via _mcp_loaded flag — concurrent sessions never re-fork
subprocesses. Per-server failures are isolated and warmup is triggered
in app.py so loading overlaps with channel startup.
Stability fixes in mcp_client.py:
- Fix stderr buffer overflow: start daemon thread to continuously drain
stderr pipe, preventing 64KB buffer fill that blocks child process
- Fix notification interference: loop readline and skip JSON-RPC messages
without 'id' field (notifications) instead of treating them as responses
- Fix concurrent race condition: wrap send+receive in _call_lock so
multiple sessions cannot interleave reads/writes on the same client
- Fix missing timeout: use select.select() with 30s timeout in
_readline_with_timeout() to prevent infinite block on dead MCP server
Config improvements in tool_manager.py:
- Add _normalize_mcp_configs() to support both list format (mcp_servers)
and dict format (mcpServers used by Claude Desktop / Cursor)
- Add _load_mcp_configs() to load from ~/cow/mcp.json first, falling back
to config.json mcp_servers field for backward compatibility
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs found during end-to-end validation with Amap and Chrome DevTools
MCP servers:
1. MCP tools were loaded into ToolManager._mcp_tool_instances but never
added to the agent's tool list. AgentInitializer._load_tools() only
iterated tool_classes (built-in tools). Added a second pass to append
all MCP tool instances.
2. When a MCP server config contains an "env" dict, it was passed directly
to subprocess.Popen, replacing the entire process environment. This
caused npx to fail because PATH and other inherited vars were missing.
Fixed by merging config env on top of os.environ.
Validated with:
- @amap/amap-maps-mcp-server (12 tools, stdio + API key env var)
- chrome-devtools-mcp (29 tools, stdio + remote debugging port)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allows CowAgent to dynamically load tools from any MCP server at startup,
extending the agent from a fixed toolset to an open, extensible tool ecosystem.
## What's added
- `agent/tools/mcp/mcp_client.py`: lightweight JSON-RPC client supporting both
stdio (subprocess) and SSE (HTTP) transports — zero extra dependencies
- `agent/tools/mcp/mcp_tool.py`: `McpTool` wraps a single MCP tool as a
`BaseTool`, with dynamic name/description/params set at instance level
- `agent/tools/tool_manager.py`: new `_load_mcp_tools()` loads MCP servers at
startup via `McpClientRegistry`; falls back gracefully on any error; no-op
when `mcp_servers` is not configured
- `config.py`: registers `mcp_servers` in `available_setting` with inline docs
## Design
- No new dependencies — JSON-RPC implemented from scratch using stdlib only
- MCP clients are long-lived (initialized once, shared across tool calls)
- `McpClientRegistry` holds all subprocess handles and shuts them down cleanly
- Server init failures are non-fatal: logged as warnings, agent continues normally
- Zero overhead when `mcp_servers` is absent from config
## Config example
```json
"mcp_servers": [
{
"name": "filesystem",
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
}
]
```
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>