Package Exports
- @switchy-ai/sdk
- @switchy-ai/sdk/mcp
Readme
@switchy-ai/sdk
Official TypeScript / JavaScript SDK for Switchy — the shared AI workspace for small teams.
Install
npm install @switchy-ai/sdkOptional, only if you want live chat updates:
npm install ablyHello world
Ten lines: list your Spaces, save a memory, search for it.
import { Switchy } from '@switchy-ai/sdk';
const client = new Switchy({ apiKey: process.env.SWITCHY_API_KEY! });
const { projects: spaces } = await client.spaces.list();
const space = spaces[0];
await client.memory.create({
content: 'Launching the new pricing page on May 15.',
visibility: 'SPACE',
spaceId: space.id,
});
const hits = await client.memory.search({ query: 'when do we launch?' });
console.log(hits[0].memory.content);What you can do
The SDK mirrors the v2 REST API one-to-one. Each namespace is fully typed.
client.spaces.list({ page, limit })
client.spaces.get(id)
client.spaces.create({ name, description, tags }, { idempotencyKey })
client.sessions.list(spaceSlug)
client.sessions.create(spaceSlug, { name, participantUserIds, defaultAgent })
client.sessions.get(spaceSlug, sessionId)
client.sessions.update(spaceSlug, sessionId, { title, model })
client.sessions.archive(spaceSlug, sessionId)
client.messages.list(spaceSlug, sessionId, { before, limit })
client.messages.send(spaceSlug, sessionId, { body, parentMessageId })
client.messages.react(spaceSlug, messageId, '👍')
client.memory.list({ visibility, spaceId, limit })
client.memory.create({ content, visibility, spaceId, tags, sourceUrl })
client.memory.search({ query, spaceId, limit })
client.memory.delete(memoryId)
client.members.list(orgId)
client.members.updateRole(orgId, userId, 'ADMIN')
client.members.remove(orgId, userId)
client.invitations.list(orgId)
client.invitations.create(orgId, { email, role: 'MEMBER' })
client.invitations.accept(token)
client.billing.credits()
client.billing.checkout({ plan: 'team', interval: 'annual' })
client.billing.portal(returnUrl)
client.billing.topup({ pack: 'medium' })
client.mcp.list()
client.mcp.create({ displayName, mentionSlug, endpointUrl, authType, secret })
client.mcp.delete(id)
client.mcp.test(id)
client.keys.list()
client.keys.create({ name, scopes })Realtime
Subscribe to live messages, typing indicators, reactions, and member events in one Space session. Requires the optional ably peer dependency.
const channel = client.realtime(space.slug, session.id);
const sub = await channel.subscribe((event) => {
switch (event.type) {
case 'message': console.log('new message:', event.message.body); break;
case 'typing': console.log(`${event.userId} is ${event.state}-typing`); break;
case 'reaction': console.log(`reaction ${event.emoji} ${event.delta}`); break;
case 'member': console.log(`${event.userId} ${event.action}`); break;
}
});
// later
await sub.unsubscribe();MCP client
Switchy is also an MCP server. From any MCP-capable client (Claude Desktop, Cursor, your own code) you can call Switchy's tools over JSON-RPC.
import { McpClient } from '@switchy-ai/sdk';
const mcp = new McpClient({ apiKey: process.env.SWITCHY_API_KEY! });
const { memories } = await mcp.searchMemory({ query: 'launch readiness' });
const { space } = await mcp.getSpace('product');
await mcp.actAsUser('user_abc').postMessage({
sessionId: 'sess_123',
content: 'Just shipped the migration guide.',
});See /docs/mcp for the full tool list and scopes.
Errors
Every method returns the unwrapped resource on success. On failure it throws a typed error you can instanceof-check:
import { ValidationError, ForbiddenError, RateLimitError } from '@switchy-ai/sdk';
try {
await client.spaces.create({ name: '' });
} catch (err) {
if (err instanceof ValidationError) console.error(err.issues);
else if (err instanceof ForbiddenError) console.error('not allowed');
else if (err instanceof RateLimitError) console.error(`retry in ${err.retryAfter}s`);
else throw err;
}The full error table — codes, HTTP status, when each fires — is at /docs/api/overview.
Idempotency
Every POST accepts an idempotencyKey. If you retry the same call with the same key within 24 hours, the server replays the original response instead of creating a duplicate.
const key = crypto.randomUUID();
await client.messages.send(slug, sessionId, { body: 'hi' }, { idempotencyKey: key });Migrating from v0.2
v0.3 is the team-workspace SDK. The shape changed because the product changed. Quick map:
| v0.2 | v0.3 |
|---|---|
client.chat.complete({ model, message }) |
client.messages.send(spaceSlug, sessionId, { body: '@model message' }) |
client.chat.stream(...) |
Use client.realtime(...) for live AGENT replies. |
client.namespaces.create({ name }) |
client.spaces.create({ name }) |
client.namespaces.list() |
client.spaces.list() then read .projects |
client.memory.createFrame(ns, ...) |
client.memory.create({ content, visibility, spaceId }) |
client.memory.search({ query, namespaces }) |
client.memory.search({ query, spaceId }) |
client.sessions.create({ namespace }) |
client.sessions.create(spaceSlug, { name }) |
client.knowledgeGraph.* |
Removed. KG is internal in v2; use semantic memory search. |
client.videos.* |
Removed. Re-shipping under /v1/videos later. |
Big-picture changes:
- Org context is implicit. Mint a key inside the org you want to talk to; the SDK doesn't need an
orgIdarg on most calls. - Visibility is required on memory writes.
PRIVATE(only you),SPACE(Space members),ORG(everyone in the org). - The base URL changed from
…/api/v1to…/api. The SDK defaults; passbaseUrlif you target staging. - Errors are typed. Replace
try { … } catch (e) { if (e.code === 'RATE_LIMIT_EXCEEDED') … }withinstanceof RateLimitError.
If you minted a key under one org and used it in another org by accident in v0.2 — that's no longer possible. Keys lock to their minting org.
Configuration reference
new Switchy({
apiKey: 'sk_live_...', // required
baseUrl: 'https://switchy.build/api', // default
timeout: 60_000, // ms, default 60s
maxRetries: 2, // 429 retries honouring Retry-After
fetch, // custom fetch (tests / non-Node)
userAgent: 'my-app/1.0', // optional
});License
MIT