Friday, 17 July 2026

Spring AI MCP Server Series: Testing Tools, Resources, and Prompts with cURL

  

In the previous post, we built a simple Employee MCP Server using Spring AI and implemented support for Tools, Resources, and Prompts.

 

Building the server is only the first step. The next step is understanding how an MCP client actually communicates with the server. Every MCP interaction follows a well defined protocol, beginning with an initialization handshake before invoking tools, reading resources, or requesting prompts.

 

In this post, we'll interact with the Employee MCP Server using nothing more than cURL. This approach helps you understand the raw JSON-RPC messages exchanged between an MCP client and server, making the protocol much easier to learn and debug.

 

We'll cover the following topics:

 

·      Protocol Handshake: Establishing communication between the client and the MCP server.

·      Tools: Listing available tools and invoking them with different inputs.

·      Resources: Discovering and reading resources exposed by the server.

·      Prompts: Listing and retrieving reusable prompt templates.

·      Validation Errors: Understanding how the server responds to invalid requests and malformed inputs.

 

By the end of this post, you'll have a solid understanding of the core MCP protocol and be able to test any MCP server directly from the command line without relying on an MCP client or IDE extension.

 

1. Protocol Handshake

Before an MCP client can invoke tools, read resources, or retrieve prompts, it must establish communication with the MCP server. This initial exchange is known as the Protocol Handshake. Think of it as introducing yourself before starting a conversation.

 

During the handshake, the client and server agree on:

 

·      The MCP protocol version

·      The capabilities supported by each side

·      Client identification information

·      The features exposed by the server

 

Once this initialization is complete, the client can safely discover and invoke the server's capabilities. The following sequence represents a typical MCP session:

Client
   │
   │ 1. initialize
   ▼
Server
   │
   │ Returns supported protocol version and capabilities
   ▼
Client
   │
   ├── tools/list
   ├── resources/list
   └── prompts/list

Let's look at each call in detail.

 

1.1 Initialize Call

The very first request sent by an MCP client is the initialize request. This request establishes the session and allows both the client and server to negotiate the protocol version and exchange capability information.

curl -s -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": {
        "name": "curl-client",
        "version": "1.0"
      }
    }
  }' | jq .

Sample Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-06-18",
    "capabilities": {
      "completions": {},
      "prompts": {
        "listChanged": false
      },
      "resources": {
        "subscribe": false,
        "listChanged": false
      },
      "tools": {
        "listChanged": false
      }
    },
    "serverInfo": {
      "name": "employee-mcp",
      "version": "1.0.0"
    },
    "instructions": "This MCP server exposes an Employee Management dataset with ~100 employees\nacross multiple countries, departments, and management levels.\n\nAvailable capabilities:\n  Tools (15):\n    - getEmployees            : Paginated employee list\n    - getEmployee             : Single employee by ID\n    - getEmployeesUnderManager: All direct reports of a manager\n    - getEmployeesByCity      : Paginated employees in a city\n    - getEmployeesByCountry   : Paginated employees in a country\n    - getEmployeesByDepartment: Paginated employees in a department\n    - searchEmployees         : Keyword search across name/designation/dept\n    - getManagers             : All employees who have direct reports\n    - getDepartments          : All unique department names\n    - getCountries            : All unique country names\n    - getCities               : All unique city names\n    - countEmployees          : Total / active / inactive counts\n    - countEmployeesByCountry : Employee count per country\n    - countEmployeesByDepartment: Employee count per department\n    - getOrganizationHierarchy: Full org tree (CEO → VP → Director → …)\n\n  Resources (15):\n    employees://all, employees://countries, employees://cities,\n    employees://departments, employees://organization, employees://managers,\n    employees://statistics, employees://engineering, employees://sales,\n    employees://finance, employees://hr, employees://marketing,\n    employees://support, employees://india, employees://usa\n\n  Prompts (7):\n    employee-summary, department-summary, country-summary,\n    manager-summary, hr-assistant, employee-search-assistant,\n    organization-explorer\n\nPagination is 0-indexed (pageNumber starts at 0).\n"
  }
}

   

If the initialize request is successful, the server responds with a JSON-RPC response similar to the above. Let's understand each field in detail.

 

a. jsonrpc

"jsonrpc": "2.0"

   

This indicates that MCP uses the JSON-RPC 2.0 protocol for communication. Every request and response exchanged between the client and server follows the JSON-RPC specification. Regardless of whether you're listing tools, reading resources, or invoking prompts, you'll always see this field.

 

b. id

"id": 1

   

The id matches the identifier sent in the original request. This is especially useful when multiple requests are processed concurrently. The client can correlate each response with the corresponding request using this value.

 

For example:

Request ID

Method

1

initialize    

2

tools/list    

3

resources/list

 

Each response contains the same id, allowing the client to match responses correctly.

 

c. result

Everything inside the result object represents the server's initialization information.

 

This includes:

 

·      Negotiated protocol version

·      Server capabilities

·      Server metadata

·      Optional usage instructions

 

Let's examine each section.

 

c.1 protocolVersion

"protocolVersion": "2025-06-18"

   

Notice that the server returned 2025-06-18, even though our request sent "protocolVersion": "2024-11-05"

 

This is the protocol version that the server has negotiated for the session. Depending on the implementation, the server may:

 

·      accept the client's requested version,

·      negotiate a compatible version,

·      or reject the request if no compatible version exists.

 

This mechanism allows MCP to evolve while maintaining backward compatibility between clients and servers.

 

c.2 capabilities

 

"capabilities": {
    ...
}

   

This section describes everything the server supports. Think of it as the server advertising its features to the client. Our Employee MCP Server exposes four capability groups:

Capabilities
├── Tools
├── Resources
├── Prompts
└── Completions

   

Let's examine each one.

 

c.2.1 Completions

"completions": {}

   

This indicates that the server supports completion requests. Completion capabilities are commonly used to provide intelligent suggestions while the user is typing.

 

For example:

 

·      employee names

·      department names

·      city names

·      countries

·      prompt arguments

 

An MCP client can leverage this capability to offer autocomplete or type ahead experiences without hardcoding possible values.

 

c.2.2 Prompts

"prompts": {
    "listChanged": false
}

   

This tells the client that the server supports reusable prompts. The listChanged property indicates whether the list of prompts can change during the lifetime of the connection. Since it is false, the client knows that the prompt list is static and doesn't need to be refreshed automatically.

 

c.2.2 Resources

"resources": {
    "subscribe": false,
    "listChanged": false
}

   

Resources expose read-only data. Here, two important properties are advertised.

 

"subscribe": false

Some MCP servers allow clients to subscribe to resource updates.

 

For example:

·      log files

·      stock prices

·      sensor readings

·      live dashboards

 

Whenever the resource changes, the server pushes notifications to subscribed clients. Our Employee MCP Server exposes static employee information, so subscriptions aren't necessary.

 

"listChanged": false

This indicates that the list of available resources remains constant throughout the session. The client can safely cache the resource list after the initial discovery.

 

c.2.3 Tools

"tools": {
    "listChanged": false
}

   

This tells the client that executable tools are available. Again:

"listChanged": false

 

means the tool catalog remains unchanged while the connection is active. An IDE or AI assistant therefore needs to fetch the tool list only once during initialization.

 

c.3 serverInfo

"serverInfo": {
    "name": "employee-mcp",
    "version": "1.0.0"
}

   

This section identifies the MCP server.

 

·      name: Server name

·      Version: Server version

 

Although simple, this metadata is extremely useful for:

 

·      logging

·      debugging

·      telemetry

·      compatibility checks

·      displaying connected server information in IDEs

 

For example, an AI assistant connected to multiple MCP servers can display something like this:

Connected Servers

✓ employee-mcp v1.0.0
✓ github-mcp v2.3.1
✓ jira-mcp v1.7.0

   

c.4 instructions

One of the most valuable parts of the initialization response is the optional instructions field.

"instructions": "This MCP server exposes..."

Unlike the structured metadata returned in capabilities, the instructions field is human-readable guidance that helps an LLM or MCP client understand the purpose and usage of the server.

 

In our Employee MCP Server, the instructions provide a concise overview of everything the server offers:

 

·      The available tools and their purpose

·      The resources that can be read

·      The reusable prompts

·      and important usage notes such as pagination behaviour.

 

For example, the instructions summarize:

 

·      15 Tools for employee search, hierarchy exploration, statistics, and filtering

·      15 Resources exposing employee datasets, departments, countries, managers, and organizational information

·      7 Prompts for common HR and employee-related tasks

·      A note that pagination is zero-indexed, ensuring clients use the correct page numbering

 

This information acts as a quick reference for AI assistants. Instead of discovering every capability individually before reasoning, an LLM can immediately understand the server's domain and available functionality from this summary.

 

For example, after reading these instructions, an AI assistant already knows that it can:

 

·      Retrieve employees by department

·      Search employees by keyword

·      Explore the organization hierarchy

·      Generate department summaries

·      and access country-level statistics

 

Without requiring any prior knowledge of the Employee MCP Server.

 

1.2 Listing Available Tools

Once the session has been initialized, the client can discover all executable operations exposed by the server.

 

curl -s -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list",
    "params": {}
  }' | jq .

   

Sample Response:

 

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "tools": [
      {
        "name": "countEmployeesByDepartment",
        "description": "Returns the number of employees per department, sorted by count descending.\nEach entry contains: group (department name) and count.\n\nUseful for understanding team sizes and organizational structure.\n",
        "inputSchema": {
          "type": "object",
          "properties": {},
          "required": [],
          "additionalProperties": false
        }
      },
      {
        "name": "getEmployeesByCity",
        "description": "Retrieve a paginated list of employees located in the specified city.\nThe city name comparison is case-insensitive (e.g., \"bangalore\" = \"Bangalore\").\n\nUse getCities() to discover all available city names.\n",
        "inputSchema": {
          "type": "object",
          "properties": {
            "city": {
              "type": "string",
              "description": "City name to filter by (e.g., \"Bangalore\", \"New York\", \"Tokyo\")."
            },
            "pageNumber": {
              "type": "integer",
              "description": "Zero-based page number. Default: 0."
            },
            "pageSize": {
              "type": "integer",
              "description": "Number of results per page (1–100). Default: 10."
            }
          },
          "required": [
            "city",
            "pageNumber",
            "pageSize"
          ],
          "additionalProperties": false
        }
      }
…….
…….
    ]
  }
}

   

The tools/list request returns metadata for every tool registered with the MCP server.

 

For our Employee MCP Server, the response may include tools such as:

 

·      getEmployees

·      getEmployeesByManager

·      getEmployeesByCity

·      getEmployeesByCountry

 

Each tool definition typically includes:

 

·      Tool name

·      Description

·      Input schema

·      Required parameters

 

The client uses this information to dynamically build tool invocation requests without any hardcoded knowledge about the server.

 

1.3 Listing Available Resources

Resources represent read-only information exposed by the MCP server. Unlike tools, resources do not execute business logic, they simply provide data that clients can read.

 

curl -s -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "resources/list",
    "params": {}
  }' | jq .

   

Sample Response

 

{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "resources": [
      {
        "uri": "employees://hr",
        "name": "HR Department",
        "description": "All employees in the Human Resources department.",
        "mimeType": "application/json"
      },
      {
        "uri": "employees://all",
        "name": "All Employees",
        "description": "Complete list of all 100 employees in the organization.",
        "mimeType": "application/json"
      }
      ...........
      ...........
    ]
  }
}

The response contains all registered resources along with their metadata, such as:

 

·      Resource URI

·      Name

·      Description

·      MIME type

 

The client can later use these URIs to retrieve the actual resource contents using the resources/read method.

 

1.4 Listing Available Prompts

Prompts are reusable prompt templates exposed by the MCP server. Instead of embedding prompt text inside the client, the server publishes prompts that can be discovered and reused.

 

curl -s -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 4,
    "method": "prompts/list",
    "params": {}
  }' | jq .

   

Sample Response

 

{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "prompts": [
      {
        "name": "manager-summary",
        "description": "Generate a team overview for a specific manager including all direct reports",
        "arguments": [
          {
            "name": "managerId",
            "description": "The numeric ID of the manager",
            "required": true
          }
        ]
      },
      {
        "name": "department-summary",
        "description": "Generate a department overview including team size, managers, and distribution",
        "arguments": [
          {
            "name": "department",
            "description": "Department name (e.g., Engineering, Sales, HR)",
            "required": true
          }
        ]
      }
      ..............
      ..............
    ]
  }
}

   

The response lists all available prompts together with:

 

·      Prompt name

·      Description

·      arguments specify argument name, description and whether it is mandatory or not.

 

For example, your Employee MCP Server might expose prompts such as:

 

·      Employee Summary

·      Team Structure

·      Manager Report

·      Organization Overview

 

The client can later retrieve a specific prompt using the prompts/get method, supplying the required arguments.

 

Why Does Discovery Matter?

One of MCP's biggest strengths is that clients don't need prior knowledge of the server. Rather than hardcoding available operations, an MCP client follows this discovery process:

 

Initialize Session
        │
        ▼
Discover Tools
        │
        ▼
Discover Resources
        │
        ▼
Discover Prompts
        │
        ▼
Invoke Tools / Read Resources / Retrieve Prompts

   

This makes MCP servers self-describing. As new tools, resources, or prompts are added, clients can discover them automatically without requiring code changes, enabling a more flexible and extensible integration model.

 

2. Tools

Tools are the action layer of the Model Context Protocol (MCP). They allow an MCP client to execute business logic exposed by the server and receive structured results.

 

Unlike Resources, which provide read-only data, or Prompts, which return reusable prompt templates, Tools perform operations such as querying data, invoking services, or executing custom business workflows.

 

In our Employee MCP Server, we've exposed several tools to retrieve employee information based on different criteria. The generic JSON-RPC request for invoking any tool looks like this:

 

{
  "jsonrpc": "2.0",
  "id": 10,
  "method": "tools/call",
  "params": {
    "name": "<tool-name>",
    "arguments": {
      ...
    }
  }
}

   

Let's invoke each tool using cURL.

 

2.1 getEmployees tool

The getEmployees tool returns a paginated list of employees from the in-memory employee repository. Since our sample dataset contains many employees, pagination helps clients to fetch the data in manageable chunks instead of retrieving everything in a single request.

 

This is one of the most common patterns you'll encounter when exposing tools that return collections of data.

 

curl -s -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 10,
    "method": "tools/call",
    "params": {
      "name": "getEmployees",
      "arguments": {
        "pageNumber": 0,
        "pageSize": 10
      }
    }
  }' | jq .

Understanding the Request

Unlike the tools/list request, which simply discovers available tools, the tools/call method actually executes a tool. The important fields inside the params object are:

 

Field

Description

name

The name of the tool to invoke

arguments

Input parameters expected by the tool

 

For this tool, the arguments are:

Argument

Description

pageNumber

Zero-based page index to retrieve.    

pageSize

Number of employees returned per page.

 

In this example:

·      pageNumber = 0 requests the first page.

·      pageSize = 10 requests 10 employees.

 

What Happens Inside the Server?

When the MCP server receives this request, it performs the following steps:

 

·      Receives the tools/call JSON-RPC request.

·      Looks up the registered tool named getEmployees.

·      Validates the required arguments.

·      Invokes the corresponding Spring method.

·      Fetches the requested employee page.

·      Serializes the result into the standard MCP response format.

·      Returns the response to the client.

 

Conceptually, the flow looks like this:

 

Client
   │
   │ tools/call
   ▼
MCP Server
   │
   │ Locate tool: getEmployees
   ▼
EmployeeService
   │
   │ Fetch page 0
   ▼
Employee Repository
   │
   ▼
Paginated Employee List

   

Expected Response

The server responds with a standard JSON-RPC response containing the tool output.

 

A simplified response might look like this:

 

{
  "jsonrpc": "2.0",
  "id": 10,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{...employee page JSON...}"
      }
    ],
    "isError": false
  }
}

   

Notice that MCP wraps the tool output inside a content array. Each entry represents a piece of content returned by the tool. In this example, the tool returns a JSON payload serialized as text. Depending on the tool implementation, the content could also include other supported content types such as images or resources, but text is the most common format for business APIs.

 

In summary,

·      tools/call is used to execute a registered MCP tool.

·      The name field identifies the tool to invoke.

·      The arguments object contains the tool's input parameters.

·      getEmployees returns a paginated list of employees.

·      Pagination enables efficient retrieval of large datasets and is a recommended pattern for collection-based tools.

 

 

3. Resources

While Tools execute business logic, Resources expose read-only information that clients can retrieve without triggering any server-side operations.

 

Think of a resource as a document or data object identified by a unique URI. Instead of invoking a function, the client simply requests the resource using its URI.

 

Resources are ideal for exposing:

 

·      Configuration data

·      Documentation

·      Metadata

·      Static or computed datasets

·      Read-only reference information

 

In our Employee MCP Server, we've registered several employee related resources that can be accessed directly using their resource URIs.

 

Unlike tools, every resource read uses the same JSON-RPC method: resources/read

 

The only thing that changes is the resource URI. The generic request looks like this:

 

{
  "jsonrpc": "2.0",
  "id": 200,
  "method": "resources/read",
  "params": {
    "uri": "<resource-uri>"
  }
}

   

Let's start by reading our first resource.

 

3.1 Reading the employees://all Resource

The employees://all resource returns the complete employee dataset maintained by the Employee MCP Server.

 

Unlike the getEmployees tool, which supports pagination and executes business logic, this resource simply exposes the entire employee collection as read-only data

 

curl -s -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 200,
    "method": "resources/read",
    "params": { "uri": "employees://all" }
  }' | jq .

   

What Happens Inside the Server?

When the MCP server receives the request, it performs the following steps:

 

·      Receives the resources/read request.

·      Locates the registered resource matching the URI employees://all.

·      Invokes the resource provider associated with that URI.

·      Retrieves the employee data.

·      Wraps the data in the standard MCP resource response.

·      Returns it to the client.

 

The flow is straightforward:

 

Client
   │
   │ resources/read
   ▼
MCP Server
   │
   │ Locate URI
   ▼
employees://all
   │
   ▼
Employee Resource Provider
   │
   ▼
Complete Employee Dataset

   

Expected Response

A typical response looks similar to the following:

 

{
  "jsonrpc": "2.0",
  "id": 200,
  "result": {
    "contents": [
      {
        "uri": "employees://all",
        "mimeType": "application/json",
        "text": "[ ... employee JSON ... ]"
      }
    ]
  }
}

   

The important fields are:

Field

Description

uri

The resource that was requested.                

mimeType

The type of content being returned.             

text

The actual resource content, serialized as text.

 

Notice that resource responses use a contents array, allowing a single response to include one or more pieces of resource content if needed.

 

Resources vs Tools

Although both return data, they serve different purposes.

 

Resources

Tools

Read-only                                        

Execute business logic                         

Identified by URI                                

Identified by tool name                       

Accessed using `resources/read`                  

Invoked using `tools/call`                    

No input beyond the URI                          

Accept structured input arguments             

Ideal for documents, metadata, and reference data

Ideal for queries, calculations, and workflows

 

For example:

·      employees://all is a resource because it simply exposes employee data.

·      getEmployees is a tool because it performs pagination based on the supplied arguments.

 

Why Use Resources?

Resources make an MCP server self-describing by exposing data through stable URIs.

 

Instead of creating a dedicated tool for every read-only dataset, clients can retrieve information directly using a resource URI. This approach simplifies the API, improves discoverability, and clearly separates data retrieval from business operations.

 

As your MCP server grows, resources can be used to expose employee directories, department information, organizational charts, configuration files, or any other reference data that clients may need to consume without invoking executable logic.

 

4. Prompts

While Tools execute business logic and Resources expose read-only data, Prompts provide reusable prompt templates that clients can retrieve and send to an LLM.

 

A prompt represents a predefined conversation or instruction template maintained by the MCP server. Instead of embedding prompt text inside every client application, the server centrally manages prompt definitions, making them reusable and consistent across different clients.

 

Prompts are ideal for:

 

·      AI assistants

·      Report generation

·      Summarization

·      Data analysis

·      Natural language workflows

·      Guided conversations

 

In our Employee MCP Server, we've registered several prompts that generate AI-ready instructions for different employee-related scenarios. Unlike tools and resources, all prompt requests use the same JSON-RPC method: prompts/get

 

The client specifies the prompt name along with any required input arguments. The generic request looks like this:

 

 

{
  "jsonrpc": "2.0",
  "id": 400,
  "method": "prompts/get",
  "params": {
    "name": "<prompt-name>",
    "arguments": {
      ...
    }
  }
}

   

Let's retrieve our first prompt.

 

4.1 Getting the employee-summary Prompt

The employee-summary prompt generates a reusable prompt template for summarizing an employee's information.

 

Rather than returning the final AI-generated answer, the MCP server returns a structured prompt that can be sent directly to a language model such as GPT, Claude, Gemini, or any other compatible LLM.

 

In this example, we'll request a summary prompt for the CEO (Employee ID 1).

 

curl -s -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 400,
    "method": "prompts/get",
    "params": {
      "name": "employee-summary",
      "arguments": { "employeeId": "1" }
    }
  }' | jq .

   

Understanding the Request

The prompts/get method requires two important parameters.

Field

Description

name

The name of the prompt to retrieve.               

arguments

Input values used to populate the prompt template.

 

For this request:

·      name: employee-summary

·      employeeId: 1

 

The server uses these values to build the final prompt returned to the client.

 

What Happens Inside the Server?

When the MCP server receives the request, it performs the following steps:

 

·      Receives the prompts/get request.

·      Locates the registered prompt named employee-summary.

·      Validates the required input arguments.

·      Retrieves the employee details for Employee ID 1.

·      Populates the prompt template with the employee data.

·      Returns the completed prompt to the client.

 

The overall flow looks like this:

 

Client
   │
   │ prompts/get
   ▼
MCP Server
   │
   │ Locate prompt
   ▼
employee-summary
   │
   ▼
Retrieve Employee Data
   │
   ▼
Populate Prompt Template
   │
   ▼
Return Prompt Messages

Unlike a tool, no business workflow is executed, and unlike a resource, raw data isn't returned directly. Instead, the server generates a prompt that is ready for an LLM.

 

Expected Response

A typical response looks similar to the following:

 

{
  "jsonrpc": "2.0",
  "id": 400,
  "result": {
    "description": "Employee Professional Summary for Aarav Sharma",
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "SYSTEM: You are an experienced HR professional generating employee profiles.\nWrite in a formal, positive tone. Structure your response as:\n1. Professional Summary (2-3 sentences)\n2. Role & Responsibilities (bullet points)\n3. Skills Overview (inferred from designation)\n4. Career Path (based on hierarchy level)"
        }
      },
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "Generate a professional summary for the following employee:\n\nName:        Aarav Sharma\nEmployee #:  EMP-0001\nDesignation: Chief Executive Officer\nDepartment:  Executive\nLocation:    Bengaluru, Karnataka, India\nManager:     N/A (CEO)\nJoined:      2010-01-15\nStatus:      Active\n\nPlease provide a structured professional summary."
        }
      }
    ]
  }
}

   

The important fields are:

Field

Description

description

A brief description of the prompt.                                         

messages

The conversation messages that make up the prompt.

role

The role associated with each message (for example, `user` or `assistant`).

content

The actual prompt text returned by the server.                   

 

Why Return Prompts Instead of AI Responses?

A common question is, Why doesn't the MCP server simply call the LLM and return the answer?

 

The answer is flexibility. By returning prompt templates instead of AI-generated responses, the MCP server remains independent of any specific language model. The client is free to choose which LLM to use, configure model parameters, combine prompts with additional context, or even modify the prompt before sending it.

 

This separation keeps the MCP server focused on providing domain knowledge while allowing clients to control the AI interaction.

 

Prompts vs Resources vs Tools

Although all three are exposed by an MCP server, they serve distinct purposes.

 

Prompts

Resources

Tools

Return reusable prompt templates

Return read-only data          

Execute business logic           

Accessed using `prompts/get`    

Accessed using `resources/read`

Accessed using `tools/call`      

Identified by prompt name       

Identified by URI              

Identified by tool name          

Accept input arguments          

Usually require only a URI     

Accept structured input arguments

Intended for LLM interactions   

Intended for data retrieval    

Intended for performing actions  

 

In our Employee MCP Server:

 

·      employee-summary is a prompt because it generates an AI-ready instruction.

·      employees://all is a resource because it exposes employee data.

·      getEmployees is a tool because it executes application logic to retrieve a paginated result.

 

Why Use Prompts?

Prompts enable MCP servers to centralize AI instructions in one place. Instead of duplicating prompt engineering logic across multiple applications, clients can discover and reuse standardized prompts directly from the server.

 

This approach promotes consistency, simplifies maintenance, and makes it easier to evolve prompt templates over time without requiring changes in every MCP client. As your MCP server grows, prompts can support use cases such as employee summaries, organizational analysis, onboarding guidance, performance reviews, and many other AI-assisted workflows.

 

Conclusion

In this post, we explored the three fundamental capabilities of the Model Context Protocol (MCP) by interacting directly with our Employee MCP Server using CURL.

 

We started with the protocol handshake, where the client initialized communication with the server and discovered its capabilities. We then invoked Tools to execute business logic, accessed Resources to retrieve read-only data using resource URIs, and fetched Prompts to obtain reusable AI prompt templates. Finally, we also looked at how MCP validates requests and returns meaningful JSON-RPC error responses when invalid inputs are provided.

 

One of the biggest strengths of MCP is its clear separation of responsibilities:

 

·      Tools: perform actions and execute business logic.

·      Resources: expose read-only information through stable URIs.

·      Prompts: provide reusable instructions that can be consumed by any LLM.

 

This separation makes MCP servers self-describing, extensible, and easy for clients to integrate with. Instead of hardcoding APIs, clients can dynamically discover what a server offers and interact with it using a consistent JSON-RPC interface.

 

Although we used simple cURL commands throughout this article, the same requests are sent behind the scenes by MCP clients such as Claude Desktop, VS Code extensions, and AI agent frameworks. Understanding these raw protocol messages gives you a much deeper understanding of how MCP actually works and makes debugging far easier.

 

In the next post, we'll move beyond cURL and see how a real MCP client discovers our Employee MCP Server, invokes tools, reads resources, and consumes prompts to power AI-assisted workflows.

 

You can download all the CURL commands here.

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment