Getting Started with the MCPServer Component


Requirements: MCPSDK

Introduction

The MCPServer component provides a simple way to expose tools, prompts, and resources to MCP-compatible LLM clients. This article provides a conceptual and practical guide for extending the server demo that comes packaged with the component to customize your MCP Clients behavior to suit your applications needs.

Contents

Requirements: MCPSDK

Conceptual Overview

The Model Context Protocol (MCP) is a lightweight communication protocol that lets external tools interact with language models by sending structured requests and receiving responses, enabling dynamic tool use, context sharing, and custom workflows.

In most traditional client/server architectures, the server is responsible for directing communication and managing state. The client sends requests and responds to instructions issued by the server.

In contrast, an MCP architecture is explicitly client-driven. The MCP client initiates all interactions, launches the server as a subprocess, and controls the flow of communication. The server acts as a passive provider of functionality, responding only when prompted by the client.

Here is a typical sequence of events in an MCP client/server interaction:

  • The client launches the MCP server as a subprocess, specifying the executable path and any configuration options.
  • The server registers available tools, resources, and prompts using the SDK's registration methods.
  • The client queries the server for available tools and resources during startup or as needed.
  • The client issues requests to execute tools or retrieve resources or prompts.
  • The server listens for these events and serves the requested information or performs the specified operation. To enable proper interaction, the server must subscribe to specific request events exposed by the SDK and implement appropriate logic to handle tool/resource execution.

Using MCP Server

MCP is a client-driven protocol where the server reacts to events triggered by the client. To handle these requests, the server subscribes to specific event handlers.

For example, the ResourceRequested event is fired when the end user prompts the client to load a resource. The client then notifies the server by triggering this event. The event includes the URI of the requested resource, and your handler determines how to locate and return the content for that URI.

server.ResourceRequested +=(s,e)=> { string data = File.ReadAllText(fullPath); server.AddResourceContent(e.Uri, data, "text/plain"); }

Another example would be the ToolRequested event, which is fired when the client decides to use a particular tool. The event arguments will include the tool Name which allows the server to identify which tool has been requested, complete the associated action and return any necessary text.

server.ToolRequested +=(s,e)=> { string data = new Random().NextDouble().ToString(); server.AddToolMessage((int)TToolMessageTypes.mtText, randomNum); }

By handling these events, the server becomes responsive to client needs while remaining agnostic to when or why the request was made.

Basic Implementations

A basic MCP server implementation is included in the demo packaged with the component. This demo provides a clear, extensible template for handling core interactions such as resource loading, prompt delivery, and tool execution. Each event handler is structured to demonstrate a typical interaction pattern between an MCP client and server. These handlers can be customized or extended to match your own use case, making the demo a strong foundation for building production-ready implementations.

For example, the demo includes an event handler for the ResourceRequested event as shown in this code snippet:


private static void Server_OnResourceRequested(object s, MCPServerResourceRequestedEventArgs e)
{
  //The client requests a resource by URI triggering this event.
  //The server expects a standard URI structure: `[protocol]://[host]/[path]`
  if (e.Uri.StartsWith(FileProtocol))
  {
    //The URI is parsed to determine the file path. 
    string fileName = e.Uri.Substring(FileProtocol.Length);
    string fullPath = Path.GetFullPath(fileName);
    Log("Resolved path: " + fullPath);

    if (File.Exists(fullPath))
    {
        string mimeType = "text/plain";
        //The server reads the file from disk.  
        string data = File.ReadAllText(fullPath);
        //A ResourceContent object is created using AddResourceContent(uri, data, mimeType).
        server.AddResourceContent(e.Uri, data, mimeType);   
    }  
  } 
}

In this handler, the server responds to a client's request by resolving a resource URI using a simple and transparent logic flow:

  • The client requests a resource by URI.
  • The server expects a standard URI structure: [protocol]://[host]/[path]
  • The URI is parsed by the server to determine the file path.
  • The server reads the file from disk.
  • A ResourceContent object is created using AddResourceContent(uri, data, mimeType).
  • The content is returned to the client.

This setup is especially useful for serving static, file-based resources and is easy to adapt to more complex workflows. Similarly, basic event handlers for serving prompts and invoking tools are included and follow the same clean, straightforward approach. You can use the included demo as a reference or starting point, customizing each handler to suit the needs of your application or environment.

Whether you're building internal tools, data integrations, or domain-specific assistants, the demo illustrates how to implement a minimal MCP server that can scale to match the needs of your business.

If you would like to learn more about the structure of a URI please review the documentation available here

Advanced Implementations

While the demo project focuses on simple, file-based examples, the MCP SDK supports much more flexible server behavior. You can customize event handlers to adapt the server responses based on the request, allowing it to serve dynamic or computed content.

Some examples of advanced implementations include:

  • Serving multiple resources in response to a single URI (e.g., a group of files or related documents)
  • Generating content dynamically based on the URI or request type (e.g., building a report or loading from a configuration)
  • Integrating with external systems, such as:
    • Databases or local data files
    • REST APIs or internal services
    • Scripts or runtime logic

These approaches allow you to expand the server utility without changing the client-driven nature of the MCP architecture. The server remains passive but becomes a more capable responder.

Tools, in particular, are highly flexible. If you can define a function, you can expose it as a tool.

Examples of tool use cases include:

  • Performing calculations or data lookups
  • Triggering local processes, such as generating a file or querying a database
  • Wrapping internal functionality behind a callable interface
  • Providing lightweight automation tasks

Although the demo includes a basic tool without parameters, the SDK also supports registering parameterized tools. Keep in mind that tool usage depends on the client, some LLM clients, like Claude, may not yet support parameter input, even though your server can define it.

By customizing your event handlers, you can make the MCP server a powerful interface layer between LLMs and your internal systems, without requiring the server to perform any reasoning or maintain state beyond a single request.

How MCP Resources Work

Resources are reference materials that provide contextual information to enhance the LLM's understanding and responses. Unlike tools that perform actions and prompts that guide conversations, resources serve as a knowledge base that the LLM can draw upon throughout the conversation.

Think of resources as the LLM's reference library. They can be code files, documentation, data sets, configuration files, or any other information source that would be helpful for the LLM to reference when responding to user queries. Once loaded, resources remain active in the conversation context, allowing the LLM to use them as needed.

Resources are particularly valuable when you want the LLM to work with specific information that isn't part of its training data, such as your codebase, internal documentation, or project-specific details.

Resource Lifecycle

The interaction between an MCP server and client for resources follows a predictable pattern:

  • Registration: The server registers available resources with their URIs and metadata
  • Discovery: The client requests the list of resources during startup
  • Selection: The user selects a resource from the client interface or the client automatically invokes it based on context or logic
  • Request: The client sends a request to fetch the specific resource
  • Loading: The server loads the resource content using its URI and serves it to the client
  • Context Integration: The client loads the resource into memory and makes it available as context
  • Persistent Access: The resource remains active and accessible throughout the conversation

Resource Descriptions and Context Usage

The description of each resource plays a crucial role in how effectively the LLM can use it. A well-crafted description helps the client determine when a particular resource is relevant to a user's query. For example, a resource description might indicate that it contains "API documentation for the payment processing module" or "configuration examples for database connections."

Types of Resources

Resources can include:

  • Source code files and documentation
  • Configuration files and templates
  • Data files and schemas
  • Reference materials and specifications
  • Project-specific information and guidelines

Best Practices

When designing resources for your MCP server, focus on providing clear, descriptive metadata that helps the LLM understand when and how to use each resource. The goal is to make relevant information easily discoverable and appropriately contextualized for the conversation at hand.

Adding Resources to an MCP Server

Adding Resources to an MCP server involves three key steps:

  • Register the resource with a well formed URI, a name, and natural language description. A natural language description is necessary for the resource to be invoked at the correct time after it has been added to the clients context.
  • Subscribe to the ResourceRequested event. This lets the server react when the client triggers a resource.
  • Handle the event. Your event handler defines how the server responds when the resource is triggered.
 
// Step 1: Register a resource with a well formed URI
server.RegisterResource(uri, name, "The source code for a simple MCP SDK Server Demo");

// Step 2: Subscribe to the ResourceRequested event 
server.OnResourceRequested += Server_OnResourceRequested;

// Step 3: Define how the server handles the Resource 
private static void Server_OnResourceRequested(object s, MCPServerResourceRequestedEventArgs e)
{
  server.ResourceContents.Clear();
  string uri = e.Uri.ToString();

  if (uri.StartsWith(FileProtocol))
  {
    string fileName = uri.Substring(FileProtocol.Length);
    string fullPath = Path.GetFullPath(fileName);
    if (File.Exists(fullPath))
    {
      string mimeType = "text/plain";
      string data = File.ReadAllText(fullPath);
      server.AddResourceContent(uri, data, mimeType);         
    }
  } 
}

Explanation:

  • RegisterResource makes the server implementation file visible to the client as a resource.
  • The event handler checks the resource name and constructs a ResourceContent using AddResourceContent, specifying the type of result(text) through the MIMEType as well as the content of the resource itself and the URI. This allows the client to handle the resource response correctly.

How MCP Tools Work

Tools are similar to resources in terms of how they are registered, discovered, and requested by the client. However, their purposes are different.

Resources are used for reference. These are static items such as code files, documents, or prompts that the client can analyze or respond to based on the user's input.

Tools, on the other hand, are used to perform work. They can be functions that access a database, manipulate files, or carry out computations. If you can write a function for it, you can expose it as a tool. The client determines when and how to use the tool based on the context of the conversation.

Many LLM clients include a human-in-the-loop process. This means the client may ask the user for permission before calling a tool, especially when the action has real-world effects, such as modifying data or accessing local files.

To try out a tool in the MCP SDK demo, you can prompt:

"Can you generate a random number for me using the MCPSDK tool?"

Depending on the client, explicitly naming the tool ("MCPSDK tool") may help guide the LLM to use the registered function. However, this is not always necessary. Many LLMs are capable of generating random numbers on their own, so the client may decide to respond without invoking the tool unless the context makes its use clearly beneficial, or the user requests it directly.

If tool usage is ambiguous, you may be prompted to approve the tool before it runs, especially if human-in-the-loop safeguards are enabled.

Tool Parameters and Client Support

Although the MCP SDK is fully capable of defining and registering parameterized tools, support for those tools ultimately depends on the LLM client being used. Since the SDK is not tied to any specific client, it may implement features that are not yet supported by all clients.

When building your own tools, keep this in mind:

  • The MCP SDK will allow tools with parameters
  • The client must also support those parameters for them to be usable at runtime

Adding Tools To An MCP Server

Adding tools to an MCP server involves three key steps:

  • Register the tool with a name and natural language description. A well formed description is necessary for the tool to be invoked at the correct time without specific prompting.
  • Subscribe to the ToolRequested event. This lets the server react when the client triggers a tool.
  • Handle the event. Your event handler defines how the server responds when the tool is triggered.
 
// Step 1: Register a tool
server.RegisterTool("random-num", "Generates a random number");

// Step 2: Subscribe to the ToolRequested event 
server.OnToolRequested += Server_OnToolRequested;

// Step 3: Define how the server handles the tool 
private static void Server_OnToolRequested(object s, MCPServerToolRequestedEventArgs e)
{
  if (e.Name == "random-num")
  {
    
    // Not all tools need to return text.
    // Some, like logging or triggering background tasks, complete silently.
    string randomNum = new Random().NextDouble().ToString();
    server.AddToolMessage((int)TToolMessageTypes.mtText, randomNum); 
  }
  else{
    e.IsError = true;
  }
}

Explanation:

  • RegisterTool makes the "random-num" tool visible to the client.
  • The event handler checks the tool name and constructs a ToolMessage using AddToolMessage, specifying the type of result(text) as well as the result itself. This allows the client to handle the tool response correctly.
  • If an error or exception is thrown during the course of the tool use or if a tool is not found then tools may return an error by setting isError to true.

How MCP Prompts Work

Prompts are pre-defined conversation starters or instructions that can be quickly inserted into the LLM's context to guide specific interactions or workflows. Unlike tools that perform actions and resources that provide reference material, prompts serve as standardized templates for common conversational patterns.

Think of prompts as reusable conversation blueprints. If you frequently ask an LLM to perform the same type of analysis, review, or task, you can create a prompt that encapsulates those instructions and context, making the interaction more efficient and consistent.

For example, if you regularly use an LLM to review code for security vulnerabilities, you might create a prompt like "Security Code Review" that includes specific instructions about what to look for, how to format findings, and what severity levels to assign.

Prompts are registered with the MCP server similarly to tools and resources. When a client discovers available prompts, users can select them to automatically populate the conversation context with the pre-defined instructions and any associated variables.

Prompt Variables and Templates

Many prompts include variables that can be customized at runtime. For instance, a "Project Analysis" prompt might include variables for project type, scope, and specific areas of focus. When the user selects this prompt, the client can present a form to collect these variables before inserting the fully populated prompt into the conversation.

When to Use Prompts

Prompts are most valuable when you have:

  • Repetitive analytical tasks that require consistent formatting
  • Complex instructions that are tedious to type repeatedly
  • Standardized workflows that benefit from structured guidance
  • Multi-step processes that need specific context or examples

Client Implementation Notes

Like tools, prompt support varies depending on client implementation. Some clients may display prompts as selectable options in the interface, while others might require specific invocation methods. The user experience for discovering and using prompts depends entirely on how the client chooses to present them.

Adding Prompts to MCP Server

Adding prompts to an MCP server involves three key steps:

  • Register the prompt and any parameters – This defines the prompt's structure and makes it available to the client.
  • Subscribe to the PromptRequested event – This lets the server react when the client triggers a prompt.
  • Handle the event – Your event handler defines how the server responds when the prompt is triggered.
 
// Step 1: Register a prompt and its argument 
server.RegisterPromptArg("code", "Python code to review", true); 
server.RegisterPrompt("Code Review", "Review python code."); 
// Step 2: Subscribe to the PromptRequested event 
server.OnPromptRequested += Server_OnPromptRequested; 
// Step 3: Define how the server handles the prompt 
private static void Server_OnPromptRequested(object s, MCPServerPromptRequestedEventArgs e) 
{ 
  if (e.Name.Equals("Code Review")) 
  { 
    string code = server.GetPromptParamValue("code"); 
    // Add messages to the prompt that will be sent to the model 
    server.AddPromptMessage((int)TRoles.rtAssistant, "Don't add comments."); 
    server.AddPromptMessage((int)TRoles.rtUser, "Please review the following code: " + code); 
  } 
} 

Explanation:

  • RegisterPromptArg defines a required parameter named "code" for the prompt.
  • RegisterPrompt makes the "Code Review" prompt visible to the client.
  • The event handler checks the prompt name and constructs the model prompt using AddPromptMessage, giving both assistant and user messages.
  • The role constants like TRoles.rtUser ensure proper message attribution in the model's context.

Setting up an MCP Server

Once you’ve registered your desired prompts, tools, and resources, and subscribed to their respective events, starting the server is straightforward. Call StartListening to begin accepting requests, then enter a loop that continuously processes incoming events with DoEvents.


try
{
  // Start the MCP server so it can begin accepting and handling requests.
  server.StartListening();

  while (true)
  {
    // Keeps the server running and responsive by processing incoming requests.
    server.DoEvents();
    // End of setup — server is now running and handling requests.
  }
}
catch (Exception ex)
{
  Console.Error.WriteLine("Error: "+ex.Message);
}

At this point, your MCP server is fully operational and ready to respond to structured model requests in real time.

Connecting to an MCP-Compatible Client

Connecting the MCP server to a client is generally simple and will feel familiar if you've ever run a program from the command line.

In most cases, an LLM client will launch your MCP server as a subprocess, just as you might want to run it from a terminal. To do this, the client needs two things:

  • The command you would normally use to launch your server
  • Any additional arguments that go with it

Most LLM clients that support MCP will have a configuration file, often in JSON format, where you can list this command. You'll usually specify:

  • A short name or label for the server
  • The command to run
  • An array of arguments (can be empty)

This tells the client how to launch your server whenever it's needed. Once it's running, the client will handle all communication by sending requests and waiting for your server to respond.

While each environment has its own details, the process is conceptually the same: tell the client what you would type to run the server, and it takes care of the rest.

If you are working with a specific LLM like Claude, setup is just as straightforward. This article walks you through connecting your MCP server to Claude Desktop, including how to configure the JSON file and verify the integration.

We appreciate your feedback. If you have any questions, comments, or suggestions about this article please contact our support team at support@nsoftware.com.