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-serverkind). - Not model tools. MCP tools aren’t added to the model’s tool list. The
agent reaches them by running
shelley mcpwith its bash tool, guided by the built-inmcpskill. A server costs one line in the system prompt until it’s needed, and results can be piped throughjqlike 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:
- registers Shelley as a client with the server’s authorization server
(dynamic client registration), with the redirect URI
<your Shelley address>/mcp/oauth/callback; - sends your browser there to log in;
- 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:
shelleyon thePATHthat Shelley runs with.- The Unix socket. Shelley exports its path to the agent’s commands as
SHELLEY_SOCKET; withserve -socket none,shelley mcpfrom 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.