Build MCP Server step by step

In-depth exploration of the entire process of building an MCP server with TypeScript.
Core content:
1. Detailed steps to create an MCP server
2. Use the OpenWeatherMap API to obtain weather data
3. Building a TypeScript development environment and generating server scaffolding
In this article, we will demonstrate how to write an MCP server through a TypeScript MCP server example. We will create a weather server that provides current weather data as a resource and let Claude use tools to get the weather forecast.
Here we need to use the OpenWeatherMap API [2] to obtain weather data. Simply register and obtain a free API key on the API keys [3] page.
Environment Preparation
We need to prepare a TypeScript development environment, so we need to install Node.js and NPM.
# Check Node.js version, v18 or higher is required
node --version
# Check npm version
npm --version
Next we can directly use@modelcontextprotocol/create-server
This tool creates a scaffolding MCP server:
$ npx @modelcontextprotocol/create-server weather-server
Need to install the following packages:
@modelcontextprotocol/create-server@0.3.1
Ok to proceed? (y) y
? What is the name of your MCP server? y
? What is the description of your server? A Model Context Protocol server
? Would you like to install this server for Claude.app? Yes
✔MCP server created successfully!
✓ Successfully added MCP server to Claude.app configuration
Next steps:
cd weather-server
npm install
npm run build # or: npm run watch
npm link # optional, to make available globally
$ cd weather-server
Then install the dependencies:
npm install --save axios dotenv
Next we need to set the environment variables, create.env
document:
OPENWEATHER_API_KEY=your-api-key-here
Make sure.env
Add files to.gitignore
in the file.
After the project is created, we can see that the project structure is as follows:
Template analysis
The project we created using the scaffolding creates a MCP server template for us by default. This template implements a simple note system that illustrates core MCP concepts such as resources and tools in the following ways:
Listing notes as resources Read personal notes Create a new note via the tool Summarize all notes with prompts
Before we write our own MCP server, we can first learn how this note system is implemented, and then we can implement our own MCP server based on this template.
First, let's look at the imported dependency packages:
import { Server } from "@modelcontextprotocol/sdk/server/index.js" ;
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" ;
import {
CallToolRequestSchema,
ListResourcesRequestSchema,
ListToolsRequestSchema,
ReadResourceRequestSchema,
ListPromptsRequestSchema,
GetPromptRequestSchema,
} from "@modelcontextprotocol/sdk/types.js" ;
The first line of code is actually imported from the MCP SDKServer
This object represents an MCP server that will automatically respond to the initialization process initiated by the client.ListResourcesRequestSchema
,ReadResourceRequestSchema
These objects represent the request types defined in the MCP protocol.
For example, in the templateServer
Created an MCP server:
const server = new Server(
{
name: "weather-server" ,
version: "0.1.0" ,
},
{
capabilities:
resources: {},
tools: {},
prompts: {},
},
}
);
This code creates an MCP server with resources (for listing/reading notes), tools (for creating new notes), and prompts (for summarizing notes), and specifies the server name and version.
Once the server is initialized, we canServer
ObjectsetRequestHandler
Method to register the processor. When the protocol object receives a request of the specified method, it will be called. This process is actually equivalent to writing an HTTP server. When a request of the specified HTTP method is received, the processor we registered will be called.
In the template, you can see several processors registered:
Resources
Here we need toResources
Resources are a concept in the MCP protocol, which means an object that can be read and operated. We can expose the data and content in the server to LLM as resources, and then LLM can operate these resources through tools. For example, we can use a note, a file, a database table, etc. as resources.
⚠️ It is important to note that resources are designed to be application controlled, which means that the client application can decide how and when to use them. Different MCP clients may handle resources in different ways. For example:
Claude Desktop now requires the user to explicitly select a resource before using it Other clients may automatically select resources Some implementations may even allow the AI model itself to determine which resources to use So server developers should be prepared to handle any of these interaction patterns when implementing resource support. To automatically expose data to the model, server authors should use model-controlled primitives, such as tools.
Resources represent any type of data that the MCP server wishes to provide to the client, and can include the following types:
File Contents Database records API Response Real-time system data Screenshots and Images Log files And much more
Each resource has a uniqueURI
Identifies and can contain text or binary data.
In the template, all note resources are exposed to the client as resources.setRequestHandler
Registered handler to handle client'sresources/list
ask:
/**
* Handler for listing available notes as resources.
* Each note is exposed as a resource with the following characteristics:
* - note:// URI scheme
* - MIME type
* - Human-readable name and description (including the note title)
*/
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: Object .entries(notes).map( ( [id, note] ) => ({
uri: `note:/// ${id} ` ,
mimeType: "text/plain" ,
name: note.title,
description: `A text note: ${note.title} ` ,
})),
};
});
Resource URI
Resources are identified using URIs that follow the following format:
For example:
file:///home/user/documents/report.pdf
postgres://database/customers/schema
screen://localhost/display1
inprotocol
Protocol andpath
The path structure is defined by the MCP server implementation, although servers may define their own custom URI schemes.
Resource Type
Resources can contain two types of content:
Text resources
Text resources contain UTF-8 encoded text data, which are suitable for:
source code Configuration Files Log files JSON/XML data Plain text
Binary resources
The binary resource containsbase64
Encoded raw binary data, these are suitable for:
picture PDF File Audio Files Video Files Other non-text formats
Resource Discovery
There are two main ways that clients can discover available resources:
Direct resources
Server viaresources/list
The endpoint exposes a list of specific resources. Each resource includes:
{
uri: string; // Unique identifier of the resource
name: string; // human readable name
description?: string; // optional description
mimeType?: string; // Optional MIME type
}
Resource Templates
For dynamic resources, the server can expose URI templates that clients can use to construct valid resource URIs:
{
uriTemplate: string; // URI template following RFC 6570
name: string; // human-readable name of the type
description?: string; // optional description
mimeType?: string; // Optional MIME type for all matching resources
}
Reading Resources
To read a resource, the client issues aresources/read
Request. The server responds with a resource content list like this:
{
contents: [
{
uri: string; // URI of the resource
mimeType?: string; // Optional MIME type
// One of:
text?: string; // For text resources
blob?: string; // for binary resources (base64 encoded)
}
]
}
The server may return multiple resources in response to aresources/read
Requests, such as returning a list of files in a directory when reading a directory.
For example, the processor for reading notes in the template is implemented as follows:
/**
* A handler for reading the contents of a specified note.
* Accepts a note:// URI and returns the note contents as plain text.
*/
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const url = new URL(request.params.uri);
const id = url.pathname.replace( /^\// , "" );
const note = notes[id];
if (!note) {
throw new Error ( `Note ${id} not found` );
}
return {
contents: [
{
uri: request.params.uri,
mimeType: "text/plain" ,
text: note.content,
},
],
};
});
Resource Update
MCP supports real-time updates of resources through two mechanisms:
List changes
When the list of available resources changes, the server cannotification/resources/list_changed
Notify the client.
Content Changes
Clients can subscribe to updates for a specific resource:
The client sends the resource URI resources/subscribe
askSent by the server when a resource changes notification/resources/update
notifyThe client can resources/read
To get the latest contentClients can unsubscribe from resources/unsubscribe
Tools
Tools enable LLMs to perform actions through your server. Tools enable servers to expose executable functions to clients. Through tools, LLMs can interact with external systems, perform calculations, and perform actions in the real world. Tools are exposed from servers to clients so that AI models can automatically call them (grant approval).
The tools in MCP allow the server to expose executable functions that can be called by clients and used by LLM to perform operations. The implementation tools mainly include the following aspects:
Discovery : The client can tools/list
Endpoint lists available tool callsCall : Use tools/call
Endpoint invocation facility where the server performs the requested operation and returns the resultFlexibility : Tools can range from simple calculations to complex API interactions
Like resources, tools are identified by a unique name and can include a description to identify their purpose, but unlike resources, tools represent dynamic operations that can modify state or interact with external systems.
Each tool is defined with the following structure:
{
name: string; // unique identifier of the tool
description?: string; // human readable description
inputSchema: { // JSON Schema for tool parameters
type: "object" ;
properties: { ... } // Tool specific parameters
}
}
For example, in the template code,setRequestHandler
The tool list processor is registered, the code is as follows:
/**
* Handler for listing available tools.
* Expose a "create_note" tool to let clients create new notes.
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "create_note" ,
description: "Create a new note" ,
inputSchema: {
type : "object" ,
properties:
title:
type : "string" ,
description: "Title of the note" ,
},
content: {
type : "string" ,
description: "Text content of the note" ,
},
},
required: [ "title" , "content" ],
},
},
],
};
});
The above code defines a tool structure namedcreate_note
This tool is used to create a new note and needs to receive two parameters:title
andcontent
, that is, the title and content of the note, so that the client knows that there iscreate_note
This tool can be called, and it is known that calling the tool requires passing intitle
andcontent
parameter.
The above register only lists all available tools. To actually call the implementation tool, you also need to register the tool call processor. The code is as follows:
/**
* Handler for creating new notes.
* Creates a new note with the provided title and content and returns a success message.
*/
server.setRequestHandler(CallToolRequestSchema, async (request) => {
switch (request.params.name) {
case "create_note" : {
const title = String (request.params.arguments?.title);
const content = String (request.params.arguments?.content);
if (!title || !content) {
throw new Error ( "Title and content are required" );
}
const id = String ( Object .keys(notes).length + 1 );
notes[id] = { title, content };
return {
content: [
{
type : "text" ,
text: `Created note ${id} : ${title} ` ,
},
],
};
}
default :
throw new Error ( "Unknown tool" );
}
});
The above code is very simple to implement, just according to the tool namecreate_note
To create a new note and return a success message, when the client callscreate_note
When the tool is used, the above processor code will be triggered.
Prompts
Prompts
Hints are the mechanism in the MCP protocol for defining reusable hint templates and workflows that clients can easily expose to users and LLMs. Hints are designed to be user-controlled, meaning they are exposed from the server to the client so that users can explicitly choose to use them.
Prompt words in MCP are predefined templates that can:
Accepts dynamic parameters Include context from resources Chain multiple interactions Guide specific workflows As UI elements (such as slash commands)
Each Prompt definition contains the following structure:
{
name: string; // unique identifier of the prompt word
description?: string; // human readable description
arguments?: [ // Optional parameter list
{
name: string; // parameter identifier
description?: string; // description of the parameter
required?: boolean; // Required?
}
]
}
The client canprompts/list
endpoint to discover all available prompt words, such as making a request like the following:
// Request
{
method: "prompts/list"
}
// Response
{
prompts: [
{
name: "analyze-code" ,
description: "Analyze code for potential improvements" ,
arguments: [
{
name: "language" ,
description: "Programming language" ,
required: true
}
]
}
]
}
Then you can passprompts/get
Endpoint to get detailed information of a specified prompt word:
// Request
{
"method" : "prompts/get" ,
"params" : {
"name" : "analyze-code" ,
"arguments" : {
"language" : "python"
}
}
}
// Response
{
"description" : "Analyze Python code for potential improvements" ,
"messages" : [
{
"role" : "user" ,
"content" : {
"type" : "text" ,
"text" : "Please analyze the following Python code for potential improvements:\n\n```python\ndef calculate_sum(numbers):\n total = 0\n for num in numbers:\n total = total + num\n return total\n\nresult = calculate_sum([1, 2, 3, 4, 5])\nprint(result)\n```"
}
}
]
}
For example, in the templatesetRequestHandler
The prompt word list processor is registered, and the code is as follows:
/**
* A handler for listing available cue words.
* Expose a "summarize_notes" prompt for summarizing all notes.
*/
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: [
{
name: "summarize_notes" ,
description: "Summarize all notes" ,
},
],
};
});
You can see that a file calledsummarize_notes
This prompt word is used to summarize all notes, but this prompt word does not define any parameters, so the client does not need to pass in any parameters when calling this prompt word.
Then to get the detailed information of the prompt word, you can useprompts/get
The endpoint is used to obtain the information. The template also usessetRequestHandler
The prompt word acquisition processor is registered, and the code is as follows:
/**
* A prompt word processor to summarize all notes.
* Returns a prompt word, requests a summary of all notes, and embeds the note contents as a resource.
*/
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
if (request.params.name !== "summarize_notes" ) {
throw new Error ( "Unknown prompt" );
}
const embeddedNotes = Object .entries(notes).map( ( [id, note] ) => ({
type : "resource" as const ,
resource:
uri: `note:/// ${id} ` ,
mimeType: "text/plain" ,
text: note.content,
},
}));
return {
messages: [
{
role: "user" ,
content: {
type : "text" ,
text: "Please summarize the following notes:" ,
},
},
...embeddedNotes.map( ( note ) => ({
role: "user" as const ,
content: note,
})),
{
role: "user" ,
content: {
type : "text" ,
text: "Provide a concise summary of all the notes above." ,
},
},
],
};
});
As you can see from the above code, when the prompt word is generated, all the note content will be embedded in the prompt word, so that the context has the relevant content of the note.
Start the server
At this point, we have implemented a simple MCP server and registered resource, tool, prompt word and other processors. Of course, we still need to start the server in the end so that the server we wrote can actually run.
In the templatestdio
The transmission starts the server, the code is as follows:
/**
* Start the server using the stdio transport.
* Allow the server to communicate via standard input/output streams.
*/
async function main () {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch( ( error ) => {
console .error( "Server error:" , error);
process.exit( 1 );
});
stdio
The transport supports communication via standard input and output streams, which is particularly useful for local integration and command-line tools. We can use it in the following situationsstdio
:
Building command line tools Implement native integration Need simple process communication Using shell scripts
Apart fromstdio
transport, MCP also supports Server-Sent Events (SSE
) transport, SSE transport supports server-to-client streaming via HTTP POST requests for client-to-server communication. We can use it in the following situationsSSE
:
Only server-to-client streaming is required Using restricted networks Implementing simple updates
Writing Code
We have analyzed the implementation of the MCP server above. Next, we can write code according to our needs. Our requirement is to provide a weather query service. Here we can use weather data as a resource and expose a weather query tool.
First, we define the type of weather resource. The code is as follows:
// src/types/weather.ts
export interface OpenWeatherResponse {
main: {
temp: number ;
humidity: number ;
};
weather: Array <{
description: string ;
}>;
wind:
speed: number ;
};
dt_txt?: string ;
}
export interface WeatherData {
temperature: number ;
conditions: string ;
humidity: number ;
wind_speed: number ;
timestamp: string ;
}
export interface ForecastDay {
date: string ;
temperature: number ;
conditions: string ;
}
export interface GetForecastArgs {
city: string ;
days?: number ;
}
// Type guard function to check the GetForecastArgs type
export function isValidForecastArgs ( args: any ): args is GetForecastArgs {
return (
typeof args === "object" &&
args !== null &&
"city" in args &&
typeof args.city === "string" &&
(args.days === undefined || typeof args.days === "number" )
);
}
The type definitions here are mainly defined based on the response data types of the OpenWeather API, so that we can use these types conveniently.
Then write the following basic code to replace the templatesrc/index.ts
The code in:
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js" ;
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" ;
import {
ListResourcesRequestSchema,
ReadResourceRequestSchema,
ListToolsRequestSchema,
CallToolRequestSchema,
ErrorCode,
McpError,
} from "@modelcontextprotocol/sdk/types.js" ;
import axios from "axios" ;
import dotenv from "dotenv" ;
import {
WeatherData,
Forecast Day,
OpenWeatherResponse,
isValidForecastArgs,
} from "./types.js" ;
dotenv.config();
const API_KEY = process.env.OPENWEATHER_API_KEY;
if (!API_KEY) {
throw new Error ( "OPENWEATHER_API_KEY environment variable is required" );
}
const API_CONFIG = {
BASE_URL: "http://api.openweathermap.org/data/2.5" ,
DEFAULT_CITY: "San Francisco" ,
ENDPOINTS: {
CURRENT: "weather" ,
FORECAST: "forecast" ,
},
} as const ;
class WeatherServer {
private server: Server;
private axiosInstance;
constructor () {
this .server = new Server(
{
name: "weather-server" ,
version: "0.1.0" ,
},
{
capabilities:
resources: {},
tools: {},
},
}
);
// Configure the axios instance
this .axiosInstance = axios.create({
baseURL: API_CONFIG.BASE_URL,
params: {
appid: API_KEY,
units: "metric" ,
},
});
this .setupHandlers();
this .setupErrorHandling();
}
private setupErrorHandling(): void {
this .server.onerror = ( error ) => {
console .error( "[MCP Error]" , error);
};
process.on( "SIGINT" , async () => {
await this .server.close();
process.exit( 0 );
});
}
private setupHandlers(): void {
this .setupResourceHandlers();
this .setupToolHandlers();
}
private setupResourceHandlers(): void {
// TODO: Implement resource handler
}
private setupToolHandlers(): void {
// TODO: Implement tool handler
}
async run(): Promise < void > {
const transport = new StdioServerTransport();
await this .server.connect(transport);
console .error( "Weather MCP server running on stdio" );
}
}
const server = new WeatherServer();
server.run().catch( console .error);
Here we have done a little encapsulation based on the template and defined it in a class way. We mainly do the following things:
Defines the type of weather resource Initialized an MCP server instance Registered resource and tool handlers Started the server
Among them, the resource and tool processors we passTODO
Now that we have marked them, we can implement these processors.
Implementing a resource handler
existsetupResourceHandlers
In the method, we implement the resource processor. First, add a processor that lists resources, and then add a processor that reads resources. The code is as follows:
private setupResourceHandlers(): void {
this .server.setRequestHandler(
ListResourcesRequestSchema,
async () => ({
resources: [{
uri: `weather:// ${API_CONFIG.DEFAULT_CITY} /current` ,
name: `Current weather in ${API_CONFIG.DEFAULT_CITY} ` ,
mimeType: "application/json" ,
description: "Real-time weather data including temperature, conditions, humidity, and wind speed"
}]
})
);
this .server.setRequestHandler(
ReadResourceRequestSchema,
async (request) => {
const city = API_CONFIG.DEFAULT_CITY;
if (request.params.uri !== `weather:// ${city} /current` ) {
throw new McpError(
ErrorCode.InvalidRequest,
`Unknown resource: ${request.params.uri} `
);
}
try {
const response = await this .axiosInstance.get<OpenWeatherResponse>(
API_CONFIG.ENDPOINTS.CURRENT,
{
params: { q: city }
}
);
const weatherData: WeatherData = {
temperature: response.data.main.temp,
conditions: response.data.weather[ 0 ].description,
humidity: response.data.main.humidity,
wind_speed: response.data.wind.speed,
timestamp: new Date ().toISOString()
};
return {
contents: [{
uri: request.params.uri,
mimeType: "application/json" ,
text: JSON .stringify(weatherData, null , 2 )
}]
};
} catch (error) {
if (axios.isAxiosError(error)) {
throw new McpError(
ErrorCode.InternalError,
`Weather API error: ${error.response?.data.message ?? error.message} `
);
}
throw error;
}
}
);
}
The implementation of the processor that lists resources is very simple. Here we customizeweather
The protocol is JSON format.axios
Request the OpenWeather API to get the current weather data, and then convert it intoWeatherData
Type and return it.
Implementing a tool processor
After the resource processor is implemented, we can implement the tool processor. The tool processor is mainly used to implement some tool functions. Here we implement a tool to query the future weather forecast. The code is as follows:
private setupToolHandlers(): void {
this .server.setRequestHandler(
ListToolsRequestSchema,
async () => ({
tools: [{
name: "get_forecast" ,
description: "Get weather forecast for a city" ,
inputSchema: {
type : "object" ,
properties:
city:
type : "string" ,
description: "City name"
},
days:
type : "number" ,
description: "Number of days (1-5)" ,
minimum: 1 ,
maximum: 5
}
},
required: [ "city" ]
}
}]
})
);
this .server.setRequestHandler(
CallToolRequestSchema,
async (request) => {
if (request.params.name !== "get_forecast" ) {
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${request.params.name} `
);
}
if (!isValidForecastArgs(request.params.arguments)) {
throw new McpError(
ErrorCode.InvalidParams,
"Invalid forecast arguments"
);
}
const city = request.params.arguments.city;
const days = Math .min(request.params.arguments.days || 3 , 5 );
try {
const response = await this .axiosInstance.get<{
list: OpenWeatherResponse[]
}>(API_CONFIG.ENDPOINTS.FORECAST, {
params: {
q: city,
cnt: days * 8 // The API returns data at 3-hour intervals
}
});
const forecasts: ForecastDay[] = [];
for ( let i = 0 ; i < response.data.list.length; i += 8 ) {
const dayData = response.data.list[i];
forecasts.push({
date: dayData.dt_txt?.split( ' ' )[ 0 ] ?? new Date ().toISOString().split( 'T' )[ 0 ],
temperature: dayData.main.temp,
conditions: dayData.weather[ 0 ].description
});
}
return {
content: [{
type : "text" ,
text: JSON .stringify(forecasts, null , 2 )
}]
};
} catch (error) {
if (axios.isAxiosError(error)) {
return {
content: [{
type : "text" ,
text: `Weather API error: ${error.response?.data.message ?? error.message} `
}],
isError: true ,
}
}
throw error;
}
}
);
}
Again, you need to implement the handler for listing tools first, and then implement the handler for calling tools. Here we only define a handler calledget_forecast
This tool is used to obtain the weather forecast for a specified city and needs to receive two parameterscity
anddays
,incity
is the city name,days
The default is 3 days. Of course, the data is obtained by requesting the OpenWeather API.
In fact, the resources we defined above can be directly obtained through tools. We can add a tool to obtain the current weather. Because the data is obtained through the OpenWeather API, it is also possible not to define resources. However, we define resources here to demonstrate the usage of MCP.
test
At this point we have implemented a simple weather MCP service, and then we can test it.
First we need to build the project:
npm run build
Then you need to update the configuration of Claude Desktop:
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
Add our weather service to the configuration as follows:
{
"mcpServers" : {
//...... Other server configurations
"weather" : {
"command" : "node" ,
"args" : [ "/Users/cnych/src/weather-server/build/index.js" ],
"env" : {
"OPENWEATHER_API_KEY" : "your_openweather_api_key"
}
}
}
}
inargs
is the file path after we build.env
This is the key of the OpenWeather API that we need to configure. Once the configuration is complete, restart Claude Desktop.
test
Next we can test it. Click the number button in the lower right corner of the Claude Desktop input box, and the definedget_forecast
tool.
Next we can test it, for example, we ask Claude for the weather forecast for the next 5 days:
Can you get me a 5-day forecast for Beijing and tell me if I should pack an umbrella?
You can see that it will callget_forecast
tool (authorization required) and displays the results.
debug
If we encounter problems during testing, we can debug it in some ways, such as viewing the detailed log of MCP:
# View logs in real time
tail -n 20 -f ~/Library/Logs/Claude/mcp* .log
The logs here capture information such as server connection events, configuration issues, runtime errors, message exchanges, etc.
In addition to the log, we can alsoChrome DevTools
To debug, access Chrome's developer tools in Claude Desktop to view client-side errors.~/Library/Application\ Support/Claude/developer_settings.json
Add the following configuration to open DevTools:
{
"allowDevTools" : true
}
Then use the shortcut keyCommand+Option+Shift+i
You can open DevTools, just like debugging in the Chrome browser.
In addition to the above conventional debugging methods, Claude MCP also provides aInspector
Tool, MCP Inspector is an interactive developer tool for testing and debugging MCP servers.
Directly throughnpx
The command can be used without installation:
npx @modelcontextprotocol/ inspector <command>
# or
npx @modelcontextprotocol/inspector < command > <arg1> <arg2>
If the server package comes from NPM, you can start it as follows:
npx -y @modelcontextprotocol/inspector npx <package-name> <args>
# For example
npx -y @modelcontextprotocol/inspector npx server-postgres postgres://127.0.0.1/testdb
If it is a locally built package, you can start it in the following way:
npx @modelcontextprotocol/inspector node path/to/server/index.js args...
For example, the weather service we built above can be started in the following way:
npx @modelcontextprotocol/inspector node /Users/cnych/src/weather-server/build/index.js
Inspector
After the tool is started,localhost:5173
Start a web page on which we can test and debug our weather service.
Please note that we need to click on the rightEnvironment Variables
button, then addOPENWEATHER_API_KEY
Environment variables, the value is the key of the OpenWeather API we applied for, and then clickConnect
button to connect to the weather service.
After the connection is successful, we can see the resources and tools of the weather service in the main window on the right, and we can test and debug. ClickList Resources
button to list the resources of the weather service, and click on the listed resources to read and display the resource content.
We can also test Tools by clickingList Tools
button to list the weather service tools, then click a specific tool, enter the parameters and clickRun Tool
button to invoke the tool and display the results.
Of course, in addition to Resources and Tools, you can also test Prompts, Sampling, etc.
So far we have implemented a simple weather MCP service.