shelley

Customizing

MCP servers

Connecting Shelley to remote MCP servers, logging in with OAuth, and how the agent calls their tools.

Shelley can use tools from Model Context Protocol servers. Two things work differently from many MCP clients:

  • HTTP only. Shelley connects to servers over Streamable HTTP. It doesn’t launch local stdio servers (the npx some-mcp-server kind).
  • Not model tools. MCP tools aren’t added to the model’s tool list. The agent reaches them by running shelley mcp with its bash tool, guided by the built-in mcp skill. A server costs one line in the system prompt until it’s needed, and results can be piped through jq like any other command output.

Adding a server

From a terminal:

shelley mcp add tracker -d 'Issue tracker for the web team' https://mcp.example.com/mcp

For a server that takes an API key, pass it as a header:

shelley mcp add search -H 'Authorization: Bearer sk-...' https://example.com/mcp

Or use MCP Servers in the conversation’s More options menu, or ask Shelley to add it. The name is what you type in commands: letters, digits, _ and -, starting with a letter or digit, and it can’t be changed later. The description goes in the system prompt, so write it for the model: what the server is for, and when to reach for it.

Servers live in Shelley’s SQLite database, not in a config file. A different --db means a different set of servers. Headers are stored as plain text there too, which is one more reason to keep the database private.

Logging in with OAuth

A server without an Authorization header can log you in with OAuth. You don’t do anything at add time. The first time the agent (or you) uses the server and it answers 401, the command fails with a link:

shelley mcp: MCP server "tracker" needs you to log in: open /mcp/login/tracker

The link is relative to wherever you reach Shelley, e.g. http://localhost:9000/mcp/login/tracker. The agent is told to hand it to you and wait. Opening it:

  1. registers Shelley as a client with the server’s authorization server (dynamic client registration), with the redirect URI <your Shelley address>/mcp/oauth/callback;
  2. sends your browser there to log in;
  3. brings you back to Shelley and stores the tokens in the database. Shelley refreshes them when they expire.

A started login waits up to ten minutes for you. shelley mcp auth NAME shows the login state and link; the MCP Servers dialog has Log in and Log out buttons.

Some servers only accept clients they’ve approved and refuse the registration; the error says so. Use a token header for those.

Editing a server’s URL keeps its login if only the path or query changes. Changing the scheme or host, or adding an Authorization header, drops it.

How the agent uses them

A new conversation’s system prompt lists the registered servers with their descriptions. If you add or remove one, or change its URL or headers, while the agent is working, Shelley tells it mid-turn. Idle conversations aren’t interrupted; their system prompt keeps the old list, but shelley mcp list is always current.

A typical exchange from the agent’s side:

shelley mcp search issue label
shelley mcp list tracker.search_issues -schema
shelley mcp call tracker.search_issues query='login bug' limit=5

Each conversation gets its own session with each server, so server-side state lasts across calls. Forks and subagents start fresh, and an unused session closes after 30 minutes.

This relies on two things:

  • shelley on the PATH that Shelley runs with.
  • The Unix socket. Shelley exports its path to the agent’s commands as SHELLEY_SOCKET; with serve -socket none, shelley mcp from the agent can’t find the server.

Commands

shelley mcp talks to a running Shelley. By default that’s the server whose socket is in $SHELLEY_SOCKET, else ~/.config/shelley/shelley.sock ($XDG_CONFIG_HOME/shelley/shelley.sock if that’s set). Point it elsewhere with -url, which goes before the command:

shelley mcp -url http://localhost:9001 list
Command Does
list Lists servers with their login state, without connecting.
list SERVER Shows the server’s instructions and its tools as TypeScript-style signatures.
list SERVER.TOOL [-schema] Shows one tool; -schema adds its JSON schemas.
list -json [SERVER[.TOOL]] The same, as JSON.
search [SERVER] TERM... Tools matching every term in their name, description, or parameters, across all servers unless one is named. -json for JSON.
call SERVER.TOOL [KEY=VALUE...] Calls a tool. Values are used as is for string parameters and parsed as JSON otherwise.
call SERVER.TOOL - Reads the arguments as a JSON object from stdin.
add NAME [-d DESC] [-H 'K: V']... URL Registers a server. -H repeats.
rm NAME Removes a server and its login.
auth NAME Shows the OAuth login state and, if needed, the login link.
restart NAME Ends this conversation’s session with the server.

call takes -json (print the raw CallToolResult) and -timeout (default 1m; a call that timed out may still have run). Images and other binary results are saved to temporary files and the output says where. If the tool reports an error, it goes to stderr and the exit status is 1.

Subcommand flags can go before or after the arguments:

shelley mcp call tracker.get_issue id=ENG-123 -json | jq .structuredContent
echo '{"query": "login bug"}' | shelley mcp call tracker.search_issues -

Troubleshooting

/debug/mcp on your Shelley shows the registered servers, open sessions, and recent events such as logins.

The code is in mcp/, server/mcp.go, and client/mcp.go.