Install
openclaw skills install pieces-mcpConnect OpenClaw to Pieces via MCP-only (no SSE) and use Pieces as external long-term memory. Use this when the human runs PiecesOS with LTM enabled on anoth...
openclaw skills install pieces-mcp---
name: pieces-mcp
description: >
Connect OpenClaw to Pieces via MCP-only (no SSE) and use Pieces as external
long-term memory. Use this when the human runs PiecesOS with LTM enabled on
another machine and exposes the MCP server via a tunnel (ngrok, custom domain,
or any HTTPS proxy). This skill tells the agent exactly how to prompt the
human, configure MCPorter and mcp-remote, use Pieces tools (ask_pieces_ltm,
create_pieces_memory, *_full_text_search), and troubleshoot common failures.
---
# Pieces MCP Skill for OpenClaw (MCP-Only, No SSE)
This skill teaches you, the OpenClaw agent, how to:
1. Know **when** to use Pieces.
2. Guide the human through tunnel setup (ngrok or custom).
3. Wire OpenClaw to the Pieces MCP server using **only `/mcp`**.
4. Use Pieces tools (`ask_pieces_ltm`, `create_pieces_memory`,
`*_full_text_search`) in your reasoning.
5. Troubleshoot when something goes wrong.
---
## 0. When to Use This Skill
Trigger this skill when:
- The human mentions **Pieces**, **PiecesOS**, **MCP**, "ask Pieces", or "long-term memory"
in the context of OpenClaw.
- They say things like:
- "I want OpenClaw to remember what I worked on."
- "Hook OpenClaw up to my Pieces long-term memory."
- You see or expect a URL like:
- `https://...ngrok-free.dev`
- `https://username.tunnel.company.stream`
- Any HTTPS URL they say is "the Pieces MCP tunnel".
If none of that is true, you probably don't need this skill.
---
## 1. Mental Model
Think of three components:
- **PiecesOS MCP server (remote machine)**
- Runs on the human's other machine.
- Long-Term Memory (LTM) is enabled.
- Exposed via an HTTPS tunnel:
`/model_context_protocol/2025-03-26/mcp`.
- **OpenClaw host (this machine)**
- This is where you (the agent) live.
- Uses **MCPorter** to integrate MCP servers.
- **Bridge: MCPorter + `mcp-remote`**
- `mcporter.json` tells MCPorter about MCP servers.
- `mcp-remote` connects to the remote `/mcp` endpoint and exposes it to
OpenClaw.
> **Important:** This integration is MCP-only. Do **not** use `/sse`.
> Always use `/mcp` for both direct calls and the bridge.
---
## 2. Human Checklist (What You Must Confirm)
You must guide/confirm these three things:
1. **PiecesOS + LTM is running** on the remote machine.
Say something like:
> "Make sure PiecesOS is installed and running on your other machine, and that
> Long-Term Memory (LTM) is enabled in the Pieces Desktop App."
2. **A tunnel is available** to expose the MCP server.
Options include:
- **ngrok** (most common): `ngrok http 39300`
- **Custom tunnel** (e.g., `username.tunnel.company.stream`)
- Any HTTPS proxy that forwards to `localhost:39300`
3. **For ngrok specifically, tell them:**
> "On the machine running PiecesOS, please run this in a terminal:
>
> ```bash
> ngrok http 39300
> ```
>
> If ngrok asks for login or an authtoken, follow its instructions once.
> When it's running, you'll see a forwarding URL like:
>
> `https://SOMETHING.ngrok-free.dev -> http://localhost:39300`
>
> Please paste that HTTPS URL here."
You cannot proceed until the human pastes a valid HTTPS tunnel URL.
---
## 3. Build and Verify the MCP URL
Once you have the base tunnel URL, construct the MCP endpoint.
If the human gave you:
```text
https://SOMETHING.ngrok-free.dev
Or a custom tunnel like:
https://name.tunnel.company.stream
Define:
MCP_URL_BASE = <the tunnel URL>
MCP_URL_MCP = <the tunnel URL>/model_context_protocol/2025-03-26/mcp
Always run this GET request first to confirm the route is alive:
curl -i "MCP_URL_MCP"
Expected success response (HTTP 400):
{
"jsonrpc": "2.0",
"error": {
"code": -32000,
"message": "Bad Request: mcp-session-id header or sessionId query parameter is required"
},
"id": null
}
This 400 error is good — it means:
If you get 404, 502, HTML, or timeout → See Section 9.1: Troubleshooting.
Before configuring MCPorter, you need to understand how MCP requests work. This section shows the exact request patterns so you don't get confused.
All MCP interactions follow this flow:
What to look for: The server-assigned session-id is typically a Unix timestamp in milliseconds — a 13-digit numeric string like
1774202062499.Pattern:
17XXXXXXXXXXX(13 digits starting with17in 2025-2026)You'll find it in the response headers, not the response body:
mcp-session-id: 1774202062499
Content-Type: application/json
Accept: application/json, text/event-stream
mcp-session-id: <SESSION_ID>
Missing either Content-Type or Accept will cause failures!
⚠️ Shell quoting can mangle JSON! Zsh and Bash handle quotes differently, and inline JSON with
-d '{...}'is fragile. Always use file-based JSON with--data-binary @filenameto avoid parsing errors.
⚠️ Use
"id": "1"(string), not"id": 1(integer).
The server is sensitive to id types, and string ids work consistently.
Create a file init.json:
{
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": "0.1.0",
"capabilities": {},
"clientInfo": {
"name": "openclaw-agent",
"version": "1.0"
}
},
"id": "1"
}
Run:
curl -i -X POST "MCP_URL_MCP" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: init-request-001" \
--data-binary @init.json
What happens:
mcp-session-id: init-request-001 (can be any string you choose)Look for this in the response headers:
HTTP/2 200
content-type: application/json
mcp-session-id: 1774202062499
...
Critical: Extract the
mcp-session-idvalue from the response header (e.g.,1774202062499). This is your server-assigned session ID. Use this exact value for all subsequent requests — do NOT continue using your initialinit-request-001.
Response body (200 OK):
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"protocolVersion": "0.1.0",
"capabilities": {
"tools": {}
},
"serverInfo": {
"name": "pieces",
"version": "1.0.0"
}
}
}
Now use the server-assigned session-id for all future requests.
Create a file query.json:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "ask_pieces_ltm",
"arguments": {
"question": "What did I work on today?",
"chat_llm": "gpt-4"
}
},
"id": "2"
}
Run (replacing <SERVER_SESSION_ID> with the value from Step 1):
curl -i -X POST "MCP_URL_MCP" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: <SERVER_SESSION_ID>" \
--data-binary @query.json
Example with actual session ID:
curl -i -X POST "MCP_URL_MCP" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: 1774202062499" \
--data-binary @query.json
Response (200 OK):
{
"jsonrpc": "2.0",
"id": "2",
"result": {
"content": [
{
"type": "text",
"text": "{\"summaries\":[...],\"events\":[...]}"
}
]
}
}
Important for OpenClaw Agents:
The response looks like "raw JSON garbage" to humans, but this is perfect for you!
You get:
summaries[] — Pre-existing memory summaries with relevance scoresevents[] — Raw activity events (browser, clipboard, audio, etc.)Parse this JSON yourself and synthesize a natural language answer for the human.
Create a file create_memory.json:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "create_pieces_memory",
"arguments": {
"summary_description": "OpenClaw + Pieces: First Integration Session",
"summary": "# OpenClaw + Pieces Integration\n\nThis memory documents our initial setup session.\n\n## What We Did\n- Verified Pieces MCP server via tunnel\n- Configured MCPorter with mcp-remote\n- Successfully tested ask_pieces_ltm\n\n## Key Learnings\n- Session management is critical\n- Use file-based JSON to avoid shell quoting issues\n- Use string JSON-RPC ids for consistency",
"project": "OpenClaw + Pieces"
}
},
"id": "3"
}
Run (using the same server-assigned session ID):
curl -i -X POST "MCP_URL_MCP" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: <SERVER_SESSION_ID>" \
--data-binary @create_memory.json
Response (200 OK):
{
"jsonrpc": "2.0",
"id": "3",
"result": {
"content": [
{
"type": "text",
"text": "Memory created successfully"
}
]
}
}
| Stage | Header Value | Source |
|---|---|---|
| Initialize request | Any string you choose (e.g., init-001, my-session, test-123) | You create it |
| Initialize response | 13-digit timestamp (e.g., 1774202062499) | Extract from response header |
| All subsequent requests | The server-assigned value from initialize | Reuse the extracted value |
| ❌ Wrong | ✅ Correct |
|---|---|
Inline JSON with -d '{...}' | File-based JSON with --data-binary @file.json |
Integer id: "id": 1 | String id: "id": "1" |
Missing Accept header | Include Accept: application/json, text/event-stream |
Using your initial session-id (init-001) for all requests | Extract server-assigned session-id from initialize response header |
| Looking for session-id in response body | Session-id is in the response headers |
| Thinking raw JSON responses are errors | Parse the JSON — it's structured data for you to synthesize! |
| Calling tools before initialize | Always call initialize first to get a session-id |
Forgetting chat_llm parameter | Include it (even though responses may still be raw JSON) |
OpenClaw uses MCPorter to manage MCP servers. The config lives at:
~/.openclaw/workspace/config/mcporter.json
As the agent, you should:
mkdir -p ~/.openclaw/workspace/config
You want a pieces entry that uses mcp-remote pointed at MCP_URL_MCP:
{
"mcpServers": {
"pieces": {
"command": "mcp-remote",
"args": [
"MCP_URL_MCP_GOES_HERE"
]
}
}
}
Example with ngrok:
{
"mcpServers": {
"pieces": {
"command": "mcp-remote",
"args": [
"https://SOMETHING.ngrok-free.dev/model_context_protocol/2025-03-26/mcp"
]
}
}
}
Example with custom tunnel:
{
"mcpServers": {
"pieces": {
"command": "mcp-remote",
"args": [
"https://username.tunnel.company.stream/model_context_protocol/2025-03-26/mcp"
]
}
}
}
No /sse here. This is the MCP-only configuration.
Note:
mcp-remotehandles the session management (initialize, session-id extraction, header management) automatically. You don't need to manually manage sessions when using MCPorter — the examples in Section 3.2 are for understanding the protocol and direct testing/troubleshooting.
Install mcp-remote globally:
npm install -g mcp-remote@0.1.38
You can run this yourself from the OpenClaw host's shell.
After editing mcporter.json, restart the OpenClaw gateway so it picks up the
Pieces MCP server:
cd ~/.openclaw/workspace
openclaw gateway restart
Wait a few seconds for it to restart.
At this point, OpenClaw should see the Pieces MCP server through MCPorter and
mcp-remote → MCP_URL_MCP.
Once connected, treat Pieces as your authoritative long-term memory.
Call Pieces (via ask_pieces_ltm / search tools) whenever:
The human asks about something in the past that you're unlikely to know from this session alone:
You need context for:
Use ask_pieces_ltm as your first tool for historical questions:
Send a natural-language question, e.g.:
Understand the response format:
summaries[] and events[] arrayscombined_string field with the contentWhen you answer, say something like:
"According to your Pieces long-term memory, …"
If ask_pieces_ltm times out or is too vague, narrow the query by time and/or
topic (see troubleshooting).
Use create_pieces_memory when you want to write an important memory:
Typical use cases:
Conceptual signature:
create_pieces_memory(
summary_description: string,
summary: string,
project?: string,
files?: string[],
externalLinks?: string[],
connected_client?: string
);
Guidelines:
summary_description — short human-readable label.
"Profile: <name> – Role at <company>""OpenClaw + Pieces: Initial Setup"summary — full markdown body:
# heading:
# Profile: <name> – Role at <company>## Role## Responsibilities## Current Focus## Notes / Preferencesproject — (optional) grouping label:
"OpenClaw + Pieces", "Website", "Standup Automation", etc.files / externalLinks / connected_client — optional context and attribution.
This makes memories both human-readable and easy for future agents to retrieve and interpret.
For debugging: Use the file-based curl examples from Section 3.2 to test the MCP server directly.
For production: Once MCPorter is configured, you don't manually manage
sessions — mcp-remote does it for you. Just call the tools through OpenClaw's
MCP integration normally.
This skill works with any HTTPS tunnel that forwards to localhost:39300 on the
PiecesOS machine:
| Tunnel Type | Example URL | Notes |
|---|---|---|
| ngrok | https://abc123.ngrok-free.dev | Most common; may require auth setup |
| Custom Pieces tunnel | https://username.tunnel.company.stream | Pre-configured by Pieces team |
| Any HTTPS proxy | https://your-domain.com | As long as it forwards to 39300 |
The MCP endpoint is always at:
<tunnel_url>/model_context_protocol/2025-03-26/mcp
Symptom:
curl MCP_URL_MCP returns 404/502/HTML or times out.
What to do:
Ask the human to:
Rebuild MCP_URL_MCP and test again with the quick sanity check.
Symptom:
curl -i -X POST "MCP_URL_MCP" ... -d '{"jsonrpc": "2.0", ...}'
# Returns: HTTP/1.1 500 Internal Server Error
Checklist:
Confirm route is alive first:
curl -i "MCP_URL_MCP"
→ Should return 400 with "mcp-session-id required" message → If you get 404/502, the tunnel is down (see 9.1)
Use file-based JSON (avoid shell quoting issues):
# Create init.json with your JSON payload
curl -i -X POST "MCP_URL_MCP" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: init-request-001" \
--data-binary @init.json
Use string JSON-RPC ids:
{"id": "1"} ✅ Correct
{"id": 1} ❌ May cause issues
Ensure BOTH headers are present:
Content-Type: application/jsonAccept: application/json, text/event-streamIf still 500: Ask the human to restart PiecesOS and/or the tunnel, then re-test.
Symptoms:
ask_pieces_ltm never seems to be called.What to check:
mcporter.json:
~/.openclaw/workspace/config/mcporter.json.pieces server using mcp-remote pointed at a valid /mcp URL.mcp-remote:
mcp-remote --help
npm install -g mcp-remote@0.1.38
Gateway:
mcporter.json:
openclaw gateway restart
Test the MCP server directly using the file-based curl examples from Section 3.2:
Symptoms:
What to try:
Narrow the question:
If repeated timeouts continue:
curl -i MCP_URL_MCPThis is NOT a problem!
summaries[] and events[] arrays contain structured data for you to parseIf you're getting raw JSON, it means:
Simply parse the JSON and create a helpful answer for the human!
Detect the need for Pieces (user mentions Pieces/MCP/long-term memory).
Guide the human:
Build and verify MCP_URL_MCP:
curl -i MCP_URL_MCP → expect 400(Optional) Test directly using file-based curl examples from Section 3.2:
"id": "1")--data-binary @file.jsonConfigure MCPorter (mcporter.json with mcp-remote → /mcp).
Ensure mcp-remote is installed.
Restart the OpenClaw gateway.
Use ask_pieces_ltm for reading history (parse raw JSON responses).
Use create_pieces_memory for writing durable summaries.
Apply troubleshooting steps if anything fails.
If you follow this skill, you should be able to reliably connect to and use Pieces as external long-term memory from a fresh OpenClaw instance with minimal human effort.