ProvenPin Install

ofetch HTTP client mocked with MSW: working versions (tested 2026-10-15)

Call APIs with ofetch against an MSW 2 HTTP worker in Node, covering JSON responses, query params and error status mapping offline.

passed2 passed · 0 failed2026-10-15 15:01 UTC
Status
passed
Last passed
2026-10-15 15:01 UTC
Tests
2 / 2
Language
TypeScript
Runtime
node v22.23.3
pinned versions4
ofetch@1.5.1msw@3.0.3@types/node@26.6.5tsx@4.23.15
run it
npm install --no-audit --no-fund --ignore-scripts=false
npm test
test environment
node v22.23.3
linux 6.17.0-1022-azure
x64
runner: github-actions 38755782594

The code

Exactly the files that passed. Your agent can pull them with get_recipe("ofetch-msw-client") (install), or see them on GitHub.

package.json
{
  "name": "ofetch-msw-client",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "node --import tsx --test test/*.test.ts"
  },
  "dependencies": {
    "ofetch": "1.5.1",
    "msw": "3.0.3"
  },
  "devDependencies": {
    "@types/node": "26.6.5",
    "tsx": "4.23.15"
  }
}
src/client.ts
import { ofetch, type FetchOptions } from "ofetch";

export type Item = { id: number; name: string };

export function createClient(baseURL: string, fetchImpl?: typeof fetch) {
  const api = ofetch.create({ baseURL, fetch: fetchImpl as typeof fetch });
  return {
    list: (q?: string) => api<Item[]>("/items", { query: q ? { q } : undefined }),
    get: (id: number) => api<Item>(`/items/${id}`),
    create: (name: string) => api<Item>("/items", { method: "POST", body: { name } }),
  };
}

export type Client = ReturnType<typeof createClient>;
test/client.test.ts
import { test, before, after } from "node:test";
import assert from "node:assert/strict";
import { http, HttpResponse } from "msw";
import { setupServer } from "msw/node";
import { createClient } from "../src/client.ts";

const items = [{ id: 1, name: "bolt" }];
const server = setupServer(
  http.get("http://local/items", ({ request }) => {
    const q = new URL(request.url).searchParams.get("q");
    return HttpResponse.json(q ? items.filter((i) => i.name.includes(q)) : items);
  }),
  http.get("http://local/items/:id", ({ params }) => {
    const item = items.find((i) => i.id === Number(params.id));
    if (!item) return HttpResponse.json({ error: "missing" }, { status: 404 });
    return HttpResponse.json(item);
  }),
  http.post("http://local/items", async ({ request }) => {
    const body = await request.json() as { name: string };
    const item = { id: items.length + 1, name: body.name };
    items.push(item);
    return HttpResponse.json(item, { status: 201 });
  }),
);

before(() => server.listen({ onUnhandledRequest: "error" }));
after(() => server.close());

test("lists, filters, creates", async () => {
  const client = createClient("http://local");
  assert.equal((await client.list()).length, 1);
  assert.equal((await client.list("bo"))[0].name, "bolt");
  const created = await client.create("nut");
  assert.equal(created.name, "nut");
  assert.equal((await client.get(created.id)).name, "nut");
});

test("maps 404 to FetchError", async () => {
  const client = createClient("http://local");
  await assert.rejects(() => client.get(999), (e: any) => e.status === 404 || e.response?.status === 404 || String(e).includes("404"));
});

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.