Rensei docs

BYOA MCP Facade

Dispatch autonomous Rensei agents from any MCP-capable host via the /api/cli/mcp JSON-RPC facade - list workflows, dispatch, poll receipts, and verify audit signatures.

The Rensei BYOA (Bring-Your-Own-Agent) MCP facade (/api/cli/mcp) is a Model Context Protocol server that lets any MCP-capable host - Hermes, Claude Desktop, an IDE plugin, or CI - dispatch autonomous Rensei agents, poll for audit receipts, and enumerate available workflows. It is a stateless JSON-RPC 2.0 endpoint: every request carries a bearer rsk_* key, and the server enforces your org's full Cedar policy on each call. Tokens never leave your environment.

This is the org-wide dispatch facade. For the per-session MCP endpoint used by a running agent (memory, knowledge graph, A2A tools) see MCP Session Tools. For the formal request/response contract see the API reference.

Prerequisites

  1. A project with BYOA workflows provisioned. You need at least one published BYOA workflow in your org. Confirm with the list_workflows tool (see Connectivity check) - there is no CLI subcommand that lists workflows.
  2. An rsk_* API key with dispatch:invoke scope. Create one in the Dashboard (Settings > API Keys) or with the CLI: rensei org api-keys create --name byoa --scope dispatch:invoke. The --scope flag is repeatable. See API Key Authentication for the full flow and scoping rules.
  3. An MCP host that supports Streamable HTTP transport with bearer auth headers.

Never commit an rsk_* token to source control. Inject it from your secret manager or an environment variable at runtime.

Wiring your host

Any MCP host that speaks Streamable HTTP with bearer auth can use this facade. Point it at https://app.rensei.ai/api/cli/mcp with an Authorization: Bearer <rsk_*> header. For a worked, host-specific example, see Hermes Integration.


Connectivity check

Verify the connection with two raw JSON-RPC calls before committing to a workflow. Shell expands $RENSEI_API_KEY in these examples; export it first.

1. Handshake

curl -s -X POST https://app.rensei.ai/api/cli/mcp \
  -H "Authorization: Bearer $RENSEI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "clientInfo": { "name": "my-host", "version": "1.0.0" }
    }
  }'

Expected response (the server always reports its own protocolVersion):

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": { "tools": {} },
    "serverInfo": { "name": "rensei-byoa-mcp", "version": "0.1.0" }
  }
}

If you get 401 Unauthorized, the key is missing, invalid, or expired. If you get 403 Forbidden, the key is valid but lacks the dispatch:invoke scope - re-create it with that scope.

2. List available workflows

curl -s -X POST https://app.rensei.ai/api/cli/mcp \
  -H "Authorization: Bearer $RENSEI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "list_workflows",
      "arguments": {}
    }
  }'

Every tool result is JSON-stringified into result.content[0].text, so parse that string:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [{
      "type": "text",
      "text": "{\"workflows\":[{\"id\":\"wf_abc\",\"projectId\":\"prj_123\",\"templateSlug\":\"byoa-research-request\",\"status\":\"published\",\"workType\":\"research\"}]}"
    }]
  }
}

Each workflow row carries id, projectId, templateSlug, status, and workType (the field is templateSlug, not slug). An empty workflows array means your org has no published BYOA templates yet.


Dispatching an agent

Use the dispatch tool to enqueue a run. The arguments are project (slug or id), goal (free text), and an optional workType. The call returns immediately.

curl -s -X POST https://app.rensei.ai/api/cli/mcp \
  -H "Authorization: Bearer $RENSEI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "dispatch",
      "arguments": {
        "project": "my-project",
        "goal": "Research the tradeoffs between vector-only and hybrid BM25+vector search for our use case.",
        "workType": "research"
      }
    }
  }'

The result payload (inside content[0].text) is { instanceId, sessionId, receiptUrl, streamUrl }:

{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [{
      "type": "text",
      "text": "{\"instanceId\":\"inst_abc123\",\"sessionId\":\"sess_xyz\",\"receiptUrl\":\"https://app.rensei.ai/...\",\"streamUrl\":\"https://app.rensei.ai/...\"}"
    }]
  }
}

Keep the instanceId - you pass it to get_receipt.

workType values

workTypeWorkflow templateUse for
research (default)byoa-research-requestAnalysis, investigation, spike
implementbyoa-implement-requestCode changes, feature work
reviewbyoa-review-requestPR/diff review, audit

Polling for the receipt

Poll get_receipt with the instanceId from dispatch. The tool returns { receipt } (again JSON-stringified into content[0].text). The run's state lives in the sessionStatus field, which moves pending -> running -> a terminal value. Poll until sessionStatus is one of the four terminal values: completed, failed, stopped, or cancelled. (A running run whose heartbeat goes stale is flagged stuck, which is not terminal; sessionStatus is null only before the session row has resolved.) There is no top-level status field.

curl -s -X POST https://app.rensei.ai/api/cli/mcp \
  -H "Authorization: Bearer $RENSEI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 4,
    "method": "tools/call",
    "params": {
      "name": "get_receipt",
      "arguments": { "instanceId": "inst_abc123" }
    }
  }'

While running, the parsed receipt looks like {"receipt":{"version":"v1","sessionStatus":"running", ...}}; once settled, sessionStatus is a terminal value such as "completed".

Recommended polling back-off: 5 s, 10 s, 30 s, 60 s (cap). Most research runs complete in 2-8 minutes; implement runs may take longer depending on scope and capacity-pool size.


Receipt verification

When a recorded audit decision is present, the receipt carries the current-path signature material needed for an explicit check. decision is null until that record exists, and retained audit history can include legacy unsigned entries. Do not treat receipt or signature presence as a verified result:

  • receipt.decision.signature - the Ed25519 signature over the audit entry (present once a decision is recorded; decision is null until then).
  • receipt.decision.signingKeyId - the kid of the public key in your workspace JWKS.
  • receipt.decision.entryHash - the hex-encoded audit entry the signature covers.
  • receipt.links.jwksVerifyUrl - the ready-made JWKS endpoint for your workspace. Use this value directly.
  • receipt.receiptIntegrity.receiptHash - a SHA-256 digest of the whole receipt for tamper-evidence. This is a hash, not a signature: recomputing it proves integrity in transit, not authenticity.

The CLI provides an explicit signature check: it reads the supplied entryHash, fetches the referenced JWKS, and checks the Ed25519 signature over that hash offline. It does not recompute the receipt hash or reconstruct the audit entry that produced entryHash:

rensei receipt verify --url "$RECEIPT_URL"
# or against a saved receipt:
rensei receipt verify --file receipt.json

To run the same signature check manually: fetch receipt.links.jwksVerifyUrl, select the JWK whose kid equals receipt.decision.signingKeyId, and verify receipt.decision.signature (Ed25519) over the raw bytes represented by receipt.decision.entryHash. Recomputing the receipt hash or the audit entry hash is a separate check and requires the corresponding canonical input fields. See Audit Keys JWKS for the key format and rotation rules.

A successful rensei receipt verify result authenticates the signature over the supplied entryHash; it does not establish whole-receipt integrity or recompute the entry hash from canonical event fields. It also does not establish event capture, full-history completeness, retention outside the supplied artifact, truth at capture, or external timestamp anchoring.

The JWKS endpoint is keyed by workspace id, not org id, and returns an empty { "keys": [] } with HTTP 200 for an unknown id (it never reveals whether a workspace exists). Always use the jwksVerifyUrl from the receipt rather than assembling the URL from an org id yourself.


Rate limits

The facade applies the byoaMcp rate-limit bucket server-side. The exact limits are not publicly enumerated. A throttled request returns HTTP 429 with a Retry-After header and a JSON-RPC error body (code -32005):

  • Retry with exponential back-off: 1 s, 2 s, 4 s, 8 s, 16 s (max 5 attempts).
  • Honor the Retry-After header.

Heavy batch usage (dozens of concurrent dispatches) should be coordinated with Rensei support to avoid hitting the bucket ceiling.


Troubleshooting

Transport and auth failures arrive as real HTTP status codes. Tool-level denials and validation errors arrive as a JSON-RPC error object inside an HTTP 200 response - inspect error.code, not the HTTP status, for those.

SymptomLikely causeFix
HTTP 401 UnauthorizedBearer key missing, invalid, revoked, or expired (or an inbound-handshake signature failure)Confirm RENSEI_API_KEY is exported and the key is active
HTTP 403 ForbiddenKey is valid but lacks the dispatch:invoke scopeRe-create the key with --scope dispatch:invoke
HTTP 429 Too Many RequestsbyoaMcp rate bucket exceeded (JSON-RPC error code -32005)Honor Retry-After and back off (see Rate limits)
HTTP 200 with a JSON-RPC error code -32003Project/workflow not allowed for this key, or Cedar policy deniesRun list_workflows to confirm availability; check the key's project scope; contact your org admin
HTTP 200 with a JSON-RPC error code -32602Invalid params: missing project/goal/instanceId, or project/instance not found in your orgCheck the arguments against the tool schema
list_workflows returns an empty workflows arrayNo published BYOA templates in your orgProvision BYOA workflows via Dashboard > Workflows or contact your org admin
dispatch succeeds but sessionStatus never becomes terminalRun stuck in the capacity-pool queueCheck Dashboard > Sessions for the run; may be a capacity-pool configuration issue

On this page