Handles

ctx.handle(), HandleStore, and no sessions.

bitmcp is stateless. There is no Mcp-Session-Id and no sticky server memory.

import { memoryHandleStore, type HandleStore } from "bitmcp";

type Job = { status: string; progress: number; source: string };
export const jobs: HandleStore<Job> = memoryHandleStore();

async execute({ jobId, source }, ctx) {
  const id = jobId ?? ctx.handle();
  const existing = await jobs.get(id);
  if (!existing) {
    await jobs.set(id, { status: "running", progress: 0, source });
  }
  const job = (await jobs.get(id)) ?? { status: "running", progress: 0, source };
  return { jobId: id, ...job };
}

ctx.handle()

Mints an opaque id (UUID). Store whatever you need under that id with a HandleStore:

  • memoryHandleStore() in development
  • KV, Redis, or D1 in production, same { get, set } shape

The model and the View pass jobId back as a normal argument on the next call.

Stateless requests

Every HTTP request builds a fresh MCP server. Tool code must assume the next call may hit another replica.

Handles make that safe. Sessions do not.

Long work

Return a handle immediately. Let the View poll status via a backing tool. See the task example.