Common tools
Seven ready-made tools from @webmcp-agents/tools: the time, the user's location, the current page, go to, go back, scroll to and set the theme. They go in your catalog beside your own.
Every site wants the same few tools: what day it is for this person, where they are, what page they are on, take them
somewhere, switch the theme. @webmcp-agents/tools ships them ready-made. Each one is a factory that returns a
catalog tool, so it sits in your catalog beside your own and is on everywhere.
A complete example
Install the package:
npm install @webmcp-agents/toolspnpm add @webmcp-agents/toolsyarn add @webmcp-agents/toolsbun add @webmcp-agents/toolsnub add @webmcp-agents/toolsaube add @webmcp-agents/toolsThen call the factories in your catalog. The highlighted lines are the common tools.
import {
getLocation,
getPage,
getTime,
goTo,
setTheme,
} from "@webmcp-agents/tools";
import { getCart } from "./get-cart";
import { listOrders } from "./list-orders";
import { trackOrder } from "./track-order";
// Every tool the app has, by name. The provider registers them, and
// each one's `match` says where it is on.
export const tools = {
// Common tools, ready-made.
getTime: getTime(),
getLocation: getLocation(),
getPage: getPage(),
goTo: goTo({
pages: { home: "/", cart: "/cart", orders: "/orders" },
}),
setTheme: setTheme(),
// Your own.
getCart,
listOrders,
trackOrder,
};import {
getLocation,
getPage,
getTime,
goTo,
setTheme,
} from "@webmcp-agents/tools";
import { getCart } from "./get-cart";
import { listOrders } from "./list-orders";
import { trackOrder } from "./track-order";
// Every tool the app has, by name. The provider registers them, and
// each one's `match` says where it is on.
export const tools = {
// Common tools, ready-made.
getTime: getTime(),
getLocation: getLocation(),
getPage: getPage(),
goTo: goTo({
pages: { home: "/", cart: "/cart", orders: "/orders" },
}),
setTheme: setTheme(),
// Your own.
getCart,
listOrders,
trackOrder,
};import {
getLocation,
getPage,
getTime,
goTo,
setTheme,
} from "@webmcp-agents/tools";
import { getCart } from "./get-cart";
import { listOrders } from "./list-orders";
import { trackOrder } from "./track-order";
// Every tool the app has, by name. The provider registers them, and
// each one's `match` says where it is on.
export const tools = {
// Common tools, ready-made.
getTime: getTime(),
getLocation: getLocation(),
getPage: getPage(),
goTo: goTo({
pages: { home: "/", cart: "/cart", orders: "/orders" },
}),
setTheme: setTheme(),
// Your own.
getCart,
listOrders,
trackOrder,
};import {
getLocation,
getPage,
getTime,
goTo,
setTheme,
} from "@webmcp-agents/tools";
import { getCart } from "./get-cart";
import { listOrders } from "./list-orders";
import { trackOrder } from "./track-order";
// Every tool the app has, by name. The provider registers them, and
// each one's `match` says where it is on.
export const tools = {
// Common tools, ready-made.
getTime: getTime(),
getLocation: getLocation(),
getPage: getPage(),
goTo: goTo({
pages: { home: "/", cart: "/cart", orders: "/orders" },
}),
setTheme: setTheme(),
// Your own.
getCart,
listOrders,
trackOrder,
};They need no framework, no router and no app state, and nothing touches the DOM until one runs, so a file your server imports can list them.
| Tool | Factory | Answers or does |
|---|---|---|
get_time |
getTime |
The user’s date, time, time zone and locale, and what “tomorrow”, “this weekend”, “next Friday” mean for them. |
get_location |
getLocation |
Coordinates and time zone after the browser’s permission prompt; city, region and country with a lookup. |
get_page |
getPage |
Where the user is: title, path, query parameters, hash, language, selected text, headings. |
go_to |
goTo |
Open one of the pages you list, by name. |
go_back |
goBack |
Back one step in history. |
scroll_to |
scrollTo |
Scroll to a heading or a piece of text and highlight it. |
set_theme |
setTheme |
Light, dark or system. |
Make them yours
Every factory takes { id?, description? }, so a tool says what your site means by it. Name your own tools in the
“next” part, the way Writing tool descriptions says. Skyline words get_time for its
flight search:
getTime({
description:
"Today's date and what 'tomorrow' or 'this weekend' mean for " +
"the user. Use before search_flights, which takes YYYY-MM-DD.",
});
goTo takes the pages it may open, by name, and only opens those. The others need nothing, and each has a few options
of its own:
getLocation({
// Name the place. The tool is then marked external.
lookup: async ({ lat, lon }, signal) => {
const res = await fetch(`/api/place?lat=${lat}&lon=${lon}`, { signal });
return res.ok ? res.json() : undefined; // { city, region, country }
},
});
setTheme({ control: { role: "button", name: "Appearance" } });
setTheme({ get: () => theme.current, set: (next) => theme.set(next) });
What each one does when it runs
get_timereads the user’s clock, time zone and locale, and works out the week by that locale (a Friday-to-Saturday weekend in some, Monday-first weeks in others), so the agent does not guess what “next Friday” is.get_locationasks the browser for the position and says it is waiting for the person while the prompt is up. It sends nothing anywhere unless you passlookup. Not shared, or unsupported: it answers with a guess from the time zone and tells the agent to ask the user.get_pageanswers from the URL and the document. Text selected inside library UI (the chat, the devtools) is ignored.go_toclicks the page’s own link to that path (found by itshref, so your router handles it and the cursor shows where it went). With no such link on screen, it loads the URL. An unknown name isinvalid_input, with the list.go_backgoes back one step and names where it went; with no history, it says so.scroll_toscrolls to a heading or a piece of text, highlights it and quotes the start of that section.set_themepresses the site’s theme switch when it finds one (a switch or button named “Theme”, “Dark mode”, “Toggle theme”…). Point it at yours withcontrol, or passgetandsetwhen the theme lives in state.
When one is not enough, build on it rather than copy it: Skyline’s get_location is the common one with a
reverse-geocoding lookup, plus Skyline’s nearest airports. Field Store keeps its own go_to_page, because its
checkout has no link to click.
Try it
In the browser console:
await agent.invoke("get_time");
It resolves to the MCP result, { content, structuredContent }: the text the agent reads, and the same facts as data.
Then await agent.invoke("go_to", { page: "orders" }): the cursor clicks your link to /orders.
Options you’ll use most
| Factory | Options |
|---|---|
| Every one | id, description |
goTo |
pages, required: the pages it may open, by name |
getLocation |
lookup, prompt, timeoutMs |
setTheme |
control, get, set |
getTime |
locale |
getPage |
maxHeadings |
scrollTo |
highlightMs |
Each factory’s page lists its options and its answer: @webmcp-agents/tools.