Skip to content

API Overview

Server

createMCPServer(info) returns an MCPServer instance.

MethodSignatureDescription
use(middleware: ToolMiddleware) => voidRegister global middleware for all subsequent tools, resources, and tasks
tool(name, ...middlewares?, config, handler) => voidRegister a tool
resource(uri, ...middlewares?, config, handler) => voidRegister a resource
task(name, ...middlewares?, config, handler) => voidRegister a task (experimental)
stdio() => Promise<void>Start stdio transport
http(options?: HttpAdapterOptions) => (req: Request) => Promise<Response>Create Web Standard HTTP handler

Tool Context

Passed as the second argument to tool and task handlers.

PropertyTypeDescription
toolNamestringName of the tool being called
sessionSessionSession-scoped state and visibility control
signalAbortSignalAbort signal from the client
sessionIdstringCurrent session ID
elicitElicitRequest information from the user
roots() => Promise<RootInfo[]>Query client-provided filesystem roots
sampleSampleRequest LLM inference from the client

Task handlers receive a TaskContext which extends ToolContext with:

PropertyTypeDescription
task.progress(percentage: number, message?: string) => voidReport progress (0-100)
task.cancelledbooleanTrue when the client has cancelled the task

Session

Available via c.session in all handlers.

MethodSignatureDescription
get<T>(key: string) => T | undefinedGet a session-scoped value
set(key: string, value: unknown) => voidSet a session-scoped value
authorize(middlewareName: string) => voidAuthorize a middleware, enabling guarded tools and resources
revoke(middlewareName: string) => voidRevoke authorization, hiding guarded tools and resources
enableTools(...names: string[]) => voidEnable specific tools by name
disableTools(...names: string[]) => voidDisable specific tools by name
enableResources(...uris: string[]) => voidEnable specific resources by URI
disableResources(...uris: string[]) => voidDisable specific resources by URI

All visibility changes trigger automatic list_changed notifications to the client.

Middleware Hooks

ToolMiddleware objects can implement any combination of these hooks.

HookSignatureDescription
namestringUnique identifier, used for authorize()/revoke()
onRegister(tool: ToolInfo) => boolean | undefinedCalled at registration. Return false to hide initially
onCall(c: ToolContext, next: () => Promise<CallToolResult>) => Promise<CallToolResult>Called on invocation. Must call next() to continue
onResult(result: CallToolResult, c: ToolContext) => CallToolResult | Promise<CallToolResult>Post-handler transform. Runs in reverse middleware order

HTTP Adapter Options

Passed to server.http(options?).

OptionTypeDefaultDescription
sessionlessbooleanfalseNew server+transport per request
sessionIdGenerator() => stringcrypto.randomUUID()Custom session ID generator
enableJsonResponsebooleanfalseReturn JSON instead of SSE streams
onRequest(req, sessionId, session) => void | Promise<void>Called on each request after session is resolved

Elicitation

For concepts and usage patterns, see Elicitation.

Request structured input from the user during tool execution. Two modes: form (structured data) and URL (external redirect).

Form Mode

ts
const result = await c.elicit.form(
  "Choose your preferences",
  z.object({
    theme: z.enum(["light", "dark"]).describe("Color theme"),
    language: z.string().describe("Preferred language"),
  }),
);

if (result.action !== "accept") {
  return c.text("Configuration cancelled.");
}
c.session.set("preferences", result.content);
return c.text(`Saved: ${JSON.stringify(result.content)}`);

c.elicit.form(message, zodSchema) takes positional arguments -- message first, Zod schema second.

Return value: { action: "accept" | "decline" | "cancel", content: z.infer<typeof schema> }

URL Mode

Direct the user to an external URL (OAuth, payment, etc.):

ts
const result = await c.elicit.url(
  "Please authorize with GitHub",
  "https://github.com/login/oauth/authorize?client_id=...",
);

For flows where an external service needs to call back, use waitForCompletion:

ts
const result = await c.elicit.url(
  "Complete payment",
  `https://pay.example.com/checkout?session=${c.sessionId}`,
  { waitForCompletion: true, timeout: 300_000 },
);

The promise resolves when server.completeElicitation(elicitationId) is called from your callback route, or the timeout expires.

URL Options

OptionTypeDefaultDescription
elicitationIdstringRandom UUIDPre-generated ID for callback matching
waitForCompletionbooleanfalseWait for completeElicitation() before resolving
timeoutnumber300000 (5 min)Timeout in ms for waiting
ModeUse case
formStructured data: settings, preferences, confirmations
urlExternal flows: OAuth, payments, document signing

Sampling

For concepts and usage patterns, see Sampling.

Request LLM inference from the client during tool execution. The client decides which model to use.

ts
const sentiment = await c.sample(args.text, {
  system: "Respond with exactly one word: positive, negative, or neutral.",
  maxTokens: 10,
});
return c.text(`Sentiment: ${sentiment}`);

Options

OptionTypeDefaultDescription
maxTokensnumber1024Maximum tokens in the response
modelstringModel hint (client decides)
systemstringSystem prompt
temperaturenumberSampling temperature
stopSequencesstring[]Stop sequences

Raw API

For full control over the SDK's CreateMessageRequestParams:

ts
const result = await c.sample.raw({
  messages: [
    { role: "user", content: { type: "text", text: "Hello" } },
  ],
  maxTokens: 256,
});
// result: CreateMessageResult — full SDK response with content, model, stopReason

Sampling is available in tool handlers and task handlers. Not available in resource handlers.

Tasks

For concepts and usage patterns, see Tasks.

@experimental -- server.task() depends on the MCP SDK's experimental Tasks API.

Tasks are long-running operations with progress reporting and cancellation. Same registration pattern as tools -- same middleware, same visibility system.

ts
server.task(
  "analyze_data",
  {
    description: "Run a slow data analysis",
    input: z.object({ query: z.string() }),
  },
  async (args, c) => {
    c.task.progress(0, "Starting analysis...");
    await new Promise((r) => setTimeout(r, 2000));
    c.task.progress(50, "Halfway...");
    await new Promise((r) => setTimeout(r, 2000));
    c.task.progress(100, "Complete");
    return c.text(`Analysis result for: ${args.query}`);
  },
);

TaskContext

PropertyTypeDescription
task.progress(percentage: number, message?: string) => voidReport progress (0-100) with optional status message
task.cancelledbooleantrue when the client has cancelled this task

Check c.task.cancelled periodically to respect client cancellation. Tasks use the same middleware system as tools -- server.use() applies to tasks, and per-task middleware works inline.

Tasks vs Tools

ToolsTasks
DurationShort (synchronous feel)Long-running
ProgressNoYes (c.task.progress())
CancellationVia c.signal (AbortSignal)Via c.task.cancelled
MiddlewareSameSame
VisibilitySameSame

Entry Points

ImportProvides
@lynq/lynqcreateMCPServer, text, json, error, image, all types
@lynq/lynq/guardguard() visibility gate
@lynq/lynq/credentialscredentials() form-based auth
@lynq/lynq/bearerbearer() Bearer token verification
@lynq/lynq/jwtjwt() JWT verification
@lynq/githubgithub(), handleCallback()
@lynq/googlegoogle(), handleCallback()
@lynq/stripestripe(), handleCallback()
@lynq/cryptocrypto(), handleCallback()
@lynq/lynq/url-actionurlAction() URL-based elicitation
@lynq/lynq/oauthoauth() generic OAuth flow
@lynq/lynq/paymentpayment() payment flow
@lynq/lynq/agent-paymentagentPayment(), verifyOnChain(), verifyViaFacilitator()
@lynq/lynq/loggerlogger() logging
@lynq/lynq/rate-limitrateLimit() rate limiting
@lynq/lynq/truncatetruncate() response truncation
@lynq/lynq/combinesome(), every(), except() combinators
@lynq/lynq/authauth() (deprecated, use guard())
@lynq/lynq/stdioStdioServerTransport re-export
@lynq/honomountLynq for Hono
@lynq/expressmountLynq for Express
@lynq/lynq/testcreateTestClient, matchers