Yes—you can build a usable MCP server in TypeScript with a small amount of code. The essential sequence is: create an McpServer, register tools (and optionally resources or prompts), select a transport, then call server.connect(transport). This guide uses the current SDK v2 package layout and a local stdio server, then explains how the same design changes for remote Streamable HTTP deployments.
What an MCP server does
Model Context Protocol (MCP) defines a contract between a host—such as an AI desktop application or coding client—and a server that exposes capabilities. A server can publish:
- Tools: operations the host may invoke with validated arguments.
- Resources: readable data addressed by URIs.
- Prompts: reusable prompt templates.
The host discovers the capabilities you register and decides when to call them. Your TypeScript process does not need to implement an AI model; it implements the capability endpoint and speaks MCP over a transport.
Choose the SDK line before writing code
The official documentation is split between two major lines. SDK v2 is the stable line for the 2026-07-28 MCP specification and uses split packages such as @modelcontextprotocol/server. SDK v1 uses the monolithic @modelcontextprotocol/sdk package and commonly installs zod alongside it. Do not combine v1 imports with v2 installation instructions.
#1 Best Overall
| Concern | SDK v2 | SDK v1 |
|---|---|---|
| Primary package shown in documentation | @modelcontextprotocol/server |
@modelcontextprotocol/sdk |
| Documentation line | Stable 2026-07-28 specification line | Earlier API/documentation line |
| Installation guidance | Install the split server package and its dependencies | Install the monolithic SDK; examples include zod |
| Best practice for this article | Use the imports shown below consistently | Follow the v1 guide end-to-end if your project is pinned to v1 |
The examples below target v2. Check your installed package’s documentation if your project deliberately remains on v1. TypeScript 6 or later may require "types": ["node"] in tsconfig.json because package declarations refer to Buffer.
Install and configure a v2 TypeScript project
Use a recent Node.js runtime supported by the SDK, then create a project and install the server package. The exact transitive dependency set can change, so keep the major-version choice explicit in your lockfile.
mkdir weather-mcp
cd weather-mcp
npm init -y
npm install @modelcontextprotocol/server zod
npm install -D typescript tsx @types/node
npx tsc --init
Set an ESM-friendly configuration and a start script. A minimal tsconfig.json is:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"types": ["node"],
"outDir": "dist"
},
"include": ["src"]
}
Add "type": "module" to package.json, and add "start": "tsx src/index.ts" to its scripts. The types entry is harmless on older TypeScript versions and addresses the declaration issue documented for newer versions.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Rank #2
- TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
- TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
Build a read-only lookup tool over stdio
Stdio is the right transport when an MCP host launches your server as a child process on the same machine. Never write logs to standard output: stdout carries the protocol messages. Send diagnostics to stderr instead.
Create src/index.ts:
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import { z } from "zod";
const server = new McpServer({
name: "weather-lookup",
version: "1.0.0"
});
server.tool(
"get_weather",
"Return a short, read-only weather summary for a city.",
{
city: z.string().min(1).max(100).describe("City name, for example London")
},
async ({ city }) => {
// Replace this deterministic example with your approved data provider.
const summary = `Weather data for ${city} is not connected yet.`;
return {
content: [{ type: "text", text: summary }]
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("weather-lookup MCP server is running over stdio");
The server has a stable identity, one clearly described capability, and a schema that rejects empty or excessively long city names before the handler runs. The placeholder response is intentional: connect a real provider only after deciding how to handle credentials, rate limits, timeouts and provider errors.
Why the handler returns content
Tool results are MCP content items. A text item is the simplest interoperable result. For structured data, return additional content or the structured-result shape supported by the SDK version you installed, and document that contract for the host. Keep errors actionable: distinguish invalid input from an upstream outage instead of returning a successful-looking sentence.
Adding a resource or prompt
Only register capabilities your application needs. A resource is appropriate for addressable, read-only information such as a configuration document. A prompt is appropriate for a reusable interaction template. Each additional registration increases the surface that hosts can discover, so give every item a specific name and description and validate any user-controlled argument.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Run the server and connect a host
- Save the file and run
npm start. The process should remain open; the status line appears on stderr. - In your MCP host, add a server entry whose command launches the project (for example,
npx tsx /absolute/path/weather-mcp/src/index.ts). Use an absolute path when the host does not inherit your shell’s working directory. - Restart or reload the host so it performs capability discovery.
- Ask the host to call
get_weatherwith a city. It should validate the argument and display the returned text.
This walkthrough describes the expected code path; it is not a claim that a particular host configuration has been tested. Hosts differ in how they name configuration files and expose discovered tools. Consult the host’s MCP settings for its exact command format.
Pick the transport that matches deployment
| Transport | Process ownership | Network exposure | Sessions and resumability | Use it when |
|---|---|---|---|---|
| stdio | The host starts and supervises a local child process | No listening network service | Process-local lifecycle | A desktop or IDE integration on one machine |
| Streamable HTTP | Your service is deployed independently | HTTP endpoint reachable by clients | Can be stateful with a session-ID generator, or stateless when undefined | Remote or multi-client access |
| HTTP+SSE | Separate HTTP service | HTTP endpoint | Legacy compatibility behavior | Only when an existing client requires it |
The current guidance favors Streamable HTTP for new remote servers. A stateful deployment supplies a session ID generator and stores the associated session state; a stateless deployment leaves the generator undefined and handles each request without server-side session state. Either model still needs normal HTTP concerns such as authentication, TLS, request limits, proxy buffering and graceful shutdown.
Adapting the example for Streamable HTTP
The capability registrations do not change. Replace the stdio transport with the SDK’s Streamable HTTP transport and mount its request handling in your HTTP framework. The exact constructor and framework adapter names are version-sensitive, so copy them from the v2 transport documentation that matches your installed package rather than pasting a v1 snippet.
At deployment time, decide:
- Whether clients need resumable, stateful sessions or a stateless endpoint.
- How clients authenticate and how credentials are prevented from reaching tool output.
- Where session state lives when more than one process serves traffic.
- How reverse proxies preserve streaming responses and do not buffer protocol events.
- How you terminate idle sessions and close the server cleanly during deploys.
Version and transport troubleshooting
“Module not found” or missing export
Cause: v1 and v2 package names or import paths were mixed. Check npm ls, then make every import agree with the installed major version. Do not install @modelcontextprotocol/sdk while importing from @modelcontextprotocol/server, or the reverse.
The host starts and immediately disconnects
Cause: the command exits, the path is relative to a different working directory, or a startup exception is written before the transport connects. Run the exact command in a terminal, use an absolute path, and inspect stderr. Keep the process alive after server.connect.
Tools are not discovered
Confirm the host reloaded its configuration and that registration occurs before connect. Ensure stdout contains only MCP protocol traffic; move all banners and debugging output to stderr.
Remote requests hang
For Streamable HTTP, inspect proxy buffering, TLS termination, authentication middleware and request timeouts. A server that works over stdio is not automatically reachable over the network; it needs an HTTP listener and deployment-specific routing.
Input validation fails unexpectedly
Check the schema and the host’s serialized arguments. Keep fields required only when the handler truly needs them, and return a clear tool error for unsupported values rather than silently coercing them.
Free tools Windows power users keep installed
One-click scans. No signup required.
Reliability, security and cost considerations
- Use bounded strings, numbers and arrays in schemas to prevent accidental resource exhaustion.
- Set upstream timeouts and return failures as failures; do not expose API keys in text content or logs.
- For remote servers, require authentication, use HTTPS, restrict origins where appropriate, and apply rate limits.
- Keep tool descriptions precise. A host uses them to decide when a capability is relevant.
- Pin the SDK major version and review release notes before upgrading; transport and import APIs can differ between major lines.
- Measure startup time, upstream latency and error rates in your own deployment. No general usage or performance statistic is established by the SDK documentation.
Or skip the browser setup
If your MCP project also needs website screenshots for a tool or resource, ScreenshotNeo provides a single HTTP call instead of maintaining browser automation. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.
See the ScreenshotNeo API documentation for all options, including full-page and element capture, device and retina settings, PDFs, custom CSS or JavaScript, request blocking, cookies and headers, geolocation, caching, signed links, asynchronous jobs and bulk capture.
Best Value
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account to get an API key.
FAQ
Can one MCP server expose both tools and resources?
Yes. Register each capability on the same McpServer, then connect that server through the transport appropriate to your deployment.
Recommended Free Tools
Should a new remote implementation use HTTP+SSE?
Use Streamable HTTP for a new remote server unless a specific existing client requires the backwards-compatible HTTP+SSE transport.
Can I switch from stdio to HTTP without rewriting every tool?
Usually. Keep registrations and business logic independent from transport, then replace the transport and add the HTTP deployment and security layer.
Frequently Asked Questions
Can one MCP server expose both tools and resources?
Yes. Register each capability on the same McpServer, then connect that server through the transport appropriate to your deployment.
Should a new remote implementation use HTTP+SSE?
Use Streamable HTTP for a new remote server unless a specific existing client requires the backwards-compatible HTTP+SSE transport.
Can I switch from stdio to HTTP without rewriting every tool?
Usually. Keep registrations and business logic independent from transport, then replace the transport and add the HTTP deployment and security layer.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

