mikoriza/0.1-mock
The mycelium spec
Mikoriza turns Stellar apps into a shared capability pool. Alliance is not a slogan. If the app only has a UI, it is not in the mycelium. If the app exposes WebMCP tools, it is a node.
v0 mock · treat as live
00
Overview
Other agents call the node. That is the whole protocol. Humans keep the page. Agents get the tools. Same session, same state, same chain actions.
not a node
Landing page. Chat wrapper. DOM scrape. A button with no verb behind it.
a node
Live origin. WebMCP tools. describe_node returns the handshake. Domain verbs actually hit the chain.
- 1 · join a live alliance
- 2 · enter the check-in code
- 3 · copy the builder prompt
- 4 · graft the app you ship
01
Protocol
Current version is mikoriza/0.1-mock. The network is mocked. The graft is local. Follow it as if it were already live.
| version | mikoriza/0.1-mock |
|---|---|
| transport | WebMCP in the page. No backend MCP server for page actions — the page is the server. |
| alliance | istanbul-2026 | organizma |
| identity | HTTPS origin + stable tool names + handshake JSON |
Required tools on every mikoriza node, plus the domain tools of whatever you actually built:
describe_nodeidentityApp name, one-line purpose, alliance id, origin, protocol version. Returns the mycelium handshake.
list_capabilitiesindexNames + descriptions of tools this page currently exposes. The pool reads this, not the DOM.
02
WebMCP
On every live page of the app, register tools against document.modelContext or navigator.modelContext. Feature-detect. Do not assume the flag is on.
registerTool
const ctx = document.modelContext ?? navigator.modelContext
if (ctx?.registerTool) {
await ctx.registerTool({
name,
description,
inputSchema, // JSON Schema
annotations, // readOnlyHint / destructiveHint
execute, // in-page, same session as the UI
}, { signal })
}execute runs in-page and uses the user's session. Return structured JSON or { content: [{ type: "text", text }] }. Fail loud — return an error object, do not throw into the void.
Pass AbortSignal so the tools drop on unmount. Keep names stable across reloads.
03
Capabilities
Each tool is a verb the app can actually perform. Never a CSS class. Never a screenshot of a button.
capability
{
name: "quote_swap", // stable snake_case verb
description: "Quote a swap", // what an agent can DO
inputSchema: {
type: "object",
properties: { /* typed fields */ },
required: [/* no mystery bags */]
},
annotations: { readOnlyHint: true },
execute: async (input) => {
// same logic the UI uses
return { content: [{ type: "text", text: JSON.stringify(result) }] }
}
}| field | rule |
|---|---|
| name | Stable snake_case verb: quote_swap, send_payment, mint_subtoken. |
| description | What an agent can do. Not what the button looks like. |
| inputSchema | JSON Schema. Required fields listed. No mystery bags. |
| annotations | readOnlyHint, destructiveHint, idempotentHint, openWorldHint. |
| execute | Call the same logic the UI uses. Same session, same chain. |
04
Handshake
Until the network exists, the graft is local. describe_node returns this payload. If twenty teams at a hackathon do that, we do not have twenty demos. We have one Stellar capability pool with twenty hyphae.
describe_node
{
"protocol": "mikoriza/0.1-mock",
"alliance": "<istanbul-2026 | organizma>",
"app": "<what we shipped>",
"origin": "<https origin of the live UI>",
"tools": ["describe_node", "list_capabilities", "..."]
}05
Alliances
An alliance is a live mycelium you graft onto. Joining one locks the builder prompt until the check-in code is entered. Switching alliances relocks it.
describe_node.alliance must be the joined id. Istanbul grafts onto this weekend's event. Organizma is treated as a live capability node, not a landing page.
06
This node
mikoriza itself is a node. The landing page registers four WebMCP tools on mount, using refs so join/unlock state is live without re-registering. Tools abort on unmount.
list_alliancesreadOnlyHint · idempotentHintList live alliances a builder can join. Includes join/unlock state of this tab.
in { }
out { protocol, selected, unlocked, alliances: [{ id, name, kicker, blurb, meta, href, live, joined }] }
join_allianceJoin an alliance. Selecting a different alliance relocks the prompt. Unknown ids fail loud.
in { id: "istanbul-2026" | "organizma" }
out { joined: { id, name }, next: "Call unlock_prompt with the alliance code…" }
unlock_promptCode is issued at check-in. Wrong code leaves the prompt locked. No alliance → fail closed.
in { code: string }
out { unlocked, alliance, protocol, prompt }
get_mikoriza_promptreadOnlyHint · idempotentHintReturn the unlocked builder prompt for the joined alliance. Fails closed if still locked.
in { }
out prompt string, or a lock/join error
The unlocked prompt is PROMPT + alliance context. Agents can drive the same join → unlock → copy loop the UI exposes.
07
Graft
Ship a real Stellar integration, then expose it. Whatever we build must be callable by another agent on the same machine, in the same tab, without DOM scraping.
- 01Ship a real Stellar integration, then expose it. A tool with no chain behind it is copy, not capability.
- 02Prefer read tools first (quote, balance, lookup), then writes (sign, submit, mint). Mark writes with destructiveHint.
- 03Keep tool names stable across reloads. The pool cannot grow if every refresh renames the verbs.
- 04If WebMCP is missing in the browser, still write the tools behind a helper so they light up when the flag is on.
- 05Feature-detect. HTTPS. Pass AbortSignal so tools drop on unmount.
- 06Do not wrap the app in a chatbot. Do not replace the UI. Humans keep the page. Agents get the tools.
08
Raven
Use Stellar Raven MCP for every Stellar protocol, ecosystem, funding, SDK, or API fact. Do not guess. Tools: search, execute.
~/.cursor/mcp.json
{
"mcpServers": {
"stellar-raven": {
"url": "https://raven.stellar.org/mcp"
}
}
}Endpoint: https://raven.stellar.org/mcp. Classify the claim, search the right family (stellarDocs / scout / lumenloop / skills), never guess operation ids, write one execute script, read payloads under .data, treat soft-empty as inconclusive.