About server-sdk
server-sdk is an MCP server published by git+SylphxAI in the AI category: sDK for building simple MCP servers with Bun & Deno.
The repository has 1 stars and 0 forks, with the last commit 7 months ago. Six months or more without a commit doesn't mean the server is broken, but check the open issues before depending on it in production.
Install
claude mcp add server-sdk -- npx -y @aklinker1/mcp-server-sdknpx -y @aklinker1/mcp-server-sdkThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Ask AI
Ask AI about server-sdk
Powered by Claude · Grounded in docs
Security checks
- ✓README presentThe repository ships documentation.
- !Scoped permissionsDoesn't declare a permission scope. Assume it can do anything its process can.
4 checks have not been run yet.
README
@sylphx/mcp-server-sdk
Pure functional MCP (Model Context Protocol) server SDK for Bun.
Features
- Pure Functional: Immutable data, composable handlers
- Type-Safe: First-class TypeScript with Vex schema integration
- Builder Pattern: Fluent API for defining tools, resources, and prompts
- Fast: Built for Bun with minimal dependencies
- Streamable HTTP: MCP 2025-03-26 spec with SSE notifications
- Complete: Tools, resources, prompts, notifications, sampling, elicitation
Installation
bun add @sylphx/mcp-server-sdk
Quick Start
import { createServer, tool, text, stdio } from "@sylphx/mcp-server-sdk"
import { object, str } from "@sylphx/vex"
// Define tools using builder pattern
const greet = tool()
.description("Greet someone")
.input(object({ name: str() }))
.handler(({ input }) => text(`Hello, ${input.name}!`))
const ping = tool()
.handler(() => text("pong"))
// Create and start server
const server = createServer({
name: "my-server",
version: "1.0.0",
tools: { greet, ping },
transport: stdio()
})
await server.start()
Tools
Tools are callable functions exposed to the AI.
import { tool, text, image, audio, json, toolError } from "@sylphx/mcp-server-sdk"
import { description, enum_, num, object, str } from "@sylphx/vex"
// Simple tool (no input)
const ping = tool()
.description("Health check")
.handler(() => text("pong"))
// Tool with typed input
const calculator = tool()
.description("Perform arithmetic")
.input(object({
a: num(description("First number")),
b: num(description("Second number")),
op: enum_(["+", "-", "*", "/"] as const),
}))
.handler(({ input }) => {
const { a, b, op } = input
const result = op === "+" ? a + b
: op === "-" ? a - b
: op === "*" ? a * b
: a / b
return text(`${a} ${op} ${b} = ${result}`)
})
// Multiple content items
const systemInfo = tool()
.description("Get system information")
.handler(() => [
text("CPU: 8 cores"),
text("Memory: 16GB")
])
// Mixed content types
const screenshot = tool()
.description("Take screenshot with description")
.handler(() => [
text("Here's the screenshot:"),
image(base64Data, "image/png")
])
// Return JSON data
const getUser = tool()
.description("Get user data")
.input(object({ id: str() }))
.handler(({ input }) => json({ id: input.id, name: "Alice" }))
// Return error
const riskyOperation = tool()
.description("May fail")
.handler(() => toolError("Something went wrong"))
Resources
Resources provide data to the AI.
import { resource, resourceTemplate, resourceText, resourceBlob } from "@sylphx/mcp-server-sdk"
// Static resource with fixed URI
const readme = resource()
.uri("file:///readme.md")
.description("Project readme")
.mimeType("text/markdown")
.handler(({ uri }) => resourceText(uri, "# My Project\n\nWelcome!"))
// Resource template for dynamic URIs
const fileReader = resourceTemplate()
.uriTemplate("file:///{path}")
.description("Read any file")
.handler(async ({ uri, params }) => {
const content = await Bun.file(`/${params.path}`).text()
return resourceText(uri, content)
})
// Binary resource
const logo = resource()
.uri("image:///logo.png")
.mimeType("image/png")
.handler(async ({ uri }) => {
const data = await Bun.file("./logo.png").bytes()
const base64 = Buffer.from(data).toString("base64")
return resourceBlob(uri, base64, "image/png")
})
Prompts
Prompts are reusable conversation templates.
import { prompt, user, assistant, messages, promptResult } from "@sylphx/mcp-server-sdk"
import { description, object, optional, str, withDefault } from "@sylphx/vex"
// Simple prompt (no arguments)
const greeting = prompt()
.description("A friendly greeting")
.handler(() => messages(
user("Hello!"),
assistant("Hi there! How can I help you today?")
))
// Prompt with typed arguments
const codeReview = prompt()
.description("Review code for issues")
.args(object({
code: str(description("Code to review")),
language: optional(str(description("Programming language"))),
}))
.handler(({ args }) => messages(
user(`Please review this ${args.language ?? "code"}:\n\`\`\`\n${args.code}\n\`\`\``),
assistant("I'll analyze this code for potential issues, best practices, and improvements.")
))
// Prompt with description in result
const translate = prompt()
.description("Translate text between languages")
.args(object({
text: str(),
from: withDefault(str(), "auto"),
to: str(),
}))
.handler(({ args }) => promptResult(
`Translation from ${args.from} to ${args.to}`,
messages(user(`Translate "${args.text}" from ${args.from} to ${args.to}`))
))
Server Configuration
import { createServer, stdio, http } from "@sylphx/mcp-server-sdk"
const server = createServer({
// Server identity
name: "my-server",
version: "1.0.0",
instructions: "This server provides...",
// Handlers (names from object keys)
tools: { greet, ping, calculator },
resources: { readme, config },
resourceTemplates: { file: fileReader },
prompts: { codeReview, translate },
// Transport
transport: stdio() // or http({ port: 3000 })
})
await server.start()
Transports
Stdio Transport
For CLI tools and subprocess communication.
import { stdio } from "@sylphx/mcp-server-sdk"
const server = createServer({
tools: { ping },
transport: stdio()
})
await server.start()
HTTP Transport
Implements MCP Streamable HTTP (2025-03-26 spec) with SSE support for real-time notifications.
import { http } from "@sylphx/mcp-server-sdk"
const server = createServer({
tools: { ping },
transport: http({
port: 3000,
cors: "*" // Enable CORS for web clients
})
})
await server.start()
// Server running at http://localhost:3000/mcp
Endpoints:
POST /mcp- JSON-RPC messages (returns JSON or SSE based on Accept header)GET /mcp/health- Health check
SSE Streaming:
When client sends Accept: text/event-stream, the server responds with SSE format, enabling real-time notifications during request processing:
event: message
data: {"jsonrpc":"2.0","method":"notifications/progress","params":{...}}
event: message
data: {"jsonrpc":"2.0","id":1,"result":{...}}
Notifications
Send server-to-client notifications for progress and logging using the simplified context API.
import { array, object, str } from "@sylphx/vex"
const processFiles = tool()
.description("Process multiple files")
.input(object({ files: array(str()) }))
.handler(async ({ input, ctx }) => {
const total = input.files.length
for (let i = 0; i < total; i++) {
// Report progress (automatically uses progressToken from request)
ctx.progress(i + 1, { total, message: `Processing ${input.files[i]}` })
await processFile(input.files[i])
}
// Log completion
ctx.log("info", { message: "Processing complete" })
return text(`Processed ${total} files`)
})
Context Methods
// Progress notification (uses progressToken from request automatically)
ctx.progress(current: number, options?: { total?: number; message?: string })
// Log notification
ctx.log(level: "debug" | "info" | "notice" | "warning" | "error" | "critical" | "alert" | "emergency", data: unknown, logger?: string)
Low-level Notification Factories
For advanced use cases, you can use the raw notification factories:
import { progress, log, resourcesListChanged, toolsListChanged, promptsListChanged, resourceUpdated, cancelled } from "@sylphx/mcp-server-sdk"
// Progress notification (requires manual token)
progress(token: string | number, current: number, options?: { total?: number; message?: string })
// Log notification
log(level: LogLevel, data: unknown, logger?: string)
// List change notifications (for dynamic capability updates)
resourcesListChanged()
toolsListChanged()
promptsListChanged()
// Resource updated notification
resourceUpdated(uri: string)
// Cancellation notification
cancelled(requestId: string | number, reason?: string)
Sampling
Request LLM completions from the client.
import { createSamplingClient } from "@sylphx/mcp-server-sdk"
import { object, str } from "@sylphx/vex"
const summarize = tool()
.description("Summarize text using AI")
.input(object({ text: str() }))
.handler(async ({ input, ctx }) => {
const sampling = createSamplingClient(ctx.requestSampling)
const result = await sampling.createMessage({
messages: [
{ role: "user", content: { type: "text", text: `Summarize: ${input.text}` } }
],
maxTokens: 500,
// Optional parameters
systemPrompt: "You are a helpful summarizer",
temperature: 0.7,
stopSequences: ["END"],
modelPreferences: {
hints: [{ name: "claude-3" }],
costPriority: 0.5,
speedPriority: 0.5,
intelligencePriority: 0.8,
},
})
// result.content is the response content
// result.model is the model used
// result.stopReason is why generation stopped
return text(result.content.text)
})
Elicitation
Request user input from the client.
import { createElicitationClient } from "@sylphx/mcp-server-sdk"
import { object, str } from "@sylphx/vex"
const confirmAction = tool()
.description("Confirm before proceeding")
.input(object({ action: str() }))
.handler(async ({ input, ctx }) => {
const elicit = createElicitationClient(ctx.requestElicitation)
const result = await elicit.elicit(
`Are you sure you want to ${input.action}?`,
{
type: "object",
properties: {
confirm: {
type: "boolean",
description: "Confirm action",
},
reason: {
type: "string",
description: "Optional reason",
},
},
required: ["confirm"],
}
)
// result.action: "accept" | "decline" | "cancel"
// result.content: { confirm: boolean, reason?: string } (when action is "accept")
if (result.action === "accept" && result.content?.confirm) {
return text(`Proceeding with ${input.action}`)
}
return text("Action cancelled")
})
Elicitation Schema Properties
interface ElicitationProperty {
type: "string" | "number" | "integer" | "boolean"
description?: string
default?: string | number | boolean
enum?: (string | number)[] // Constrain to specific values
enumNames?: string[] // Display names for enum values
// String-specific
format?: "email" | "uri" | "date" | "date-time"
minLength?: number
maxLength?: number
// Number-specific
minimum?: number
maximum?: number
}
Pagination
Paginate large result sets.
import { paginate } from "@sylphx/mcp-server-sdk"
import { object, optional, str } from "@sylphx/vex"
const listItems = tool()
.description("List items with pagination")
.input(object({ cursor: optional(str()) }))
.handler(async ({ input }) => {
const allItems = await fetchAllItems()
const result = paginate(allItems, input.cursor, {
defaultPageSize: 10,
maxPageSize: 100,
})
// result.items: current page items
// result.nextCursor: cursor for next page (undefined if last page)
return json({
items: result.items,
nextCursor: result.nextCursor,
})
})
API Reference
Server
createServer(config: ServerConfig): Server
interface ServerConfig {
name?: string // Default: "mcp-server"
version?: string // Default: "1.0.0"
instructions?: string // Instructions for the LLM
tools?: Record<string, ToolDefinition>
resources?: Record<string, ResourceDefinition>
resourceTemplates?: Record<string, ResourceTemplateDefinition>
prompts?: Record<string, PromptDefinition>
transport: TransportFactory
}
Tool Builder
tool()
.description(string) // Optional description
.input(VexSchema) // Optional input schema
.handler(fn: HandlerFn) -> ToolDefinition
// Handler signature
({ input, ctx }) => ToolResult | Promise<ToolResult>
// Handler can return:
// - Single content: text("hello")
// - Array: [text("hi"), image(data, "image/png")]
// - Full result: { content: [...], isError: true }
Resource Builder
resource()
.uri(string) // Required URI
.description(string) // Optional description
.mimeType(string) // Optional MIME type
.handler(fn) -> ResourceDefinition
resourceTemplate()
.uriTemplate(string) // Required URI template (RFC 6570)
.description(string) // Optional description
.mimeType(string) // Optional MIME type
.handler(fn) -> ResourceTemplateDefinition
// Handler receives { uri, ctx } or { uri, params, ctx }
Prompt Builder
prompt()
.description(string) // Optional description
.args(VexSchema) // Optional arguments schema
.handler(fn) -> PromptDefinition
// Handler receives { args, ctx } or { ctx }
Content Helpers
// Tool content
text(content: string, annotations?): TextContent
image(data: string, mimeType: string, annotations?): ImageContent
audio(data: string, mimeType: string, annotations?): AudioContent
embedded(resource: EmbeddedResource, annotations?): ResourceContent
json(data: unknown): TextContent
toolError(message: string): ToolsCallResult
// Resources
resourceText(uri: string, text: string, mimeType?: string): ResourcesReadResult
resourceBlob(uri: string, blob: string, mimeType: string): ResourcesReadResult
resourceContents(...items: EmbeddedResource[]): ResourcesReadResult
// Prompts
user(content: string): PromptMessage
assistant(content: string): PromptMessage
messages(...msgs: PromptMessage[]): PromptsGetResult
promptResult(description: string, result: PromptsGetResult): PromptsGetResult
Transports
stdio(options?: StdioOptions): TransportFactory
http(options?: HttpOptions): TransportFactory
interface HttpOptions {
port?: number // Default: 3000
hostname?: string // Default: "localhost"
}
Notifications
progress(token, current, options?): ProgressNotification
log(level, data, logger?): LogNotification
resourcesListChanged(): Notification
toolsListChanged(): Notification
promptsListChanged(): Notification
resourceUpdated(uri): Notification
cancelled(requestId, reason?): Notification
Sampling
createSamplingClient(sender: SamplingRequestSender): SamplingClient
interface SamplingClient {
createMessage(params: SamplingCreateParams): Promise<SamplingCreateResult>
}
Elicitation
createElicitationClient(sender: ElicitationRequestSender): ElicitationClient
interface ElicitationClient {
elicit(message: string, schema: ElicitationSchema): Promise<ElicitationCreateResult>
}
Pagination
paginate<T>(items: T[], cursor?: string, options?: PaginationOptions): PageResult<T>
interface PaginationOptions {
defaultPageSize?: number // Default: 50
maxPageSize?: number // Default: 100
}
interface PageResult<T> {
items: T[]
nextCursor?: string
}
Powered by Sylphx
- @sylphx/vex - Ultra-fast schema validation
- @sylphx/gust - Lightweight HTTP framework for Bun
- @sylphx/biome-config - Shared Biome configuration
- @sylphx/tsconfig - Shared TypeScript configuration
- @sylphx/doctor - Project health checker
- @sylphx/bump - Version management
License
MIT
README copy captured when this server was indexed; it may lag the repository. The original is authoritative.