Skip to main content
The @phosra/mcp package turns the Phosra API into Model Context Protocol tools. Point Claude Desktop, Cursor, or any MCP-compatible client at it and your agent can read a family’s Brief, audit enforcement decisions, and apply age-appropriate policy — through natural conversation, over a signed, per-family token.
Everything on this page is verified against the published package @phosra/mcp@0.4.0 (npm), run end-to-end against the hosted sandbox https://phosra-api-sandbox-production.up.railway.app/api/v1. The binary is phosra-mcp; a live tools/list returns 79 tools. Every JSON response below is real, unedited server output.

How it works

The server runs locally, as a subprocess of your MCP client, and speaks JSON-RPC over stdio — no inbound ports, no daemon. Each of its 79 tools maps 1:1 to a Phosra REST endpoint. Every request carries your MCP token as Authorization: Bearer phomcp_…; the API resolves it to your user and family, so the agent only ever sees your own data.

Step 1 — Get an MCP token

MCP tokens are scoped to a single family and start with the prefix phomcp_. They are hashed at rest (SHA-256) and shown in plaintext exactly once, at creation.
Treat an MCP token like a password. It grants an agent full read/write access to your family’s policies. If one leaks, revoke it — revocation is instant and irreversible.

Step 2 — Install

Run directly with npx (no install), pointing at the hosted sandbox while you build:
Or install globally:
The --api-key and --api-url flags also read from PHOSRA_API_KEY and PHOSRA_API_URL. When --api-url is omitted the server targets production (https://prodapi.phosra.com/api/v1).
Sandbox first. The hosted sandbox is seeded, resettable, and safe to write to — every example on this page runs against it. Switch to production only once your flow works end-to-end.

Step 3 — Configure your client

Add to claude_desktop_config.json, then fully quit and reopen Claude Desktop:
The config file lives at:
  • macOS~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows%APPDATA%\Claude\claude_desktop_config.json

Step 4 — Verify the connection

You don’t need a full client to smoke-test the server — pipe a JSON-RPC handshake over stdio. Against the hosted sandbox this returns the live tool list:
On startup phosra-mcp@0.4.0 prints Registering 42 tools... to stderr. That banner string is cosmetic and undercounts — the authoritative number is whatever tools/list returns, which is 79. Ignore the banner.

How a tool call maps to HTTP

Every tool is a thin wrapper over one REST endpoint. The server (tools.tsresolveToolPath) routes your arguments in a fixed, predictable way:
  1. Path parameters — any argument whose name matches a {param} in the endpoint path (e.g. policy_id in /policies/{policy_id}/rules) is substituted into the URL.
  2. Query string — on a GET tool, every remaining argument becomes a ?key=value pair.
  3. JSON body — on a POST / PUT / DELETE tool, every remaining argument becomes a top-level field of the request body.
So create_rule with {policy_id, category, enabled, config} sends POST /policies/{policy_id}/rules with a body of {category, enabled, config}policy_id never appears in the body. You never construct URLs yourself; you pass flat named arguments and the server does the rest.

Reading the field reference

Every tool below lists its complete argument set with type and whether it is required:
  • bold typerequired (the API rejects the call without it).
  • plain type · opt — optional.
All arguments are hoisted to the top of the JSON-RPC arguments object. Types are the JSON Schema types declared in the shipped @phosra/mcp@0.4.0 source — the same schemas your MCP client sees in tools/list. Nested object shapes (create_rule.config, connect_platform.credentials, consult_advisor.context) are documented in Nested payload shapes below.

Tool reference

All 79 tools, grouped by domain. Each row links to the matching API reference page where one exists — that page documents the full response body, every field, and every status code for the underlying endpoint.

Account & quick setup

quick_setup is the one-call onboarding path: it creates a family, adds a child, generates age-appropriate rules, and activates the policy. strictness is recommended (default), strict, or relaxed. Pass family_id to add the child to an existing family instead of minting a new one.

Families & members

Children

Policies

Rules

Enforcement

trigger_enforcement.platform_ids narrows the fan-out to a subset (e.g. ["netflix", "youtube"]); omit it and enforcement pushes to all connected platforms. Valid IDs: netflix, paramount_plus, youtube_tv, peacock, prime_video, youtube, nextdns, android, fire_tablet, apple_watch, fire_tv_stick.

Ratings

Webhooks

Sources (parental-control app adapters)

Sources are the parental-control-app adapters (Bark, Qustodio, Kidslox, …). On the sandbox they are api_tier: guided, auth_type: none — connect them without credentials and drive setup through get_source_guided_steps. Managed sources that need credentials accept a credentials object on connect_source.

Brief (what changed in child-safety)

apply_brief_item is the autonomous-loop write: it applies a Brief item’s proposed policy diff (only on items whose recommended_action.kind == "policy_diff"). Preview it first with dry_run_apply (below).

Decisions & provenance

query_decisions_by_law answers “show me everything we did because of KOSA” — pass law_id as a lowercase registry slug like kosa, coppa-2, or ca-sb-976.

Safe autonomy (preview, snapshot, revert)

snapshot_policy before any bulk autonomous change; revert_to_snapshot atomically restores the captured policy graph if a change turns out wrong.

Streaming (Server-Sent Events)

Each returns a stream_url the agent connects to (with the same Bearer token) to replace a polling loop.

Federation (advisor agents)

register_advisor_agent has two conditionally required fields (marked · opt* above): scope_family_id is required when scope="family", and manifest_url is required when scope="global". consult_advisor calls a registered third-party advisor with an RFC 9421 Ed25519-signed request and writes an immutable audit row; list_advisor_audit_log returns those rows with preserved response signatures for non-repudiation.

Nested payload shapes

Four tool arguments are typed object / array<object> in the schema — the schema deliberately leaves them open because their shape depends on the rule category, platform, or capability. Here are the real, sandbox-verified shapes.

create_rule / bulk_upsert_rules config

config is a category-specific JSON object. create_rule.category must be one of Phosra’s supported rule categories — the shipped tool description names 45, and the canonical, per-category list is enumerated in the Create rule API reference. The table below is the real config emitted by quick_setup (captured live from list_rules against the sandbox) for the 20 categories in the recommended set — copy these shapes directly into create_rule: A single create_rule call, exactly as the agent sends it over tools/call:
bulk_upsert_rules takes the same objects in an array under rules:

connect_platform credentials

Shipped-schema caveat (verified against the live sandbox). The tool schema types credentials as an object, but the underlying POST /compliance endpoint currently deserializes credentials as a JSON string — the raw secret for the platform (e.g. a DNS provider’s API key). Passing an object is rejected:
Send the credential as a plain string instead — "credentials": "<api_key_or_oauth_token>". This mismatch is real in @phosra/mcp@0.4.0; it is tracked for a follow-up release. manual-auth platforms (most streaming and gaming platforms on the sandbox) need no credential at all — connect them and enforce through their guided setup.
Platforms advertise how they authenticate via list_platforms (auth_type is api_key, oauth2, or manual). The DNS providers (nextdns, controld, cleanbrowsing) are api_key; android and microsoft are oauth2; streaming/gaming platforms are manual.

consult_advisor context

context is a free-form JSON object whose keys are defined by the advisor’s declared capability — e.g. for a homework_focus capability:
The advisor receives this verbatim inside the RFC 9421-signed request body. context is passed straight through; Phosra never inspects it.

Enumerated argument values

Three arguments are typed as an open string / array<string> in the shipped schema but only accept a fixed set of values. Here is the full, source-verified enumeration for each.

create_webhook events

events is an array<string>. The schema does not constrain the members, but the census fires exactly five events today — subscribe only to these:
Do not subscribe to device.* or other speculative names. They are reserved/internal and never delivered — a subscription to them is silently inert, not an error. The authoritative, per-event payload shape, the X-Phosra-Signature HMAC scheme, and the delivery/retry contract are documented in the Webhook events guide.

list_platforms_by_capability capability

capability is a single string. It must be one of the 18 capability identifiers defined in the census (internal/provider/adapter.go). An unknown capability is not an error — the endpoint returns a JSON null (empty match), so validate against this list client-side:
The same capabilities array is on every element of list_platforms and get_platform, so you can also filter client-side without a round-trip.

Tool results & the error format

This is the part most MCP integrations get wrong, so it is worth stating precisely. Each tool runs its HTTP request and returns the API’s JSON response as the content of a successful tools/call result. The Phosra MCP server (server.ts) sets the result’s isError flag from the HTTP status — it does not raise a JSON-RPC protocol error. A 403 or 404 therefore arrives inside a normal result, not as a transport-level failure your client would throw on:
Your agent should branch on isError (or parse content[0].text and read code) — never assume a tool “threw.”

The error envelope

Every Phosra error body is the same shape (from pkg/httputil in the census). The three base fields are always present: Rule-plane and OCSS Trust-Framework endpoints add two more fields on a governance rejection:

Real error bodies (captured from the live sandbox)

These are unedited responses from https://phosra-api-sandbox-production.up.railway.app/api/v1:
A 403 standing_failure — emitted by rule-plane writes and by trigger_enforcement when the authority-holder’s §6.2 standing does not back the binding — carries the extra class + failed_step fields:
Internal (500) errors always serialize a fixed, sanitized body — { "error": "Internal Server Error", "message": "internal error", "code": 500 } — with no class and no cause text. The real cause is logged server-side, never returned on the wire.

End-to-end worked example

This is a real, unedited round-trip against the hosted sandbox. Set up a child in one call, then read back the generated rules — chaining the returned IDs, exactly as an agent would.

1. Provision a child with quick_setup

The tools/call result content (real server output, trimmed to the head):
One call produced a family, a child, an active policy, and 20 rules — the recommended set for a 9-year-old.

2. Read the generated rules with list_rules

Feed the returned policy.id straight into the next tool:
Real response (first two of the 20 rules):
The full set spans 20 categories: addictive_design_control, age_gate, algo_feed_control, content_rating, data_deletion_request, dm_restriction, geolocation_opt_in, monitoring_activity, notification_curfew, privacy_account_creation, privacy_profile_visibility, purchase_approval, purchase_block_iap, social_chat_control, targeted_ad_block, time_daily_limit, time_scheduled_hours, usage_timer_notification, web_filter_level, and web_safesearch.

What it looks like in a real conversation

Once the server is wired into your client, this happens in plain language.

Setting up a new child

You: Set up parental controls for my 8-year-old, Maya, born June 15 2017. Claude: (calls quick_setup) Done — Maya now has an active protection policy with 20 age-appropriate rules: addictive-design controls off, a 13+ age gate, content ratings, a daily time limit, scheduled hours, and safe-search enforced. Want me to connect a device or DNS filter to enforce these?

Adjusting a rule

You: Her teacher needs her to reach youtube.com for homework. Allow it. Claude: (calls list_rules, then update_rule on the web-filter rule) Added youtube.com to Maya’s allowlist. Every other web filter stays in place.

A safe autonomous change

You: A new state law just dropped in my Brief — handle it, but let me undo it if I disagree. Claude: (calls snapshot_policy, then dry_run_apply) Snapshot pre-sb976 saved. The dry run would add 2 rule categories across 1 policy, nothing removed. (calls apply_brief_item) Applied. If you want it reverted, just say so and I’ll revert_to_snapshot.

Security & token lifecycle

  • Family-scoped. A token authorizes exactly one family. It cannot see or touch another family’s data.
  • Hashed at rest. Only the SHA-256 hash and an 8-character prefix are stored. Phosra cannot show you a token again after creation.
  • Revocable instantly. Revoking from the dashboard or DELETE /mcp-tokens/{id} takes effect on the agent’s next call.
  • Auditable. last_used_at updates on every authenticated call, and autonomous writes land in the Decisions provenance log — review them with list_unreviewed_agent_decisions.
  • Local execution. The server runs on your machine as a client subprocess; your token never leaves your environment except as a Bearer header to the Phosra API over HTTPS.

Troubleshooting

Next steps