Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions docs/prd/PRD-006-mcp-tool-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- State: Draft for execution (revised)
- Owner: Netclaw engineering
- Date: 2026-02-21
- Revised: 2026-07-22 (secure SDK-owned OAuth and concurrent client lifecycle)
- Revised: 2026-08-07 (MCP prompts as dynamic Netclaw skills)
- Depends on: `PRD-001`, `PRD-002`, `PRD-004`

## Goal
Expand All @@ -21,6 +21,7 @@ learning.
2. MCP tools are available to Netclaw sessions only when policy allows.
3. MCP connectivity and failures are visible in diagnostics.
4. Memorizer provides durable cross-session knowledge that outlives compaction.
5. MCP prompt workflows are discoverable through the existing skill system.

## Two-Tier Memory Architecture

Expand Down Expand Up @@ -118,12 +119,34 @@ Concurrent authorization and reconnect attempts SHALL coalesce per server.
Ambiguous transport failures SHALL NOT automatically replay a tool invocation,
because the remote operation may already have completed.

### MCP-011 Prompt Discovery and Skill Adaptation

Netclaw SHALL discover prompt descriptors from each enabled server that
declares prompt support. It SHALL publish tools and prompts in one immutable
server generation.

Each prompt SHALL enter the unified skill catalog as
`mcp__<server>__<prompt>`. The agent SHALL render a selected prompt through
`skill_load` and `prompts/get`.

The existing MCP server grant SHALL control prompt discovery and use. A prompt
SHALL NOT grant a tool or bypass a tool approval.

`skill_load` SHALL validate required and unknown prompt arguments before the
remote request. It SHALL preserve prompt roles and source attribution.

A failed prompt discovery or refresh SHALL keep the last good generation.
The existing catalog poll SHALL include prompt descriptors.

## Non-Goals (MVP)

- Dynamic marketplace discovery of MCP servers
- Unmanaged auto-install of remote tool bundles
- Multi-tenant tool permission partitioning
- Hot-reload of MCP tool definitions (requires session reboot)
- Proactive MCP catalog subscriptions
- MCP resource discovery and read operations
- MCP prompt completion API support
- First-party client autocomplete for prompt skills

## Acceptance Criteria

Expand All @@ -144,6 +167,11 @@ because the remote operation may already have completed.
credential state.
11. A transport failure may reconnect the server for later calls but does not
replay the failed tool invocation automatically.
12. A prompt-capable server contributes canonical MCP prompt skills to an
authorized session.
13. `skill_load` renders an MCP prompt with validated arguments and source
attribution.
14. A denied server contributes no prompt skills to that audience.

## Cross-References

Expand Down
2 changes: 1 addition & 1 deletion evals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ log patterns** (skill loading, memory recall, checkpoint formation).
| Category | Cases | What It Validates |
|----------|-------|-------------------|
| Identity & Self-Awareness | 5 | Bot knows its name, version, repo, session ID, and routes all identity-file concerns without a skill dependency |
| Skill Auto-Loading | 4 | Keyword matching triggers correct skills |
| Skill Discovery and Activation | 20 | Models load relevant file, feed, and MCP prompt skills while they skip unrelated skills |
| Memory Pipeline | 4 | Memory recall is active, identity-vs-memory routing is correct, explicit saves use memory tools, and automatic checkpointing still fires |
| Tool Discovery & Use | 9 | Progressive tool discovery and invocation, including timestamped webhook configuration |
| Grounding & Alignment | 4 | Uses tools to verify facts, admits uncertainty, and resolves announced attachment paths from the authoritative session root |
Expand Down
8 changes: 8 additions & 0 deletions evals/fixtures/config/netclaw.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
"Webhooks": {
"Enabled": true
},
"McpServers": {
"eval_analytics": {
"Transport": "stdio",
"Command": "python3",
"Arguments": ["/home/netclaw/.netclaw/evals/prompt_server.py"],
"Enabled": true
}
},
"Tools": {
"AudienceProfiles": {
"Personal": {
Expand Down
84 changes: 84 additions & 0 deletions evals/fixtures/mcp/prompt_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""A deterministic MCP stdio server for prompt skill evals."""

import json
import sys


def send(message):
sys.stdout.write(json.dumps(message, separators=(",", ":")) + "\n")
sys.stdout.flush()


for line in sys.stdin:
try:
request = json.loads(line)
method = request.get("method")
request_id = request.get("id")

if method == "initialize":
send({
"jsonrpc": "2.0",
"id": request_id,
"result": {
"protocolVersion": request["params"]["protocolVersion"],
"capabilities": {
"tools": {},
"prompts": {"listChanged": False},
},
"serverInfo": {
"name": "netclaw-eval-prompt-server",
"version": "1.0.0",
},
},
})
elif method == "tools/list":
send({"jsonrpc": "2.0", "id": request_id, "result": {"tools": []}})
elif method == "prompts/list":
send({
"jsonrpc": "2.0",
"id": request_id,
"result": {
"prompts": [{
"name": "property-analytics",
"title": "Property analytics workflow",
"description": (
"Use this skill for complete-month property analytics "
"through the live query endpoint."
),
"arguments": [{
"name": "property",
"description": "The property identifier.",
"required": True,
}],
}],
},
})
elif method == "prompts/get":
property_name = request.get("params", {}).get("arguments", {}).get("property", "")
send({
"jsonrpc": "2.0",
"id": request_id,
"result": {
"description": "A deterministic analytics workflow.",
"messages": [{
"role": "user",
"content": {
"type": "text",
"text": (
"EVAL-MCP-PROMPT-7421: Use the live query endpoint for "
f"property {property_name}. Compare only complete calendar months."
),
},
}],
},
})
elif request_id is not None:
send({
"jsonrpc": "2.0",
"id": request_id,
"error": {"code": -32601, "message": f"Method not found: {method}"},
})
except Exception as error:
sys.stderr.write(f"prompt server error: {error}\n")
sys.stderr.flush()
24 changes: 24 additions & 0 deletions evals/run-evals.sh
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,13 @@ start_eval_daemon() {
cp -r "$REPO_ROOT/evals/fixtures/agents/." "$EVAL_HOME/data/agents/"
fi

if [[ -f "$REPO_ROOT/evals/fixtures/mcp/prompt_server.py" ]]; then
mkdir -p "$EVAL_HOME/data/evals"
cp "$REPO_ROOT/evals/fixtures/mcp/prompt_server.py" \
"$EVAL_HOME/data/evals/prompt_server.py"
chmod ugo+x "$EVAL_HOME/data/evals/prompt_server.py"
fi

# Install the eval-only approval policy before daemon startup. Headless eval
# sessions cannot answer approval prompts, so tools must be automatic for the
# Personal audience. Exposure, filesystem, and command-deny rules remain in force.
Expand Down Expand Up @@ -1073,6 +1080,17 @@ assert_skill_server_feed_logical_access() {
&& stdout_no_skill_file_read_called
}

assert_mcp_prompt_skill_activation() {
daemon_log_skill_loaded_via_skill_tool 'mcp__eval_analytics__property-analytics' \
&& stdout_tool_called 'skill_load' \
&& stdout_contains 'EVAL-MCP-PROMPT-7421' \
&& stdout_no_skill_file_read_called
}

assert_mcp_prompt_skill_unrelated() {
! daemon_log_skill_loaded 'mcp__eval_analytics__property-analytics'
}

assert_skill_explicit_physical_inspection() {
stdout_tool_called 'file_read' \
&& daemon_log_skill_loaded_via_file_read 'modern-csharp-coding-standards'
Expand Down Expand Up @@ -1727,6 +1745,12 @@ run_all() {
run_case skill_server_feed_logical_access "server-feed skill and resource loaded by logical name" \
"Use the logical-feed-probe skill and its listed reference resource. What exact verification phrase does the resource contain?"

run_case mcp_prompt_skill_activation "MCP prompt skill loaded with arguments" \
"For property alpha, find the exact complete-month analytics process for the live query endpoint. Load the relevant remote workflow before you answer."

run_case mcp_prompt_skill_unrelated "unrelated request does not load MCP prompt skill" \
"Explain the difference between a stack and a queue."

run_case skill_explicit_physical_inspection "explicit physical inspection may use file_read" \
"Explicitly inspect the physical file /home/netclaw/.netclaw/skills/modern-csharp-coding-standards/SKILL.md with file_read and tell me its title. This is a filesystem inspection request, not normal skill activation."

Expand Down
11 changes: 10 additions & 1 deletion feeds/skills/.system/files/netclaw-operations/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: netclaw-operations
description: "REQUIRED when the user asks about scheduling, reminders, cron jobs, timers, background jobs, diagnostics, troubleshooting, MCP tools, daemon health, identity updates, or Netclaw capabilities and self-maintenance."
metadata:
author: netclaw
version: "2.44.0"
version: "2.45.0"
---

# Netclaw Operations
Expand Down Expand Up @@ -115,6 +115,15 @@ Only a core toolset is always loaded. Use `search_tools(query)` to find addition
or MCP tools by capability before concluding a tool doesn't exist. Full guidance:
`skill_read_resource('netclaw-operations', 'references/tools.md')`.

MCP servers can also supply workflow skills. These skills use names such as
`mcp__gigatron__month_over_month`. Review the normal skill index first. Use
`skill_load(name, arguments)` when one of these workflows matches the request.

The argument hint marks values that the MCP server requires. Supply those
values exactly. Do not invent a missing value. A loaded prompt can name MCP
tools, but it does not grant them. Use the normal `search_tools` and
`load_tool` flow for each required tool.

## MCP OAuth

For HTTP/SSE MCP servers, the Model Context Protocol .NET SDK owns PKCE,
Expand Down
2 changes: 2 additions & 0 deletions openspec/changes/add-mcp-prompt-skills/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-08-07
Loading
Loading