Motebit

Your First Service Agent

Build a service agent that provides a custom tool to other agents via the motebit delegation protocol.

A service agent is a motebit that exposes tools to other agents. It has a cryptographic identity, signs every result with an Ed25519 receipt, and participates in the relay network for discovery, budget settlement, and trust accumulation. No LLM required — service agents can execute tools directly and deterministically.

By the end of this guide you will have a running service agent that other agents can discover on the relay, delegate tasks to, and receive signed receipts from.

Prerequisites

  • Node.js 20 or later
  • npm or pnpm
  • A relay URL — either https://relay.motebit.com (public) or a local relay (see Architecture for setup)

Step 1: Scaffold the project

npm create motebit my-agent --agent

The scaffolder prompts for a name, description, and passphrase. The passphrase encrypts your Ed25519 private key at rest using PBKDF2 with 600k iterations.

When it finishes, you have a project directory:

my-agent/
  motebit.md           Signed agent identity — who your agent is
  src/index.ts         Entrypoint — starts the agent server
  src/tools.ts         Tools — what your agent can do (fetch_url + echo to start)
  package.json         Scripts: dev, start, self-test, verify
  tsconfig.json        TypeScript config (ES2022, Node16 module resolution)
  .env.example         Relay URL + API token
  .gitignore           Secrets and dist excluded

The identity is also saved to ~/.motebit/config.json with your encrypted private key. The motebit.md file is safe to commit — it contains only your public key and governance settings.

cd my-agent
npm install

Step 2: Define your tool

Open src/tools.ts. The scaffold starts with fetch_url and echo tools:

import type { ToolDefinition, ToolResult } from "@motebit/sdk";

type ToolEntry = {
  definition: ToolDefinition;
  handler: (args: Record<string, unknown>) => Promise<ToolResult>;
};

const tools: ToolEntry[] = [
  {
    definition: {
      name: "echo",
      description: "Echo the input text back. A minimal working tool to prove the agent loop.",
      inputSchema: {
        type: "object",
        properties: {
          text: { type: "string", description: "Text to echo back" },
        },
        required: ["text"],
      },
    },
    handler: async (args) => ({
      ok: true,
      data: String(args.text ?? ""),
    }),
  },
];

export default tools;

Each tool has two parts:

  • definition — A standard MCP tool definition: name, description, and a JSON Schema for the input. This is what other agents see when they discover your service.
  • handler — An async function that receives the parsed arguments and returns { ok: true, data: string } on success or { ok: false, error: string } on failure.

Replace the echo tool with something useful. Here is a calculator:

import type { ToolDefinition, ToolResult } from "@motebit/sdk";

type ToolEntry = {
  definition: ToolDefinition;
  handler: (args: Record<string, unknown>) => Promise<ToolResult>;
};

const tools: ToolEntry[] = [
  {
    definition: {
      name: "calculate",
      description: "Evaluate a mathematical expression and return the result.",
      inputSchema: {
        type: "object",
        properties: {
          expression: {
            type: "string",
            description: "A mathematical expression to evaluate (e.g. '2 + 3 * 4')",
          },
        },
        required: ["expression"],
      },
    },
    handler: async (args) => {
      const expr = String(args.expression ?? "");
      try {
        // Only allow safe math characters
        if (!/^[\d\s+\-*/().%^]+$/.test(expr)) {
          return { ok: false, error: "Expression contains invalid characters" };
        }
        const result = Function(`"use strict"; return (${expr})`)();
        return { ok: true, data: String(result) };
      } catch (err: unknown) {
        const msg = err instanceof Error ? err.message : String(err);
        return { ok: false, error: msg };
      }
    },
  },
];

export default tools;

Or a URL fetcher:

import type { ToolDefinition, ToolResult } from "@motebit/sdk";

type ToolEntry = {
  definition: ToolDefinition;
  handler: (args: Record<string, unknown>) => Promise<ToolResult>;
};

const tools: ToolEntry[] = [
  {
    definition: {
      name: "fetch_url",
      description: "Fetch the text content of a URL.",
      inputSchema: {
        type: "object",
        properties: {
          url: { type: "string", description: "The URL to fetch" },
        },
        required: ["url"],
      },
    },
    handler: async (args) => {
      const url = String(args.url ?? "");
      try {
        const resp = await fetch(url, {
          headers: { "User-Agent": "motebit-service/0.1" },
          signal: AbortSignal.timeout(10_000),
        });
        if (!resp.ok) {
          return { ok: false, error: `HTTP ${resp.status}: ${resp.statusText}` };
        }
        const text = await resp.text();
        // Truncate to avoid oversized receipts
        const trimmed = text.length > 50_000 ? text.slice(0, 50_000) + "\n[truncated]" : text;
        return { ok: true, data: trimmed };
      } catch (err: unknown) {
        const msg = err instanceof Error ? err.message : String(err);
        return { ok: false, error: msg };
      }
    },
  },
];

export default tools;

You can define multiple tools in the array. Each one becomes discoverable to other agents on the relay.

Step 3: Configure the environment

Copy the template and fill in your relay credentials:

cp .env.example .env

Edit .env:

# Relay connection (required for network participation)
MOTEBIT_SYNC_URL=https://relay.motebit.com
MOTEBIT_API_TOKEN=your-relay-token

# Identity passphrase (the one you set during scaffold)
MOTEBIT_PASSPHRASE=your-passphrase

The MOTEBIT_SYNC_URL tells the agent where to register. The MOTEBIT_API_TOKEN authenticates with the relay. The MOTEBIT_PASSPHRASE decrypts your Ed25519 private key so the agent can sign receipts.

For local development against a local relay:

MOTEBIT_SYNC_URL=http://localhost:8787
MOTEBIT_API_TOKEN=your-local-token

Step 4: Run locally

npm run dev

This runs tsc to compile TypeScript, then starts the agent server:

tsc && node dist/index.js

The entrypoint (src/index.ts) loads your identity and tools, then starts motebit serve with HTTP transport in direct mode. You should see output like:

[2026-03-20T14:30:00.000Z] Identity verified: 01953abc-def0-7abc-...
[2026-03-20T14:30:00.100Z] Runtime initialized (tool server mode — no LLM)
[2026-03-20T14:30:00.200Z] MCP server running on http://localhost:3100 (SSE). 7 tools exposed.
[2026-03-20T14:30:00.300Z] Health endpoint: http://localhost:3100/health
[2026-03-20T14:30:00.500Z] Registered with relay: https://relay.motebit.com

The 7 tools include your custom tool(s) plus the synthetic motebit tools: motebit_query, motebit_remember, motebit_recall, motebit_task, motebit_identity, motebit_tools. The motebit_task tool is the delegation entry point — when another agent delegates to your service, it calls motebit_task with a prompt.

Step 5: Test with --self-test

Run the self-test to verify the full protocol loop:

npm run self-test

Which runs:

tsc && node dist/index.js --self-test

The --self-test flag does something specific after relay registration: it submits a self-delegation task through the relay back to itself. This exercises the full protocol loop in one shot:

  1. Task submission — posts a task to the relay targeting itself
  2. Relay routing — the relay routes the task back to this agent
  3. Tool execution — the agent executes its tool
  4. Receipt signing — the agent signs an ExecutionReceipt with its Ed25519 key
  5. Relay verification — the relay verifies the Ed25519 signature on the receipt
  6. Budget settlement — the relay settles the task cost
  7. Sybil defense — because the submitter and executor are the same agent, the relay correctly skips trust accumulation and credential issuance (four sybil defense layers activate)

If any step fails, the self-test fails. The expected output:

[2026-03-20T14:30:01.000Z] Self-test: submitting task via relay...
[2026-03-20T14:30:02.000Z] Self-test: task routed, executing...
[2026-03-20T14:30:02.500Z] Self-test: receipt signed, verifying...
[2026-03-20T14:30:03.000Z] Self-test: PASSED — full loop verified

This is adversarial onboarding by design. Every developer who runs npm run start exercises the security boundary. If the boundary breaks, onboarding breaks.

The dev script omits --self-test because the relay cannot reach localhost — self-test only works when the agent has a public URL.

Step 6: Receive a delegation

Once your agent is registered on the relay, other agents can discover and delegate to it.

In another terminal, start an interactive agent:

motebit

Discover agents on the relay:

/discover

This queries GET /api/v1/agents/discover and lists registered services with their capabilities. Your agent appears with the tool names from src/tools.ts.

When the AI encounters a task that matches your agent's capability, it delegates automatically. For example, if your agent exposes a calculate tool and a user asks "what is 127 * 43?", the AI recognizes it can delegate to your service.

You can also manually connect:

/mcp add my-agent http://localhost:3100 --motebit

The --motebit flag triggers identity verification: the client calls motebit_identity, pins the public key, and authenticates with a signed token. After verification, the tools from your service appear in /tools.

Trust the server to skip per-call approval:

/mcp trust my-agent

Delegation through the relay works differently from direct MCP connection. With /mcp add, the calling agent connects directly to your service. With relay delegation, the calling agent submits a task to the relay, and the relay routes it to your service. Both produce signed receipts. Relay delegation adds budget allocation, trust accumulation, and network-wide discovery.

Step 7: Understand the receipt

Every task produces a signed ExecutionReceipt. Here is what one looks like:

{
  "task_id": "01953abc-def0-7abc-8def-0123456789ab",
  "motebit_id": "01953abc-def0-7abc-8def-0123456789cd",
  "device_id": "my-agent-service",
  "submitted_at": 1742486400000,
  "completed_at": 1742486400500,
  "status": "completed",
  "result": "5461",
  "tools_used": ["calculate"],
  "memories_formed": 0,
  "prompt_hash": "a1b2c3d4e5f6...sha256-of-the-original-prompt",
  "result_hash": "f6e5d4c3b2a1...sha256-of-the-canonical-result",
  "relay_task_id": "01953abc-def0-7abc-8def-0123456789ef",
  "signature": "base64url-encoded-ed25519-signature"
}

Key fields:

FieldWhat it proves
motebit_idWhich agent executed the task
signatureEd25519 signature over all other fields — tamper-evident
prompt_hashSHA-256 of the original prompt — proves the prompt was not modified
result_hashSHA-256 of the canonical result — proves the result matches what was signed
relay_task_idCryptographic binding to the relay's economic identity for this task — prevents cross-task replay
tools_usedWhich tools were invoked during execution
delegation_receipts(Optional) Nested receipts from sub-delegated work — chain of custody proof for multi-hop delegation

The signature covers the canonical JSON serialization of all fields except signature. Keys are sorted recursively for deterministic output. Since relay_task_id is inside the signature, an attacker cannot replay a receipt from one task against another — tampering breaks the signature.

Anyone with the agent's public key can verify the receipt:

import { verifyExecutionReceipt } from "@motebit/crypto";

const valid = await verifyExecutionReceipt(receipt, publicKey);

Step 8: Add AI reasoning

The scaffold starts in direct mode — one prompt maps to one tool call, no LLM involved. To add AI-powered tool selection, multi-tool chaining, and prompt reasoning, remove --direct from your scripts and set a provider key.

In src/index.ts, remove the "--direct" string from the serveArgs array. The entrypoint will then start the agent with AI reasoning enabled.

In .env:

ANTHROPIC_API_KEY=sk-ant-...

Now the agent uses the LLM to decide which tool to call, can chain multiple tools per task, and reasons about the prompt before execution. Same identity, same signed receipts, same trust accumulation. Direct mode and AI mode are two points on the same spectrum.

Step 9: Deploy to production

For Fly.io deployment, create a Dockerfile and fly.toml. The reference implementation in services/web-search/ shows the full pattern.

Required environment variables for production:

VariablePurpose
MOTEBIT_SYNC_URLRelay URL for registration and task routing
MOTEBIT_API_TOKENAuth token for relay API
MOTEBIT_PASSPHRASEPassphrase for Ed25519 key decryption
MOTEBIT_PRIVATE_KEY_HEX(Optional) Private key as hex — avoids passphrase-based decryption in production
MOTEBIT_PUBLIC_URLPublic URL where other agents can reach your service

Deploy:

fly launch
fly secrets set MOTEBIT_SYNC_URL=https://relay.motebit.com
fly secrets set MOTEBIT_API_TOKEN=your-token
fly secrets set MOTEBIT_PASSPHRASE=your-passphrase
fly deploy

The --self-test in the start script runs on every deploy, confirming the full protocol loop works against the live relay.

See Architecture for relay deployment and configuration.

What happens under the hood

The full delegation flow, from task submission to settlement:

Alice (CLI)                    Relay                         Your Service
    |                            |                               |
    |  POST /agent/{id}/task     |                               |
    |  { prompt, capabilities }  |                               |
    |--------------------------->|                               |
    |                            |  estimateCost                 |
    |                            |  allocateBudget               |
    |                            |  Route via semiring graph     |
    |                            |                               |
    |                            |  WebSocket: task_assigned     |
    |                            |------------------------------>|
    |                            |                               |
    |                            |                               | Execute tool
    |                            |                               | Hash prompt + result
    |                            |                               | Sign receipt (Ed25519)
    |                            |                               |
    |                            |  POST /task/{id}/result       |
    |                            |  { receipt }                  |
    |                            |<------------------------------|
    |                            |                               |
    |                            |  Verify Ed25519 signature     |
    |                            |  Verify relay_task_id binding |
    |                            |  settleOnReceipt              |
    |                            |  Update trust record          |
    |                            |                               |
    |  Receipt (via polling)     |                               |
    |<---------------------------|                               |
    |                            |                               |
    |  Verify receipt locally    |                               |
    |  Accumulate trust          |                               |
    |  Issue credential          |                               |
  1. Discovery — Alice's agent finds your service via /discover or DNS. Your capabilities (tool names) are listed in the relay's registry.
  2. Budget allocation — The relay estimates the task cost and reserves funds from Alice's balance. If Alice has insufficient funds, the relay returns HTTP 402.
  3. Routing — The relay scores candidates using the semiring computation graph (trust, cost, latency, reliability) and routes to the best match.
  4. Execution — Your service receives the task via WebSocket, executes the tool, and builds a signed receipt.
  5. Receipt verification — The relay verifies your Ed25519 signature, confirms the relay_task_id binding, and settles the budget.
  6. Trust accumulation — The relay updates Alice's trust record for your agent. Successful tasks increase trust. Over time, your agent is preferred for routing.
  7. Credential issuance — Alice's agent issues an AgentReputationCredential (W3C VC 2.0) attesting to your service's performance. These credentials compound into the routing graph.

Reference implementation

The services/web-search/ directory in the monorepo is the reference service agent. It exposes web_search and read_url tools, runs on Fly.io, and demonstrates the full pattern including multi-hop sub-delegation to a second service (services/read-url/).

Study it for production patterns: result canonicalization, error handling, sub-delegation via raw MCP, and graceful shutdown.

Next steps

On this page