MCP Tools Reference
Complete reference for Nexus-Catalyst MCP tools exposed via tools/list: six discrete memory tools and two Linear integration tools, split by read/write.
Overview
Section titled “Overview”Nexus-Catalyst exposes 8 client-facing MCP tools. Models call them directly by name; action is only used on the two integration tools, which each cover several related Linear operations.
| Tool | Type | Purpose |
|---|---|---|
recall_context |
Discrete | Session-start semantic recall + persona hint |
get_persona_definition |
Discrete | Load a specialized AI persona |
search_memories |
Discrete | Mid-session ad-hoc memory search |
get_memory_detail |
Discrete | Fetch full content for a specific memory |
store_context |
Discrete | Save session memory at end of turn |
update_user_facts |
Discrete | Update the durable user facts document |
integration_query |
Read-only | Fetch Linear issues, projects, teams, labels, comments |
integration_mutate |
Write | Create or modify Linear issues, projects, labels, comments |
Session workflow
Section titled “Session workflow”Every session should follow this pattern:
1. recall_context → search memories, get persona_hint2. get_persona_definition → only if persona_hint returned3. [work happens — search_memories / get_memory_detail as needed]4. store_context → persist what was decided5. update_user_facts → only if a durable fact about the user surfacedintegration_query / integration_mutate are used when you need Linear — for
example searching for a related issue, or creating one from a debugging
session.
recall_context
Section titled “recall_context”Always call first at the start of every session, before responding.
Performs semantic search of your NC memory store and returns truncated summaries plus an optional persona_hint.
Parameters
Section titled “Parameters”| Parameter | Required | Description |
|---|---|---|
query |
Yes | Short topic/keywords for the request — the fallback search term when user_query_full is absent |
user_query_full |
No (recommended) | The user’s complete, verbatim message for this turn, untruncated. When provided, recall matches on it instead of query for materially better retrieval on long or detailed requests |
use_full_query |
No | Whether to search on user_query_full when present. Defaults true — you normally don’t set this |
topics |
No | Comma-separated distinct topics (each ≤10 words). A hint, not a switch — see Topic fan-out below. Only send topics that are genuinely distinct |
conversation_id |
No | Thread ID, format: nc-[topic]-[YYYYMMDD]. Reuse across turns in the same session |
program_tool |
No | Client identifier: claude-desktop, cursor, claude-code, web-interface, api-direct |
Topic fan-out: when a request is genuinely broad, recall runs one focused search per topic instead of a single blurred one.
It triggers only when the query is at least 200 characters and either carries 2+ distinct topics or exceeds the length threshold. Sending several topics on a short question does not fan out — the hint alone cannot trigger it.
memories is always the flat, deduplicated result set. When a request fans out, the response adds:
"fanned_out": true,"topic_count": 3,"topic_groups": [ { "topic": "bearer auth", "memory_ids": ["mem_abc123", "mem_def456"] }, { "topic": "deploy", "memory_ids": ["mem_def456"] }]topic_groups carries ids only — join them against memories, which holds the objects. A memory relevant to two topics appears in both groups but exactly once in memories.
Results scale with the number of topics, deduplicated:
| Branches | Per topic | Max total |
|---|---|---|
| 1 (no fan-out) | 10 | 10 |
| 2 | 8 | 16 |
| 3 | 7 | 21 |
| 4 | 6 | 24 |
| 5 (ceiling) | 6 | 30 |
Example
Section titled “Example”What database setup did we decide on for the e-commerce project?Tool call:
{ "query": "e-commerce database setup decision", "user_query_full": "What database setup did we decide on for the e-commerce project?", "conversation_id": "nc-ecommerce-db-20260529", "program_tool": "cursor"}Response (shape):
{ "memories": [ { "memory_id": "mem_abc123", "summary": "Database: PostgreSQL 15 with Prisma ORM", "confidence": 0.95 } ], "persona_hint": "jack"}If persona_hint is present → call get_persona_definition next. Use returned memory_id values with get_memory_detail when summaries are not enough.
get_persona_definition
Section titled “get_persona_definition”Retrieves the full definition, communication style, and behavioral guidelines for a recommended persona.
Only call when recall_context returns a persona_hint in the current session.
Parameters
Section titled “Parameters”| Parameter | Required | Description |
|---|---|---|
persona_name |
Yes | Persona to load (e.g. jack, betty, nora) |
Example
Section titled “Example”{ "persona_name": "jack"}search_memories
Section titled “search_memories”Ad-hoc semantic search mid-session. Use when you need something specific that was not returned by recall_context at session start.
Returns truncated summaries and memory_id values — use get_memory_detail for full content.
Parameters
Section titled “Parameters”| Parameter | Required | Description |
|---|---|---|
query |
Yes | Search query (fallback when user_query_full is absent) |
user_query_full |
No (recommended) | The user’s complete, verbatim request. When provided, search matches on it instead of query |
conversation_id |
No | Thread identifier |
max_results |
No | Max results (default 5, max 20) |
Example
Section titled “Example”Search for authentication decisions in the last month{ "query": "authentication JWT refresh token decisions", "user_query_full": "Search for authentication decisions in the last month", "max_results": 10}get_memory_detail
Section titled “get_memory_detail”Fetch the full content of a specific memory by memory_id. Second step of the recall → detail pattern.
Parameters
Section titled “Parameters”| Parameter | Required | Description |
|---|---|---|
memory_id |
Yes | ID returned by recall_context or search_memories |
Example
Section titled “Example”{ "memory_id": "mem_abc123"}Returns full fields: summary, context (untruncated), conversation_id, timestamp, persona, artifact_url, company_client, client_project.
recall_context returns each memory’s summary capped at 1200 characters — enough to answer most questions without a follow-up. Call get_memory_detail(memory_id) when you need the untruncated context; that is the division of labour, so recall stays cheap for the caller’s context window.
store_context
Section titled “store_context”Call at the end of every session to record what was discussed. The model generates summary and context — do not ask the user to provide them.
Parameters
Section titled “Parameters”| Parameter | Required | Description |
|---|---|---|
summary |
Yes | What was asked and decided. 50–75 tokens max |
context |
Yes | Full technical detail. Target 2000–8000 tokens; do not truncate errors, paths, or commands |
conversation_id |
Yes | Must match the ID used in recall_context this session |
user_query |
No | User’s verbatim opening question — improves future recall matching |
persona |
No | Persona used this session |
billable |
No | Mark as billable client work |
company_client |
No | Client name |
client_project |
No | Project name |
artifact_url |
No | Associated artifact URL (nc-doc://… or https://…) |
Example
Section titled “Example”{ "summary": "Decided on PostgreSQL 15 with Prisma ORM for e-commerce API.", "user_query": "What ORM should we use with Postgres for the e-commerce project?", "context": "Evaluated Prisma vs Drizzle. Chose Prisma for migration tooling and team familiarity. Connection string in .env as DATABASE_URL. Schema lives in prisma/schema.prisma.", "conversation_id": "nc-ecommerce-db-20260529", "client_project": "e-commerce"}update_user_facts
Section titled “update_user_facts”Update the user’s durable facts document — a JSON merge patch (set paths to values; null deletes a path). The merged document is auto-injected into the first recall_context of every new conversation, so quality matters more than completeness.
Write ONLY when all three hold — see the fact-write gate for the full rule:
- Durable, not situational — a truth about the user, their people, businesses, or infrastructure, not a session outcome.
- Data, not a rule —
wife.name: Nicoleis a fact; “never fabricate a last name” is an instruction and must never be stored here. - Correct over append — on conflict, overwrite or delete the stale path rather than accreting variants.
Parameters
Section titled “Parameters”| Parameter | Required | Description |
|---|---|---|
facts_patch |
Yes | JSON merge patch object. Nested objects merge; scalars/arrays replace; null deletes a path |
Example
Section titled “Example”{ "facts_patch": { "family": { "wife": { "name": "Nicole" } } } }The merged document is hard-capped at ~4KB; an oversize write is rejected — prune stale paths first. Facts are always private to the user and never enter team recall or any shared surface.
integration_query
Section titled “integration_query”Read-only Linear queries: fetch issues, projects, teams, labels, statuses, users and comments. This tool never creates or modifies anything — use integration_mutate for that.
An action parameter selects the operation; everything else is passed as a top-level parameter.
Every response uses the standard envelope:
{ "success": true, "message": "Linear issues retrieved", "data": { "…": "…" } }Choosing a workspace
Section titled “Choosing a workspace”Nexus-Catalyst stores one Linear API key per organization, labelled ("AV", "JL", …) when you connect it.
- One org connected —
workspaceis optional and ignored. - Two or more — pass
workspaceon every call. Omitting it returns a disambiguation response rather than an error:success: truewithdisambiguation_required: true,available_workspaces, and a message naming the valid labels. An unrecognised label returns the same shape. Matching is case-insensitive.
This is what lets one conversation read and write across separate Linear organizations without re-authenticating — see Nexus-Catalyst and Linear’s own MCP.
Actions
Section titled “Actions”| Action | Required | Returns |
|---|---|---|
get_linear_teams |
— | teams[], total |
get_linear_projects |
— | projects[], total |
get_linear_issue |
issue_id |
issue — full description, no excerpting |
search_linear_issues |
query or team_id |
see Searching issues |
get_linear_project_detail |
project_id |
project with members, teams, labels, issue_count |
get_linear_team_detail |
team_id |
team with members, states, labels, issue_count |
list_linear_labels |
— | labels[] — workspace-wide, see Labels |
list_linear_project_labels |
project_id |
labels[] |
list_linear_statuses |
team_id |
statuses[] — workflow state UUIDs |
list_linear_users |
— | users[] — assignee UUIDs |
list_linear_comments |
issue_id |
comments[] (first 50, excerpted) |
get_linear_comment_detail |
issue_id |
Full, unexcerpted comment thread — the drill-in from list_linear_comments |
get_project_context |
project_id or team_id |
issues + linked memories, see get_project_context |
Searching issues
Section titled “Searching issues”search_linear_issues requires at least one of query or team_id — a filter alone is not enough to scope a search.
| Parameter | Description |
|---|---|
query |
Matches the issue title only, case-insensitively, first 100 characters of the term. It does not search descriptions or comments |
team_id |
Restrict to one team |
limit |
Default 25, max 100 |
description_chars |
Excerpt length for descriptions, default 200, 0 to omit — see Description excerpts |
search_filters |
{ state, label, project } — nothing else |
search_filters
| Key | Value |
|---|---|
state |
open (started + unstarted), closed (completed + cancelled), or a specific one: active, in_progress, started, todo, unstarted, done, completed, cancelled, backlog, triage |
label |
Label name, exact and case-sensitive. Matches issues carrying it among other labels. "Launch-Critical" finds nothing when the label is launch-critical |
project |
Project UUID from get_linear_projects |
Response
| Field | Meaning |
|---|---|
issues |
The matches |
returned_issues |
How many issues are in this response |
total |
The same number. Kept for older callers; it is the count returned, not the count matching |
may_be_truncated |
true when the result filled the limit, so more may exist |
applied_filters |
The filter keys that were actually used — read this back |
detail_hint |
Present when descriptions were shortened; names the drill-in call |
query, team_id |
Echoed back when supplied |
Description excerpts
Section titled “Description excerpts”Issue descriptions dominate the payload of a multi-issue response — on one real project, 94KB of 123KB. Both search_linear_issues and get_project_context therefore excerpt them, and always say so.
description_chars — default 200, maximum 2000, clamped rather than trusted. 0 omits descriptions entirely.
Per issue, the response then carries:
description— the excerpt (absent whendescription_chars: 0)description_truncated: true— when the body was cutdescription_chars_available— the full length, so you can tell an omitted body from an empty one
Use get_linear_issue with the identifier for one issue’s complete description. It never excerpts.
get_project_context
Section titled “get_project_context”Issues plus the Nexus-Catalyst memories linked to them, scoped by project_id or team_id.
| Parameter | Description |
|---|---|
project_id / team_id |
One of the two is required |
include_closed |
Default false |
description_chars |
As above |
| Response field | Meaning |
|---|---|
scoped_by |
"team" or "project" — which one answered |
issues |
Up to 50, description-trimmed |
returned_issues |
How many came back |
issues_may_be_truncated |
true when the 50-issue cap was hit |
related_memories, total_memories |
Memories linked to the first 20 issues |
patterns |
Aggregate signal across the issues |
detail_hint |
As above |
Listing labels
Section titled “Listing labels”list_linear_labels returns every label in the organization, including workspace-level ones. It takes team_id only to echo it back — workspace-level labels have no team association, so filtering by team would hide the labels most issues actually use.
Example — find open issues carrying a label
Section titled “Example — find open issues carrying a label”{ "action": "search_linear_issues", "workspace": "AV", "team_id": "a0a14518-cdb4-4a17-912e-6d28e77a8d01", "search_filters": { "state": "open", "label": "launch-critical" }, "description_chars": 0, "limit": 100}integration_mutate
Section titled “integration_mutate”Linear actions that create or modify records: issues, projects, labels, comments, and the memory↔issue cross-reference rows. Every action here writes — use integration_query for reads.
An action parameter selects the operation; everything else is passed as a top-level parameter. Same response envelope and workspace selection as integration_query.
Actions
Section titled “Actions”| Action | Required | Notes |
|---|---|---|
create_linear_issue |
title, team_id |
Alias of save_linear_issue without an issue_id |
save_linear_issue |
issue_id to update; title + team_id to create |
See Writing issues |
auto_create_linear_issue |
title, team_id |
Legacy alias of create_linear_issue — prefer create_linear_issue |
create_linear_project |
name, team_ids or team_id |
Projects belong to one or more teams, so this one takes an array |
create_linear_label |
name |
team_id optional and meaningful — see Creating and renaming labels |
update_linear_label |
id + one of name / color / description |
Rename preserves the label id and every attachment |
create_linear_comment |
issue_id, body |
|
link_memory_to_linear_issue |
memory_id, issue_id |
Stores the link and stamps the memory |
cross_reference_linear_memory |
memory_id |
Finds issues related to a memory and links them |
update_ticket_from_memory |
issue_id, memory_id |
update_type: add_comment (default) or update_description |
Writing issues
Section titled “Writing issues”Pass write fields at the top level — never inside search_filters.
| Parameter | Applies to | Description |
|---|---|---|
title |
create / update | Required to create |
description |
create / update | Markdown body |
team_id |
create / update | Team UUID from get_linear_teams. Required to create. On an update this MOVES the issue — see below |
project_id |
create / update | Project UUID from get_linear_projects |
priority |
create / update | 1 Urgent, 2 High, 3 Medium, 4 Low. Defaults to 3 on create |
state_id |
create / update | Workflow state UUID from list_linear_statuses. This is how you close an issue |
assignee_id |
create / update | User UUID from list_linear_users |
labels |
create / update | Array of label UUIDs, not names. See the replacement warning below |
issue_id |
update | UUID or human identifier (NC-103); an identifier is resolved for you |
An update that supplies no recognised field is refused rather than reported as a success. Linear’s issueUpdate answers success: true for an empty input, so a typo’d parameter name would otherwise look exactly like a completed write.
Creating and renaming labels
Section titled “Creating and renaming labels”create_linear_label — the team_id parameter decides the label’s scope, permanently in practice:
- omit
team_id→ a workspace-level label every team can use - supply
team_id→ a team-scoped label. Linear refuses to put it on another team’s issue
The response reports scope as "workspace" or "team", read back from Linear rather than echoed from the request.
update_linear_label renames or recolours a label in place. The id survives, so every issue already carrying it keeps it. Linear label names are case-sensitive and it does not dedupe on case, so a rename is the right fix for a casing mistake — creating the lowercase one alongside leaves you with two.
Example — create an issue with labels
Section titled “Example — create an issue with labels”{ "action": "create_linear_issue", "workspace": "AV", "title": "CORS error from localhost", "description": "API returns CORS failure when the frontend calls from localhost:3000", "team_id": "a0a14518-cdb4-4a17-912e-6d28e77a8d01", "priority": 2, "labels": ["bdddef24-39ce-45f9-bc2b-7497ffd8e6d5"]}Example — close an issue
Section titled “Example — close an issue”{ "action": "save_linear_issue", "workspace": "AV", "issue_id": "NC-103", "state_id": "61eb3529-20a8-44cc-b0a6-d773e3d74ad5"}Resolve the state UUID with list_linear_statuses first. Writing “Closes NC-103” in a commit message does nothing here.
Example — link a memory to an issue
Section titled “Example — link a memory to an issue”{ "action": "link_memory_to_linear_issue", "workspace": "AV", "memory_id": "mem_abc123", "issue_id": "NC-103"}Common patterns
Section titled “Common patterns”Chaining memory + integration
Section titled “Chaining memory + integration”Bug reported→ recall_context (similar past bugs)→ search_memories (narrow search if needed)→ get_memory_detail (full prior fix write-up)→ integration_mutate.create_linear_issue (track bug)→ store_context (save resolution workflow)Recall → detail pattern
Section titled “Recall → detail pattern”recall_context or search_memories → summaries + memory_idsget_memory_detail → full preserved conversationError format
Section titled “Error format”All tools return a consistent error shape:
{ "error": true, "message": "Descriptive error message", "code": "ERROR_CODE", "suggestion": "How to fix the issue"}Best practices
Section titled “Best practices”Use natural language — let the client choose the right tool:
✅ "What did we discuss about authentication?"❌ "Execute recall_context with query authentication"Follow the session workflow — recall_context first, store_context last.
Use get_memory_detail when summaries are insufficient — don’t guess from truncated text.
Don’t skip recall at session start — context from prior sessions won’t load automatically otherwise.
Don’t nest Linear write fields in search_filters — pass title, team_id, etc. at the top level.
Don’t call get_persona_definition without a persona_hint — wastes a tool call.
Related docs
Section titled “Related docs”-
MCP Instructions — the standing prompt that makes a client use these tools unprompted.
-
Memory System — Storage tiers and organization
-
Linear Integration — Setup and workflows
Back to: Documentation Home