MCP Template
Enables protecting the MCP server with OAuth 2.0 bearer tokens issued by Auth0, including token introspection and audience validation.
Enables protecting the MCP server with OAuth 2.0 bearer tokens issued by Keycloak, including token introspection and audience validation.
Enables protecting the MCP server with OAuth 2.0 bearer tokens issued by Okta, including token introspection and audience validation.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@MCP Templategreet John"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
MCP Template
Production-ready Model Context Protocol server template in TypeScript β MCP 2026-07-28, SOLID architecture, dynamic tool registration, dual transport (stdio + HTTP), and pluggable authentication.
Clone it, drop a file in src/tools/, and you have a new tool. No registry to edit, no boilerplate to wire.
Highlights
π MCP 2026-07-28 β stateless core,
server/discover, cache hints, and multi-round-trip tools, on the v2 TypeScript SDK. Pre-2026 clients keep working on the same endpoint.π Drop-in tools β create
src/tools/<name>.tool.tsand it's auto-discovered and registered at startup.π§± SOLID by design β clear seams between config, transport, registry, auth, and tools; dependencies injected, never reached for globally.
π¦ Two transports β
stdiofor local clients (Claude Desktop, Claude Code) and Streamable HTTP for remote deployments, from the same code.π API key or OAuth 2.0 β a shared secret for simple deployments, or a full RFC 9728 resource server for enterprise IdPs (Entra, Okta), selected by env.
πΌοΈ MCP Apps β tools can render an interactive UI panel in the conversation, with a text fallback for hosts that don't support it.
β Quality baked in β strict TypeScript, Zod validation, structured logging (pino), Vitest, ESLint + Prettier, GitHub Actions, Docker.
Related MCP server: MCP Server Starter
Quick start
npm install
cp .env.example .env
npm run dev # stdio transport, hot reload
# or
TRANSPORT=http npm run devBuild and run for production:
npm run build
npm startCreating a tool
This is the whole workflow. Create a file ending in .tool.ts under src/tools/:
// src/tools/greet.tool.ts
import { z } from "zod";
import { defineTool } from "../core/tool.js";
export default defineTool({
name: "greet",
description: "Greets a person by name.",
inputSchema: z.object({
name: z.string().min(1).describe("Who to greet"),
}),
outputSchema: z.object({
greeting: z.string(),
}),
handler: ({ name }, { logger }) => {
logger.debug({ name }, "greeting");
return {
content: [{ type: "text", text: `Hello, ${name}!` }],
structuredContent: { greeting: `Hello, ${name}!` },
};
},
});Restart the server β greet is live. The args are fully typed from inputSchema, and the second argument is the injected ToolContext (config, logger, httpClient) plus mcp, the per-request MCP context.
A few things worth knowing:
Schemas are Standard Schema values, so Zod is the default but ArkType or Valibot work unchanged.
outputSchemais optional, but once declared every non-error result must carrystructuredContent.Throwing a
ToolErrorreturns a proper error result to the model rather than aborting the call with a protocol error.Tools are registered in sorted order, because 2026-07-28 asks
tools/listto be stable so clients and prompt caches can rely on it.
Need an external API? Use the injected client instead of fetch:
const data = await httpClient.get(`/resources/${id}`);The bundled get-current-weather.tool.ts and get-forecast.tool.ts are working references: they consult the free Open-Meteo API (no key needed) through the injected client and a dedicated WeatherService.
Configuration
All config is validated at startup in src/config/env.ts. See .env.example for every supported variable and its defaults.
Protocol versions and compatibility
This server speaks MCP 2026-07-28, which is a breaking redesign rather than an increment: there is no initialize handshake, no Mcp-Session-Id, and every request carries its protocol version and client capabilities in _meta. The server answers the new mandatory server/discover RPC, and the SDK stamps resultType and the _meta server-identity envelope on every response.
Statelessness is the headline. A fresh server instance is built per request, so there is nothing to keep in memory between calls and the process fits serverless and edge runtimes. Anything that must survive across calls travels in explicit, server-signed handles (see multi-round-trip tools below).
Older clients still work. LEGACY_MODE=stateless (the default) serves pre-2026 clients from the same endpoint and the same server factory, so today's hosts keep working while you develop against the new protocol. Set LEGACY_MODE=reject to accept only 2026 clients.
Two consequences worth remembering:
pingno longer exists β use the/healthHTTP route for liveness checks.Roots, Sampling and Logging are deprecated (SEP-2577). This server logs to stderr, which is the recommended replacement.
Inspecting the server
npm run inspect # stdio, negotiates 2026-07-28
npm run inspect -- http://localhost:3000/mcp
npm run inspect -- --legacy # force the pre-2026 pathscripts/inspect.ts connects with the v2 client and prints the negotiated protocol era, the tools (flagging which have a UI), the resources, and a sample call.
Using the official MCP Inspector
Inspector 2.x is built on the v2 client and speaks 2026-07-28 β but it connects on the legacy path by default, because the SDK's own default is versionNegotiation: { mode: "legacy" }. Modern is opt-in. Point it at a config that pins the era:
// inspector.json
{
"mcpServers": {
"mcp-template": {
"type": "http",
"url": "http://127.0.0.1:3000/mcp",
"protocolEra": "modern", // "legacy" (default) | "auto" | "modern"
},
},
}npx @modelcontextprotocol/inspector --config ./inspector.json --server mcp-templateWithout protocolEra, you are testing the compatibility path: server/discover, the _meta envelope and resultType never come into play, and multi-round-trip tools cannot work at all β legacy stateless HTTP has no server-to-client request channel, so plan_outfit fails with a capability error that looks like a server bug but is not.
Two more things worth knowing:
A
GET /mcpreturning405is expected and spec-compliant for stateless serving; the client handles it.The published Inspector 2.x packages omit
clients/web/static/from theirfileslist, so the MCP Apps sandbox fails withENOENT: β¦ sandbox_proxy.html. Until that is fixed upstream, copy the file from the repository'smainbranch into the installed package.
Authentication
Protect the server (inbound). Two modes, selected by AUTH_MODE:
Mode | When to use | How clients authenticate |
| Single-tenant deployments, internal tools |
|
| Enterprise IdPs (Entra, Okta, Auth0, Keycloak) | OAuth 2.0 bearer token, validated by RFC 7662 introspection |
api-key is a constant-time comparison against MCP_API_KEY. Setting the older REQUIRE_AUTH=true still selects it, so existing .env files keep working.
oauth turns the server into a proper OAuth 2.0 resource server. It publishes RFC 9728 protected-resource metadata at /.well-known/oauth-protected-resource, so clients can discover the authorization server, and answers unauthenticated requests with a 401 carrying the correct WWW-Authenticate challenge. Tokens are checked for expiry and for audience (RFC 8707) β a token minted for another resource is refused rather than silently accepted. Verified identity reaches handlers as ctx.mcp.http?.authInfo.
Both modes sit behind the same AuthStrategy interface, so a different scheme (mTLS, HMAC, local JWT verification) is a new file, not a transport change.
Consume an external API (outbound). Set EXTERNAL_API_BASE_URL / EXTERNAL_API_KEY. The shared HttpClient injects the key, applies timeouts and retries, and is handed to every tool via the context.
MCP Apps: tools with a UI
A tool can render an interactive panel in the conversation instead of only returning text. Point it at a UI resource:
_meta: uiToolMeta("ui://mcp-template/weather-panel.html"),The panel itself is registered in src/apps/register.ts and served as an text/html;profile=mcp-app resource. get-forecast.tool.ts is a working example.
Hosts that don't support the extension ignore _meta and fall back to the tool's text content, so always return readable text as well. Set MCP_APPS_ENABLED=false to stop advertising the extension entirely.
The server side is implemented directly against the wire contract rather than via @modelcontextprotocol/ext-apps, which is still a v1-only package and cannot be mixed with the v2 SDK.
Multi-round-trip tools
When a tool needs input mid-call, 2026-07-28 replaces server-initiated elicitation with MRTR: the handler returns input_required and the client retries the same call with the answers.
plan-outfit.tool.ts is a complete example. The pattern:
const previous = mcp.mcpReq.requestState?.<State>();
if (!previous) {
return inputRequired({
inputRequests: { preferences: inputRequired.elicit({ message, requestedSchema }) },
requestState: await codec.mint({ phase: "awaiting-preferences", city }, mcp),
});
}
const answers = acceptedContent(mcp.mcpReq.inputResponses, "preferences", schema);Two rules that are easy to get wrong:
inputResponsesare per round and never accumulate. Thread everything the next round needs throughrequestStateand switch on an explicit phase β never infer progress from which keys happen to be present.requestStateis signed, not encrypted. It is readable by the client, so never put secrets in it. SetREQUEST_STATE_SECRET(32+ chars) in production; without it an ephemeral key is generated and paused calls break across restarts and replicas.
Clients must declare the elicitation capability. One that doesn't gets a clean -32021 refusal naming what was missing, rather than an opaque failure.
What's not here: the Tasks extension
io.modelcontextprotocol/tasks is not implemented, because it cannot currently be served on a 2026-07-28 connection. The SDK's dispatch checks a per-era method registry before handler lookup, and the 2026 registry contains exactly ten methods β tasks/get and tasks/cancel are not among them, so they answer -32601 even with a handler registered. There is also no @modelcontextprotocol/ext-tasks package on npm.
For long-running work today, use notifications/progress with ctx.mcp.mcpReq.signal for cancellation, and MRTR for human-in-the-loop pauses. See SEP-2663 for the extension's design.
Using with Claude Desktop
Add to claude_desktop_config.json (stdio):
{
"mcpServers": {
"mcp-template": {
"command": "node",
"args": ["/absolute/path/to/mcp-template/dist/index.js"]
}
}
}Architecture
src/
βββ index.ts Composition root: load config β pick transport
βββ server.ts Per-request server factory: capabilities, cache hints, registry
βββ config/env.ts Zod-validated environment (single source of truth)
βββ core/
β βββ tool.ts ToolDefinition contract + defineTool() helper
β βββ tool-registry.ts Dynamic discovery & registration of *.tool.ts
β βββ tool-context.ts Dependency-injection container for handlers
β βββ mcp-meta.ts Typed access to the 2026 _meta request envelope
β βββ request-state.ts Signed state for multi-round-trip calls
β βββ logger.ts Structured logging (to stderr, stdio-safe)
βββ transports/ stdio (serveStdio) + Streamable HTTP (createMcpHandler)
βββ auth/ AuthStrategy + API-key, OAuth bearer, token introspection
βββ apps/ MCP Apps: UI resource and its wire constants
βββ clients/ Shared HTTP client for outbound API calls
βββ services/ Domain logic (e.g. WeatherService) used by tools
βββ tools/ π your tools β one file each, auto-registered
βββ utils/errors.ts Typed errorsHow SOLID shows up here:
Single responsibility β one tool per file; registry, transport, config, and auth are each isolated.
Open/closed β add a tool or an auth strategy by adding a file; nothing existing changes.
Liskov β every tool is interchangeable behind
ToolDefinition.Interface segregation β small, focused contracts (
ToolDefinition,AuthStrategy).Dependency inversion β handlers depend on the injected
ToolContext, never on globals.
Scripts
Script | Purpose |
| Run with hot reload (tsx) |
| Clean |
| Run the compiled server |
| Connect as a client and dump the server |
| Type-check sources and tests |
| ESLint |
| Prettier (write) |
| Run the test suite (Vitest) |
buildcleansdist/first on purpose: tools are discovered by scanning the output directory, so a stale.tool.jsfrom an earlier build would otherwise still be served after you delete its source.
Docker
docker build -t mcp-template .
docker run -p 3000:3000 -e AUTH_MODE=api-key -e MCP_API_KEY=secret mcp-templateBecause the protocol is stateless, containers scale horizontally with no sticky sessions. Set REQUEST_STATE_SECRET to the same value across replicas so a multi-round-trip call can resume on any of them.
License
MIT Β© Vitor Kaez
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
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 Connectors
A simple Typescript MCP server built using the official MCP Typescript SDK and smithery/cli. Thisβ¦
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yoβ¦
MCP server for progressive tool usage at any scale (see https://klavis.ai)
A TypeScript MCP server for Home Assistant, enabling programmatic management of entities, automatiβ¦
Related MCP Servers
- AlicenseCqualityDmaintenanceA production-ready template for creating Model Context Protocol servers with TypeScript, providing tools for efficient testing, development, and deployment.18947MIT
- AlicenseCqualityDmaintenanceA TypeScript-based template for building Model Context Protocol servers, featuring fast testing, automated version management, and a clean structure for MCP tool implementations.1894MIT
- FlicenseNot gradedqualityDmaintenanceA template repository for building Model Context Protocol (MCP) servers with TypeScript, featuring full TypeScript support, testing setup, CI/CD pipelines, and modular architecture for easy extension.11-
- AlicenseNot gradedqualityAmaintenanceA starter template for building remote Model Context Protocol servers using TypeScript, providing modern tooling and best practices while leveraging the MCP TypeScript SDK.54MIT
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/VitorKaeZ/mcp-server-template'
If you have feedback or need assistance with the MCP directory API, please join our Discord server