docs
MCP serverSearchOnce added, your AI can search and read the StitchAPI documentation. Ask a question about StitchAPI and it will find the relevant sections in the docs and read the pages it needs, so its answers come from the actual documentation.
Use docs in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add docs and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use docs to search docs
No key or account is asked for. Not verified by us yet: Ahel has not run a call through it.
Details
Available today. Use it from your connected AI after setup.
No key or account is asked for. Not verified by us yet: Ahel has not run a call through it.
After adding it, ask your AI anything about StitchAPI. It will search the docs for relevant sections and read the matching page to answer.
What your AI can do with it
- Search the StitchAPI docs for sections matching a question
- Read a full documentation page
- Find the right section without you knowing where it is
- Answer StitchAPI questions using wording taken straight from the docs
Install docs
The server’s own address, for the clients that take one directly. Or connect Ahel once and every client you use reads it from one address, with the account kept on Ahel rather than in each client’s config.
Claude Code
claude mcp add --transport http --scope user docs 'https://stitchapi.dev/api/mcp'Run it once in your project, then open /mcp to approve any sign-in the server asks for.
Claude Desktop
https://stitchapi.dev/api/mcpAdd a custom connector in Settings, paste this address, and approve the sign-in.
Cursor
cursor://anysphere.cursor-deeplink/mcp/install?name=docs&config=eyJ1cmwiOiJodHRwczovL3N0aXRjaGFwaS5kZXYvYXBpL21jcCJ9Open the link and Cursor adds the server at that address.
ChatGPT
https://stitchapi.dev/api/mcpIn Settings, enable Developer mode, create an MCP app, and paste this address. Your plan and workspace must allow custom apps.
Codex
codex mcp add docs --url 'https://stitchapi.dev/api/mcp'Run it once, then sign in with codex mcp login docs if the server asks for an account.
From the project's README
As published by rejifald/stitchapi in README.md.
[!NOTE]
StitchAPI is at
1.0.0-rc.8. The core runtime is feature-complete, zero-dependency, covered by a green test gate, and already running in production in two projects. We're validating in the wild before stamping a stable1.0.0— pin an exact version and expect only small, documented changes. Feedback is welcome.
Table of Contents
- What is a stitch?
- Motivation
- Why StitchAPI
- What StitchAPI is not
- Features
- Install
- Quick start
- Composition: seam, extends,
.with() - Validation & leveled drift
- Resilience: retry, throttle, timeout
- Caching
- Auth as a boundary
- Surfaces: any request style
- Four front doors
- Agent-native
- Errors & pitfalls
- Zero-infra observability
- Packages
- Documentation
- Contributing
- License
What is a stitch?
A stitch is StitchAPI's core primitive: it takes a single endpoint and hands back a callable. You declare the contract once — input, output, auth, resilience — and call it like a local function. The same definition your code calls, the CLI runs and an AI agent invokes — and every caller gets a capability, not a credential. A service with more than one endpoint is a seam — a group of stitches that share a base URL, auth, and one runtime (throttle budget, store, trace sink).
import { stitch } from 'stitchapi';
import { z } from 'zod';
const getUser = stitch({
path: 'https://api.example.com/users/{id}',
output: z.object({ id: z.number(), name: z.string() }),
pick: 'data',
});
const user = await getUser({ params: { id: 1 } }); // typed · validated
Keep the fetch or axios you already have — it's the adapter underneath. A stitch sits above the transport and turns an endpoint into a function rather than replacing the call.
Motivation
In almost every project there's a src/api/ folder of thin functions that fire an HTTP request and pull the payload out of the response. Everything that actually makes an integration reliable — auth lifecycle, retries, rate limits, timeouts, response validation, drift detection, observability — gets re-implemented at every call site, and each wrapper rots independently. fetch hands back opaque bytes, and raw bytes aren't what application code (or an AI agent) needs; both want structured, validated, observable results.
A stitch folds all of that back into the call:
Around raw fetch, you hand-roll… | …a stitch declares it once |
|---|---|
| Opaque bytes you parse and hope are the right shape | Schema-validated, typed results — drift caught on every call |
| A throw on the first failure, then it's on you | Retries with backoff + jitter, honoring Retry-After |
| One coarse timeout, if you remember it | Layered total / per-attempt timeouts with real aborts |
| No rate control — you meet the 429s in production | Proactive throttle: rate + concurrency caps, shared per host |
| A raw byte stream you frame and paginate yourself | SSE framing, delta concatenation, auto-pagination |
| Zero visibility into what the call did | A typed event stream + opt-in traces: latency, retries, drift |
Why StitchAPI
There are plenty of ways to get a typed API client — spec-based generators, hand-authored contract clients, workflow platforms, or a folder of hand-rolled fetch wrappers. StitchAPI sits in a spot none of them cover: it turns one endpoint at a time into a resilient, validated, observable function — no spec, no codegen, no config files, no server; only explicit composition.
- Atomic, not spec-first. Spec-based generators (openapi-generator, Orval, Kubb, …) need a complete OpenAPI document first — and most real-world APIs (internal, undocumented, the long tail) never get one. A stitch needs a URL and one example response.
- A runtime, not a code generator. No generated SDK to commit, diff, and regenerate. The declaration is the client, validated on every live call — so a silently renamed field is a loud, leveled drift signal, not an
undefinedthree layers downstream. - Resilience is declared, not hand-rolled. Retries, throttling, timeouts, circuit breaking, pagination — configuration on the stitch, uniform across every integration.
- Auth is a boundary. A stitch owns its credential and lifecycle; callers get a capability, not the credential. That matters double when the caller is an AI agent.
- Agents are first-class callers. One definition is a function, a CLI command, an HTTP endpoint, and an MCP tool — returning structured, schema-validated, traceable results instead of opaque bytes.
- A library, not a platform. Zero runtime dependencies, embeds in your project, nothing to operate.
| Alternative | Needs | You maintain | StitchAPI instead |
|---|---|---|---|
| Spec-based codegen (openapi-generator, Orval, Kubb, …) | a complete OpenAPI spec | a generated SDK, regenerated on every API change | one endpoint at a time, validated live at runtime |
| Typed runtime clients (Zodios, ts-rest, …) | a hand-authored contract | your own retry / auth / rate-limit code around it | resilience, auth lifecycle, and drift detection built into the primitive |
| Workflow platforms (Windmill, n8n, …) | a server to deploy | flows inside someone else's runtime | a zero-dependency library; composition is just code |
Hand-rolled fetch wrappers | nothing | bespoke auth, retries, limits — re-solved per project | the same concerns, declared once per stitch |
For the full competitive landscape and positioning, see the Overview.
What StitchAPI is not
Knowing what a tool refuses to be is how you trust what it is:
- Not an HTTP client or
fetchreplacement —fetch/axios are the substrate underneath; a stitch sits above the transport. - Not a code generator — no SDK to commit, diff, and regenerate; the declaration is the runtime, validated live.
- Not spec-first — no OpenAPI document required; a URL and one example response is enough.
- Not a server to deploy — a zero-dependency library you import, for the APIs you don't control.
- Not a workflow engine or iPaaS — no orchestration, queues, or visual builder; composition is plain TypeScript.
- No config files or hidden inheritance — nothing ambient a stitch silently reads.
No server, no codegen, no config files, no implicit inheritance — only explicit composition.
Features
- One primitive, scoped to a surface —
stitch(url | config)returns a typed callable; a service with more than one endpoint is aseamthat shares base, auth, throttle budget, store, and trace sink across its members. - Event-stream core — every call yields a typed stream (
start → progress → drift → result → done);awaitis sugar that returns the final validated value. - Bring-your-own validation — Zod or any Standard Schema validator (Valibot, ArkType, …); types are inferred from the schemas.
- Leveled drift detection — live responses validated against the declared schema (the contract); a required field missing/incompatible throws, while soft drift (a coercion, an undeclared or defaulted field) surfaces as a non-fatal
warn/info/verbosefinding instead of a silentundefined. - Declared resilience — retry with backoff and
Retry-After, proactive throttle, layered timeouts, a circuit breaker, and idempotency keys. - Read-through caching — opt-in response cache + in-process coalescing, keyed by a derived, principal-scoped key, loaded lazily from
stitchapi/cache. - Auth as a boundary —
bearer,apiKey,basic,cookieSession(auto-login/re-login),oauth2; secrets resolve at call time and never reach the caller. - Any request style —
httpby default;graphql,sse,stream,download,llm,shell, andpostmessageare peer surfaces behind subpath imports. - Pluggable state store — throttle state, cache and sessions behind a
get/setstore with optional atomic verbs; swap in Redis/Postgres to go distributed. - Zero-infra observability — tracing is off by default; opt in per stitch or via
STITCH_TRACE_*env vars. No collector, no dashboard. - Four front doors, one definition — in-process function, CLI (
stitch run), HTTP (stitch serve), and MCP (stitch mcp). - Zero runtime dependencies —
"dependencies": {}, built on globalfetch, tree-shakeable; ~21 kB min+gzip for the whole entry, ~19 kB for a typicalimport { stitch }(the cache engine and the OTLP exporter load lazily, on first use).
Install
npm install stitchapi@rc # or: pnpm add stitchapi@rc · yarn add stitchapi@rc
Validation is bring-your-own — pass a Zod schema or any Standard Schema validator; none is bundled. The examples below use Zod for familiarity, and api.example.com as an illustrative host — point them at your own API to run them, or try them as-is in the playground, which serves that host from an in-browser simulator.
Quick start
The smallest stitch is a URL — declare once, call many times:
import { stitch } from 'stitchapi';
const getUsers = stitch('https://api.example.com/users');
const users = await getUsers(); // GET, parsed JSON
Path params use RFC 6570 URI templates; params, query, headers, and body all travel in one input object:
const getUser = stitch('https://api.example.com/users/{id}');
await getUser({ params: { id: 1 }, query: { expand: 'roles' } });
// → GET https://api.example.com/users/1?expand=roles
Reach for the full set of knobs only when you need them — they default off:
const getUser = stitch({
path: 'https://api.example.com/users/{id}',
output: User, // a validator of your choice
pick: 'data',
retry: 3, // ≡ { attempts: 3 }
timeout: '5s', // ≡ { total: '5s' }
cache: '1m', // ≡ { ttl: '1m' }
});
const user = await getUser({ params: { id: 42 } });
// → typed · validated · retried · cached
A stitch yields a typed event stream; await consumes it and returns the final value, while .safe() resolves to { ok, data, error } and .stream() yields progress, throttle waits, retries, and drift as they happen.
Composition: seam, extends, .with()
Everything reusable is a named value, and a stitch composes values — no global config is ever required. A service with more than one endpoint is a seam: declare the shared base, auth, and budget once; each endpoint is a member that inherits the config and shares one runtime (throttle bucket, store, trace sink):
import { seam } from 'stitchapi';
const api = seam({
baseUrl: 'https://api.example.com',
retry: { attempts: 3, on: [429, 503] },
timeout: { total: '30s' },
});
const listUsers = api.stitch({
path: '/users',
output: User.array(),
pick: 'data',
});
const getUser = api.stitch({
path: '/users/{id}',
output: User,
pick: 'data',
});
For lighter reuse, extends: [fragment | stitch] merges config left→right (own fields win), and .with() pre-binds part of the input while reusing the same runtime. Full guide: Authoring.
Validation & leveled drift
The declared output schema is the contract. Wrap it in drift(): a response is validated (the call returns the validated value — coerced, defaulted, unknown keys stripped), and the difference between the raw body and that validated value is reported as a leveled, non-fatal signal:
| Change | What it means | Level |
|---|---|---|
| invalid | a required field missing or incompatible — throws | error |
| coerced | a coercion ("42"→42): a wire-type shift validation hid | warn |
| undeclared | a key the schema strips | info |
| defaulted | a .default() fired (field absent) | verbose |
import { drift, stitch } from 'stitchapi';
import { z } from 'zod';
const listOrders = stitch({
path: 'https://api.example.com/users/{id}/orders',
pick: 'data',
output: drift(
z.array(z.object({ id: z.number(), total: z.number().optional() })),
{
ignore: ['[].meta'], // acknowledged, unconsumed — don't report it
level: { coerced: 'info' }, // re-level a kind, or pass a level/list to filter
},
),
});
Drift is schema-anchored — no snapshot to manage. Severity lives in the schema: a required field missing/incompatible is a hard invalid that throws; everything else is non-fatal drift on the event stream. Declared variance (an optional field, a nullable, an empty array) validates clean, so it's never a false alarm; ignore silences known-but-unconsumed fields and level filters or re-levels the soft signals. The request side validates too: input takes a schema per part and fails fast before any request is sent. Full guide: Validation & drift.
Resilience: retry, throttle, timeout
The things every src/api/ folder reinvents are configuration here — uniform across every stitch:
const listUsers = stitch({
baseUrl: 'https://api.example.com',
path: '/users',
retry: { attempts: 4, on: [429, 502, 503] },
throttle: { rate: '1/s', concurrency: 2, pool: 'host' },
timeout: { total: '30s', each: '10s' },
});
throttle is proactive (keeps you under a limit before it bites; pool: 'host' shares a limiter across stitches), retry is reactive (backoff + Retry-After), and timeout aborts with a real AbortSignal. Three more knobs round it out: circuit fast-fails a dependency that's already down, idempotency injects a stable Idempotency-Key on writes, and verdict declares what counts as success — accept treats a non-2xx (e.g. 404) as a normal result instead of a throw, flag fails a 200 whose body says it failed. Full guide: Resilience.
Caching
A read-through response cache with in-process request coalescing — off by default, loaded lazily from stitchapi/cache only when a stitch sets cache. The key is derived from the resolved request (no caller-authored keys to drift) and principal-scoped by default, so user A is never served user B's cached response:
const getUser = stitch({
path: 'https://api.example.com/users/{id}',
output: User,
pick: 'data',
cache: '5m',
});
const listAnnouncements = stitch({
path: 'https://api.example.com/announcements',
output: z.array(z.object({ id: z.number(), title: z.string() })),
pick: 'data',
cache: {
ttl: '1h',
tenancy: 'app',
vary: ['accept-language'],
entries: 500,
fingerprint: 1, // pins the shape — cacheable without a fingerprinter
},
});
Caching is sound by construction: a stitch with an output schema caches only when that schema can be fingerprinted or you pin a fingerprint tag — otherwise it refuses to cache rather than serve a stale shape. Mark sensitive data sensitive: true to opt out entirely.
Auth as a boundary
Auth is a field on the stitch — never global. Secrets resolve at call time (env(), credential.file()), the declaration is committable, and the caller gets data without ever seeing the credential:
import { stitch } from 'stitchapi';
import { bearer, env } from 'stitchapi/auth';
const getUser = stitch({
path: 'https://api.example.com/users/{id}',
auth: bearer(env('API_TOKEN')), // resolved per call; the caller passes no secret
});
bearer, apiKey, and basic are header strategies; oauth2() runs the client-credentials grant and caches/refreshes the token; and cookieSession runs a login, captures the cookie, replays it, and re-logs-in when the wall returns — the caller never sees the password or writes the cookie dance. Full guide: Auth.
Surfaces: any request style
A surface is the request style a stitch speaks. http is the default; the rest are peer surfaces on the same engine — auth, retry, throttle, timeout, validation, and the event stream compose with every one. Each ships as its own subpath import, so import { stitch } pulls in http alone.
| Surface | Import | Shapes | await resolves to |
|---|---|---|---|
http | stitch (default) | a JSON-over-HTTP call | the validated body |
graphql | stitchapi/graphql | POST { query, variables }, picks data | the data payload |
sse | stitchapi/sse | a text/event-stream reader (over fetch) | every parsed event, collected |
stream | stitchapi/stream | a raw ReadableStream reader | every decoded chunk, collected |
download | stitchapi/download | a buffered binary GET | { blob, filename } |
llm | stitchapi/llm | a chat-completion via a provider contract | the normalised { text, … } |
shell | @stitchapi/shell (peer pkg) | a local command, args + stdin | the command's stdout |
postmessage | stitchapi/postmessage | a typed iframe ↔ parent RPC / event call | the typed RPC response |
Full guide: Surfaces.
Four front doors
The same typed unit is reachable four ways, so humans and agents call exactly the same validated, observable thing:
| Front door | How | Example |
|---|---|---|
| In-process function | import and call | await listUsers() |
| CLI command | stitch run | $ stitch run listUsers |
| HTTP endpoint | stitch serve | POST /stitch/listUsers |
| MCP / agent tool | stitch mcp | run_stitch { "name": "listUsers" } |
The three non-function doors address a stitch by its export name, the same name list_stitches and GET / list.
stitch run streams every event as one JSON line on stdout (ready for jq); stitch trace summarizes the run log — runs, failures, retries, drift, and latency percentiles. Full guide: Surfaces → CLI.
Agent-native
Shortened here. Read the whole README on GitHub.
Tools it offers (2)
What this server listed when Ahel dialed its public endpoint in Sep 2026, with no key and no account of yours. The names are the server’s own.
search_docsget_doc
Signals
- GitHub stars
- 20
- Last commit
- Oct 2026
- Weekly_downloads
- 38 weekly_downloads
- Hacker News mentions
- 20
Advanced
- Delivery
- docs MCP server → your Ahel connector (mcp.ahel.ai) → your AI.
- Item type
- mcp-server
- Key
dev-stitchapi-docs- Source
- github.com/rejifald/stitchapi
- Hosted endpoint
https://stitchapi.dev/api/mcp