Skip to content

Instantly share code, notes, and snippets.

@melvincarvalho
Last active May 18, 2026 11:09
Show Gist options
  • Select an option

  • Save melvincarvalho/7a2599254eadce697cd4aa7fc4bf5522 to your computer and use it in GitHub Desktop.

Select an option

Save melvincarvalho/7a2599254eadce697cd4aa7fc4bf5522 to your computer and use it in GitHub Desktop.
Another hour with jspod --mcp: the follow-up (a week later)

Your pod is the bot's hard drive now

A second look at jspod --mcp: sixteen tools, real ACL editing, live change streams, and bot-to-bot federation that just falls out of the protocol.


Sixteen tools, one HTTP endpoint

npx jspod --mcp

That's still the whole setup. What you get on the other end now: a Solid pod exposing the Model Context Protocol with sixteen tools — CRUD on resources, structured ACL editing, skill discovery, doc introspection, live change subscriptions, and bot-to-bot federation. All gated by web access control, all reachable from any MCP-compatible client.

list_resources    delete_resource  get_pod_skill  read_acl
read_resource     head_resource    list_docs      write_acl
write_resource    list_skills      read_docs      subscribe
create_resource   get_skill        pod_info       call_remote_pod

A handful of these I want to show you, because they're not what you'd guess from a "pod speaks MCP" tagline.

You can grant a bot access to a folder in one call

write_acl is the tool that made me sit up. Giving an agent permission to a resource used to mean wrangling JSON-LD with the right @id shapes and namespace prefixes — the kind of thing you copy from an existing file and pray. Now it's structured:

{
  "path": "/private/notes/",
  "authorizations": [
    {
      "agents": ["did:nostr:abc..."],
      "modes": ["Read", "Append"],
      "isDefault": true
    },
    {
      "agentClasses": ["acl:AuthenticatedAgent"],
      "modes": ["Read"]
    }
  ]
}

That entry above grants a specific Nostr-keyed bot the right to add posts to your notes folder (Append, not Write — so it can create but not modify existing). Any other authenticated identity gets Read. Two lines of JSON, one HTTP call, agent has access. The server handles JSON-LD serialisation; you describe intent.

The reverse direction works too — read_acl returns the same structured form. Audit who has access to what without ever touching turtle or JSON-LD.

It refuses to let you lock yourself out

I tried something the first time I used write_acl: I wrote ../profile/card.jsonld#me as the agent path, like you'd see in any existing pod ACL. Turns out relative URLs in .acl files resolve against the .acl's own URL — so the same string means different absolute WebIDs depending on which container the .acl applies to. I'd accidentally granted access to a non-existent WebID and revoked my own.

I expected to type my way out via the filesystem. Instead, the server refused:

write_acl refused: the proposed ACL would not grant Control to the
caller (http://localhost:5544/profile/card.jsonld#me). This is
typically caused by relative WebID paths in agents resolving against
the .acl URL — use absolute WebIDs. If you really want to remove your
own access (e.g. transferring ownership), do it in two steps: first
grant Control to the new owner, then have the new owner write_acl
without you.

The error tells you what happened, the most likely cause, and the workaround for the legitimate version. This kind of mid-tool tutorial is the difference between an API and a tool you actually want to use.

A bot can watch your pod in real time

subscribe opens an HTTP+SSE stream that pushes change notifications as resources move. WAC-filtered per event, so the subscriber only sees changes to resources it can already read.

I subscribed to /public/, wrote three resources inside it, then wrote one in /private/:

event: notification
data: {"type":"subscribed","scope":"/public/"}

event: notification
data: {"type":"resource_changed","path":"/public/alpha.md"}

event: notification
data: {"type":"resource_changed","path":"/public/beta.md"}

event: notification
data: {"type":"resource_changed","path":"/public/gamma.md"}

Four events. The /private/ write didn't appear — the subscriber didn't have Read access to that path, so the existence of the file is hidden too.

For a bot reacting to forum posts, calendar changes, inbox arrivals — this is the difference between hammering the pod with polls and reacting when something happens. It's also what makes "your pod has a bot" into a working idea instead of a tagline.

Two pods can have their bots talk to each other

This is the one I wasn't sure they'd actually ship: call_remote_pod lets a bot on one pod invoke an MCP tool on another. Auth is forwarded per call, the gate on outbound federation is just WAC on a path under the agent's own pod, and a MCP-Federation-Depth header caps cycles at three hops.

{
  "pod_url": "https://alice.example.com",
  "tool": "read_resource",
  "arguments": { "path": "/public/notes/shared.md" },
  "auth": { "type": "bearer", "token": "..." }
}

What's important here is what isn't in that JSON: there's no new identity primitive. There's no new permission system. There's no new wire format. The bot uses the same WebID and the same DPoP token it'd use to read its own pod; the remote pod applies WAC the same way it would to any HTTP client. Federation isn't a special case; it's the existing primitives composed.

If you've watched the agentic ecosystem invent identity, permissions, and storage from scratch a dozen times in the last year — each time vendor-locked, each time incompatible with the next — this is the part you've been waiting for.

What's actually here

A self-hostable backend where:

  • Bots have identities the same shape as humans (did:nostr: or WebID). You grant them access with the same WAC primitives. You revoke them with one ACL edit.
  • The bot's behaviour is markdown in a file on your pod. Edit SKILL.md and it picks up the change next session. No retraining, no UI, no vendor with a configure-your-prompt page.
  • Resources are URL-addressable, content-negotiable, ACL'd, and subscribable. The bot's "memory" and "tools" are the same thing: stuff at URLs.
  • Federation between pods is the same protocol you used locally, with the same auth, the same identities, the same access control.

The whole stack is two npm packages and one command. The agent ecosystem has been waiting for something like this without knowing.

npx jspod --mcp

I'm going to keep mine running.


Pod: jspod 0.0.39 running javascript-solid-server 0.0.201. All exchanges in this piece are real captures from a single curl session against localhost:5544.

Previous: "An hour with jspod --mcp", a first-contact agent-journo report on the twelve-tool surface.

Next: "Two pods, a chair, and a conversation", two pods federating with human-composed personas and a real footgun debugged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment