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>
The translate module previously only supported Baidu translation, and the
factory raised a bare RuntimeError for any other type. This change adds
Youdao Translation as a second provider and improves the factory's error
message.
Implementation details:
- New YoudaoTranslator class in translate/youdao/youdao_translate.py
- Implements Youdao's v3 SHA-256 signature scheme, including the
truncate-input rule for queries longer than 20 characters
- Maps ISO 639-1 language codes to Youdao-specific codes
(zh -> zh-CHS, zh-TW -> zh-CHT, others pass through)
- Differentiates network errors, API error codes, and empty translations
- factory.create_translator now lists the supported types in its
RuntimeError message instead of failing silently
- Default config exposes youdao_translate_app_key and
youdao_translate_app_secret
Adds 17 unit tests covering signature correctness, language code mapping,
input truncation edge cases, the full request/response flow, and factory
dispatch. All tests pass under Python 3.11.
- rewrite streaming reply to official cardkit v2.0 API (default on, auto-fallback)
- fix Whisper hallucination: bump ASR sample rate to 16k, pass language=zh
- fix lock-over-IO and tmp file cleanup from #2791
- drop deprecated feishu_bot_name; quiet unknown-key warnings
- docs: cardkit permission and feishu_stream_reply usage
Further refinements on top of #2795:
- persist real session_id (notify_session_id) at task creation so group chats
correctly map back to the user's actual conversation
- mark scheduler turns with [SCHEDULED] (recognise legacy "Scheduled task"
prefix too for backward-compatible pruning)
- prune both DB and in-memory to scheduler_inject_max_per_session (default 3),
only marker-tagged pairs are touched; regular user turns never deleted
- send_message type gated by scheduler_inject_send_message (default false) —
fixed reminder text rarely benefits follow-up Q&A
Co-authored-by: huangrichao2020 <grdomai43881@gmail.com>
Address review feedback from #2794:
1. Use notify_session_id instead of receiver for correct group chat mapping
- Task creation should store the real session_id in action.notify_session_id
- Falls back to receiver for backward compatibility with old tasks
2. Add injection to all four execution branches:
- _execute_agent_task
- _execute_send_message
- _execute_tool_call
- _execute_skill_call (also fixed missing channel.send)
3. Add config switch and content truncation:
- scheduler_inject_to_session (default: true) to toggle the feature
- 2000 char limit prevents high-frequency tasks from bloating sessions
Fixes#2793
- Receive audio messages: map msg_type=audio to ContextType.VOICE and
download opus file via lazy _prepare_fn for STT pipeline
- Send voice replies: upload opus audio via Feishu file API, auto-convert
non-opus formats (e.g. mp3) using pydub before upload
- Streaming text reply: inject on_event callback into context; send a
card
placeholder on first delta, then PATCH-update it in-place at a
configurable interval (feishu_stream_interval_ms) to achieve typewriter
effect; set feishu_streamed=True to suppress duplicate send()
- Enable NOT_SUPPORT_REPLYTYPE=[] to unblock voice and image reply types
- Fix AudioSegment mutation bug in audio_convert.py: set_frame_rate /
set_channels return new objects and must be reassigned
- Add -nostdin to ffmpeg invocation to prevent stdin deadlock in daemon
- Add feishu_bot_name, feishu_stream_reply, feishu_stream_interval_ms
config keys to config-template.json