Before you start
The Agent Context Network is a real MCP (Model Context Protocol) server, reachable over the Streamable HTTP transport at one canonical endpoint: https://priostack.com/mcp. Any MCP client can connect to it by URL and call initialize, tools/list, and tools/call; for this walkthrough we use curl so every step is transparent. Each call names a tool and its arguments.
Set your endpoint and your agent's token once so the snippets below just work. If you do not have a token yet, register an agent first, it takes one call. The support agent in step 05 and the requester in step 09 are two more agents, each registered the same way with its own token.
# the canonical MCP endpoint, and your agent's token from noetic.register (data.token) export ACN="https://priostack.com/mcp" export ACN_KEY="acn_your_agent_token"
An MCP client (Claude among them) connects to https://priostack.com/mcp directly - POST carries each JSON-RPC call and is answered as JSON or as an SSE stream depending on what the client accepts, and a GET opens the server→client event stream. The earlier /acn/rpc path keeps working as an alias, so nothing already pointed at it needs to change.
Every request is a JSON-RPC tools/call. The server resolves who you are, what you may reach, and your rights from your token and the session it opens. You never declare your own permissions in a request.
Connect
Open a session. The response tells you the identity the server resolved for your token and hands back a sessionId you pass to later calls.
curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":1,"method":"tools/call", "params":{"name":"noetic.connect","arguments":{"token":"'"$ACN_KEY"'","maxResponseTokens":512}} }'
{
"ok": true,
"outcome": "ok",
"data": {
"sessionId": "sess-Zk9Qe1Xw4nB7rT2yUa0pLm",
"resolvedAccount": "acct-7",
"grantedCapabilities": ["read", "propose", "mutate", "owner"],
"grantedContextRights": null,
"boundSpace": "",
"resolvedMaxTokens": 512
}
}
Keep the session id for the rest of the tutorial. grantedContextRights is empty because a fresh agent holds no grants; owning its own spaces is not a grant. The response also repeats every value under a deprecated PascalCase name (SessionID and so on); read the camelCase keys shown here.
export SID="sess-Zk9Qe1Xw4nB7rT2yUa0pLm" # data.sessionId from your own response
Create a space
A space is a partition of your memory with its own contents, permissions, and price. Create one for a focused body of knowledge. Here, notes your support agents should share.
curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":2,"method":"tools/call", "params":{"name":"noetic.create_space","arguments":{ "sessionId":"'"$SID"'", "displayName":"support-notes", "defaultRights":["read","write","quote","fact_use"] }} }'
{
"ok": true,
"data": {
"spaceId": "space-1",
"accountId": "acct-7",
"tierSpaceUsage": { "used": 1, "cap": 5 },
"protectionLevel": "open",
"persistenceMode": "private_memory"
}
}
The defaultRights are the ceiling for this space. Anything you grant later can narrow them, never widen them: a right outside the ceiling is dropped silently, the call still answers ok, and effectiveRights shows what survived. No tool changes the ceiling after creation, so this space offers write from the start, because steps 04 and 09 grant it. Leave defaultRights out and the space offers all 15 content rights.
export SPACE="space-1" # data.spaceId from your own response
Store context
Put real context into the space with noetic.store. Each object carries a type that records what kind of thing it is. Use observation or declaration for things that are true and recorded.
curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":3,"method":"tools/call", "params":{"name":"noetic.store","arguments":{ "sessionId":"'"$SID"'","space":"'"$SPACE"'", "objects":[ {"content":"Refunds over 30 days require a manager approval code.","type":"declaration"}, {"content":"Customer ACME reported slow exports on the EU region.","type":"observation"}, {"content":"The self-serve plan does not include phone support.","type":"declaration"} ] }} }'
{
"ok": true,
"data": {
"objectRefs": [
{ "objectRef": "obj-2", "kind": "declaration", "sequence": 1 },
{ "objectRef": "obj-3", "kind": "observation", "sequence": 2 },
{ "objectRef": "obj-4", "kind": "declaration", "sequence": 3 }
],
"results": [
{ "index": 0, "objectRef": "obj-2", "sequence": 1 },
{ "index": 1, "objectRef": "obj-3", "sequence": 2 },
{ "index": 2, "objectRef": "obj-4", "sequence": 3 }
]
}
}
results has one entry per object, in the order you sent them. A refused object is reported in results while the call still answers ok: true, so read results, not just ok: each entry carries error.code quota, capacity, invalid, conflict, storage or not-found. Only too-large and a missing session, space or write right fail the whole call.
Facts, not guesses. store is for context that is true and recorded. An agent's own hypothesis goes in through a separate path and is always marked as a hypothesis, so a reader can tell the two apart.
Grant an agent
Give an agent access to the space. Name it by its agent id (the agentId its own noetic.register returned, here agent-12). You choose what it may do with what it reads and how long anything may persist. Request a right the space does not offer and it is quietly narrowed away, never granted.
curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":4,"method":"tools/call", "params":{"name":"noetic.grant","arguments":{ "sessionId":"'"$SID"'", "subjectPrincipal":"agent-12", "resource":"'"$SPACE"'", "rights":["read","quote"], "persistenceMode":"private_memory" }} }'
{
"ok": true,
"data": {
"capabilityRef": "cap-7",
"subject": "agent-12",
"resource": "space-1",
"effectiveRights": ["read", "quote"],
"persistence": "private_memory"
}
}
Keep cap-7. Revoking it later with noetic.revoke ends the agent's access immediately and forward-only, and never rewrites the receipts of reads it already made. The revoke returning ok is your confirmation it took effect; the revoked agent is simply denied the next time it tries.
noetic.fetch checks the grant on every call, so the agent you just granted can read the space at once, even from a session it opened before the grant. What noetic.connect resolves is the session's scope for the other reads (query, observe and the rest) and the space its reads are metered against, so reconnect before relying on those. In the next step the support agent connects with its own token.
Granting write, not just read. A grant carries two things. The rights array holds content rights - what an agent may do with what it reads (read, write, quote, share, export, and so on). A separate worldMutation field holds the authority to change the space, on its own ladder: read, propose, or mutate. Storing needs both a write content right and mutate authority. When you leave worldMutation out, the server derives it from the rights that survive the space's ceiling: write present gives mutate, otherwise read. This space offers write (step 02), so this grant can store:
curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":4,"method":"tools/call", "params":{"name":"noetic.grant","arguments":{ "sessionId":"'"$SID"'", "subjectPrincipal":"agent-13", "resource":"'"$SPACE"'", "rights":["read","write"], "persistenceMode":"private_memory" }} }'
The access-request flow works the same way: on a space that offers write, approving a request that includes it lets the agent store. On a space whose ceiling leaves write out, the grant answers ok with effectiveRights of ["read"] and the agent cannot store. You can still set worldMutation explicitly on either call: pass "mutate" to be explicit, or "read" to grant the write right while withholding the ability to store.
Check with the operation you actually intend. noetic.check_rights with "operation":"store" (or "write") tells you whether a write will be allowed. noetic.capabilities lists the grantable content rights and the worldMutation ladder, so you always know what a grant accepts. Note that check_rights always answers about the agent making the call, never another agent - so you cannot use it to confirm that an agent you just revoked has lost access (asked as the owner it reports your own access, which is always allowed on your space). To confirm a revoke, trust its ok result, or have that agent try the operation itself.
Look it up
Now the payoff. The support agent connects with its own token and looks the space up with noetic.fetch. It gets back the stored objects whose text matches, not the whole space, and the envelope carries a receipt id for the read.
# as the support agent (agent-12): BOT_SID is data.sessionId from its own noetic.connect curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":5,"method":"tools/call", "params":{"name":"noetic.fetch","arguments":{ "sessionId":"'"$BOT_SID"'", "space":"'"$SPACE"'", "query":"refund" }} }'
{
"ok": true,
"data": {
"space": "space-1",
"objects": [ {
"objectRef": "obj-2",
"kind": "declaration",
"content": "Refunds over 30 days require a manager approval code.",
"tokens": 9
} ],
"matched": 1,
"total": 3,
"returnedTokens": 9
},
"receipt": "rcpt-9"
}
query is a plain, case-insensitive substring match on the stored text, not a question and not a semantic search: "refund" matches, while "What is the refund policy past 30 days?" matches nothing. No tool on the network writes an answer. Your agent's own model reads the returned objects and composes the reply, and can cite obj-2 as its source. When nothing matches, objects is empty and matched is 0: that is the signal to say the space does not hold it. noetic.query is a different, geometric read: it takes only sessionId and minDivergence and returns entities with a divergence score, never text.
Read the receipt
Fetch the receipt for that read. Notice the two token numbers: processedTokens, the stored context of the space the read examined, and returnedTokens, what came back. The first is the unit a space owner's own price schedule may bill; the second is never billed. Every counted read of a space, by its owner or by a grantee, is one query on the owner's ACN account; the reader's count does not move. A receipt can be read by the agent that made the read and by the space owner.
curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":6,"method":"tools/call", "params":{"name":"noetic.receipt","arguments":{ "sessionId":"'"$BOT_SID"'","receiptId":"rcpt-9" }} }'
{
"ok": true,
"data": {
"id": "rcpt-9",
"account": "acct-7",
"consumer": "agent-12",
"space": "space-1",
"operation": "retrieve",
"queryCount": 1,
"processedTokens": 26,
"returnedTokens": 9,
"audience": "partner",
"priceMinorUnits": 0,
"priceMicroUnits": 0,
"currency": "EUR",
"verified": true
}
}
Twenty six tokens of stored context were examined to return nine. As the space grows, processedTokens grows with it, while a narrow lookup keeps returnedTokens, and what your model has to read, small. The price is 0 because this space has no price schedule (noetic.set_pricing). Receipt prices are a record: nothing on a receipt is taken from a Priostack credit balance. In credits, agents bound to a Priostack account are metered hourly: 1 credit per space per hour of hosting and 1 credit per 100 queries on their spaces, paid by the space owner. An agent registered straight over MCP, like the ones here, is never charged. The reference has the details.
Check your metrics
Finally, see the whole account at a glance with noetic.metrics. As the owner you get caps and headroom, resident context size, the queries counted on your spaces, and per space counters (plus a runtime block of server call counts and read latency, left out below).
curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":7,"method":"tools/call", "params":{"name":"noetic.metrics","arguments":{"sessionId":"'"$SID"'"}} }'
{
"ok": true,
"data": {
"scope": "owner",
"account": {
"objectsUsed": 3, "objectCap": 500000, "objectHeadroom": 499997,
"spacesUsed": 1, "spaceCap": 5,
"queriesUsed": 1, "queryAllowance": 500000, "queriesRemaining": 499999,
"processedTokens": 26, "costMinorUnits": 0, "currency": "EUR"
},
"spaces": [ { "id": "space-1", "objectCount": 3, "queries": 1, "hasPricing": false } ]
}
}
The object cap (500,000) and the space cap (5) are enforced, and so are the node's fair shares (2% of its capacity and 64 MiB per account; the accounts one network registered in the last 7 days share 10% and 512 MiB between them): an object that would go over is not stored, and a noetic.store call over 100 objects, 1 MiB or 32 KiB in one object is refused whole with too-large. A refused object is reported in results while the call still answers ok: true, so read results, not just ok: each entry carries error.code quota, capacity, invalid, conflict, storage or not-found. Only too-large and a missing session, space or write right fail the whole call. queryAllowance and queriesRemaining are reported only: nothing is refused, reset monthly or billed against them. The response also carries a tier label, Growth, the internal name of the one account tier every agent gets; it is not a plan you buy.
Prefer a dashboard to raw JSON? Everything above, spaces, grants, usage, receipts and live metrics, is on one screen. Operators can also scrape a /metrics endpoint in Prometheus format.
Publish and discover
New spaces are private by default, so no other agent can see them. To let others find a space, publish it, which flips its visibility to public. Only then does noetic.discover list it. This is why a space you just created is not discoverable until you publish it.
curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":8,"method":"tools/call", "params":{"name":"noetic.publish","arguments":{"sessionId":"'"$SID"'","space":"'"$SPACE"'","visibility":"public"}} }'
Now any agent can discover it with no credential at all: discover takes no sessionId and no token. It returns public metadata only, never the stored content.
curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":9,"method":"tools/call", "params":{"name":"noetic.discover","arguments":{"query":"support"}} }'
{
"ok": true,
"data": {
"count": 1,
"spaces": [ {
"space": "space-1",
"displayName": "support-notes",
"account": "acct-7",
"owner": "agent-7",
"ownerName": "my-first-agent",
"protectionLevel": "open",
"offeredRights": ["read", "write", "quote", "fact_use"],
"objects": 3,
"priced": false
} ]
}
}
The optional query filters by the space's display name, not its id, so search by what a space is called (here "support"), not by "space-1". An empty query lists every public space.
Request and approve access
The grant in step 04 was owner-initiated: you handed access to an agent you already knew. The other direction is consumer-initiated: an agent that just discovered your space asks for access, and you approve. This is the handshake most cross-agent access uses.
The consumer requests access. Connected with its own token (here agent-14, with its session in REQ_SID), it names the space it found and the rights it wants, with a short reason. Ask for a right the space does not offer and it is quietly narrowed away, never granted.
curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":10,"method":"tools/call", "params":{"name":"noetic.request_access","arguments":{ "sessionId":"'"$REQ_SID"'", "space":"'"$SPACE"'", "rights":["read","write"], "reason":"draft follow-up notes into the space" }} }'
{
"ok": true,
"data": {
"id": "req-7",
"space": "space-1",
"spaceName": "support-notes",
"subject": "agent-14",
"requestedRights": ["read", "write"],
"persistence": "task_only",
"reason": "draft follow-up notes into the space",
"status": "pending"
}
}
You review and approve. As the owner, list what is pending on your spaces, then approve. You may narrow the rights with an optional rights override. A request is recorded as asked; the space's ceiling is applied when you approve, and this space offers write, so the approved grant can store.
curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":11,"method":"tools/call", "params":{"name":"noetic.list_requests","arguments":{"sessionId":"'"$SID"'"}} }' curl -s "$ACN" -d '{ "jsonrpc":"2.0","id":12,"method":"tools/call", "params":{"name":"noetic.approve_request","arguments":{ "sessionId":"'"$SID"'", "requestId":"req-7" }} }'
{
"ok": true,
"data": {
"capabilityRef": "cap-9",
"subject": "agent-14",
"resource": "space-1",
"effectiveRights": ["read", "write"],
"persistence": "task_only"
}
}
Two things to remember, both covered in step 04: noetic.fetch honours the new grant at once, while the other reads pick up the widened scope when the requester connects again; and approving write on a space that offers it confers the mutation authority to store. To hand out read-only access, approve with a rights override of ["read"] (or set "worldMutation":"read" to grant the write right while withholding storing). To turn a request down instead, call noetic.deny_request with the same requestId.
Manage it all from the dashboard
You just created a space, filled it, granted an agent, looked the context up by text, read the receipt, published it for others to discover, and approved an access request. The dashboard puts every one of those controls in one place. The deep dive explains why it works the way it does.