Skip to content
Open
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
28 changes: 28 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,34 @@ Keep it small. Keep it durable. Keep it routing-focused.
- Default to smallest safe change that advances MVP.
- Prefer explicit tradeoffs over hidden complexity.

## Communication Standard

Write all agent output in ASD-STE100 Simplified Technical English (STE). This
applies to chat replies, commit messages, PR descriptions, code comments, docs,
and spec text.

Rules:

- Give one sentence one idea. Keep an instruction to 20 words or fewer. Keep a
descriptive sentence to 25 words or fewer.
- Use the active voice. Name the actor that does the action.
- Use simple tenses: past, present, and future. Do not use perfect or
progressive tenses.
- Do not use an `-ing` word as a noun or as an adjective. Use a noun or a
relative clause.
- Keep the articles `a`, `an`, and `the`. Do not delete words to make a
sentence shorter.
- Give one word one meaning. Do not use the same word as a noun and as a verb.
- Keep a descriptive paragraph to six sentences or fewer.
- Put complex information in a vertical list.
- Put a warning or a caution before the step that it applies to.

STE does not apply to code identifiers, file paths, log text, or quoted
material. Keep those exact.

STE does not override accuracy. If the approved vocabulary cannot state a
technical fact correctly, state the fact correctly and keep the sentence short.

## Current Product Direction

- Netclaw is an open-source, self-hosted autonomous operations agent built on Akka.Agents.
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
103 changes: 102 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.38.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,106 @@ 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 PKCE,
authorization-code exchange, token refresh, and the related HTTP calls. Netclaw
owns protected-resource discovery and dynamic client registration (DCR),
presents the authorization URL, brokers the browser callback, and durably stores
active credentials. 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.

Netclaw registers rather than letting the SDK do it because the SDK hard-codes
`token_endpoint_auth_method: "client_secret_post"` and ignores what the
authorization server advertises, which fails against servers that accept public
clients only. Netclaw registers with the method the server advertises first.
Registration happens only during `netclaw mcp auth <name>`, never on a
background reconnect.

### 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 keeps exchanged credentials local to the
candidate, then commits them once and publishes the client only after tool
discovery succeeds. A failed replacement does not alter durable credentials or
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 token record written before resource binding existed is migrated in place when
its legacy resource describes the configured endpoint, so upgrading does not
force reauthorization. A trailing slash, path case, and a bare-origin resource
indicator all still match; a different scheme, host, port, query, or sibling path
does not, and those report `AwaitingAuth` with both bindings written to the
daemon log. An explicitly configured static OAuth client ID remains
authoritative.

If a server rejects the stored client identity as `invalid_client` — usually
because the registration was deleted on their side — Netclaw discards that
identity, keeps the tokens, and registers a new client on the next
`netclaw mcp auth <name>`. No manual cleanup is needed.

If a server's authorization server publishes no `registration_endpoint`, or
rejects registration, the error names the remedy: register a client manually
with that provider and set it with `netclaw mcp add --client-id <id> ...`.

### 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, active credentials do not change, 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