mCP 2.0
MCP sin estado, aplicación con estado
Un proyecto mínimo de JavaScript que demuestra los principales patrones de solicitud/respuesta de MCP 2026-07-28 con el SDK TypeScript de MCP v2.
La demo cubre:
Solicitudes HTTP sin estado, sin
Mcp-Session-Id.Estado gestionado por la aplicación y direccionado mediante identificadores explícitos.
Solicitudes de varios viajes de ida y vuelta para la confirmación del usuario.
Actualizaciones de progreso limitadas a una operación individual.
Descubrimiento de herramientas almacenable en caché con TTL y ámbito de compartición.
Cómo ejecutarlo
Requiere Node.js 20 o posterior.
npm install
npm run demoEl comando inicia un servidor en un puerto local disponible, recorre todos los escenarios, imprime los resultados y apaga el servidor. El ejemplo de eliminación usa un conjunto de archivos virtuales en memoria y nunca toca archivos en el disco.
Ejecuta las pruebas de integración con:
npm testPara dejar el servidor en ejecución para otro cliente MCP:
npm run serverEl endpoint es http://127.0.0.1:3000/mcp. Define PORT para cambiar el puerto.
Related MCP server: MCP RC Long-Running Task Prototype
1. Solicitudes sin estado
MCP 2026-07-28 elimina las sesiones HTTP a nivel de protocolo. Este proyecto crea un McpServer nuevo para cada solicitud, de modo que cualquier solicitud puede llegar a cualquier instancia del servidor:
const mcpHandler = createMcpHandler(
() => createStateServer(demoFiles),
{
legacy: "reject",
onerror: (error) => console.error("MCP error:", error),
},
);El cliente selecciona explícitamente la revisión moderna del protocolo:
const client = new Client(
{ name: "state-demo-client", version: "0.1.0" },
{
capabilities: { elicitation: { form: {} } },
versionNegotiation: {
mode: { pin: "2026-07-28" },
},
},
);Por tanto, el estado almacenado dentro de una instancia de McpServer desaparece después de esa solicitud. Llamar dos veces al contador efímero de la demo produce 1 desde dos instancias de servidor diferentes.
2. Datos de aplicación con estado
Un MCP sin estado no requiere una aplicación sin estado. El estado duradero pertenece fuera de la instancia de MCP de cada solicitud y se selecciona mediante un identificador explícito:
const countersById = new Map();
server.registerTool(
"create-counter",
{
outputSchema: z.object({ counterId: z.uuid(), value: z.number() }),
},
async () => {
const counterId = randomUUID();
countersById.set(counterId, 0);
const structuredContent = { counterId, value: 0 };
return {
content: [{ type: "text", text: JSON.stringify(structuredContent) }],
structuredContent,
};
},
);El cliente transporta el identificador entre llamadas que por lo demás son independientes:
const created = await client.callTool({ name: "create-counter" });
const { counterId } = created.structuredContent;
await client.callTool({
name: "increment-counter",
arguments: { counterId },
});El Map en memoria es solo un marcador provisional. Un servidor de producción debería usar una base de datos o un almacén compartido, vincular los identificadores al principal autenticado y aplicar autorización y caducidad en cada búsqueda.
3. Confirmación de varios viajes de ida y vuelta
Una herramienta que necesita más información devuelve input_required. El cliente obtiene la respuesta e repite la solicitud original con inputResponses, de modo que el servidor no inicia una segunda petición JSON-RPC en el flujo de operaciones.
server.registerTool(
"delete-files",
{
inputSchema: z.object({ files: z.array(z.string()).min(1) }),
annotations: { destructiveHint: true },
},
async ({ files }, ctx) => {
const confirmation = acceptedContent(
ctx.mcpReq.inputResponses,
"confirm",
confirmationSchema,
);
if (confirmation === undefined) {
return inputRequired({
inputRequests: {
confirm: inputRequired.elicit({
message: `Delete ${files.length} virtual files?`,
requestedSchema: confirmationSchema,
}),
},
});
}
if (!confirmation.confirm) {
return {
content: [{ type: "text", text: "Cancelled" }],
structuredContent: { status: "cancelled", deleted: [] },
};
}
const deleted = files.filter((file) => demoFiles.delete(file));
return {
content: [{ type: "text", text: `Deleted: ${deleted.join(", ")}` }],
structuredContent: { status: "deleted", deleted },
};
},
);El SDK puede atender la solicitud y reintentarla automáticamente mediante el controlador normal de elicitación:
client.setRequestHandler("elicitation/create", async (request) => {
const confirm = await askUser(request.params.message);
return {
action: "accept",
content: { confirm },
};
});La confirmación es una experiencia de usuario, no una autorización. El servidor debe seguir autenticando al llamador y hacer cumplir de forma independiente el permiso para eliminar archivos.
4. Progreso con ámbito de solicitud
El progreso permanece asociado a la solicitud que inició el trabajo. Las operaciones concurrentes reciben solo sus propias actualizaciones en lugar de compartir un único flujo global de eventos.
server.registerTool(
"run-work",
{ inputSchema: z.object({ job: z.string() }) },
async ({ job }, ctx) => {
const progressToken = ctx.mcpReq._meta?.progressToken;
for (const progress of [10, 30, 70]) {
if (progressToken !== undefined) {
await ctx.mcpReq.notify({
method: "notifications/progress",
params: {
progressToken,
progress,
total: 100,
message: `${job}: ${progress}%`,
},
});
}
}
return {
content: [{ type: "text", text: `${job}: complete` }],
structuredContent: { job, status: "complete" },
};
},
);Cada llamada del cliente proporciona su propia función de retorno de progreso:
await Promise.all([
client.callTool(
{ name: "run-work", arguments: { job: "alpha" } },
{ onprogress: (update) => alphaProgress.push(update) },
),
client.callTool(
{ name: "run-work", arguments: { job: "beta" } },
{ onprogress: (update) => betaProgress.push(update) },
),
]);5. Capacidad de almacenamiento en caché
Las respuestas almacenables en caché incluyen una duración de frescura y una política de compartición. Esto reduce el tráfico repetido de descubrimiento cuando un agente se conecta a muchos servidores MCP.
Este servidor marca su catálogo de herramientas como reutilizable durante cinco minutos:
const server = new McpServer(
{ name: "mcp-state-demo", version: "0.1.0" },
{
cacheHints: {
"tools/list": {
ttlMs: 300_000,
cacheScope: "public",
},
},
},
);El cliente usa las entradas frescas automáticamente:
await client.listTools(); // network request; stores the result
await client.listTools(); // cache hit; no network request
await client.listTools(undefined, {
cacheMode: "refresh",
}); // forces a network request and updates the cacheLos campos se aplican a los resultados de tools/list, prompts/list, resources/list, resources/templates/list y resources/read.
publicpermite que clientes e intermediarios compartidos reutilicen el resultado entre usuarios.privaterestringe la reutilización al contexto de autorización de la solicitud. Cuando un almacén de caché esté compartido, define elcachePartitiondel cliente como un identificador estable del principal.
Un TTL es una estimación de frescura. Las notificaciones de cambios en las listas pueden invalidar catálogos almacenados en caché antes de que expire su TTL.
Estructura del proyecto
src/server.js MCP server and tool implementations
src/demo.js Client exercising all five scenarios
test/state.test.js Integration tests for every scenarioLos paquetes MCP están fijados en 2.0.0, incluidas las API de inputRequired, acceptedContent, de caché y de progreso con ámbito de solicitud que se usan aquí.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA reference implementation demonstrating proper MCP server patterns with HTTP transport, featuring session management, progress notifications, and example tools for testing server functionality. Serves as a clean template for building MCP servers with streamable responses and comprehensive error handling.7
- FlicenseNot gradedqualityCmaintenanceDemonstrates MCP 2026-07-28 behavior for long-running tool calls, task lifecycle (get, update, cancel), and elicitation clarification during async tasks.
- AlicenseNot gradedqualityCmaintenanceEducational MCP server demonstrating the 2026-07-28 stateless protocol with raw Starlette, no SDK, featuring tools, request state handles, MRTR elicitation, and subscriptions.MIT
- AlicenseNot gradedqualityBmaintenanceDemo MCP server for ACEL, a runtime verification middleware that blocks a rule-violating tool call before it executes. 5 tools (authenticate, read/validate/delete records, send payment) showing ACEL enforcing call ordering and state preconditions live via the official MCP SDK's middleware hook.MIT
Related MCP Connectors
AI Reasoning Cache & Consensus Layer with 11 MCP tools via Streamable HTTP.
Hash-chained HMAC-signed audit log MCP for A2A (agent-to-agent) calls. Every tool-call, agent-ha...
Workflow diagnostics, capability routing, and x402 settlement for MCP-compatible agents.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/weijianzhg/mcp-2-0'
If you have feedback or need assistance with the MCP directory API, please join our Discord server