Skip to content

MCP — Expose (library)

Embedding the MCP server inside another Node app — createMcpServer({ gadgets, skills }).

For the CLI command, see llmist mcp serve.

import { createGadget, createMcpServer, GadgetRegistry, z } from "llmist";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const calc = createGadget({
name: "Calculator",
description: "Adds two numbers",
schema: z.object({ a: z.number(), b: z.number() }),
execute: ({ a, b }) => String(a + b),
});
const registry = new GadgetRegistry();
registry.registerByClass(calc);
const handle = createMcpServer({ gadgets: registry });
const transport = new StdioServerTransport();
await handle.connect(transport);

The handle stays alive until you call handle.stop() or the transport closes.

interface CreateMcpServerOptions {
gadgets: GadgetRegistry; // Native gadgets exposed as MCP tools
skills?: SkillRegistry; // Native skills exposed as MCP prompts (optional)
protocolVersion?: string; // Default: 2025-06-18
serverInfo?: { name: string; version: string };
}

Returns an McpServerHandle:

interface McpServerHandle {
connect(transport: Transport): Promise<void>;
stop(): Promise<void>;
readonly running: boolean;
}

Exports the gadget’s name, description, and JSON-Schema input descriptor. Available as a building block for callers wiring their own server (e.g. behind an HTTP gateway).

Runs the gadget against the supplied arguments, validating against its Zod schema and returning an MCP-shaped { content, isError? } result. Used internally by createMcpServer.

skillToMcpPrompt(skill) and renderSkillForMcpPrompt(skill, args)

Section titled “skillToMcpPrompt(skill) and renderSkillForMcpPrompt(skill, args)”

The prompt analogs.

ConditionAdvertised
gadgets non-emptytools
skills non-emptyprompts

Resources, sampling, and elicitation are not advertised — scoped for follow-up specs.

The handle’s lifecycle is independent of any signal handlers. If you embed the server in a long-running process, install your own SIGTERM/SIGINT handlers that call handle.stop(). The CLI’s mcp serve does this for you.

The exported createMcpServer works seamlessly with the SDK’s InMemoryTransport.createLinkedPair() for in-process tests. See packages/llmist/src/mcp/server.test.ts for an example.

llmist conceptMCP primitiveMapping
Gadget nametool nameidentity
Gadget descriptiontool descriptionfalls back to a synthesized string when empty
parameterSchema (Zod)inputSchema (JSON Schema)via existing schemaToJSONSchema
Gadget execute return stringtool result content[] with one text blockidentity
Gadget execute return { result, media }text block + per-media blockimage/audio passed through with mime type
Gadget thrown errortool result with isError: trueerror message goes in the text block
Skill nameprompt nameidentity
Skill descriptionprompt descriptionfalls back to synthesized when empty
argumentHintsingle optional arguments parameterthe existing $ARGUMENTS / $0 substitution kicks in at render time
Skill bodyprompts/get response — single user text messageactivated through Skill.activate() so substitution and shell preprocessing all run