Workspace MCP Reference
AgentRQ exposes 11 MCP tools to Claude Code within a specific workspace. All tools are available
after connecting via .mcp.json.
createTask
Create a task for the human or agent to handle
| Name | Type | Required | Description |
|---|---|---|---|
title |
string | required | Short task title shown in the dashboard |
body |
string | required | Full task description — include all context the human needs |
assignee |
"human" | "agent" | optional | Who the task is assigned to. Default: "agent" |
attachments |
Attachment[] | optional | Array of file attachments (see Attachment type below) |
cronSchedule |
string | optional | Cron schedule (5 fields: minute hour dom month dow). Recurring schedules (wildcard dom/month) are hourly at most — the minute must be a single integer 0–59, e.g. 30 * * * *. One-time schedules with a fixed dom and month, e.g. 30 14 25 4 *, allow minute precision. |
eventId |
string | optional | Event ID (base62). When this task completes the named event is published automatically. |
clearContext |
boolean | optional | Ask for a clean slate — /clear is sent to the agent's Claude Code terminal before this task is handed over, so it starts without the previous task's context. Ignored when the workspace has no running Claude Code session. Defaults to the workspace's own setting. |
task created with id=<taskId> — the base62
ID of the new task, as a plain string rather than a JSON object
const result = await createTask({
title: "Review: DB migration for user_sessions table",
body: `## What I'm about to do
Add a new \`user_sessions\` table to store auth tokens.
## Migration SQL
\`\`\`sql
CREATE TABLE user_sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id INTEGER REFERENCES users(id),
token_hash VARCHAR(64) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX ON user_sessions(expires_at);
\`\`\`
## Approve to proceed?`,
assignee: "human"
});
console.log(result); // "task created with id=0ZRgCquBZ7R"
updateTaskStatus
Transition a task to a new status
| Name | Type | Required | Description |
|---|---|---|---|
taskId |
string | required | ID of the task to update |
status |
"ongoing" | "completed" | "rejected" | "notstarted" | required | The new status to transition to |
updateTaskStatus("ongoing") as the
first thing when you start working on a task. This
signals to the human that the agent has seen their request.
// Received task — immediately mark as ongoing
await updateTaskStatus({
taskId: "0ZRgCquBZ7R",
status: "ongoing"
});
// ... do work ...
// Done — mark completed
await updateTaskStatus({
taskId: "0ZRgCquBZ7R",
status: "completed"
});
reply
Send a message in a task thread
| Name | Type | Required | Description |
|---|---|---|---|
chatId |
string | required | The chat ID from the channel message —
the chat_id attribute on the
<channel> tag |
text |
string | required | The message text to send |
attachments |
Attachment[] | optional | Files to attach to the reply |
await reply({
chatId: "0ZRgCquBZ7R",
text: "Migration complete. 14,322 rows deleted. Here's the query plan:",
attachments: [{
id: "att_plan_001",
filename: "query-plan.txt",
mimeType: "text/plain",
data: btoa(queryPlanText) // base64 encoded
}]
});
getWorkspace
Fetch workspace metadata and context
const workspace = await getWorkspace();
// Returns:
{
id: "0ZPO4WBMZIP",
name: "my-saas-backend",
owner: "[email protected]",
mission: "Build the v2 API. Ask before any DB changes or deploys."
}
getTask
Fetch the next "not started" task, or a specific task — optionally with its conversation history
taskId, getTask
dequeues the oldest "not started" task assigned to the agent (and associates it with the
current session). Pass a taskId to fetch that specific
task instead. Set includeConversation to append the
task's chat history.
| Name | Type | Required | Description |
|---|---|---|---|
taskId |
string | optional | A specific task to fetch. Omit to dequeue the next "not started" task. |
includeConversation |
boolean | optional | When true, appends the task's chat history. Default: false |
cursor |
string | null | optional | Pagination cursor for conversation messages. null = start from beginning |
limit |
integer | optional | Max conversation messages to return. Default: 20, max: 100 |
includeConversation is true, the response also
carries the chat history as { messages, total, cursor }.
// Dequeue the next "not started" task
const task = await getTask();
if (task) {
console.log(`Found next task: ${task.title}`);
await updateTaskStatus({ taskId: task.id, status: "ongoing" });
} else {
console.log("No pending tasks.");
}
// Fetch a specific task with its conversation history
const result = await getTask({
taskId: "0ZRgCquBZ7R",
includeConversation: true,
cursor: null,
limit: 50
});
// result.messages → { messages: [...], total, cursor }
downloadAttachment
Fetch a file attached by the human in the dashboard
| Name | Type | Required | Description |
|---|---|---|---|
attachmentId |
string | required | The attachment ID from the channel message |
taskId |
string | required | The ID of the task the attachment belongs to |
<channel> message that announced the
attachment, not from this response.
// Channel message contains attachment ID
// <channel ...> [attachment: att_abc123 — design.png] </channel>
const data = await downloadAttachment({
attachmentId: "att_abc123",
taskId: "0ZRgCquBZ7R"
});
// the response is the content itself — decode to use
const content = Buffer.from(data, "base64");
publishEvent
Fire a named signal so subscriber workspaces spawn their trigger tasks
| Name | Type | Required | Description |
|---|---|---|---|
name |
string | required | The event to publish. Must already exist in the account that owns this workspace. |
payload |
string | optional | Free text describing what happened.
Lands wherever a trigger's body says
{{EVENT_PAYLOAD}}. |
taskId |
string | optional | The task you are completing (base62). Identifies which workflow run this publish continues — see the warning below. |
faq |
{ q, a }[] | optional | Question/answer pairs of extra context.
Lands wherever a trigger's body says
{{EVENT_FAQ}}. |
Copy name and
taskId exactly as the task gave them to you.
When a task carries a publishEvent instruction, that
taskId is what identifies the workflow run being
continued — omitting it can leave the run stranded. Write the payload yourself.
event "<name>" published. Subscriber
workspaces create their trigger tasks automatically — this call does not wait for
them.
// The task said:
// [On completion: call publishEvent("tests_passed", "<payload>")]
await updateTaskStatus({ taskId: "0ZRgCquBZ7R", status: "completed" });
await publishEvent({
name: "tests_passed", // copied from the instruction
taskId: "0ZRgCquBZ7R", // copied from the instruction
payload: "482 passed, 0 failed on build 2.3.1.",
faq: [
{ q: "Any flakes?", a: "One retry in the billing suite, passed on rerun." }
]
});
loadMemory
Read what this workspace remembers, written by earlier tasks
| Name | Type | Required | Description |
|---|---|---|---|
name |
string | optional | Which memory to read. Default:
MEMORY.md, the index that says what else this workspace
remembers
|
memory://<name> that
look relevant to what you are about to do.
// Start with the index.
const index = await loadMemory();
// # What this workspace remembers
// - [How we deploy](memory://deploys.md) — the two gates that are not automated.
// - [The flaky tests](memory://flaky-tests.md) — which failures are real.
// Then load only what this task needs.
const deploys = await loadMemory({ name: "deploys.md" });
saveMemory
Write something worth remembering, so the next task starts with it
| Name | Type | Required | Description |
|---|---|---|---|
content |
string | required | The full new content. Replaces the memory entirely |
name |
string | optional | Which memory to write. Default:
MEMORY.md, which should stay an index
|
.md, up to 32 characters —
release-notes.md is fine, anything else is
refused. One memory holds at most 16 KiB. Both limits
refuse rather than truncate, so a memory that is too
large comes back as an error telling you to split it and index the parts — nothing is
silently cut in half.
MEMORY.md so the index stays short enough to read
first.
// Write the detail into its own memory.
await saveMemory({
name: "deploys.md",
content: [
"# How we deploy",
"",
"Production needs a human to approve the release.",
"Drain settlement retries first: a release mid-drain",
"leaves duplicate charges to reconcile by hand."
].join("\n")
});
// Then link it from the index, which agents read first.
await saveMemory({
content: [
"# What this workspace remembers",
"",
"- [How we deploy](memory://deploys.md) — the manual gate."
].join("\n")
});
// Returns: Saved "memory.md" (98 bytes).
deleteMemory
Delete one of the workspace's memories, by name
name it deletes
MEMORY.md itself — think before doing that,
since it is the index the other memories link from.
| Name | Type | Required | Description |
|---|---|---|---|
name |
string | optional | Which memory to delete. Default:
MEMORY.md, the index that says what else this workspace
remembers
|
MEMORY.md itself removes the
index other memories are linked from, not the memories it pointed to — update it
instead of deleting it unless you mean to abandon those links too.
// The flaky-tests memory no longer applies; remove it.
await deleteMemory({ name: "flaky-tests.md" });
// Returns: Deleted "flaky-tests.md".
// Retrying a delete that already happened is not an error.
await deleteMemory({ name: "flaky-tests.md" });
// Returns: No memory was stored under "flaky-tests.md"; nothing to delete.
elicit
Ask the human a question and block until they answer
Mirrors the MCP protocol's client-side
elicitation/create capability. Use it when you need a
decision before you can continue — it waits, rather than guessing and carrying on.
| Name | Type | Required | Description |
|---|---|---|---|
taskId |
string | required | The task the question relates to (base62) |
message |
string | required | The question or prompt shown to the human |
mode |
"form" | "url" | required | form
collects structured input;
url points the human at a link and
waits for them to confirm they are done. |
requestedSchema |
object | form only | A flat JSON Schema:
type: "object" whose properties are
each primitive (string, number, integer, boolean, optionally with
enum), an array of one of those
(renders as multi-select), or a
oneOf/anyOf
enum. No nested objects. |
url |
string | url only | The link to show the human |
timeoutSeconds |
integer | optional | How long to wait. Default and maximum are both 3600 (one hour). |
{ action, content? } —
action is
"accept" (with
content holding the form values),
"decline" or
"cancel". A timeout returns
{ action: "cancel" } rather than an
error — the human simply did not answer in time, so handle it as an answer.
const answer = await elicit({
taskId: "0ZRgCquBZ7R",
mode: "form",
message: "Which environment should I deploy to?",
requestedSchema: {
type: "object",
properties: {
environment: { type: "string", enum: ["staging", "production"] },
runMigrations: { type: "boolean" }
},
required: ["environment"]
}
});
// A timeout is a "cancel", not an error — treat it as an answer.
if (answer.action !== "accept") return;
deploy(answer.content.environment, answer.content.runMigrations);
Attachment Type
Used in createTask and reply
when sending files from Claude to the human:
| Field | Type | Description |
|---|---|---|
id |
string | Unique ID for this attachment (any string) |
filename |
string | Display filename (e.g., "schema.sql") |
mimeType |
string | MIME type (e.g., "text/plain", "image/png") |
data |
string | Base64-encoded file content |