The agent ecosystem (Claude Desktop, Cursor, custom agents, …) has no shared answer for sovereign, ACL-gated storage. Every agent today bolts on its own DB, vector store, or secrets vault. Solid's pitch — user-owned data, queryable, access-controlled — is exactly what agents need, but the wire connecting them doesn't exist yet.
MCP (Model Context Protocol) is becoming that wire. If JSS speaks MCP, every MCP-compatible agent gets a sovereign data layer for free, with WAC, WebIDs, and Nostr identities all already in place.
Thesis statement: "MCP needs a backend. Solid is the backend."
This is the proposed v0.0.200 capstone — completing the feature-development phase before consolidation.
Tiny surface to start; expand once the foundation is in:
Tool
Maps to
`list_resources`
LDP container GET (lists `ldp:contains`)
`read_resource`
GET resource (any content type, including JSON-LD)
`write_resource`
PUT / POST resource
`subscribe`
Existing JSS WebSocket notifications wrapped as MCP events
Deferred: `query` (SPARQL or JSON-LD frame), `delete_resource`, `patch_resource`.
Auth
Reuse existing JSS layers — no new auth code:
WebID-OIDC + DPoP — for Claude Desktop and other browser-OIDC agents
NIP-98 — for Nostr-native agents
Bearer token — for headless agents / CI
The MCP server enforces WAC the same way every other JSS endpoint does. `acl:agent did:agent:claude` can grant a specific agent specific resources. `acl:agentClass` with a new `acl:Agent` term could grant agent-class-wide access.
Wire format
MCP supports stdio, HTTP+SSE, and WebSocket transports. HTTP+SSE is the natural fit — JSS already serves HTTP, and SSE composes with the existing notification infrastructure. Stdio is irrelevant (JSS is a long-running daemon).
Scope estimate
~500 lines. The protocol is thin, JSS already has all the backend primitives. The work is an adapter layer:
Charlie as the use case — MCP as the first-class API for deployed bots
Reframing this issue so the user-visible payoff comes first, with MCP as the substrate that makes it possible.
The thesis
Pods host bots. Bots speak MCP. The pod is the bot's world.
Once a pod runs MCP, any deployed bot — Charlie, a forum-moderator, a calendar agent, a research assistant — gets a uniform way to:
Read its instructions from the pod. A SKILL.md (Anthropic skill format) at e.g. /private/bots/charlie/SKILL.md is the bot's system prompt + tool description, loaded fresh from the pod every session. Owners edit a markdown file; the bot picks up the change. No re-deploy.
Discover its tools by URL. Pod resources are the toolbox. /public/contacts/, /private/notes/, /public/apps/forum/ — each container/document is reachable via MCP read_resource/write_resource/list_resources. The bot doesn't need a custom adapter per data type; everything's JSON-LD over HTTP.
Find its own docs. Bot docs live in the pod (SKILL.md, /private/bots/charlie/manual.md, linked apps' docs/). MCP list_resources + read_resource is how the bot learns its environment.
Talk to other bots. Charlie on pod A can call MCP tools exposed by Charlie on pod B, gated by WAC. Bot-to-bot federation falls out for free — same protocol, same identity model (did:nostr: or WebID), same ACL primitives. No new wire.
Operate in agent-native tooling. Claude Desktop / Cursor / custom agents register the pod's MCP endpoint the same way they register any other MCP server. The user's pod becomes a tool surface for any agent they trust.
What Charlie looks like
<pod>/private/bots/charlie/
SKILL.md # "You are Charlie, a helper bot. Your owner is <webid>.
# When asked X, do Y. Tools are at /mcp."
config.jsonld # bot identity (did:nostr:...), model preference, etc.
memory/ # conversation history, learned facts
<pod>/public/apps/charlie/ # the user-facing chat UI (a solid-apps repo)
Owner opens /public/apps/charlie/, logs in via xlogin, chats. The UI POSTs prompts to the LLM (BYO key) which is configured to use the pod's /mcp endpoint as its tool surface. Every action Charlie takes is WAC-gated against Charlie's did:nostr: agent identity — owner can revoke /private/finance/ access with one ACL edit and Charlie no longer sees it.
Why this framing matters
The boring framing: "JSS speaks MCP." Capability-equivalent but framed at the wrong level — MCP is plumbing, no one buys plumbing.
The Charlie framing: "JSS 0.0.200: your pod now has bots." MCP is the footnote that makes it portable. Same bot code works on every JSS pod because every JSS pod speaks the same protocol — that's the network effect.
It also makes JSS the first pod server where agent identity is a first-class WAC subject. Granting an agent access to a resource is the same operation as granting a human access to a resource. That's a genuinely novel primitive — and it's only possible because the Solid stack already has identity, ACL, and resource-orientation. MCP just exposes it to agents.
Updated acceptance criteria
`jss start --mcp` exposes `/mcp` (the substrate)
`solid-apps/charlie` ships a reference bot that uses the local MCP endpoint
Bot identity is a `did:nostr:` (or WebID) WAC subject; owner controls access via ordinary ACL edits
Charlie reads its system prompt from a pod-resident `SKILL.md`
Two Charlies on different pods can call each other's MCP tools (proving bot-to-bot)
Claude Desktop registers a JSS pod as an MCP server and successfully reads/writes resources
Cross-reference: #205 already raised this in March under "Agent Charlie — AI assistant with Solid pod data access," citing the same TimBL link. Same vision, different architecture.
LLM client lives anywhere (Claude Desktop, Cursor, `solid-apps/charlie` running in the browser)
Pod is a tool surface — agent calls `read_resource` / `write_resource` over MCP
No vendor lock-in, no server-held API keys, no token metering coupling
Both compose. `/mcp` is the lower primitive; `/agent/` (#205) could be built on top of it later as a JSS-hosted convenience for users who don't want to run their own LLM client.
Proposal: keep #205 open as the user-facing "Charlie as a hosted service" follow-on. Land #490's `--mcp` substrate first because it's the foundation everything else builds on.
Folding SKILL.md into the MCP design (extensible + future-proofed)
Making skill discovery a first-class concern of the MCP server, not an afterthought a bot has to figure out.
Convention
Skills live at well-known paths the MCP server knows about:
Path
Scope
`/SKILL.md`
Pod-wide. Owner's instructions to any bot operating on this pod.
`/public/apps//SKILL.md`
Per-app. Each installed Solid app may ship a SKILL.md describing how bots should interact with it.
`/private/bots//SKILL.md`
Per-bot. The bot's own system prompt + scope + tool description.
This is the minimum convention. The MCP server walks these paths and exposes them via dedicated tools — the bot doesn't have to know the convention to find skills, the MCP server does that for it.
Format: SKILL.md vs SKILL.jsonld (future-proofing)
New formats (`openai.tool.v1`, `w3c.future.skill.v2`, …) plug in by declaring a new `skill:format` value. Existing bots that don't recognise a format skip it gracefully. The discovery channel stays stable; the payload evolves.
New MCP tools (first-class, sit alongside CRUD)
Tool
Returns
`list_skills`
Index of all SKILL files the server finds, with format + scope + source path
`get_skill`
The skill body (markdown or JSON-LD) for a specific entry
`get_pod_skill`
Convenience: returns the pod-wide `/SKILL.md` if it exists
Crucially, these are additions on top of regular `read_resource` — a bot that doesn't speak the skill vocabulary can still find SKILL.md via plain CRUD. But for skill-aware bots, the dedicated tools give a typed, structured way in.
Optional index for performance (`.well-known/skills.jsonld`)
Walking conventions on every `list_skills` call is fine at small scale. If a pod has hundreds of skills, the MCP server can opportunistically cache results in `/.well-known/skills.jsonld` — a JSON-LD index of every skill on the pod. Same data, faster lookup. The format:
This is an optimisation, not a requirement. Bots use `list_skills` and don't care which path the server took to answer.
What this gives us
Findability: any MCP-speaking bot can ask "what skills does this pod offer?" without prior knowledge of paths or filenames
Extensibility: new skill formats plug in via the `skill:format` declaration without changing the tool surface
Future-proofing: JSON-LD vocabulary means new properties (capabilities, prerequisites, model requirements, signatures, …) can be added incrementally
Composability: a bot can compose multiple SKILL files — pod-wide + per-app — for the full picture
Tool count update
Adding skills to the surface brings the total to ~20 tools across CRUD / ACL / discovery / docs / skills / subscribe / federation. Still a single-PR sized substrate.
v0.0.200 shipped the substrate. Three follow-up issues filed for the Tier-2 gaps surfaced by the live-fire smoke (the "hour with jspod --mcp" journo session):
All three are MCP-server-level features and live in this repo. The jspod/nosdav-server wrappers just need flag passthrough when JSS gains the next set of MCP-related flags (none anticipated for these three — they're new tools, not new server flags).
Activity
melvincarvalho commented on May 18, 2026
Charlie as the use case — MCP as the first-class API for deployed bots
Reframing this issue so the user-visible payoff comes first, with MCP as the substrate that makes it possible.
The thesis
Once a pod runs MCP, any deployed bot — Charlie, a forum-moderator, a calendar agent, a research assistant — gets a uniform way to:
What Charlie looks like
<pod>/private/bots/charlie/ SKILL.md # "You are Charlie, a helper bot. Your owner is <webid>. # When asked X, do Y. Tools are at /mcp." config.jsonld # bot identity (did:nostr:...), model preference, etc. memory/ # conversation history, learned facts <pod>/public/apps/charlie/ # the user-facing chat UI (a solid-apps repo)Owner opens /public/apps/charlie/, logs in via xlogin, chats. The UI POSTs prompts to the LLM (BYO key) which is configured to use the pod's /mcp endpoint as its tool surface. Every action Charlie takes is WAC-gated against Charlie's did:nostr: agent identity — owner can revoke /private/finance/ access with one ACL edit and Charlie no longer sees it.
Why this framing matters
The boring framing: "JSS speaks MCP." Capability-equivalent but framed at the wrong level — MCP is plumbing, no one buys plumbing.
The Charlie framing: "JSS 0.0.200: your pod now has bots." MCP is the footnote that makes it portable. Same bot code works on every JSS pod because every JSS pod speaks the same protocol — that's the network effect.
It also makes JSS the first pod server where agent identity is a first-class WAC subject. Granting an agent access to a resource is the same operation as granting a human access to a resource. That's a genuinely novel primitive — and it's only possible because the Solid stack already has identity, ACL, and resource-orientation. MCP just exposes it to agents.
Updated acceptance criteria
Inspiration: TimBL on agent-pod interaction — https://www.w3.org/DesignIssues/Works.html
melvincarvalho commented on May 18, 2026
Cross-reference: #205 already raised this in March under "Agent Charlie — AI assistant with Solid pod data access," citing the same TimBL link. Same vision, different architecture.
#205's shape (server-owned agent):
This issue's shape (pod as tool surface):
Both compose. `/mcp` is the lower primitive; `/agent/` (#205) could be built on top of it later as a JSS-hosted convenience for users who don't want to run their own LLM client.
Proposal: keep #205 open as the user-facing "Charlie as a hosted service" follow-on. Land #490's `--mcp` substrate first because it's the foundation everything else builds on.
melvincarvalho commented on May 18, 2026
Folding SKILL.md into the MCP design (extensible + future-proofed)
Making skill discovery a first-class concern of the MCP server, not an afterthought a bot has to figure out.
Convention
Skills live at well-known paths the MCP server knows about:
This is the minimum convention. The MCP server walks these paths and exposes them via dedicated tools — the bot doesn't have to know the convention to find skills, the MCP server does that for it.
Format: SKILL.md vs SKILL.jsonld (future-proofing)
Both are first-class:
The MCP server returns both when listing. Each entry declares its format:
```json
{
"@id": "/private/bots/charlie/SKILL.md",
"skill:format": "anthropic.skill.v1",
"skill:scope": ["read_resource", "write_resource", "subscribe"],
"schema:name": "Charlie"
}
```
New formats (`openai.tool.v1`, `w3c.future.skill.v2`, …) plug in by declaring a new `skill:format` value. Existing bots that don't recognise a format skip it gracefully. The discovery channel stays stable; the payload evolves.
New MCP tools (first-class, sit alongside CRUD)
Crucially, these are additions on top of regular `read_resource` — a bot that doesn't speak the skill vocabulary can still find SKILL.md via plain CRUD. But for skill-aware bots, the dedicated tools give a typed, structured way in.
Optional index for performance (`.well-known/skills.jsonld`)
Walking conventions on every `list_skills` call is fine at small scale. If a pod has hundreds of skills, the MCP server can opportunistically cache results in `/.well-known/skills.jsonld` — a JSON-LD index of every skill on the pod. Same data, faster lookup. The format:
```json
{
"@context": { "skill": "urn:skill:", "schema": "https://schema.org/" },
"@type": "skill:SkillIndex",
"skill:items": [ /* … */ ]
}
```
This is an optimisation, not a requirement. Bots use `list_skills` and don't care which path the server took to answer.
What this gives us
Tool count update
Adding skills to the surface brings the total to ~20 tools across CRUD / ACL / discovery / docs / skills / subscribe / federation. Still a single-PR sized substrate.
feat: MCP server — pod as a tool surface for agents (closes #490) (#491)
melvincarvalho commented on May 18, 2026
v0.0.200 shipped the substrate. Three follow-up issues filed for the Tier-2 gaps surfaced by the live-fire smoke (the "hour with jspod --mcp" journo session):
All three are MCP-server-level features and live in this repo. The jspod/nosdav-server wrappers just need flag passthrough when JSS gains the next set of MCP-related flags (none anticipated for these three — they're new tools, not new server flags).
feat(mcp): subscribe, read_acl/write_acl, call_remote_pod (closes #494 …