Skip to content

MCP integration

ShellWatch exposes a Model Context Protocol server over streamable HTTP at /mcp. Each MCP client connection gets an isolated AgentSession — agents can only see and control sessions they themselves created. The Web UI for the same account sees those sessions too (labelled (mcp)), so a human can watch an agent live — but visibility never crosses account boundaries.

Authentication is OAuth 2.1 with Dynamic Client Registration, issued by Ory Hydra (the OAuth authority that ships with every ShellWatch deployment) and gated by your passkey. Point the client at the bare /mcp URL and the flow handles everything — there are no API keys to mint or paste.

Works in any MCP client (Claude Desktop, Claude Code, MCP Inspector, …). Just give the client the bare URL:

{
"mcpServers": {
"shellwatch": {
"type": "streamable-http",
"url": "https://app.shellwatch.ai/mcp"
}
}
}

What happens under the hood:

  1. The client hits /mcp unauthenticated → ShellWatch responds with WWW-Authenticate pointing at the OAuth discovery metadata (/.well-known/oauth-authorization-server).
  2. The client registers itself at /api/hydra/register (mediated DCR: ShellWatch validates the redirect URI and requested scope against local policy, then provisions a real per-client client_id in Hydra).
  3. The client opens Hydra’s /oauth2/auth in your browser → Hydra redirects to ShellWatch’s passkey login page → you authenticate and consent to the requested scope.
  4. The client exchanges the authorization code (PKCE) at /oauth2/token for an mcp-scoped access token plus a refresh token.

Access tokens are short-lived (30 minutes); the refresh token (30 days, rotating) lets the client renew silently, so you only see the browser once per client per month. To cut a client off, revoke its grant — log out / revoke sessions in ShellWatch — rather than hunting for a key to delete.

The flow is scope-aware: MCP clients get mcp; the same flow is how shellwatch-agent login enrols with agent scope. The ui scope is reserved for ShellWatch’s own web UI and is never grantable via DCR. The token authorises routing, not the privileged operation — opening a session still triggers a passkey approval. See Concepts → Security model.

On self-hosted instances, which redirect URIs may register is policy you control (hydra.dcr.redirectUriPatterns). The default is loopback-only — fine for the agent-client and local MCP tools, but a hosted client like Claude.ai (which registers an https://claude.ai/api/mcp/auth_callback redirect) is rejected until you add its pattern. See the Configuration reference for how to extend the list. (On app.shellwatch.ai the hosted Claude callbacks are already allowed.)

ToolDescription
shellwatch_create_sessionOpen a new terminal session against a configured endpoint. Returns a sessionId.
shellwatch_list_sessionsList sessions owned by this agent connection.
shellwatch_send_keysSend keystrokes / text to a session. Keys are an array — ["text:ls -la", "enter"], ["ctrl:c"], ["text:hello", "tab"], etc.
shellwatch_read_outputRead session output. afterOffset lets you do incremental reads — pass back the last endOffset you saw.
shellwatch_close_sessionClose a session. (Sessions also close automatically when the MCP transport disconnects.)
shellwatch_manage_endpointsList, create, update, or delete SSH endpoints. Useful when you want the agent to provision its own targets — most operators leave this scoped tightly.
shellwatch_manage_keysList available SSH keys.

See the MCP tools reference for the exact schema of each tool.

ShellWatch pushes two kinds of MCP notifications back to the agent:

  • output_available — new output is ready (debounced ~100ms). Carries sessionId and offset. The agent should follow up with shellwatch_read_output(sessionId, afterOffset=offset).
  • session_status — session state changed (open, closed, errored). Carries sessionId, status, endpointId.

Notifications are scoped to the agent’s own sessions only.

Each MCP client connection is one AgentSession. Two Claude Desktop instances connecting under the same account get two independent agent sessions and cannot see each other’s terminal sessions through the MCP tools. The Web UI for that same account sees both (labelled (mcp)) so the human owner of the account can observe their own agents — but visibility never crosses to another user’s account.

When the MCP transport disconnects, all owned terminal sessions are closed.

A typical agent flow:

1. shellwatch_manage_endpoints → list endpoints, get an endpointId
2. shellwatch_create_session(endpointId) → returns sessionId
(the SSH handshake triggers a /sign/:id approval before the session opens)
3. shellwatch_send_keys(sessionId, ["text:uname -a", "enter"])
4. → output_available notification arrives
5. shellwatch_read_output(sessionId, afterOffset=0) → reads "Linux ..."
6. shellwatch_send_keys(sessionId, ["text:exit", "enter"])
7. → session_status notification → closed

If you want a human to watch the agent live, open the matching session in the Web UI sidebar — the same buffer streams there in real time.