mcp-server-template
This server provides tools for text analysis, weather retrieval, and persistent note management:
text_stats: Count characters, words, and lines in a given text.get_weather: Fetch current weather for any city using the free Open-Meteo API (no API key required), with support for Celsius or Fahrenheit units.note_set: Save or overwrite a note using a key-value pair (file-backed persistence).note_get: Retrieve a saved note by its key.note_list: View all saved note keys.note_delete: Remove a note by its key.
All tool inputs are schema-validated, and the server is designed to be easily extensible with new custom tools.
Click on "Deploy 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-server-templatecalculate text stats for 'hello world'"
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 Server Template
A production-ready starting point for a Model Context Protocol server: typed tools with schema-validated inputs, real tests, a multi-stage Docker image, and a CI pipeline — the boring parts done right so the next MCP server starts at mile 10.
It ships with three example tools that cover the shapes you'll actually build: a pure tool, a networked tool, and a stateful tool.
Architecture
flowchart TD
Client["MCP client<br/>Claude Desktop · IDE · agent"] -->|JSON-RPC over stdio| Entry["index.ts<br/>load env · connect transport"]
Entry --> Server["server.ts<br/>buildServer()"]
Server --> Tools["tools/*<br/>name + zod schema + handler"]
Tools --> Lib["lib/*<br/>logger · result · http · env"]Construction is kept separate from transport, so the same server runs over stdio in production and over an in-memory transport in tests — no mocks. Full write-up in ARCHITECTURE.md.
Related MCP server: my-mcp-server
Enterprise architecture
Pillar | How the template applies it |
Resilience | a thrown tool handler is contained per call (returned as an error result) — one bad call never crashes the server |
Observability | structured, leveled logging to stderr (stdout is reserved for the MCP protocol) |
Reproducibility | committed lockfile + Node pinned via |
Features
🧩 Modular tools — one file per tool group, registered in one place
✅ Schema-validated inputs via zod — bad calls fail fast
🧪 Real tests — an in-memory client/server harness, no mocks (
node:test)🐳 Multi-stage Docker image that runs as a non-root user
🤖 CI — format, type-check, test, build, and a Docker build, on every push/PR
🔇 Protocol-safe logging — stdout is reserved for MCP; logs go to stderr
🔑 No secrets in the repo — config via env, with a
.env.example
Quickstart
nvm use # or Node 20+
npm install
npm run dev # run the server over stdio (Ctrl-C to stop)
npm test # run the test suite
npm run build # compile to dist/Example tools
Tool | Kind | What it shows |
| pure | Validated input → structured output, no side effects |
| networked | External API (Open-Meteo, no key), timeouts, error handling |
| stateful | File-backed persistence shared across tools |
Adding your own tool
This is the whole point of the template — it's a three-step change:
Create
src/tools/my-thing.ts:import { z } from "zod"; import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { text } from "../lib/result.js"; export function registerMyThingTools(server: McpServer): void { server.tool( "greet", "Say hello to someone.", { name: z.string().describe("Who to greet") }, async ({ name }) => text(`Hello, ${name}!`), ); }Register it in
src/tools/index.ts(one line).Done — transport, validation, error handling, tests, and Docker need no changes.
Connect it to a client
Build first (npm run build), then point your MCP client at dist/index.js.
Example config (e.g. Claude Desktop) in examples/mcp-config.json:
{
"mcpServers": {
"template": {
"command": "node",
"args": ["/absolute/path/to/mcp-server-template/dist/index.js"]
}
}
}Testing
npm test builds the real server, connects a real MCP client over an in-memory
transport, and exercises tools end to end (schema validation included). Network
tools aren't hit in CI — tests stay fast and deterministic.
Docker
docker build -t mcp-server-template .
docker run --rm -i mcp-server-template # -i: MCP talks over stdioProject structure
src/
├── index.ts # entrypoint: load env, build server, connect stdio
├── server.ts # buildServer() — construction, transport-agnostic
├── lib/
│ ├── logger.ts # stderr-only logging
│ ├── result.ts # text() / errorResult() content helpers
│ ├── http.ts # fetchJson() with timeout
│ └── env.ts # minimal .env loader
└── tools/
├── index.ts # registerAllTools() — add new groups here
├── text.ts # pure tool
├── weather.ts # networked tool
└── notes.ts # stateful tool
test/ # in-memory client/server testsLicense
MIT © Eric Agyemang
Available Tools
6 toolsget_weatherA
Current weather for a city via the free Open-Meteo API (no API key required).
| Name | Required | Description | Default |
|---|---|---|---|
| city | Yes | City name, e.g. 'Austin' or 'Austin, TX' | |
| units | No | Temperature units | fahrenheit |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description must carry the burden. It discloses critical behavioral traits: free API, no API key required. For a simple read tool, this is sufficient, though additional details like rate limits or data freshness would improve it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, well-structured sentence that is front-loaded with the key action and resource. No unnecessary words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description should hint at return format. It does not describe what the weather output contains (e.g., temperature, conditions). For a simple tool, this is a moderate gap; could be more complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% for both parameters. The description adds no extra meaning beyond the schema's examples and defaults, so baseline 3 is appropriate. The schema already provides adequate parameter semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool retrieves current weather for a city using a free API, with a specific verb and resource. It distinguishes itself from sibling tools (note and text operations) by domain.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
While the description implies usage for weather queries, it provides no explicit guidance on when to use this tool versus alternatives or any exclusions. The context of siblings is different, so no alternatives needed, but lack of explicit when-not is a minor gap.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
note_deleteC
Delete a note by key.
| Name | Required | Description | Default |
|---|---|---|---|
| key | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the full burden. It indicates deletion but does not disclose any side effects (e.g., permanence, permissions needed, error behavior). The agent lacks critical behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single concise sentence with no wasted words. However, its brevity omits necessary details, making it less effective than a slightly longer but more informative description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter tool, the description lacks completeness: no info on success/error responses, idempotency, or return value. Annotations and output schema are absent, so the agent has minimal context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description only mentions 'by key' without explaining the format or meaning of 'key'. The schema provides only type and minLength, so the agent cannot infer the expected value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action (delete), resource (note), and identifier (by key). It distinguishes from sibling tools like note_get, note_set, and note_list, as deletion is a unique operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides no guidance on when to use this tool versus alternatives, no prerequisites, no warnings about irreversible actions. It simply states the operation without context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
note_getC
Read a note by key.
| Name | Required | Description | Default |
|---|---|---|---|
| key | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must disclose behavioral traits. It only says 'read', implying a non-destructive operation, but does not explain what happens on missing key, rate limits, or authentication requirements.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise (5 words) and front-loaded with the core action. While no words are wasted, it could benefit from a bit more detail without becoming verbose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read tool with one parameter and no output schema, the description provides the essential purpose. However, it lacks completeness regarding return behavior, error states, and any context about the note system.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter 'key' is named and referenced in the description ('by key'), but the description does not explain what the key represents (e.g., UUID, title) or add any value beyond the schema's basic type and length constraints.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's action ('read') and resource ('note'), and explicitly mentions the identifier ('key'). This differentiates it from siblings like note_delete and note_list, though it lacks scope information.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is provided on when to use this tool versus alternatives like note_list or note_set. There are no examples, prerequisites, or exclusions mentioned.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
note_listA
List all saved note keys.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description must handle behavioral disclosure. It states the action (list all keys), but does not mention if this is a read-only operation, if it returns data in any particular format, or if there are any side effects. With no annotations, it carries full burden but is minimal.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence, front-loaded with verb and resource. No wasted words. Perfectly concise for a trivial tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Tool is simple (0 params, no output schema), so description is arguably complete. However, no mention of expected output format or behavior when no notes exist. Could clarify that it returns a list of strings. Given simplicity, a 3 is reasonable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema has zero parameters and 100% coverage (no need for additional param info). The description does not add any parameter meaning, but since schema is empty, baseline is 4. It correctly provides context about what is listed (keys), which is useful.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states the tool lists all saved note keys. It is specific about the resource (note keys) and the action (list). Sibling tools include note_set, note_get, note_delete, so it is distinct from others.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit when-to-use or when-not-to-use guidance. The description implies it is for listing all keys, but does not clarify that this returns only keys, not values, or that it is a simple enumeration. No alternative tool suggested (e.g., note_get for retrieving a value).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
note_setC
Save or overwrite a note by key.
| Name | Required | Description | Default |
|---|---|---|---|
| key | Yes | ||
| value | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description must disclose behavioral traits, but it only mentions the action (save/overwrite). It omits details like whether the operation is idempotent, what error conditions exist, or what the response looks like. A mutation tool without annotations requires more context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence, which is concise, but it sacrifices necessary detail. For a tool with only two parameters, it could be slightly longer to include essential parameter behavior without losing conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the lack of output schema, annotations, and parameter descriptions, the description is insufficient to fully understand the tool's behavior, return value, or side effects. The context signaling indicates high complexity due to missing metadata, but the description does not compensate.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Both parameters (key, value) have no description in the schema, and the description adds no semantic context beyond 'by key'. The term 'value' is not explained, and key's format or constraints are not clarified, despite 0% schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('Save or overwrite') and the resource ('a note by key'), which distinctively sets it apart from siblings like note_delete, note_get, and note_list. The verb-resource pairing is specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies that the tool is used for saving or overwriting notes when a key is known, but it does not provide explicit guidance on when to prefer note_set over other tools, such as when to use note_get for reading or note_delete for removal. No when-not-to-use instructions are given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
text_statsB
Count characters, words, and lines in a piece of text.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | The text to analyze |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, and the description does not disclose behavioral traits such as performance on large texts, encoding handling, or side effects. It only states the basic function without additional context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, front-loaded sentence that states the tool's purpose efficiently. Every word is necessary and no extraneous information is included.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple tool with one parameter and no output schema, the description is minimally adequate. However, it does not hint at the output structure or any constraints, leaving some ambiguity for the agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% for the single parameter 'text', which is described as 'The text to analyze' in the schema. The description adds no additional meaning beyond what the schema provides, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'count' and the resource 'text', specifying exactly what is counted (characters, words, lines). It distinguishes itself from sibling tools (weather, notes) which are unrelated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is provided on when to use this tool versus alternatives. The description only states what the tool does, without mentioning when or when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v1.0.0- First observed
get_weather - First observed
note_delete - First observed
note_get - First observed
note_list - First observed
note_set - First observed
text_stats
TDQS
Scored across 6 tools
Each tool targets a distinct operation: weather lookup, note CRUD (create, read, list, delete), and text analysis. There is no overlap or ambiguity.
Note tools follow a noun_verb pattern (e.g., note_delete), but get_weather uses verb_noun and text_stats uses noun_noun, creating inconsistency. While readable, the pattern is mixed.
With 6 tools, the server demonstrates core functionalities without being too sparse or bloated. It's well-scoped for a template.
For the intended demo purpose, the notes have full CRUD, weather provides a single essential query, and text stats cover basic analysis. No obvious gaps.
Maintenance
Related MCP Connectors
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA production-ready MCP server with tools for weather, calculator, and mock database queries, plus resources and prompt templates, featuring a glassmorphism admin dashboard and WebSocket support.-
- AlicenseNot gradedqualityDmaintenanceA minimal starter template for building Model Context Protocol (MCP) servers using TypeScript and FastMCP, including an example weather tool for demonstration.10 npm1ISC
- AlicenseNot gradedqualityCmaintenanceA production-ready template for developing MCP servers with Python and FastMCP, including example tools like a multiply calculator and code review prompt generator.Apache 2.0
- AlicenseNot gradedqualityDmaintenanceA minimal MCP server starter template with a simple hello-world tool and Docker support for building AI assistant tools.-