Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions Netclaw.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<Project Path="samples/Netclaw.Demo.AppHost.IntegrationTests/Netclaw.Demo.AppHost.IntegrationTests.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/Netclaw.SecretsLockProbe/Netclaw.SecretsLockProbe.csproj" />
<Project Path="tests/Netclaw.SmokeMcpServer/Netclaw.SmokeMcpServer.csproj" />
</Folder>
<Folder Name="/benchmarks/">
Expand Down
31 changes: 28 additions & 3 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-02-21 (Memorizer as external memory tier, tool loading)
- Revised: 2026-07-22 (secure SDK-owned OAuth and concurrent client lifecycle)
- Depends on: `PRD-001`, `PRD-002`, `PRD-004`

## Goal
Expand Down Expand Up @@ -96,11 +96,28 @@ Runtime SHALL degrade gracefully when MCP server is unavailable:

### MCP-009 Daemon-Bound Server Ownership

Each configured MCP server SHALL have at most one live client connection per
Netclaw daemon. A local STDIO server process and its internal state are shared
Each configured MCP server SHALL have at most one published client generation
per Netclaw daemon. A replacement MAY initialize while the published generation
continues serving, but it SHALL remain unpublished until initialization
succeeds, and the replaced generation SHALL not be disposed while it has
in-flight calls. A local STDIO server process and its internal state are shared
by all sessions authorized to use that server; Netclaw session identity SHALL
not launch or select a separate MCP process.

### MCP-010 Secure OAuth Lifecycle

HTTP MCP OAuth SHALL delegate protocol operations to the MCP C# SDK while
Netclaw owns local browser-flow brokering, credential persistence, and client
lifecycle. Persisted tokens and dynamically registered client credentials SHALL
be bound to the configured MCP resource identity and SHALL NOT be supplied after
that identity changes or when a legacy record lacks a binding. Credential
persistence failures, invalid callback state, and authorization failures SHALL
fail visibly without deleting the last working credentials or connection.

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.

## Non-Goals (MVP)

- Dynamic marketplace discovery of MCP servers
Expand All @@ -119,6 +136,14 @@ not launch or select a separate MCP process.
6. Unavailable MCP server does not crash the session.
7. Calls from different authorized sessions to one local STDIO profile use the
same daemon-owned client and child process.
8. Repointing an MCP profile does not send credentials bound to its old resource
identity to the new endpoint.
9. Legacy OAuth records without a resource binding fail closed and direct the
operator to reauthorize.
10. OAuth token persistence failure is visible and does not advance caller-visible
credential state.
11. A transport failure may reconnect the server for later calls but does not
replay the failed tool invocation automatically.

## Cross-References

Expand Down
80 changes: 79 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.35.0"
version: "2.36.0"
---

# Netclaw Operations
Expand All @@ -28,6 +28,7 @@ a reference file — load the one matching the user's intent with
| Update identity / where facts go (identity vs memory) | [Identity](#identity) |
| Work on a project, switch projects | `skill_read_resource('netclaw-operations', 'references/projects.md')` |
| Discover MCP / available tools | `skill_read_resource('netclaw-operations', 'references/tools.md')` |
| Authorize or diagnose an HTTP/SSE MCP server | [MCP OAuth](#mcp-oauth) |
| Manage skills and sources | `skill_read_resource('netclaw-operations', 'references/skills.md')` |
| Manage inbound webhooks / attachments | `skill_read_resource('netclaw-operations', 'references/webhooks.md')` |
| Add/switch LLM or search provider, OAuth login | `skill_read_resource('netclaw-operations', 'references/providers.md')` |
Expand Down Expand Up @@ -113,6 +114,83 @@ 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 OAuth

For HTTP/SSE MCP servers, the Model Context Protocol .NET SDK owns OAuth
discovery, dynamic client registration (DCR), PKCE, authorization-code exchange,
token refresh, and the related HTTP calls. Netclaw presents the SDK's
authorization URL, brokers the browser callback, and durably stores the SDK token
cache. Do not fetch metadata or token endpoints by hand, build PKCE requests, or
create or repair `mcp-oauth-metadata.json`; legacy metadata files are ignored.

### Authorize a server

Run this with the daemon active:

```bash
netclaw mcp auth <name>
```

The command starts an unpublished client candidate, opens the authorization URL
when possible, always prints it, and waits up to five minutes. Complete the
browser flow normally. If the callback cannot reach this machine, paste the full
redirect URL into the command. Netclaw publishes the candidate only after the SDK
exchanges the code, credentials persist, the client connects, and tool discovery
succeeds. A failed replacement does not displace an existing healthy connection.

The SDK redirect URI is
`http://127.0.0.1:{Daemon.Port}/api/mcp/oauth/callback`. If the provider requires
a pre-registered redirect URI, use the configured `Daemon.Port`, not a fixed
default port.

A configured `Authorization` header takes precedence over SDK OAuth. Netclaw
sends that header unchanged, does not start SDK OAuth after a challenge, and
rejects `netclaw mcp auth <name>` until the header is removed. Check or rotate the
configured header instead of trying to layer OAuth on top of it.

OAuth credentials are bound to the server's canonical configured resource
identity. If the same profile name is pointed at another resource, Netclaw
withholds its old tokens and dynamically registered client credentials, reports
`AwaitingAuth`, and preserves the old durable record until replacement succeeds.
A legacy token record without a resource binding is also preserved but never
silently bound; reauthorize it. An explicitly configured static OAuth client ID
remains authoritative.

### Read connection states

| State | Meaning and action |
|-------|--------------------|
| `Connected` | A usable client generation is published. The status includes its discovered tool count. |
| `AwaitingAuth` | No usable OAuth credential is bound to this resource, or an access token expired without a refresh token. Run `netclaw mcp auth <name>`. Startup and background reconnects never open a browser or block. |
| `AuthFailed` | The server rejected credentials that were supplied. Reauthorize SDK-managed OAuth, or check the configured `Authorization` header if it owns auth. |
| `Unreachable` | A non-auth transport, network, timeout, or initialization failure prevented connection. Check the endpoint and daemon logs. |

### Diagnose failures

```bash
netclaw mcp list # configured servers plus live daemon connection states
netclaw doctor # MCP config and health checks
netclaw status # daemon connector health, including MCP
```

`netclaw doctor` uses live daemon state when available. If the daemon is down, it
can probe connectivity but cannot verify SDK-managed OAuth; start the daemon for
an authoritative auth result.

OAuth failures return safe structured errors with an `error`, an `operation`,
and, when known, an HTTP `status`. The CLI prints the useful message rather than
raw JSON. A blank provider body still produces a structured daemon error from its
HTTP status. If the daemon response body is blank or malformed, the CLI falls
back to `HTTP <code> <reason>` instead of showing an empty error. Check daemon
logs for full server context; operator-facing errors omit authorization codes,
tokens, PKCE data, and client secrets.

Credential persistence fails loudly. If the durable secrets write fails,
authorization fails, the shared cache does not advance, and the candidate is not
published. Fix the filesystem or secrets-store error shown in daemon logs, then
run `netclaw mcp auth <name>` again; browser success alone does not mean the MCP
connection is ready.

## Approval Prompts

MCP approval prompts show a bounded, redacted preview of the call arguments.
Expand Down
2 changes: 2 additions & 0 deletions openspec/changes/simplify-mcp-oauth-lifecycle/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-07-22
Loading
Loading