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, memory search, context retrieval, moments timeline exploration, and chat export. 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_checkorget_statuswhenever tool availability, DB readiness, or setup state is uncertain. - Start broad, then narrow.
- Treat
list_contactsandlist_sessionsas 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, oritems[].contentDescare truly absent.
Tool coverage
This skill is expected to use all currently exposed CipherTalk MCP tools when relevant:
health_checkget_statusget_moments_timelineresolve_sessionexport_chatlist_sessionsget_messageslist_contactssearch_messagessearch_memoryget_session_contexttranscribe_voice_messagetranscribe_audio_file
Default routing
- If readiness is unclear, start with
health_check, thenget_status. - If the user describes a person or chat loosely, start with
list_contactsandlist_sessions. - When the clue is especially fuzzy or typo-prone, prefer
resolve_sessionfirst 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"). Useget_messagesonly 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_messagesacross 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 passitems[].contactIdintoget_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. - If the user asks what a voice message says, first use
get_messages,get_session_context, orsearch_messagesto locate thekind="voice"message, then calltranscribe_voice_messagewith that message'ssessionId,cursor.localId, andcursor.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.transcriptis already present, answer from that cached transcript. - If the user asks to transcribe or inspect the voice content, call
transcribe_voice_message. - Use
force=trueonly 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_filedirectly. - 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[*].evidencebefore 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.confidenceas a hint, not a blind verdict. - Use
recommended.evidenceto explain why this candidate is strongest. - If confidence is only
mediumorlow, verify withget_session_contextorsearch_messagesbefore committing.
When search_messages returns global or multi-session hits:
- Read
sessionSummariesfirst. - Use
sessionSummariesto see which session is accumulating the strongest evidence. - Use
sampleExcerptsto 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
missingFieldsis non-empty, preferfollowUpQuestions; otherwise fall back tonextQuestion. - 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_chatwithoutvalidateOnlyafter the request is complete.
When the user did not ask to export:
- do not jump to
export_chatjust 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[].contactIdasget_moments_timeline.usernames[]. - Treat
get_moments_timeline(limit=N)as “latest N posts”. - Use
keywordfirst only when the user remembers caption/topic text rather than the poster identity. - If
keywordsearch returns multiple posters, readnickname/username, lock the poster, then re-runget_moments_timeline(usernames=[...], limit=N)before answering. - Add
startTime/endTimewhen the user implies recency or a specific period. - Keep
includeRaw=falseby default. - Use
includeRaw=trueonly 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
nicknameandusername - inspect
likes - inspect
comments - inspect
shareInfo - use
rawXmlonly as a fallback, not the default reading surface
Never do this
- Do not conclude “没有数据” after a single failed query.
- Do not skip
get_statuswhen readiness is obviously uncertain. - Do not insist on exact
sessionIdwhen fuzzy resolution is possible. - Do not ignore
hintor candidate summaries returned by MCP. - Do not ignore
evidenceon resolved candidates orsessionSummarieson 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 realcontactIdfirst. - Do not use
keywordas 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=truefor moments. - Do not use export as the default workaround when content tools already returned usable rows.
References
- Read references/queries.md when you need concrete fuzzy-query playbooks, fallback chains, or battle-report examples.
- Read references/export.md when the user asks to export chat history.
- Read references/moments.md when the user asks about朋友圈、点赞、评论、转发内容或时间段动态。