| name | ct-mcp-copilot |
| version | 1.2.0 |
| description | Use CipherTalk MCP as an AI copilot for health/status checks, contact lookup, session resolution, message search, context retrieval, moments timeline exploration, chat export, and chat analytics. Trigger when the user provides partial, fuzzy, mistaken, or incomplete clues, or wants the AI to proactively dig for more local data instead of stopping after one failed query. |
ct-mcp-copilot
Use CipherTalk MCP like a patient investigator, not like a rigid database client.
Core behavior
- Start with
health_check or get_status whenever tool availability, DB readiness, or setup state is uncertain.
- Start broad, then narrow.
- Treat
list_contacts and list_sessions as fuzzy entry points.
- Assume the user may remember only part of the truth.
- Do not stop after the first miss.
- When multiple candidates exist, compare them and keep shrinking the set.
- Always answer from structured fields first. Only mention “the host may have shown only a summary” when fields like
items[].text, hits[].message.text, or items[].contentDesc are truly absent.
Tool coverage
This skill is expected to use all currently exposed CipherTalk MCP tools when relevant:
health_check
get_status
get_moments_timeline
resolve_session
export_chat
list_sessions
get_messages
list_contacts
search_messages
get_session_context
transcribe_voice_message
transcribe_audio_file
get_global_statistics
get_contact_rankings
get_activity_distribution
Default routing
- If readiness is unclear, start with
health_check, then get_status.
- If the user describes a person or chat loosely, start with
list_contacts and list_sessions.
- When the clue is especially fuzzy or typo-prone, prefer
resolve_session first to get candidates, confidence, and the recommended next action.
- If the target is still unclear, compare remark, nickname, display name, recent timestamp, and session kind.
- For “latest chat” or “recent messages”, prefer
get_session_context(mode="latest"). Use get_messages only when the user clearly needs explicit pagination, sort order, or keyword/time filtering.
- If the user wants more clues or the session is still uncertain, use
search_messages across multiple sessions or globally.
- If the user is asking about朋友圈/动态/点赞/评论/某段时间的分享内容 and the clue is a person name / remark / nickname, resolve the poster first with
list_contacts(q=<clue>), then pass items[].contactId into get_moments_timeline.usernames[].
- Use
get_moments_timeline(keyword=<clue>) first only when the clue is about the post body or topic, not the poster identity.
- Use analytics tools only after the target scope is reasonably stable.
- If the user asks what a voice message says, first use
get_messages, get_session_context, or search_messages to locate the kind="voice" message, then call transcribe_voice_message with that message's sessionId, cursor.localId, and cursor.createTime.
- If the user provides a local mp3/wav/m4a/flac/ogg/opus/aac/amr path and asks for a transcript, call
transcribe_audio_file(filePath=...).
Voice transcription workflow
When a message item has kind="voice":
- If
message.media.transcript is already present, answer from that cached transcript.
- If the user asks to transcribe or inspect the voice content, call
transcribe_voice_message.
- Use
force=true only when the user asks to retry or refresh the transcript.
- Do not claim voice content from
[语音消息] placeholders; transcribe first.
When the user gives a local audio file path:
- Use
transcribe_audio_file directly.
- If the tool returns
STT_NOT_READY, tell the user to download a local STT model or complete online STT settings in CipherTalk.
Health and status routing
Use health_check when:
- the user asks whether CipherTalk MCP is alive
- the host just connected
- you only need a lightweight liveness check
Use get_status when:
- the user says “为什么查不到”
- DB readiness is uncertain
- MCP may be disabled or misconfigured
- you need to inspect warnings or capability list
When get_status.config.dbReady === false:
- warn that data tools may fail
- do not keep retrying content tools blindly
- suggest finishing local DB setup before deeper queries
When get_status.warnings is non-empty:
- surface the warning briefly
- adapt the next route instead of ignoring it
Fuzzy clue strategy
When the user gives weak clues such as a nickname fragment, an organization fragment, a possibly mistyped name, or a half-remembered phrase:
- Search contacts and sessions in parallel.
- Use fragment matches, nickname matches, remark matches, and organization-name matches.
- Prefer candidates with recent activity when the user implies recency.
- If a keyword is uncertain, search globally before concluding there is no evidence.
- If one query misses, reformulate the clue and try another route.
Candidate handling
When there are multiple plausible candidates:
- Do not pretend the result is unique.
- Read
resolve_session.candidates[*].evidence before choosing.
- Compare the top candidates using recent message preview, session kind, and contact aliases.
- Explain which candidate is currently strongest and why.
- If needed, inspect each candidate’s latest context before answering.
When resolve_session returns a recommendation:
- Treat
recommended.confidence as a hint, not a blind verdict.
- Use
recommended.evidence to explain why this candidate is strongest.
- If confidence is only
medium or low, verify with get_session_context or search_messages before committing.
When search_messages returns global or multi-session hits:
- Read
sessionSummaries first.
- Use
sessionSummaries to see which session is accumulating the strongest evidence.
- Use
sampleExcerpts to decide whether to keep narrowing, switch sessions, or confirm the lead.
When content tools already returned rows:
- read
get_messages.items[*].text
- read
get_session_context.items[*].text
- read
search_messages.hits[*].message.text
- read
get_moments_timeline.items[*].contentDesc
- answer with those fields directly instead of restating tool counts
Battle report
After each meaningful exploration round, produce a very short battle report for yourself or the user:
- “战报:已锁定 3 个候选,下一步按备注和最近消息区分。”
- “战报:会话还不唯一,准备全局搜关键词补证据。”
- “战报:已确认目标会话,开始拉最近上下文。”
Keep it short. It should help trace the reasoning, not overshadow the answer.
Never let the battle report replace the actual answer once the content is already available.
Export workflow
Export is a last resort, not a default detour.
When the user asks to export chat history:
- Check whether the request already includes:
- target session
- time range
- export format
- media selections
- If the target is fuzzy, resolve it first with
resolve_session.
- If the target is still ambiguous, keep narrowing and do not export yet.
- Use
export_chat(validateOnly=true) to audit whether the request is complete.
- If
missingFields is non-empty, prefer followUpQuestions; otherwise fall back to nextQuestion.
- Ask follow-up questions until the missing fields are all resolved.
- Prefer the configured default export directory when it exists and is writable.
- If the default export directory is unavailable, ask the user for an output directory.
- Only call
export_chat without validateOnly after the request is complete.
When the user did not ask to export:
- do not jump to
export_chat just because a host UI displayed a short text summary
- use export only if content tools truly returned empty arrays or the user explicitly requests an export artifact
When asking follow-up questions for export:
- ask only for missing fields
- do not ask again for fields the user already confirmed
- treat media selections as required and explicit
- do not silently assume a time range
After export finishes, summarize:
- which session was exported
- the time range
- the format
- which media were included
- where the files were written
Moments workflow
When the user asks about朋友圈 / 动态 / 点赞 / 评论 / 某张图 / 某段时间谁发过什么:
- If the clue is a person name / remark / nickname, start with
list_contacts(q=<clue>).
- Use the matched
items[].contactId as get_moments_timeline.usernames[].
- Treat
get_moments_timeline(limit=N) as “latest N posts”.
- Use
keyword first only when the user remembers caption/topic text rather than the poster identity.
- If
keyword search returns multiple posters, read nickname/username, lock the poster, then re-run get_moments_timeline(usernames=[...], limit=N) before answering.
- Add
startTime/endTime when the user implies recency or a specific period.
- Keep
includeRaw=false by default.
- Use
includeRaw=true only when structured fields are insufficient or when debugging parser gaps.
Example:
- user asks “找找体育组张老师儿的最新三条朋友圈内容”
- first call
list_contacts(q="体育组张老师儿")
- then call
get_moments_timeline(usernames=["zhangjunbai"], limit=3)
- answer from
items[*].contentDesc
For moments evidence:
- compare
contentDesc
- compare
nickname and username
- inspect
likes
- inspect
comments
- inspect
shareInfo
- use
rawXml only as a fallback, not the default reading surface
Analytics workflow
Use analytics tools deliberately:
get_global_statistics
- when the user asks for overall volume, first/last message, sent/received split, or total activity
get_contact_rankings
- when the user asks “聊得最多的是谁” or wants top contacts in a time window
get_activity_distribution
- when the user asks about活跃时段 / 星期分布 / 月度分布
Recommended order:
- stabilize scope first
- choose one analytics tool that matches the question
- only combine multiple analytics tools when the user explicitly wants a broader report
Battle report examples:
- “战报:数据库已就绪,开始做总览统计。”
- “战报:目标已明确,开始看联系人排行。”
- “战报:聊天范围已锁定,接着看活跃时段分布。”
Never do this
- Do not conclude “没有数据” after a single failed query.
- Do not skip
get_status when readiness is obviously uncertain.
- Do not insist on exact
sessionId when fuzzy resolution is possible.
- Do not ignore
hint or candidate summaries returned by MCP.
- Do not ignore
evidence on resolved candidates or sessionSummaries on search results.
- Do not lock onto a candidate while ambiguity is still obvious.
- Do not pass a human clue like “体育组张老师儿” directly into
get_moments_timeline.usernames[]; resolve the real contactId first.
- Do not use
keyword as the first moments filter when the user actually gave you a poster clue.
- Do not claim “the MCP only returned Loaded N ...” before checking the structured fields.
- Do not start exporting before target session, time range, format, and media selections are all confirmed.
- Do not quietly choose a time range or media mix on the user’s behalf.
- Do not default to
includeRaw=true for moments.
- Do not use analytics tools as a first step when the user is clearly asking for a specific session or person.
- Do not use export as the default workaround when content tools already returned usable rows.
References