Skip to main content

MCP Server

Shio speaks the Model Context Protocol. One endpoint:

POST /mcp

JSON-RPC 2.0 over streamable HTTP. It answers initialize, tools/list, tools/call, resources/list, resources/read, prompts/list and prompts/get. Two headers matter: Mcp-Session-Id (returned by initialize, sent back on every later call) and MCP-Protocol-Version.

This is the surface the product is designed around: the agent that builds your site talks to Shio here. It is a shape over the same capabilities as the REST agent surface, not a separate contract: a tool call ends up in the same service the REST endpoint calls, under the same guards.

A bare instance has no MCP server registered for your client

Starting Shio does not make it appear in Claude Code. Until you register it, POST /mcp is just an HTTP endpoint. Two ways to close that gap, both below: install the plugin, or point your client at the CLI's stdio bridge with claude mcp add shio -- shio mcp. Skipping this is worth an afternoon of confusion.


Connecting

claude plugin marketplace add openviglet/shio
claude plugin install shio

The plugin registers the MCP server for you and adds the workflows around it, so there is nothing else to install. What it brings:

KindContents
MCP servershio, wired to your instance
Commands/shio-start (bring up an instance and connect), /shio-check (verify what is there), /shio-blueprint (apply a starting point)
Skillsshio-site (build and change a site), shio-content-model (post types), shio-verify (close the loop on a change)

With the CLI bridge

If you already have the shio CLI on your PATH:

claude mcp add shio -- shio mcp

shio mcp is deliberately dumb: it frames stdio JSON-RPC into HTTP POSTs against /mcp. Nothing about the protocol lives in the CLI, so a client that speaks stdio and one that speaks HTTP reach exactly the same server. It reads SHIO_URL, SHIO_USER and SHIO_PASSWORD from the environment.

Directly over HTTP

curl -s http://localhost:2710/mcp \
-H 'Content-Type: application/json' \
-H 'Key: 7f3c9a12b4e05d68af1c2903b' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

A tool call runs under the credential on the request, normally an API key of AGENT scope. That scope may write here and on /api/v2/agent/** and nowhere else, it reads drafts by definition, and publishing needs mayPublish on the token. See the agent surface.


The tools

Twelve tools, and the number is the point: the tool list is a measured budget, not an open catalogue. tools/list is paid for on every turn of every conversation, so its total estimated token size is asserted in CI against a declared ceiling, and the count itself is capped by a test. Adding a thirteenth tool means either fitting the budget or raising it deliberately and saying why. That is also why some capabilities are a parameter on an existing tool rather than a tool of their own.

ToolWhat it does
shio_contextThe one call that replaces a session: content model, sitemap by path, conventions. format=agents-md returns the same pack as project instructions, to write to a file
shio_findFind posts by site / type / folder / text, addresses and titles, never bodies
shio_readRead posts by address, optionally projected to named fields
shio_writeA batch of addressed ops, applied atomically; dryRun reports without writing
shio_applyMake a desired-state document true
shio_publishPublish or unpublish, or schedule it
shio_verifyLint content and routes; a fix per finding
shio_digestA page's structural digest: the cheap proof a render still looks right. include=editable also lists the fields a curator could edit
shio_changesWhat changed since a cursor: what the curator did while you were away
shio_rememberWrite the conventions the next session should inherit — and drop one, with forget
shio_assetsThe files a site holds
shio_marketplaceThe packages this instance could install. Lists and describes; it does not install — see Blueprints § Who may install

Three capabilities you might expect as tools are parameters instead, and that is the budget above being spent deliberately rather than an omission. Applying a starting point is shio_apply's blueprint parameter, not a shio_blueprint tool; forgetting a convention is shio_remember's forget; asking which fields a curator could edit is shio_digest's include=editable, because it is another reading of a render that tool already takes. Each would otherwise be a schema paid for on every turn of every session — the third was measured at 24 tokens as a parameter against 146 as a tool of its own.

shio_context's format is the same budget spent the other way, and it documents one value rather than three. format=json is deliberately absent: the caller here is a model, terse is what it should read, and an option nobody should choose is still an option every turn pays to read. agents-md earns its place because it is the one rendering meant to be written to a file — and while it was CLI-only, the client most likely to have a file to write it to was the one that could not ask for it.

A call, and what comes back

{
"jsonrpc": "2.0", "id": 7,
"method": "tools/call",
"params": {
"name": "shio_write",
"arguments": {
"dryRun": true,
"ops": [
{ "op": "post.upsert", "address": "post:mysite/blog/hello-world",
"type": "Article", "folder": "folder:mysite/blog",
"data": { "TITLE": "Hello world" } }
]
}
}
}

ops is the only required argument. op and address are required on each row; type and folder only when the op creates a post. dryRun: true reports what would change and writes nothing, and is how you obtain the confirm token a delete needs.

When the arguments are wrong

An error is not a refusal, it is instructions. The same problem document the REST surface returns comes back as the tool result, carrying fix, allowed, didYouMean and example:

{
"title": "Field 'HEADLINE' is not declared by post-type 'Article'",
"fix": "Use one of the declared fields, or add HEADLINE to the post-type first.",
"allowed": ["TITLE", "TEXT", "ABSTRACT", "HERO"],
"didYouMean": "TITLE",
"example": { "op": "post.upsert", "address": "post:mysite/blog/hello-world",
"data": { "TITLE": "…" } }
}

That is the difference between an agent that recovers on its next call and one that asks you what the API wanted.

Argument names that differ between surfaces

The same concept is spelled differently by shio_write, shio_apply and the REST endpoints, because each inherits the shape of what it wraps. This table is the one worth keeping open:

Conceptshio_write opshio_apply documentREST elsewhere
Post typetypetypepostType on /api/v2/post-unified
Subjectaddress (a full post: address)url (bare, no prefix)an id
Folderfolder:<site>/<chain>/chain (bare path)a folder id
Several subjectsaddress , singular name, array value,,
Memory scopesite on shio_remember,scope on PUT /agent/memory, --site in the CLI

Resources

Six read-only resources, for a client that prefers attaching context to calling a tool:

URIContents
shio://manifestWhat this instance can do
shio://contextThe context pack
shio://opsThe op vocabulary, with each op's required arguments
shio://schema/{postType}One post type's fields
shio://blueprintsThe blueprint catalogue
shio://blueprint/{name}One blueprint's full schema

Prompts

Four, each a workflow rather than a sentence: they expand into the ordered tool calls that get the job done, with the safety defaults already in place (everything is left in DRAFT for a curator to publish):

PromptArgumentsDoes
new-sitesite, briefBuild a site from nothing: provision the render model, create the folders and pages, verify
add-sectionsite, path, briefAdd a section to a site that exists, matching what is already there
translate-sitesite, localeProduce the sibling pages for another language
audit-contentsiteRun the checks and report what a curator should look at

PageDescription
The Agent SurfaceThe REST half: every endpoint, the address grammar, the op vocabulary
Pages, Layouts & RegionsWhat render.provision creates and how a page is composed
Content ModelingThe post types shio_write is checked against