API Reference
Internal Pickaxe MCP
First-party Pickaxe MCP servers, native tools, schemas, and call contracts.
Related In Learn
Actions
Connect your agents to external tools, APIs, and other agents so they can take real actions.
Pickaxe runs first-party MCP servers that expose Pickaxe-owned capabilities to managed agent runtimes. Some of the same first-party tools are also exposed to the standard runtime through its native tool bridge. This page documents only those internal tools and their runtime exposure.
It does not document third-party MCP servers that a builder can attach to an agent, the tools provided by those servers, or the Studio APIs used to connect them.
Internal server inventory
| MCP server | Purpose | First-party tools |
|---|---|---|
pickaxe | Session-aware tools for the current Pickaxe run | pickaxe_info, search, conditional send_email_to_user, enabled Pickaxe Action tools, and conditional parent-page tools |
| Per-Pickaxe agent-host server | Published Pickaxe tools for an imported/hosted agent | pickaxe_manifest, enabled Pickaxe Action tools, and search_pickaxe_knowledge_base when knowledge exists |
pickaxe_followups | Pickaxe-managed reminders and scheduled chat follow-ups | schedule_followup, list_scheduled_followups, cancel_scheduled_followup |
pickaxe_cron_mirror | Dashboard mirror for native OpenClaw cron jobs | record_openclaw_cron, list_openclaw_crons |
Tool discovery is authoritative. The Action and parent-page portions of the surface are dynamic, so clients should call tools/list after connecting instead of hard-coding the complete list.
In normal engineering conversation, “the internal Pickaxe MCP” means the core server registered as pickaxe. The other entries above are separate Pickaxe-owned servers used by specific managed runtimes.
Calling an internal MCP tool
The core and agent-host servers use MCP over Streamable HTTP. The follow-up and cron-mirror endpoints implement the same MCP initialization, discovery, and call methods as stateless JSON-RPC-over-HTTP handlers. A client initializes the connection, lists tools, and calls a discovered tool by name.
import asyncio
import os
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
MCP_URL = os.environ["PICKAXE_MCP_URL"]
MCP_TOKEN = os.environ.get("PICKAXE_MCP_TOKEN", "")
async def main():
headers = (
{"Authorization": f"Bearer {MCP_TOKEN}"}
if MCP_TOKEN
else None
)
async with streamablehttp_client(
MCP_URL,
headers=headers,
timeout=30,
sse_read_timeout=300,
) as (read_stream, write_stream, _):
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
discovered = await session.list_tools()
for tool in discovered.tools:
print(tool.name, tool.description, tool.inputSchema)
result = await session.call_tool("pickaxe_info", {})
print(result.model_dump())
asyncio.run(main())
The MCP sequence is:
initializenotifications/initializedtools/listtools/callwith{ "name": "<tool>", "arguments": { ... } }
The MCP SDK manages response framing and any MCP session headers. The URL is not a conventional REST tool endpoint.
Core pickaxe server
Managed Claude and OpenClaw runtimes register this server under the name pickaxe. The standard runtime does not attach the whole server; it can receive selected first-party tools through its native function-tool bridge.
The internal URL shape is:
https://<pickaxe-mcp-host>/mcp/<pickaxe_id>?sessionId=<session_id>
sessionIdis required.userIdentifieroruser_identifiercan supply a fallback identity when no stored submission context exists.- Managed runtimes can append compatibility or page-context markers such as
mcpToolsVersionandparentPageCapturedAt. - The URL is generated internally. Do not treat a session ID as a public API credential or hard-code the underlying Modal hostname.
The server reconstructs the live submission context when available. That gives its tools access to the current Pickaxe, user identity, uploaded files, knowledge document IDs, memory rules, metadata, and usage tracking.
Core tool summary
| Tool | Availability | Purpose |
|---|---|---|
pickaxe_info | Always | Return metadata about the current internal MCP session |
search | Always | Retrieve relevant Pickaxe knowledge, uploaded-file content, and permitted user memory |
send_email_to_user | When the current user has an email and the Pickaxe has an email deployment | Send completed content to the current user and start a replyable email conversation |
<action.name> | One per enabled Pickaxe Action | Execute the Action with the live run context |
pickaxe_click_parent_page_elements | When parent-page interaction is enabled and click is allowed | Queue clicks against captured host-page elements |
pickaxe_parent_page_action | When parent-page interaction is enabled and the mode is not suggest | Queue a validated fill, select, check, scroll, reload, navigate, or other allowed page action |
An internal tools/list response can also contain wrappers named after attached third-party MCP servers. Those wrappers are configuration-derived external integrations and are deliberately outside this page's scope.
pickaxe_info
Returns basic metadata and the parent-page capabilities available in this session.
Arguments
{}
Result
{
"sessionId": "session-123",
"pickaxeId": "support-agent",
"title": "Support Agent",
"description": "Answers product questions and handles support workflows.",
"parentPageEnabled": true,
"parentPageMode": "confirm",
"parentPageAllowedActions": ["click", "scroll", "reload"],
"parentPageTools": [
"pickaxe_click_parent_page_elements",
"pickaxe_parent_page_action"
],
"emailToolAvailable": true,
"emailTools": ["send_email_to_user"]
}
parentPageTools is empty when the embed has not opted in, no usable page context exists, no action is allowed, or the interaction mode is suggest.
Call
result = await session.call_tool("pickaxe_info", {})
search
Searches all retrieval sources available to the current run:
- the Pickaxe knowledge base;
- documents uploaded in the conversation;
- user memory, when the session's origin and user context allow it.
Arguments
{
"query": "What does our refund policy say about annual plans?"
}
query is a required string.
Result
The result is a string assembled from matching sources. It can contain:
<knowledge_base>...</knowledge_base><user_uploaded_document_content>...</user_uploaded_document_content><user_memory>...</user_memory>
An empty string means retrieval returned no matching context. Retrieval exceptions are converted to the literal string Document search failed.
The tool also records used document IDs, memory IDs, knowledge-source information, and source categories in the current submission.
Call
result = await session.call_tool(
"search",
{"query": "What is the annual-plan refund policy?"},
)
send_email_to_user
Sends complete, user-facing content to the current user through the Pickaxe's existing email deployment. It is available to both runtime families:
- Managed Claude and OpenClaw runtimes discover and call it through the already-registered core
pickaxeMCP server. - Standard, non-agentic runtimes receive the same
send_email_to_usercontract as a first-party native function tool. The submission stream intercepts the call, invokes the same session-bound email executor, returns the tool result to the model, and removes the tool before final regeneration so the response cannot send twice.
Neither path attaches a separate email MCP server.
The tool appears in tools/list only when all of the following are true:
- the current session resolves either to an existing conversation for the same Pickaxe or to trusted current runtime user context (including on the first response before the source conversation is saved);
- the current user resolves to an email address;
- the Pickaxe has a public email deployment with a configured sender address.
If any condition stops being true, the core server's session-aware cache signature changes and the tool is removed on the next request. pickaxe_info.emailToolAvailable and pickaxe_info.emailTools report the same availability for managed runtimes. The standard runtime performs the equivalent eligibility check while constructing each provider request and omits both the tool schema and its system instructions when email delivery is unavailable.
Arguments
{
"subject": "Project update",
"message": "Here is the final project update you requested."
}
| Argument | Required | Contract |
|---|---|---|
subject | Yes | Concise email subject, with a maximum of 200 characters |
message | Yes | Complete final email body, with Markdown support and a 20,000-character maximum |
There is deliberately no to, recipient, or email-address argument. The recipient is derived from the current session identity, so the tool cannot contact third parties or use an address supplied in the conversation.
Call
result = await session.call_tool(
"send_email_to_user",
{
"subject": "Project update",
"message": "Here is the final project update you requested.",
},
)
Result
{
"success": true,
"sent": true,
"deploymentId": "email-deployment-123",
"sessionId": "email-deployment-123-<new-id>",
"sourceSessionId": "source-chat-session-123",
"subject": "Project update",
"recipient": "current_user",
"replyExpected": true
}
Every successful call creates a separate Pickaxe conversation with originType: EMAIL. The outbound message receives a new RFC Message-Id, and Pickaxe stores that Message-Id with the new session. When the user replies, the inbound email's In-Reply-To or References header maps the reply back to that email session, so the conversation continues from the email rather than the source chat.
Internally, both runtime paths delegate execution to the Pickaxe API's session-bound email executor. The executor revalidates the session or trusted current runtime context, current user, and deployment before sending; records the assistant message and outbound email event through the normal agent-inbound delivery path; and returns failures as tool errors. The internal transport URL is not another server that agent runtimes must configure.
Dynamic Pickaxe Action tools
Every enabled Pickaxe Action is registered as an MCP tool.
| Property | Contract |
|---|---|
| Tool name | The Action's internal name |
| Description | The Action's description |
| Arguments | One keyword argument per Action input, using the input id |
| Required arguments | Inputs with isRequired: true |
| Argument types | Strings in the session-aware core server |
| Result | The Action executor's result, or No result when the executor returned no result field |
Example for an Action named create_support_ticket:
result = await session.call_tool(
"create_support_ticket",
{
"subject": "Unable to access the billing portal",
"priority": "high",
},
)
The Action receives the live Pickaxe ID, session ID, user identity, metadata, uploaded-file context, and stored credentials. Its Action ID is persisted in the submission's used-tool data.
pickaxe_click_parent_page_elements
Exposed only when:
- the embed supplied sanitized parent-page context;
- parent-page interaction is enabled;
- the mode is not
suggest; clickis one of the allowed actions.
Arguments
{
"targetIds": ["el_apply_discount"],
"reason": "Apply the discount selected by the user"
}
targetIds(string[], required): IDs copied exactly fromparentPage.elements.reason(string, optional): Short explanation. It defaults toClick requested parent-page elements.
The server converts each target into a click action, validates the target and captured selector, rejects disabled or unknown elements, and queues the action for the active embed.
pickaxe_parent_page_action
Exposed when parent-page interaction is active, at least one action is allowed, and the mode is not suggest.
Arguments
{
"actions": [
{
"kind": "fill",
"targetId": "el_company_name",
"value": "Pickaxe",
"reason": "Fill the company name requested by the user"
}
]
}
Send the next single interaction per call. Wait for refreshed page context before performing another step.
| Field | Required | Meaning |
|---|---|---|
kind | Yes | An action permitted by the current page context |
reason | Yes | Short user-facing reason |
targetId | For element actions | Exact ID from parentPage.elements |
value | For fill and select | Value to enter or select |
url | For navigate | Destination URL; clicking a captured link is preferred |
The complete implementation supports click, fill, select, check, uncheck, scroll, reload, and navigate. The current page's allowlist is enforced on every call. If no explicit allowlist exists, the safe defaults are click, scroll, and reload.
Successful calls return a queue acknowledgement:
{
"success": true,
"accepted": true,
"actionCount": 1,
"outboxIds": ["outbox-123"],
"message": "The parent-page action sequence was accepted and will be streamed to the active embed for immediate execution."
}
accepted means validated and queued. It does not prove that the browser completed the action.
Per-Pickaxe agent-host server
The agent-host runtime can create a generated first-party MCP server for a published Pickaxe. Its stable URL shape is:
https://<provider-gateway>/external/pickaxe/mcp/<pickaxe_id>/mcp/
The provider gateway can require Authorization: Bearer <internal-token>. Managed callers can also forward x-pickaxe-generation-id, x-pickaxe-session-id, and x-pickaxe-user-id so Action and knowledge calls retain the correct run context.
Only the Pickaxe-owned tools are covered below. Generated passthrough tools for attached third-party MCP servers are outside scope.
pickaxe_manifest
Returns the generated first-party tool-surface summary.
result = await session.call_tool("pickaxe_manifest", {})
{
"pickaxeId": "support-agent",
"workspaceId": "workspace-123",
"title": "Support Agent",
"actionCount": 2,
"subagentTargetCount": 1,
"workspaceAgentServerCount": 1,
"workspaceAgentToolCount": 1,
"attachedMcpServerCount": 0,
"attachedMcpToolCount": 0,
"knowledgeDocumentCount": 12
}
The attached-MCP counts can be nonzero in a real manifest, but those integrations are not first-party tools and are not documented here.
Generated Action tools
Enabled Pickaxe Actions are exposed directly.
- The name prefers
name, thendisplayName, thenactionId. - Duplicate names receive an index suffix.
- Input names are normalized to lowercase Python-safe identifiers and mapped back to the original Action input IDs during execution.
- Integer, number, and boolean Action inputs retain those types; other inputs become strings.
- Optional inputs default to
null.
Use tools/list for the final generated name and schema.
search_pickaxe_knowledge_base
Present only when the Pickaxe has enabled knowledge documents.
Arguments
{
"query": "What is the cancellation policy?",
"knowledge_budget": 10000,
"knowledge_tokens": 750,
"relevance_threshold": 60,
"chat_input_length": 1000
}
Only query is required. Omitted numeric values fall back to the Pickaxe's RAG settings.
The result includes normalized text, the raw retrieval response, and the configured document count. With live request headers, the raw response can include Pickaxe knowledge, conversation-upload chunks, user memory, source information, and used document or memory IDs.
pickaxe_followups server
This is a separate internal MCP server for Pickaxe-managed reminders and scheduled messages. Its URL shape is:
https://<pickaxe-api-host>/mcp/followups/<pickaxe_id>?contextRef=<signed_context_ref>
The runtime supplies a signed context reference or session context and, when configured, an internal bearer. Calls without valid Pickaxe chat context are rejected.
schedule_followup
Schedules a one-time or recurring assistant message in the current Pickaxe chat.
Important arguments:
| Argument | Meaning |
|---|---|
message | Exact later message for static delivery, or a short label/fallback for generate delivery |
deliveryMode | static or generate |
generationPrompt | Required for generated delivery; hidden prompt executed on each run |
at | ISO-8601 first-delivery timestamp |
delaySeconds, delayMinutes | Relative first-delivery delay |
everySeconds, everyMinutes, everyMs | Recurring interval; when supplied alone, also determines the first delivery |
weeklyDayOfWeek | Weekly day, where Monday is 0 and Sunday is 6 |
weeklyHour, weeklyMinute, timezone | Weekly wall-clock schedule |
name | Optional dashboard label |
jobId | Optional caller correlation ID |
deleteAfterRun | Delete a one-time job after delivery |
At least one first-delivery schedule is required: an absolute timestamp, a relative delay, a recurring interval, or a complete weekly schedule.
Static example:
result = await session.call_tool(
"schedule_followup",
{
"message": "Go shopping.",
"delayMinutes": 5,
"deliveryMode": "static",
},
)
Generated recurring example:
result = await session.call_tool(
"schedule_followup",
{
"name": "Daily product tip",
"message": "Daily product tip",
"deliveryMode": "generate",
"generationPrompt": "Generate one new concise product tip for the user.",
"delayMinutes": 1,
"everyMinutes": 1440,
},
)
The structured result contains the job ID, status, first delivery time, recurrence fields, delivery mode, and session association.
list_scheduled_followups
Lists active scheduled follow-ups for the current Pickaxe chat.
result = await session.call_tool("list_scheduled_followups", {})
The structured result contains count and a followups array with job IDs, messages, status, schedule, recurrence, timezone, and delivery metadata.
cancel_scheduled_followup
Cancels one job or all active jobs in the current chat.
result = await session.call_tool(
"cancel_scheduled_followup",
{
"jobId": "sf_123",
"reason": "User cancelled the reminder",
},
)
- Pass
jobIdto cancel one job. - Pass
cancelAll: true, or omitjobId, to cancel all active follow-ups in the current chat.
The structured result reports cancelledCount and the affected job IDs.
pickaxe_cron_mirror server
This companion server mirrors native OpenClaw cron state into Pickaxe so scheduled jobs can appear in the Pickaxe dashboard. It does not execute or schedule the native cron itself.
Its URL shape is:
https://<pickaxe-api-host>/mcp/openclaw-crons/<pickaxe_id>?sessionId=<session_id>
Managed OpenClaw runtimes also send the external session, runtime, user, submission, origin, and deployment context needed to associate the mirror with the correct chat. When configured, the endpoint requires its internal bearer.
record_openclaw_cron
Creates, updates, or cancels a Pickaxe mirror after the runtime has already changed the native OpenClaw cron.
Key arguments:
| Argument | Meaning |
|---|---|
operation | upsert or cancel; defaults to upsert |
providerJobId | Native OpenClaw job ID |
jobId | Optional mirror correlation ID when no provider ID exists |
name, description | Display metadata |
message | Fixed user-facing message or generated-delivery label |
deliveryMode | static or generate |
generationPrompt | Prompt used for generated fallback delivery |
at, nextRunAt, scheduledAt | Absolute schedule metadata |
delaySeconds, delayMinutes | Relative schedule metadata |
everySeconds, everyMinutes, everyMs | Recurrence metadata |
cronExpression, timezone | Native cron schedule metadata |
raw | Optional original provider payload |
result = await session.call_tool(
"record_openclaw_cron",
{
"operation": "upsert",
"providerJobId": "openclaw-cron-1",
"name": "Stretch reminder",
"message": "Stretch.",
"deliveryMode": "static",
"everyMinutes": 30,
},
)
To cancel, delete the native OpenClaw cron first, then call the tool with operation: "cancel" and its providerJobId or mirror jobId.
list_openclaw_crons
Lists active OpenClaw cron mirror records for the current Pickaxe chat.
result = await session.call_tool("list_openclaw_crons", {})
The result includes count and a crons array with native provider IDs, mirror IDs, status, schedule, recurrence, delivery mode, and session metadata. Use the returned provider ID when modifying or deleting the actual native cron.
Lifecycle and errors
- The core
pickaxeserver caches a tool surface by Pickaxe and session. A new session or host restart is required to guarantee that changed Action configuration is reloaded. - The agent-host server publishes a stable gateway URL backed by a temporary sandbox. Clients should tolerate cold starts and rediscover tools after reconnecting.
- Parent-page tools can appear or disappear as the captured page context and interaction mode change.
- The email tool can appear or disappear as the session identity or Pickaxe email deployment changes. Always use
tools/listinstead of assuming it is available. 400 Missing required query parameter: sessionIdmeans the core URL omitted its required session.404 Pickaxe '<id>' not foundmeans the core server could not resolve the Pickaxe.401 invalid_internal_bearermeans an internal gateway or companion server rejected its configured bearer.- Tool results with
success: falseindicate that MCP transport succeeded but validation or the underlying first-party executor failed.
