Define once.
Deploy everywhere.
Build MCP servers, REST APIs, and CLI tools from a single set of actions. No framework lock-in. No boilerplate. Just composition.
pnpm add @silkweave/core The Problem
Agents need services. Services need many doors.
The agentic landscape demands that services be reachable via MCP, REST, CLI, and more. Building each transport from scratch is unsustainable.
Fragmented Transports
MCP stdio, HTTP, REST, CLI — each requires a different server setup, different libraries, different patterns.
Boilerplate Overload
Every new endpoint means repeating validation, error handling, and serialization. Your business logic drowns in glue code.
Framework Lock-in
Pick Express? Now you're stuck. Pick Fastify? Same story. Your actions are trapped inside your transport choice.
How It Works
One action. Every transport.
Define your business logic once as an Action. Pick an Adapter. Silkweave wires them together.
import { createAction } from '@silkweave/core'
import z from 'zod'
export const GreetAction = createAction({
name: 'greet',
description: 'Greet a user by name',
input: z.object({
name: z.string().describe('Name of the person to greet')
}),
run: async ({ name }) => {
return { message: `Hello, ${name}!` }
}
}) Adapters
Same action. Five transports.
Swap one line to change how your action is served. The business logic stays identical.
import { silkweave } from '@silkweave/core'
import { stdio } from '@silkweave/mcp'
import { GreetAction } from './actions/greet.js'
await silkweave({ name: 'my-server', version: '1.0.0' })
.adapter(stdio())
.action(GreetAction)
.start() import { silkweave } from '@silkweave/core'
import { http } from '@silkweave/mcp'
import { GreetAction } from './actions/greet.js'
await silkweave({ name: 'my-server', version: '1.0.0' })
.adapter(http({ port: 8080 }))
.action(GreetAction)
.start() import { silkweave } from '@silkweave/core'
import { fastify } from '@silkweave/fastify'
import { GreetAction } from './actions/greet.js'
await silkweave({ name: 'my-server', version: '1.0.0' })
.adapter(fastify({ port: 8080 }))
.action(GreetAction)
.start() import { silkweave } from '@silkweave/core'
import { cli } from '@silkweave/cli'
import { GreetAction } from './actions/greet.js'
await silkweave({ name: 'my-tool', version: '1.0.0' })
.adapter(cli())
.action(GreetAction)
.start() import { silkweave } from '@silkweave/core'
import { vercel } from '@silkweave/vercel'
import { GreetAction } from './actions/greet.js'
const { adapter, handler } = vercel()
await silkweave({ name: 'my-server', version: '1.0.0' })
.adapter(adapter)
.action(GreetAction)
.start()
export default { fetch: handler } Features
Everything you need. Nothing you don't.
Zod-Powered Schemas
Define inputs with Zod. Get validation, type inference, and auto-generated docs for free.
Built-in OAuth 2.1
Full OAuth proxy with PKCE, refresh tokens, and dynamic client registration out of the box.
Auto-Generated Swagger
The Fastify adapter produces OpenAPI specs and interactive Swagger UI automatically.
Session & Context
Fluent .set() API for per-request context. Actions receive a typed context bag — no globals.
Vercel Serverless
Deploy stateless MCP servers to Vercel edge functions. Zero infrastructure to manage.
CLI with Prompts
Expose the same actions as a CLI tool with interactive prompts, powered by Commander + Clack.
Quick Start
Up and running in 3 steps.
Install
pnpm add @silkweave/core @silkweave/mcp zod Define an action
import { createAction } from '@silkweave/core'
import z from 'zod'
export const GreetAction = createAction({
name: 'greet',
description: 'Greet a user by name',
input: z.object({
name: z.string().describe('Name to greet')
}),
run: async ({ name }) => {
return { message: `Hello, ${name}!` }
}
}) Pick an adapter and start
import { silkweave } from '@silkweave/core'
import { stdio } from '@silkweave/mcp'
import { GreetAction } from './actions/greet.js'
await silkweave({ name: 'my-server', version: '1.0.0' })
.adapter(stdio())
.action(GreetAction)
.start()