Un framework TypeScript pour créer des MCP server stateless, déployables dans des environnements serverless et edge.
Aussi connu sous vitemcp/server, @vitemcp/server
- Étoiles
- 22
- Outils
- 1
- Téléchargements, 30 jours
- —
- Dernière version
- —
- Dernier commit
- 11 nov. 2026
- Licence
- MIT
Qu'est-ce que ViteMCP MCP ?
ViteMCP est un serveur MCP. Un framework TypeScript pour créer des MCP server stateless, déployables dans des environnements serverless et edge. Il expose 1 outil, comme title.
Résumé généré à partir du README du projet
À propos du MCP server ViteMCP
ViteMCP est un framework TypeScript conçu pour les développeurs qui doivent construire des MCP server stateless. Il cible spécifiquement la révision du protocole du 2026-07-28, qui supprime la nécessité d'un handshake d'initialisation ou d'identifiants de session. Cette architecture rend le framework idéal pour un déploiement dans des environnements serverless et edge comme Cloudflare Workers ou Deno Deploy, car chaque requête est autonome.
Le framework simplifie le processus de développement en gérant les tâches répétitives telles que la gestion des connexions, le traitement des outils et le formatage des réponses. Les développeurs peuvent définir des outils, des ressources et des prompts en utilisant la spécification Standard Schema, permettant ainsi une intégration avec des bibliothèques comme Zod, ArkType ou Valibot. Il permet également de générer des serveurs entiers à partir de documents OpenAPI et offre un support intégré pour le contenu image et audio. Pour les tests, il inclut un transport en mémoire et un CLI pour inspecter et déboguer les fonctionnalités du serveur sans avoir à se lier à un port local.
Ce que vous pouvez faire avec
- 1Automatisation de la création de serveurs à partir d'APIVous pouvez rapidement construire un serveur complet en fournissant un document OpenAPI existant.
Extrait du README du projet. Chaque cas d'utilisation est vérifié par rapport à une citation du document.
Comment installer ViteMCP MCP
Formats vérifiés le 10 nov. 2026Ajouter à CursorAjouter à GooseInstaller ViteMCP MCP dans Claude Code
$ claude mcp add server -- npx -y @vitemcp/serverInstaller ViteMCP MCP dans Cursor
~/.cursor/mcp.json
{
"mcpServers": {
"server": {
"command": "npx",
"args": [
"-y",
"@vitemcp/server"
]
}
}
}Installer ViteMCP MCP dans VS Code
.vscode/mcp.json
{
"servers": {
"server": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@vitemcp/server"
]
}
}
}Installer ViteMCP MCP dans VS Code CLI
$ code --add-mcp '{"name":"server","type":"stdio","command":"npx","args":["-y","@vitemcp/server"]}'Installer ViteMCP MCP dans Claude Desktop
claude_desktop_config.json
{
"mcpServers": {
"server": {
"command": "npx",
"args": [
"-y",
"@vitemcp/server"
]
}
}
}Installer ViteMCP MCP dans Codex
$ codex mcp add server -- npx -y @vitemcp/serverInstaller ViteMCP MCP dans Codex config.toml
~/.codex/config.toml
[mcp_servers.server] command = "npx" args = ["-y", "@vitemcp/server"]
Installer ViteMCP MCP dans Gemini CLI
$ gemini mcp add -s user server npx -- -y @vitemcp/serverInstaller ViteMCP MCP dans Gemini settings.json
~/.gemini/settings.json
{
"mcpServers": {
"server": {
"command": "npx",
"args": [
"-y",
"@vitemcp/server"
]
}
}
}Outils MCP ViteMCP 1
Détectés depuis le READMEtitle
| title | — |
Configuration
YOUR_ENV_VAR
| Nom | Description | Type |
|---|---|---|
| YOUR_ENV_VAR | — |
Vérifications automatisées
Exécuté le 12 nov. 2026Rapport complet →A passé nos vérifications automatisées. Il ne s'agit pas d'un audit de sécurité : lisez le code et donnez au serveur l'accès le plus restreint dont il a besoin. Fonctionnement des vérifications
- Dépôt source lié et accessibleRéussi
- Licence open source (OSI)Réussi
- Commit ou version publiée au cours de l'année écouléeRéussi
- Nom du package non proche d'un package populaireRéussi
- L'endpoint distant utilise HTTPSNon vérifié
- Les outils destructifs sont étiquetésRéussi
- Aucune instruction cachée destinée au modèleRéussi
- Aucun avis critique dans le package ou ses dépendancesNon vérifié
README
Voir sur GitHubViteMCP
A TypeScript framework for building stateless MCP servers.
[!IMPORTANT]
ViteMCP targets MCP revision 2026-07-28, which made the protocol stateless. There is no
initializehandshake and noMcp-Session-Id: every request is self-contained and carries its own protocol version and client capabilities.If you are coming from a session-based version, see Migrating from the session-based API.
Contents
- Features
- When to use ViteMCP over the official SDK?
- Installation
- Quickstart
- Core Concepts
- Server Features
- Deployment
- Testing and Debugging
- FAQ
- Showcase
- Acknowledgements
Authentication has a dedicated reference — see the OAuth guide. So does OpenAPI conversion — see the OpenAPI guide.
Features
- Simple Tool, Resource, Prompt definition
- Authentication
- Per-request auth context
- Image content
- Audio content
- Embedded
- Error handling
- HTTP Streaming
- HTTPS Support for secure connections
- Custom HTTP routes for REST APIs, webhooks, and admin interfaces
- Edge Runtime Support for Cloudflare Workers, Deno Deploy, and more
- Stateless by construction — every request stands alone, so serverless just works
- CORS (enabled by default)
- Progress notifications
- Multi round-trip requests for asking the client for more input
- Prompt argument auto-completion
- Cacheable list results (
ttlMs/cacheScope) - Health-check endpoint
- Generate a whole server from an OpenAPI document
- In-memory transport for unit testing without binding a port
- CLI for testing and debugging
When to use ViteMCP over the official SDK?
ViteMCP is built on top of the official SDK.
The official SDK provides foundational blocks for building MCPs, but leaves many implementation details to you — all of which src/ViteMCP.ts handles on your behalf:
- Initiating and configuring all the server components
- Handling of connections
- Handling of tools
- Handling of responses
- Handling of resources
- Adding prompts, resources and resource templates
- Embedding resource, image and audio content blocks
ViteMCP eliminates this complexity by providing an opinionated framework that:
- Handles all the boilerplate automatically
- Provides simple, intuitive APIs for common tasks
- Includes built-in best practices and error handling
- Lets you focus on your MCP's core functionality
When to choose ViteMCP: You want to build MCP servers quickly without dealing with low-level implementation details.
When to use the official SDK: You need maximum control or have specific architectural requirements. In this case, we encourage referencing ViteMCP's implementation to avoid common pitfalls.
Installation
npm install @vitemcp/server
Quickstart
[!NOTE]
There are many real-world examples of using ViteMCP in the wild. See the Showcase for examples.
import { ViteMCP } from "@vitemcp/server";
import { z } from "zod"; // Or any validation library that supports Standard Schema
const server = new ViteMCP({
name: "My Server",
version: "1.0.0",
});
server.addTool({
name: "add",
description: "Add two numbers",
parameters: z.object({
a: z.number(),
b: z.number(),
}),
execute: async (args) => {
return String(args.a + args.b);
},
});
server.start({
transportType: "stdio",
});
That's it! You have a working MCP server.
You can test the server in terminal with:
git clone https://github.com/vitemcp/server.git
cd server
pnpm install
pnpm build
# Test the addition server example using CLI:
npx @vitemcp/server dev src/examples/addition.ts
# Test the addition server example using MCP Inspector:
npx @vitemcp/server inspect src/examples/addition.ts
If you are looking for something to start from, src/examples/ has runnable servers covering tools, authentication, custom routes and edge deployment.
Core Concepts
Tools
Tools in MCP allow servers to expose executable functions that can be invoked by clients and used by LLMs to perform actions.
ViteMCP uses the Standard Schema specification for defining tool parameters. This allows you to use your preferred schema validation library (like Zod, ArkType, or Valibot) as long as it implements the spec.
Zod Example:
import { z } from "zod";
server.addTool({
name: "fetch-zod",
description: "Fetch the content of a url (using Zod)",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return await fetchWebpageContent(args.url);
},
});
ArkType Example:
import { type } from "arktype";
server.addTool({
name: "fetch-arktype",
description: "Fetch the content of a url (using ArkType)",
parameters: type({
url: "string",
}),
execute: async (args) => {
return await fetchWebpageContent(args.url);
},
});
Valibot Example:
Valibot requires the peer dependency @valibot/to-json-schema.
import * as v from "valibot";
server.addTool({
name: "fetch-valibot",
description: "Fetch the content of a url (using Valibot)",
parameters: v.object({
url: v.string(),
}),
execute: async (args) => {
return await fetchWebpageContent(args.url);
},
});
Plain JSON Schema Example:
If you already have a JSON Schema — from an OpenAPI document, a config file, or
another server — jsonSchemaAdapter wraps it so it can be used directly, with
no schema library in between.
It requires the peer dependency ajv, which does the validation, plus
ajv-formats if your schema uses format keywords such as email or uri.
Both are imported the first time a tool is called, so servers that don't use
this pay nothing for it.
npm install ajv ajv-formats
import { jsonSchemaAdapter } from "@vitemcp/server";
server.addTool({
name: "fetch-json-schema",
description: "Fetch the content of a url (using plain JSON Schema)",
parameters: jsonSchemaAdapter({
type: "object",
properties: {
url: { type: "string", format: "uri" },
},
required: ["url"],
}),
execute: async (args) => {
const { url } = args as { url: string };
return await fetchWebpageContent(url);
},
});
Works for outputSchema too, and is advertised as written — as are Zod and
ArkType schemas, which emit their own JSON Schema. Where one of those holds
something JSON Schema cannot express — a z.date(), a z.bigint(), an ArkType
narrow() — that part alone is advertised as open, as {} or as the type a
narrow() narrows, and the library still enforces it. A schema from any other
library (Valibot, for one) is converted to JSON Schema for tools/list only;
arguments and results are still validated by the library itself, so a
v.transform() reaches execute and a v.check() is enforced even though
JSON Schema can express neither. Converted input schemas get
additionalProperties: false on every object, except where a record or rest
schema (v.record(), v.objectWithRest()) describes the extra keys, or where
the keys are declared in the branches of a v.union(), v.variant() or
v.intersect(), or behind the $ref a v.lazy() becomes; converted output
schemas are left as they are. A tool whose schema cannot be converted at all,
or whose parameters do not describe an object, is left out of the server and
reported through the logger.
A plain JSON Schema is validated in the dialect its $schema declares —
2020-12, 2019-09, draft-07 or draft-06 — and as 2020-12 when it declares none,
which is how MCP clients read it.
Unlike the schema libraries above, a plain JSON Schema carries no TypeScript
types, so execute receives unknown arguments. Cast or narrow them yourself.
Tools Without Parameters
When creating tools that don't require parameters, you have two options:
-
Omit the parameters property entirely:
server.addTool({ name: "sayHello", description: "Say hello", // No parameters property execute: async () => { return "Hello, world!"; }, }); -
Explicitly define empty parameters:
import { z } from "zod"; server.addTool({ name: "sayHello", description: "Say hello", parameters: z.object({}), // Empty object execute: async () => { return "Hello, world!"; }, });
[!NOTE]
Both approaches are fully compatible with all MCP clients, including Cursor. ViteMCP automatically generates the proper schema in both cases.
Structured Tool Output
Tools can declare an outputSchema and return structured data. ViteMCP exposes that value as MCP structuredContent, while also returning a JSON text fallback for clients that only render text content.
server.addTool({
name: "get-weather",
description: "Get weather for a city",
parameters: z.object({
city: z.string(),
}),
outputSchema: z.object({
temperature: z.number(),
humidity: z.number(),
}),
execute: async ({ city }) => {
const weather = await getWeather(city);
return {
temperature: weather.temperature,
humidity: weather.humidity,
};
},
});
You can also return explicit text content and structured content together:
server.addTool({
name: "get-weather",
description: "Get weather for a city",
parameters: z.object({
city: z.string(),
}),
outputSchema: z.object({
temperature: z.number(),
humidity: z.number(),
}),
execute: async ({ city }) => {
const weather = await getWeather(city);
return {
content: [
{
type: "text",
text: `${city}: ${weather.temperature}F`,
},
],
structuredContent: {
temperature: weather.temperature,
humidity: weather.humidity,
},
};
},
});
When outputSchema is provided, ViteMCP validates structuredContent before sending the tool result. Invalid structured output is returned to the client as a tool error instead of silently violating the advertised schema.
ViteMCP tells a structured payload apart from MCP content blocks by shape, so a payload that looks like one of the other shapes needs saying outright. Return it under structuredContent and there is nothing left to infer — the JSON text fallback is still written for you:
server.addTool({
name: "get-document",
parameters: z.object({ id: z.string() }),
outputSchema: z.object({
title: z.string(),
content: z.array(z.string()),
}),
execute: async ({ id }) => {
const doc = await getDocument(id);
return {
structuredContent: { title: doc.title, content: doc.paragraphs },
};
},
});
Three payload shapes need it: an array-valued content field (as above), a resultType of "input_required", and a lone structuredContent field of your own. Everything else is inferred correctly — including a payload that has a structuredContent field among others. In particular a top-level type field is safe — including type: "text" and the other content-block kinds — because a declared outputSchema turns off the single-content-block shorthand.
The wrapper must stand alone: { structuredContent }, optionally with isError or _meta. An object carrying anything else is read as a payload in its own right, so keys execute meant to keep never reach the client.
Restricting who can call a tool
A tool's optional canAccess receives the request's auth context and returns whether the caller may use it. Tools it rejects are filtered out of tools/list entirely. A caller who may use none of them gets an empty list rather than an error.
server.addTool({
name: "admin-tool",
description: "An admin-only tool",
canAccess: (auth) => auth?.role === "admin",
execute: async () => "Welcome, admin!",
});
Built-in helpers — requireAuth, requireScopes, requireRole, requireAll, requireAny — cover the usual cases; see Tool Authorization.
Return types
execute may return a plain string, a content object, or an array of content
blocks. A bare string is shorthand for a single text block — these two are
equivalent:
execute: async () => "Hello, world!";
execute: async () => ({ content: [{ type: "text", text: "Hello, world!" }] });
| Return value | Produces |
|---|---|
"some string" |
one text block |
{ content: [...] } |
any mix of text, image, audio and resource blocks |
imageContent({ ... }) |
one image block |
audioContent({ ... }) |
one audio block |
imageContent and audioContent build a block from a url, a path, or a
buffer — exactly one of the three. Both accept timeoutMs to bound a URL
download (30 seconds by default).
import { audioContent, imageContent } from "@vitemcp/server";
server.addTool({
name: "fetch-image",
description: "Fetch an image",
parameters: z.object({ url: z.string() }),
execute: async (args) => imageContent({ url: args.url }),
// ...or imageContent({ path: "/path/to/image.png" })
// ...or imageContent({ buffer: Buffer.from(base64Png, "base64") })
});
Each helper returns a single block, so combine them under content to send
more than one:
execute: async () => ({
content: [
{ type: "text", text: "Here is what I found:" },
await imageContent({ url: "https://example.com/image.png" }),
await audioContent({ url: "https://example.com/audio.mp3" }),
],
});
Raw blocks work too when you already hold base64 data:
execute: async () => ({
content: [
{ type: "image", data: base64Png, mimeType: "image/png" },
{ type: "audio", data: base64Mp3, mimeType: "audio/mpeg" },
],
});
Tool Annotations
Tools can include annotations that provide richer context and control by adding metadata about a tool's behavior:
server.addTool({
name: "fetch-content",
description: "Fetch content from a URL",
parameters: z.object({
url: z.string(),
}),
annotations: {
title: "Web Content Fetcher", // Human-readable title for UI display
readOnlyHint: true, // Tool doesn't modify its environment
openWorldHint: true, // Tool interacts with external entities
},
execute: async (args) => {
return await fetchWebpageContent(args.url);
},
});
The available annotations are:
| Annotation | Type | Default | Description |
|---|---|---|---|
title |
string | - | A human-readable title for the tool, useful for UI display |
readOnlyHint |
boolean | false |
If true, indicates the tool does not modify its environment |
destructiveHint |
boolean | true |
If true, the tool may perform destructive updates (only meaningful when readOnlyHint is false) |
idempotentHint |
boolean | false |
If true, calling the tool repeatedly with the same arguments has no additional effect (only meaningful when readOnlyHint is false) |
openWorldHint |
boolean | true |
If true, the tool may interact with an "open world" of external entities |
These annotations help clients and LLMs better understand how to use the tools and what to expect when calling them.
Resources
Resources represent any kind of data that an MCP server wants to make available to clients. This can include:
- File contents
- Screenshots and images
- Log files
- And more
Each resource is identified by a unique URI and can contain either text or binary data.
server.addResource({
uri: "file:///logs/app.log",
name: "Application Logs",
mimeType: "text/plain",
async load() {
return {
text: await readLogFile(),
};
},
});
[!NOTE]
loadcan return multiple resources. This could be used, for example, to return a list of files inside a directory when the directory is read.async load() { return [ { text: "First file content", }, { text: "Second file content", }, ]; }
You can also return binary contents in load:
async load() {
return {
blob: 'base64-encoded-data'
};
}
load receives a context object mirroring what tool.execute gets — auth, log, requestId and the multi-round-trip helpers. reportProgress is not included, since it is tied to a tool call's progress token:
server.addResource({
uri: "file:///logs/app.log",
name: "Application Logs",
mimeType: "text/plain",
async load(context) {
context.log.info("loading application logs", { requestedBy: auth?.userId });
return {
text: await readLogFile(),
};
},
});
Resource templates
You can also define resource templates:
server.addResourceTemplate({
uriTemplate: "file:///logs/{name}.log",
name: "Application Logs",
mimeType: "text/plain",
arguments: [
{
name: "name",
description: "Name of the log",
required: true,
},
],
async load({ name }) {
return {
text: `Example log content for ${name}`,
};
},
});
Like plain resources, load also receives auth and context as its second and third arguments (see Resources).
Resource template argument auto-completion
Provide complete functions for resource template arguments to enable automatic completion:
server.addResourceTemplate({
uriTemplate: "file:///logs/{name}.log",
name: "Application Logs",
mimeType: "text/plain",
arguments: [
{
name: "name",
description: "Name of the log",
required: true,
complete: async (value) => {
if (value === "Example") {
return {
values: ["Example Log"],
};
}
return {
values: [],
};
},
},
],
async load({ name }) {
return {
text: `Example log content for ${name}`,
};
},
});
Embedded Resources
ViteMCP provides a convenient embedded() method that simplifies including resources in tool responses. This feature reduces code duplication and makes it easier to reference resources from within tools.
Basic Usage
server.addTool({
name: "get_user_data",
description: "Retrieve user information",
parameters: z.object({
userId: z.string(),
}),
execute: async (args) => {
return {
content: [
{
type: "resource",
resource: await server.embedded(`user://profile/${args.userId}`),
},
],
};
},
});
Working with Resource Templates
The embedded() method works seamlessly with resource templates. A URI reaches
the same template, with the same arguments, as it would through
resources/read:
// Define a resource template
server.addResourceTemplate({
uriTemplate: "docs://project/{section}",
name: "Project Documentation",
mimeType: "text/markdown",
arguments: [
{
name: "section",
required: true,
},
],
async load(args) {
const docs = {
"getting-started": "# Getting Started\n\nWelcome to our project!",
"api-reference": "# API Reference\n\nAuthentication is required.",
};
return {
text: docs[args.section] || "Documentation not found",
};
},
});
// Use embedded resources in a tool
server.addTool({
name: "get_documentation",
description: "Retrieve project documentation",
parameters: z.object({
section: z.enum(["getting-started", "api-reference"]),
}),
execute: async (args) => {
return {
content: [
{
type: "resource",
resource: await server.embedded(`docs://project/${args.section}`),
},
],
};
},
});
Working with Direct Resources
It also works with directly defined resources:
// Define a direct resource
server.addResource({
uri: "system://status",
name: "System Status",
mimeType: "text/plain",
async load() {
return {
text: "System operational",
};
},
});
// Use in a tool
server.addTool({
name: "get_system_status",
description: "Get current system status",
parameters: z.object({}),
execute: async () => {
return {
content: [
{
type: "resource",
resource: await server.embedded("system://status"),
},
],
};
},
});
Questions sur ViteMCP MCP
Comment ajouter ViteMCP MCP à Claude Code ?
Exécutez claude mcp add server -- npx -y @vitemcp/server dans votre terminal. Ajoutez --scope user pour le rendre disponible dans tous les projets.
Comment ajouter ViteMCP MCP à Cursor ?
Ouvrez ~/.cursor/mcp.json et ajoutez le serveur sous mcpServers, avec la configuration Cursor indiquée dans la section d'installation ci-dessus.
Quels outils ViteMCP MCP fournit-il ?
Il expose 1 outil, dont title.
Comment l'utiliser avec Claude Desktop ?
Le README contient une section FAQ spécifique pour Claude Desktop, bien que la réponse détaillée ne soit pas fournie dans l'extrait.
Puis-je générer un serveur à partir d'un document OpenAPI ?
Oui, ViteMCP vous permet de générer un serveur complet à partir d'un document OpenAPI.
Qu'est-ce qui différencie ViteMCP du SDK officiel ?
ViteMCP est un framework structuré construit sur le SDK officiel qui gère le code répétitif (boilerplate) tel que la gestion des connexions, le traitement des outils et l'intégration des ressources.