Server-side implementation patterns for server.tool(), server.resource(), and server.prompt().
Sign in to save downloads to your library and vote.
Preview
Tools, Resources, and Prompts
Server-side implementation patterns for server.tool(), server.resource(), and server.prompt().
Tools
Tools are actions the AI model can call.
Basic Tool
import { MCPServer, text, object, error } from "mcp-use/server";
import { z } from "zod";
const server = new MCPServer({
name: "my-server",
version: "1.0.0",
baseUrl: process.env.MCP_URL || "http://localhost:3000",
});
server.tool(
{
name: "translate-text",
description: "Translate text between languages",
schema: z.object({
text: z.string().describe("Text to translate"),
targetLanguage: z
.string()
.describe("Target language (e.g., 'Spanish', 'French')"),
sourceLanguage: z
.string()
.optional()
.describe("Source language (auto-detected if omitted)"),
}),
},
async ({ text: inputText, targetLanguage, sourceLanguage }) => {
// Your logic here
const translated = await translateAPI(
inputText,
targetLanguage,
sourceLanguage,
);
return text(`${translated}`);
},
);
Tool with Widget
When a tool returns visual UI, add widget config and return widget():
import { widget, text } from "mcp-use/server";
server.tool(
{
name: "get-weather",
description: "Get current weather for a city",
schema: z.object({
city: z.string().describe("City name"),
}),
widget: {
name: "weather-display", // Must match resources/weather-display.tsx
invoking: "Fetching weather...",
invoked: "Weather loaded",
},
},
async ({ city }) => {
const data = getWeather(city);
return widget({
props: { city, temp: data.temp, conditions: data.conditions }, // Sent to widget UI
output: text(`Weather in ${city}: ${data.temp}°C, ${data.conditions}`), // Model sees this
});
},
);
Tool Annotations
Declare the nature of your tool:
server.tool(
{
name: "delete-item",
description: "Delete an item permanently",
schema: z.object({ id: z.string().describe("Item ID") }),
annotations: {
destructiveHint: true, // Deletes or overwrites data
readOnlyHint: false, // Has side effects
openWorldHint: false, // Stays within user's account
},
},
async ({ id }) => {
await deleteItem(id);
return text(`Item ${id} deleted.`);
},
);
Tool Context
The second parameter to tool callbacks provides advanced capabilities:
server.tool(
{ name: "process-data", schema: z.object({ data: z.string() }) },
async ({ data }, ctx) => {
// Progress reporting
await ctx.reportProgress?.(0, 100, "Starting...");
// Structured logging
await ctx.log("info", `Processing ${data.length} chars`);
// Check client capabilities
if (ctx.client.can("sampling")) {
// Ask the LLM to help process
const result = await ctx.sample(`Summarize this: ${data}`);
}
await ctx.reportProgress?.(100, 100, "Done");
return text("Processed successfully");
},
);
Structured Output
Use outputSchema for typed, validated output:
server.tool(
{
name: "get-stats",
schema: z.object({ period: z.string() }),
outputSchema: z.object({
total: z.number(),
average: z.number(),
trend: z.enum(["up", "down", "flat"]),
}),
},
async ({ period }) => {
return object({ total: 150, average: 42.5, trend: "up" });
},
);
Resources
Resources expose read-only data clients can fetch.
Static Resource
import { object, text, markdown } from "mcp-use/server";
server.resource(
{
uri: "config://settings",
name: "Application Settings",
description: "Current server configuration",
mimeType: "application/json",
},
async () => object({ theme: "dark", version: "1.0.0", language: "en" }),
);
server.resource(
{
uri: "docs://guide",
name: "User Guide",
mimeType: "text/markdown",
},
async () => markdown("# User Guide\n\nWelcome to the app!"),
);
Dynamic Resource
server.resource(
{
uri: "stats://current",
name: "Current Stats",
mimeType: "application/json",
},
async () => {
const stats = await getStats();
return object(stats);
},
);
Parameterized Resource (Templates)
server.resourceTemplate(
{
uriTemplate: "user://{userId}/profile",
name: "User Profile",
description: "Get user profile by ID",
mimeType: "application/json",
},
async (uri, { userId }) => {
const user = await fetchUser(userId);
return object(user);
},
);
For advanced URI patterns, see resource-templates.md.
Prompts
Prompts are reusable message templates for AI interactions.
server.prompt(
{
name: "code-review",
description: "Generate a code review for the given language",
schema: z.object({
language: z.string().describe("Programming language"),
focusArea: z.string().optional().describe("Specific area to focus on"),
}),
},
async ({ language, focusArea }) => {
const focus = focusArea ? ` Focus on ${focusArea}.` : "";
return text(
`Please review this ${language} code for best practices and potential issues.${focus}`,
);
},
);
Zod Schema Best Practices
// Good: descriptive, with constraints
const schema = z.object({
city: z.string().describe("City name (e.g., 'New York', 'Tokyo')"),
units: z
.enum(["celsius", "fahrenheit"])
.optional()
.describe("Temperature units"),
limit: z.number().min(1).max(50).optional().describe("Max results to return"),
});
// Bad: no descriptions
const schema = z.object({
city: z.string(),
units: z.string(),
limit: z.number(),
});
Rules:
- Always add
.describe()to every field - Use
.optional()for non-required fields - Add validation (
.min(),.max(),.enum()) where appropriate - Use
z.enum()instead ofz.string()when there's a fixed set of values
Error Handling
import { text, error } from "mcp-use/server";
server.tool(
{ name: "fetch-data", schema: z.object({ id: z.string() }) },
async ({ id }) => {
try {
const data = await fetchFromAPI(id);
if (!data) {
return error(`No data found for ID: ${id}`);
}
return object(data);
} catch (err) {
return error(
`Failed to fetch data: ${err instanceof Error ? err.message : "Unknown error"}`,
);
}
},
);
Environment Variables
When your server needs API keys or configuration:
// index.ts - read from environment
const API_KEY = process.env.WEATHER_API_KEY;
server.tool(
{ name: "get-weather", schema: z.object({ city: z.string() }) },
async ({ city }) => {
if (!API_KEY) {
return error(
"WEATHER_API_KEY not configured. Please set it in the Env tab.",
);
}
const data = await fetch(
`https://api.weather.com/v1?key=${API_KEY}&city=${city}`,
);
// ...
},
);
Create a .env.example documenting required variables:
# Weather API key (get from weatherapi.com)
WEATHER_API_KEY=
Custom HTTP Routes
MCPServer extends Hono, so you can add custom API endpoints:
server.get("/api/health", (c) => c.json({ status: "ok" }));
server.post("/api/webhook", async (c) => {
const body = await c.req.json();
// Handle webhook
return c.json({ received: true });
});
Server Startup
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000;
server.listen(PORT);
Ingestion metadata
- Source catalog
- awesome-llm-apps
- Repository
- Shubhamsaboo/awesome-llm-apps · main
- File path
- generative_ui_agents/ai-mcp-app-builder/apps/mcp-use-server/.agent/skills/mcp-builder/references/tools-and-resources.md
- Last refreshed
- 10/24/2026, 3:00:14 AM (5h ago)
- Refresh schedule
- Daily · 03:00 UTC
- Dedupe status
- Unique · deduped by (source, url)