Create an MCP Server using the official TypeScript SDK—the alternative for JavaScript developers.
MCP has two official SDKs maintained by Anthropic: the @modelcontextprotocol/sdk for TypeScript and the fastmcp for Python. Both implement the same specification and produce servers that are fully compatible with each other. Choosing between them depends on your stack and personal preference — the end result is identical for the client.
The TypeScript SDK (@modelcontextprotocol/sdk) is more verbose than Python, but offers strong typing with interfaces and types defined for each request and response. The Python SDK (fastmcp) uses decorators and is more concise, making it ideal for rapid prototyping. Both communicate via JSON-RPC over stdio or SSE, producing servers that are 100% compatible with any MCP client. The choice is purely a matter of preference — the protocol is the same.
The MCP ecosystem is split between the two SDKs: approximately 55% of servers from the community are written in Python (for its simplicity) and 45% in TypeScript (for type robustness). Official servers such as filesystem, fetch e memory are written in TypeScript. Both SDKs receive updates simultaneously when the spec evolves.
Creating an MCP Server in TypeScript follows the standard workflow for any Node.js/TS project: initialize with npm, configure TypeScript, install the SDK, and write the code in the directory src/. The structure is simple and familiar to anyone who already works with TypeScript.
The minimum structure of a TypeScript MCP Server consists of: package.json with the dependency @modelcontextprotocol/sdk, tsconfig.json configured for ESModules and target ES2020+, and src/index.ts with the server code. The entry point creates an instance of Server, registers the handlers, and connects to the stdio transport with StdioServerTransport.
mkdir meu-mcp-server && cd meu-mcp-server && npm init -y — Creates the project directory and initializes package.json.
npm install @modelcontextprotocol/sdk — Installs the official MCP SDK for TypeScript.
npx tsc --init — Generates tsconfig.json. Configure "module": "ESNext", "target": "ES2020" e "outDir": "./dist".
mkdir src && touch src/index.ts — Creates the main file where the server will be implemented.
Add "type": "module" in your package.json to use ES modules natively. In tsconfig.json, set "moduleResolution": "node16" for compatibility. Also add a script "build": "tsc" e "start": "node dist/index.js" in package.json to make the workflow easier.
In the TypeScript SDK, tools are registered using request handlers. Unlike Python, where decorators simplify the syntax, in TS you explicitly define the handler for CallToolRequestSchema and declares the inputs using JSON Schema. It’s more verbose, but each type is explicit and validated at compile time.
To implement tools in TypeScript, you use server.setRequestHandler(ListToolsRequestSchema, ...) to list the available tools (returning name, description, and inputSchema) and server.setRequestHandler(CallToolRequestSchema, ...) to run them. The inputSchema is defined as standard JSON Schema with type, properties, and required. Inside the CallTool handler, you switch on the request.params.name to dispatch the correct logic.
The pattern in TS is to return { content: [{ type: "text", text: resultado }] } from the CallTool handler. For errors, add isError: true in the response. Create a helper function makeTextResponse(text: string) to avoid repetition — a common pattern in well-structured TS servers.
In addition to Tools, the TypeScript SDK supports Resources e Prompts using the same request handler pattern. Resources expose data for the model to read, while Prompts provide reusable templates. The API follows the same philosophy: register a handler for the corresponding schema.
For Resources, use server.setRequestHandler(ListResourcesRequestSchema, ...) to list resources and server.setRequestHandler(ReadResourceRequestSchema, ...) to serve the content. Each resource has a URI single (e.g., file:///config.json) and returns content as text or a blob. For Prompts, the pattern is identical: ListPromptsRequestSchema e GetPromptRequestSchema. Same architecture, different schemas.
Implement the ListResources handler to return the list of resources with uri, name, and description.
Implement the ReadResource handler to receive the URI and return the content with the appropriate mimeType.
Check in MCP Inspector whether the resources appear and return content correctly.
In practice, Tools are the most used feature (present in ~95% of servers), followed by Resources (~40%) and Prompts (~15%). However, Resources are especially valuable in enterprise scenarios where the model needs to access documentation, configs, or data without performing actions. Prompts are ideal for standardizing recurring interactions with the model.
Unlike Python, where you run the file directly, in TypeScript you need to compile before running. The command tsc compiles the TypeScript to JavaScript in the dist/, and then you run it with node dist/index.js. The Claude Desktop configuration points to the compiled JavaScript file.
The build workflow is: npm run build (which runs tsc) to compile, then node dist/index.js to run. In the Claude Desktop, the configuration looks like this: "command": "node", "args": ["/caminho/absoluto/dist/index.js"]. Always use absolute paths in the config to avoid problems. Alternatively, use npx ts-node src/index.ts during development to skip the build.
Add "build": "tsc" e "watch": "tsc --watch" in the package.json scripts. During development, use npm run watch in a terminal to automatically recompile after every change. For production, always run npm run build and test with node dist/index.js before configuring it in Claude Desktop.
Always compile before running! A common mistake is editing the file .ts and forget to recompile, running the old version of .js. If your changes don't seem to take effect, check whether you ran tsc before restarting the server. Tip: configure a script "dev": "tsc && node dist/index.js" that compiles and runs in sequence.
Testing an MCP Server in TypeScript follows the same workflow as Python: use the MCP Inspector for interactive debugging and the Claude Desktop for real-world usage testing. The result should be identical to that of an equivalent Python server—if both implement the same tool, the output to the client is the same.
O MCP Inspector (npx @modelcontextprotocol/inspector node dist/index.js) connects to your server and lets you test each tool, resource, and prompt individually. You can see the JSON-RPC requests sent and responses received in real time. For production testing, configure the server in Claude Desktop and use it in real conversations. Compare the result with an equivalent Python server to ensure compatibility.
Run npm run build and verify that there are no TypeScript compilation errors.
Test in the MCP Inspector — verify that tools, resources, and prompts appear and respond correctly.
Configure it in Claude Desktop and test it in real conversations to validate the user experience.
If you have an equivalent Python server, compare the outputs to ensure identical behavior.
Next Module: 5.5 — Exploring Community Servers