Skip to main content

McpServer

The main McpServer class

This is the instance that controls the logic behind your MCP server. You will use this instance to register your tool/resource/prompts, to send notifications, to read the context etc.

At his heart this class is basically a JSON-rpc server and you can use the method receive to handle JSON-rpc payloads.

Initialization

McpServer is a class that you can instantiate with the information of your server (mainly name and version) and your capabilities:

		const server = new McpServer(
	{
		name: 'a-super-basic-server',
		version: '1.0.0',
	},
	{
		adapter: undefined,
		capabilities: {},
	},
);
	

this will give you a very basic server that can only respond to ping requests and initialize requests. You can enhance your capabilities by specifying them in the capabilities object:

		const server = new McpServer(
	{
		name: 'my-awesome-server',
		version: '1.0.0',
	},
	{
		adapter: undefined,
		capabilities: {
			tools: {
				listChanged: true,
			},
			resources: {
				subscribe: true,
				listChanged: true,
			},
			prompts: {
				listChanged: true,
			},
			logging: {},
			completions: {},
		},
	},
);
	

You might notice something unusual in both of this code snippets: we are explicitly setting the adapter to undefined. The adapter is required to receive any kind of input or to send elicitation requests so, unless you are planning only to add tools that don't receive any input you should actually specify an adapter. As a nicety and for quick prototyping you can omit the adapter but you will get errors as soon as you try to define a schema for a tool so that's why you need to specifically pass undefined.

But we are not prototyping here so...

Specifying an adapter

The official MCP spec requires the server to send back the information about the input of a tool/prompt (or the requested schema of an elicitation request) in JSON-schema. While very powerful JSON-schema is also very verbose and very often developers already have a validation library installed in their project.

Thanks to standard schema you can use every library that supports standard schema with tmcp. However we still need a way to convert from your validation library of choice to JSON-schema and that could be as simple as doing schema.toJSONSchema() in zod version 4 or as complex as finding and installing a separate library to do it for you (like zod v3 or valibot).

Introducing, adapters.

We did most of the work for you for the most common validation libraries:

  • Valibot
  • Zod V3
  • Zod V4
  • Arktype
  • Effect

so if you are using one of these libraries you are golden: just run

		pnpm add valibot @tmcp/adapter-valibot
	

once you do that you'll find a named export with a descriptive name that you can instantiate and pass as adapter to your McpServer class.

		import { ValibotJsonSchemaAdapter } from '@tmcp/adapter-valibot';
 
const server = new McpServer(
	{
		name: 'a-super-basic-server',
		version: '1.0.0',
	},
	{
		adapter: new ValibotJsonSchemaAdapter(),
		capabilities: {},
	},
);
	

Then you will be able to pass valibot schemas to your tools/prompts/elicitations.

What if my standard schema library is not one of those?

Don't worry!

Writing your own adapter is very simple (given that your library supports converting into JSON-schema...if that's not the case I have bad news 😅).

tmcp also exports a class named JsonSchemaAdapter, all you have to do is create a class extends that class and override the toJsonSchema method

		import { JsonSchemaAdapter } from 'tmcp/adapter';
// every validation library exports their base type
// which needs to be a StandardSchema compatible type
import type { BaseSchemaType } from 'your-validation-library';
 
class MyCustomJsonSchemaAdapter extends JsonSchemaAdapter<BaseSchemaType> {
	async toJsonSchema(schema: BaseSchemaType) {
		// find a way to convert to json schema
		return schema.toJsonSchema();
	}
}
	

That's it! Now you can use your library with tmcp...and if you feel it you can also open a PR to tmcp to make this an officially supported library!

Server instructions

An optional property of the configuration object is instructions...you can fill this to instruct the LLM on how and when to use your server

		const server = new McpServer(
	{
		name: 'my-awesome-server',
		version: '1.0.0',
	},
	{
		adapter: undefined,
		capabilities: {
			tools: {
				listChanged: true,
			},
			resources: {
				subscribe: true,
				listChanged: true,
			},
			prompts: {
				listChanged: true,
			},
			logging: {},
			completions: {},
		},
		instructions:
			'You can use the server to do the most awesome thing in the world',
	},
);
	

Pagination options

The MCP spec allows servers to paginate the results of list calls so that if you have a lot of tools/resources/prompts you can send them in batches instead of returning a single list that could overflow the tokens limits of some agent. tmcp handle all of this for you automatically, you just need to specify the page size:

		const server = new McpServer(
	{
		name: 'my-awesome-server',
		version: '1.0.0',
	},
	{
		adapter: undefined,
		capabilities: {
			tools: {
				listChanged: true,
			},
			resources: {
				subscribe: true,
				listChanged: true,
			},
			prompts: {
				listChanged: true,
			},
			logging: {},
			completions: {},
		},
		pagination: {
			prompts: {
				size: 15,
			},
			tools: {
				size: 15,
			},
			resources: {
				size: 15,
			},
		},
	},
);
	

Context

We are gonna explore how context work in it's own section but is worth mentioning here that after you create you McpServer instance you have a withContext utility that allows you to specify the type for the custom context you expect. This type flows through the transports and in the server.ctx getter so you can actually get intellisense about the context you defined. To specify a type you can do:

		const server = new McpServer({...}).withContext<{ db: Db }>();