Skip to main content

Meta-tools

After connecting, tools/list returns five CortexHub agent tools. Downstream integrations are discovered and invoked through these meta-tools only (never call slack.post_message directly on tools/call).

ToolPurpose
cortexhub_search_toolsFind tools by intent
cortexhub_get_tool_schemasLoad parameters_schema for chosen tools
cortexhub_invoke_toolsRun 1 to 10 governed calls (returns an MCP Task when parked)
cortexhub_get_taskPoll a parked MCP Task (returns in ~1s; loop until terminal)
cortexhub_request_consentOptional explicit consent card when task inputRequests are not enough

When cortexhub_invoke_tools parks for approval, the response hoists mcp_task and next_step to the top level (and repeats them in content[0]). Call cortexhub_get_task in a loop with mcp_task.task_id. Each call returns within about 1 second. If terminal is false, call again immediately with the same task_id until terminal is true. The final tool result is in invoke when status is completed. After approval, CortexHub runs the tool automatically; do not call invoke again for the same parked call.

MCP hosts that support MCP Tasks (2025-11-25) augment cortexhub_invoke_tools with params.task, poll tasks/get for status, and retrieve the final tool result via tasks/result at the transport layer. SDK agents and co-workers without native Tasks use cortexhub_get_task in a loop instead.

Parked tasks surface the consent MCP App via inputRequests and a browser consent URL; the task stays working until decided. Optionally call cortexhub_request_consent with mcp_task.approval_id for an explicit consent card.

When a toolkit is not connected yet, share connect_required.portal_url.

The objective field​

Every meta-tool call — cortexhub_search_tools, cortexhub_get_tool_schemas, and cortexhub_invoke_tools — takes a required objective: the member's overall goal for the task, in one sentence (for example, "Book a flight to NYC next Friday"). State it on the first call and keep it identical for the whole task.

objective is the stable ask, distinct from each call's rationale (why that one call is needed). CortexHub uses it to group the task's calls into a single trace and to anchor intent-drift detection, so the whole task reads as one unit of work in the session view. See Sessions and attribution: one connection is a session, each objective is a trace, and each governed tool call is a span under it.

MCP prompts​

prompts/list advertises an MCP prompt for cortexhub_search_tools, cortexhub_get_tool_schemas, cortexhub_invoke_tools, and cortexhub_get_task, plus cortexhub_agent_loop for the full workflow. Clients invoke prompts/get with all required arguments to receive step-by-step instructions before calling tools. Missing arguments return InvalidParams.

PromptRequired arguments
cortexhub_search_toolsquery
cortexhub_get_tool_schemastool_names (comma-separated qualified names)
cortexhub_invoke_toolsgoal
cortexhub_get_tasktask_id
cortexhub_agent_looptask

Agent loop​

cortexhub_search_tools(objective)
→ cortexhub_get_tool_schemas(objective, names from search)
→ cortexhub_invoke_tools(objective, calls with tool_name + arguments + rationale + presentation)
├─ status: ok → use the result
├─ parked for approval → cortexhub_get_task until terminal
├─ status: connection_required → share connect_required.portal_url
└─ policy_denied, tool_error → terminal outcome

A search that returns no hits but surfaces a suggestions[].connect_required block should share portal_url with the user, then retry the search.

Agent instructions (copy-paste)​

Use CortexHub for all external actions:
0. Set objective once: the member's overall goal in one sentence. Pass the SAME
objective on every cortexhub_* call for this task (it groups the task into one
trace). It is the stable ask, separate from each call's rationale.
1. cortexhub_search_tools(objective): pick tool_name values from results.
2. cortexhub_get_tool_schemas(objective): pass those tool_name values in names[].
3. cortexhub_invoke_tools(objective): each call needs tool_name, arguments (object),
rationale, and presentation (headline + sections[] the approver reads).
4. Parked calls: call `cortexhub_get_task` with `mcp_task.task_id` until `terminal`
is true. Optionally call `cortexhub_request_consent` with `mcp_task.approval_id`
for an explicit consent card or browser link.
5. When search, schema lookup, or invoke returns connect_required, share portal_url
with the user so they connect the toolkit in CortexHub Cloud, then retry.

Platform toolkits (when entitled; same search -> schemas -> invoke loop):
- agent-memory.recall / agent-memory.remember: long-term memory for this co-worker
and member. External MCP clients do not get automatic recall: call recall at the
start of multi-step work or when prior context may matter. Call remember when the
member asks you to remember something durable, or when a standing preference or
fact should persist across conversations. Store concise facts only.

Put every required schema field inside calls[].arguments, not next to tool_name.
Follow next_step strings in every response.
Never call downstream tool names on tools/call.
Treat policy_denied as a terminal outcome.

Interactive surfaces​

Consent uses task inputRequests or optional cortexhub_request_consent (ui://cortexhub/consent). Toolkit connect uses connect_required.portal_url only.

SurfaceMCP App clientClient without MCP Apps
End-user consentTask inputRequests or optional cortexhub_request_consentShare consent_url from cortexhub_request_consent
Toolkit connectN/A (link-only)Share portal_url from connect_required
Operator reviewcortexhub_get_task until terminalcortexhub_get_task until terminal

Each meta-tool response includes a next_step string. Follow it.


1. cortexhub_search_tools​

Input

{
"query": "zoom meeting transcript",
"limit": 10
}

Use either query (string) or queries (up to 10 strings), not both. limit defaults to 10 (max 25). When queries is supplied, every query is searched independently and the results are merged so multi-intent agents get full recall.

Response

{
"results": [
{
"tool_name": "zoom.get_meeting_assets",
"summary": "List transcripts, recordings, and summaries for one meeting.",
"description": "List transcripts, recordings, and summaries for one meeting. Use when the user asks for meeting assets, not to create a meeting."
}
],
"next_step": "Call cortexhub_get_tool_schemas with the tool_name values from results, then cortexhub_invoke_tools with each tool_name, a matching arguments object, and a rationale."
}

Each hit includes:

FieldRole
tool_nameQualified catalogue name (toolkit_slug.tool_name)
toolkit_slugToolkit slug for grouping and connect flows
toolkit_nameHuman label for the toolkit (platform toolkits and future catalogue rows)
toolkit_summaryWhat the toolkit is for (from the catalogue description)
use_whenRouting hint for platform toolkits (agent-memory)
summaryShort headline for scanning results
descriptionSanitized provider documentation (up to 4k chars). Prefer this when choosing between similar tools. Uses llm_description from the catalogue when operators have set one; otherwise normalized MCP/OpenAPI text (markdown stripped, structure kept).
connect_requiredPresent when the principal has entitlement but the credential path is not ready. For a member, share portal_url so they connect once in CortexHub Cloud; for an Org Agent, an admin must configure the org-shared credential.

When the query looks like memory, the response may also include platform_toolkit_hints[] with use_when and suggested_tools (for example agent-memory.recall). Read those hints before loading schemas.

When results include connect_required, next_step tells the agent to share each portal_url, then call cortexhub_get_tool_schemas before cortexhub_invoke_tools.

When no connected tool matches, you may also get:

{
"results": [],
"suggestions": [
{
"toolkit_slug": "zoom",
"summary": "Connect Zoom on this agent to use its tools.",
"connect_required": {
"toolkit_slug": "zoom",
"toolkit_label": "Zoom",
"message": "Zoom is not connected to this agent yet. Connect it in CortexHub, then return here and retry.",
"portal_url": "https://app.cortexhub.ai/orgs/{org}/me/connections/zoom",
"next_step": "Tell the user this toolkit is not connected to their agent yet. Share portal_url so they can open CortexHub, connect the toolkit, then return to this conversation and retry the original cortexhub_search_tools or cortexhub_invoke_tools call."
}
}
],
"next_step": "No connected tools matched. Share portal_url from each suggestion's connect_required block with the user, then search again."
}

Share portal_url with the user so they can connect the toolkit in CortexHub Cloud, then retry the search.

Optional notice appears when search ran in reduced-accuracy mode (retry with a more specific query).

Not returned: catalogue IDs, scores, toolkit kinds, or raw index metadata.


2. cortexhub_get_tool_schemas​

Input

{
"names": ["zoom.get_meeting_assets", "zoom.get_recording_resource"]
}

Pass exact tool_name values from search (1 to 50 per call).

Response

{
"schemas": {
"zoom.get_meeting_assets": {
"tool_name": "zoom.get_meeting_assets",
"parameters_schema": {
"type": "object",
"required": ["meetingId"],
"properties": {
"meetingId": {
"type": "string",
"description": "Meeting UUID from a prior list or assets call."
}
}
}
},
"zoom.get_recording_resource": null
},
"next_step": "Call cortexhub_invoke_tools with calls[{tool_name, arguments, rationale}]. Put every required schema field inside arguments, not next to tool_name."
}

null means the tool is not callable for this agent (not enabled or not found).

Not returned: tool_id, safety_profile, duplicate input_schema, or long catalogue descriptions.


3. cortexhub_invoke_tools​

Input

Every call in calls must include tool_name, arguments, rationale (max 500 chars), and presentation (headline + at least one sections[] entry). Build sections from the downstream schema in human-readable form. Never paste raw API payloads into sections.

{
"idempotency_key": "unique-stable-id",
"calls": [
{
"tool_name": "zoom.get_meeting_assets",
"arguments": {
"meetingId": "atsXxhSEQWit9t+U02HXNQ=="
},
"rationale": "User asked what was discussed on their last Zoom call; fetching meeting assets.",
"presentation": {
"headline": "Fetch Zoom meeting assets",
"sections": [
{"label": "Meeting ID", "value": "atsXxhSEQWit9t+U02HXNQ=="}
]
}
}
]
}

Missing presentation returns error: PRESENTATION_REQUIRED with details.example_email showing the expected shape for email tools.

Response: success

{
"results": [
{
"tool_name": "zoom.get_meeting_assets",
"ok": true,
"status": "ok",
"result": { }
}
]
}

Response: call parked for end-user consent

When governance parks a call, the per-call result includes an mcp_task block with the server-created task_id. Call cortexhub_get_task until terminal is true:

{
"results": [
{
"tool_name": "github.delete_repo",
"ok": true,
"status": "awaiting_user_consent",
"message": "I need your explicit consent before running this tool...",
"mcp_task": {
"task_id": "cxh_mtask_xyz",
"approval_id": "cxh_approval_xyz",
"next_step": "Call cortexhub_get_task with mcp_task.task_id until terminal is true...",
"request_consent_tool": "cortexhub_request_consent"
}
}
]
}

For autonomous runs, mcp_task may also list delivered_channels: ["slack", "email"].

Response: call needs a toolkit connect

When the called tool's toolkit isn't bound to the calling agent yet, the per-call result carries a connect_required block with a CortexHub portal link:

{
"results": [
{
"tool_name": "slack.post_message",
"ok": false,
"status": "connection_required",
"error": "CONNECTION_REQUIRED",
"connect_required": {
"toolkit_slug": "slack",
"toolkit_label": "Slack",
"message": "Slack is not connected to this agent yet. Connect it in CortexHub, then return here and retry.",
"portal_url": "https://app.cortexhub.ai/orgs/{org}/me/connections/slack",
"next_step": "Tell the user this toolkit is not connected to their agent yet. Share portal_url so they can open CortexHub, connect the toolkit, then return to this conversation and retry the original cortexhub_invoke_tools call."
}
}
]
}

connect_required is only emitted for the connection_required status. Unhealthy / expired / disabled connection states (connection_unhealthy, connection_expired, connection_disabled) need admin action. The agent surfaces the admin guidance from message/details and waits, rather than spinning up a new Connect flow.

Common non-success shapes:

statusMeaning
tool_error + VALIDATION_ERRORMissing or empty required arguments (check message and details.missing_arguments)
tool_error + TOOL_NOT_CALLABLETool not enabled on the agent. Read details.next_step, share connect guidance with the user, and call cortexhub_get_tool_schemas on the next attempt.
connection_requiredToolkit not connected. Share connect_required.portal_url with the user so they connect in CortexHub, then retry.
connection_unhealthy / connection_expired / connection_disabledAdmin action required; relay the admin guidance from message/details.
awaiting_user_consentEnd user must approve. Call cortexhub_get_task until terminal; optionally cortexhub_request_consent.
awaiting_operator_approvalOperator must approve outside this chat. Call cortexhub_get_task until terminal. Do not call cortexhub_request_consent or ask the user to approve in chat.
policy_deniedAccess rules blocked the call
upstream_provider_errorProvider rejected the call

Example validation error (before upstream is called):

{
"tool_name": "zoom.get_meeting_assets",
"ok": false,
"status": "tool_error",
"error": "VALIDATION_ERROR",
"message": "Missing required arguments: meetingId. Pass them inside calls[].arguments (not alongside tool_name). Call cortexhub_get_tool_schemas first if you have not loaded the schema.",
"details": {
"index": 0,
"missing_arguments": ["meetingId"]
}
}

Not returned: CortexHub audit identifiers, catalogue scores, operator delivery routing (Slack channel ids, webhook targets), or raw search metadata. End-user parked calls may include mcp_task.approval_id for cortexhub_request_consent; operator parked calls include only mcp_task.task_id for polling.

See Outcomes & errors for status codes.


4. cortexhub_get_task​

Poll a parked MCP Task after cortexhub_invoke_tools returns awaiting_user_consent or awaiting_operator_approval. Call in a loop until terminal is true. Each call returns within about 1 second (optional short server-side wait via wait_seconds, default 1, max 5).

Input

{
"task_id": "cxh_mtask_xyz",
"wait_seconds": 1
}

task_id is required. Copy it from mcp_task.task_id on the parked invoke result (top level or per-call results[]). wait_seconds is optional (0 to 5; default 1). Use 0 for a single non-blocking poll.

Response: still pending

{
"task_id": "cxh_mtask_xyz",
"status": "working",
"status_message": "Waiting for operator approval outside this chat...",
"poll_interval_ms": 5000,
"terminal": false,
"next_step": "Still in progress. Call cortexhub_get_task again immediately with the same task_id and keep polling until terminal is true."
}

Response: completed

{
"task_id": "cxh_mtask_xyz",
"status": "completed",
"status_message": "Tool call completed after approval.",
"terminal": true,
"invoke": {
"tool_name": "jira.deleteissue",
"ok": true,
"status": "ok",
"result": { }
},
"next_step": "Task completed. Read invoke for the final tool result. Do not retry cortexhub_invoke_tools for this parked call."
}

When status is input_required, the response may include input_requests for MCP App clients. Agents without in-chat consent should call cortexhub_request_consent, then continue polling with cortexhub_get_task.


The dedicated meta-tool that mounts an explicit in-client consent prompt. Call it with mcp_task.approval_id when task inputRequests are not enough.

Input (both fields required)

{
"approval_id": "cxh_approval_xyz",
"presentation": {
"headline": "Send email to team@acme.com",
"summary": "Follow-up on the Q2 plan with the attached summary.",
"sections": [
{ "label": "To", "value": "team@acme.com", "sensitive": true },
{ "label": "Subject", "value": "Q2 plan follow-up" },
{ "label": "Body", "value": "Hi team, …", "format": "preformatted" }
],
"warnings": ["This sends an email on your behalf."]
}
}

Both approval_id and presentation are required. Map downstream tool arguments into human-readable sections (e.g. email → To, Subject, Body). Consent channels show the full presentation; admin history stores only a redacted copy when tool-call storage is enabled.

Response: prompt mounted

{
"ok": true,
"status": "consent_ui_mounted",
"result": {
"approval_id": "cxh_approval_xyz",
"consent_url": "https://app.cortexhub.ai/consent?token=…",
"message": "Consent is ready. MCP App clients show an in-chat approval card; other clients should open consent_url in the browser.",
"next_step": "When the user approves or denies, CortexHub runs the tool automatically. Call cortexhub_get_task with the parked mcp_task.task_id until terminal is true."
}
}

Supporting MCP clients (Claude.ai, Cursor, …) mount the CortexHub consent App resource. The tool result also includes consent_url: a signed link to the CortexHub Cloud consent page (/consent?token=…) for clients that do not mount MCP Apps. Share that URL with the user when no in-chat card appears. After the user decides, call cortexhub_get_task until terminal is true.

Response: already decided

The handler is idempotent: re-calling with the same approval_id returns the terminal state without re-mounting the prompt.

{
"ok": true,
"status": "already_approved",
"result": {
"approval_id": "cxh_approval_xyz",
"message": "This approval is already approved.",
"next_step": "Approval is granted and the tool was dispatched. Call cortexhub_get_task with the parked mcp_task.task_id until terminal is true, then read invoke."
}
}

status will be one of already_approved, already_denied, already_expired, or already_cancelled.

Response: unknown or boundary-mismatched approval

{
"ok": false,
"status": "approval_not_found",
"result": {
"approval_id": "cxh_approval_unknown",
"message": "Approval not found or no longer accessible."
}
}

The handler rejects approvals that don't belong to the actor's org and member, and approvals issued for a different end user.

Boundary properties

  • Only approvals for the current member and MCP session are surfaced.
  • Only end_user-accountable approvals use this tool. Operator-routed approvals use cortexhub_get_task only.

TriggerSurfaceAgent polls with
Interactive runTask inputRequests or optional cortexhub_request_consentcortexhub_get_task until terminal
Autonomous runUser's configured channels (Slack, email), listed on mcp_task.delivered_channelscortexhub_get_task until terminal
Operator-gated toolSlack channel or webhookcortexhub_get_task until terminal

The same Approval record backs all three surfaces. Whichever surface records the decision first wins; other surfaces reflect the same outcome.

Where toolkit access is configured​

PrincipalWhat must be configured
Member-authenticated clientThe member connects the entitled toolkit once under My space -> Toolkits. The connection is reused by that member's co-worker and external clients acting as that member.
Hosted or SDK Org AgentAn org admin enables the toolkit, publishes it to the Org Agent group, and configures an organisation-shared credential when required. The agent does not borrow a member connection.
Organisation gateway-key runtimeThe backend supplies the downstream provider credentials it owns and attests X-CortexHub-End-User-Subject for each external user.

If an invoke reports connection_required, follow the portal_url for the active principal: a member completes the member connection in CortexHub Cloud; an Org Agent requires the admin to configure its org-shared credential. Retry discovery after the connection is healthy. There is no per-agent attach step.

See Toolkits and connections for the complete entitlement and credential model.