Skip to content

Commit 2f6e886

Browse files
committed
feat: Add MCP tool titles and hints to all Cloudflare tools
- Add MCP-compliant tool titles and annotations to 27 tools across 8 files - Implement readOnlyHint and destructiveHint for intelligent default approval - Follow MCP specification: 2-5 words, sentence case, active verbs - Enhanced UX with friendly tool names in MCP clients like Claude.ai - Enable auto-approval for 15 read-only tools, safety warnings for 4 destructive tools Files updated: - d1.tools.ts (5 tools) - account.tools.ts (2 tools) - docs-vectorize.tools.ts (2 tools) - hyperdrive.tools.ts (4 tools) - kv_namespace.tools.ts (5 tools) - r2_bucket.tools.ts (4 tools) - worker.tools.ts (3 tools) - zone.tools.ts (2 tools)
1 parent 50d442b commit 2f6e886

File tree

8 files changed

+184
-1
lines changed

8 files changed

+184
-1
lines changed

packages/mcp-common/src/tools/account.tools.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,17 @@ import type { CloudflareMcpAgent } from '../types/cloudflare-mcp-agent.types'
77

88
export function registerAccountTools(agent: CloudflareMcpAgent) {
99
// Tool to list all accounts
10-
agent.server.tool('accounts_list', 'List all accounts in your Cloudflare account', async () => {
10+
agent.server.tool(
11+
'accounts_list',
12+
'List all accounts in your Cloudflare account',
13+
{},
14+
{
15+
title: 'List accounts',
16+
annotations: {
17+
readOnlyHint: true,
18+
},
19+
},
20+
async () => {
1121
try {
1222
const results = await handleAccountsList({
1323
client: getCloudflareClient(agent.props.accessToken),
@@ -67,6 +77,13 @@ export function registerAccountTools(agent: CloudflareMcpAgent) {
6777
{
6878
activeAccountIdParam,
6979
},
80+
{
81+
title: 'Set active account',
82+
annotations: {
83+
readOnlyHint: false,
84+
destructiveHint: false,
85+
},
86+
},
7087
async (params) => {
7188
try {
7289
const { activeAccountIdParam: activeAccountId } = params

packages/mcp-common/src/tools/d1.tools.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ export function registerD1Tools(agent: CloudflareMcpAgent) {
2020
page: PaginationPageParam,
2121
per_page: PaginationPerPageParam,
2222
},
23+
{
24+
title: 'List D1 databases',
25+
annotations: {
26+
readOnlyHint: true,
27+
},
28+
},
2329
async ({ name, page, per_page }) => {
2430
const account_id = await agent.getActiveAccountId()
2531
if (!account_id) {
@@ -65,6 +71,13 @@ export function registerD1Tools(agent: CloudflareMcpAgent) {
6571
name: D1DatabaseNameParam,
6672
primary_location_hint: D1DatabasePrimaryLocationHintParam.nullable().optional(),
6773
},
74+
{
75+
title: 'Create D1 database',
76+
annotations: {
77+
readOnlyHint: false,
78+
destructiveHint: false,
79+
},
80+
},
6881
async ({ name, primary_location_hint }) => {
6982
const account_id = await agent.getActiveAccountId()
7083
if (!account_id) {
@@ -103,6 +116,13 @@ export function registerD1Tools(agent: CloudflareMcpAgent) {
103116
'd1_database_delete',
104117
'Delete a d1 database in your Cloudflare account',
105118
{ database_id: z.string() },
119+
{
120+
title: 'Delete D1 database',
121+
annotations: {
122+
readOnlyHint: false,
123+
destructiveHint: true,
124+
},
125+
},
106126
async ({ database_id }) => {
107127
const account_id = await agent.getActiveAccountId()
108128
if (!account_id) {
@@ -138,6 +158,12 @@ export function registerD1Tools(agent: CloudflareMcpAgent) {
138158
'd1_database_get',
139159
'Get a D1 database in your Cloudflare account',
140160
{ database_id: z.string() },
161+
{
162+
title: 'Get D1 database',
163+
annotations: {
164+
readOnlyHint: true,
165+
},
166+
},
141167
async ({ database_id }) => {
142168
const account_id = await agent.getActiveAccountId()
143169
if (!account_id) {
@@ -178,6 +204,13 @@ export function registerD1Tools(agent: CloudflareMcpAgent) {
178204
sql: D1DatabaseQuerySqlParam,
179205
params: D1DatabaseQueryParamsParam.nullable(),
180206
},
207+
{
208+
title: 'Query D1 database',
209+
annotations: {
210+
readOnlyHint: false,
211+
destructiveHint: false,
212+
},
213+
},
181214
async ({ database_id, sql, params }) => {
182215
const account_id = await agent.getActiveAccountId()
183216
if (!account_id) {

packages/mcp-common/src/tools/docs-vectorize.tools.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ export function registerDocsTools(agent: CloudflareMcpAgentNoAccount, env: Requi
3030
{
3131
query: z.string(),
3232
},
33+
{
34+
title: 'Search Cloudflare docs',
35+
annotations: {
36+
readOnlyHint: true,
37+
},
38+
},
3339
async ({ query }) => {
3440
const results = await queryVectorize(env.AI, env.VECTORIZE, query, TOP_K)
3541
const resultsAsXml = results
@@ -53,6 +59,13 @@ ${result.text}
5359
agent.server.tool(
5460
'migrate_pages_to_workers_guide',
5561
`ALWAYS read this guide before migrating Pages projects to Workers.`,
62+
{},
63+
{
64+
title: 'Get Pages migration guide',
65+
annotations: {
66+
readOnlyHint: true,
67+
},
68+
},
5669
async () => {
5770
const res = await fetch(
5871
'https://developers.cloudflare.com/workers/prompts/pages-to-workers.txt',

packages/mcp-common/src/tools/hyperdrive.tools.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) {
4343
order: HyperdriveListParamOrderSchema.nullable(),
4444
direction: HyperdriveListParamDirectionSchema.nullable(),
4545
},
46+
{
47+
title: 'List Hyperdrive configs',
48+
annotations: {
49+
readOnlyHint: true,
50+
},
51+
},
4652
async ({ page, per_page, order, direction }) => {
4753
const account_id = await agent.getActiveAccountId()
4854
if (!account_id) {
@@ -164,6 +170,13 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) {
164170
{
165171
hyperdrive_id: HyperdriveConfigIdSchema,
166172
},
173+
{
174+
title: 'Delete Hyperdrive config',
175+
annotations: {
176+
readOnlyHint: false,
177+
destructiveHint: true,
178+
},
179+
},
167180
async ({ hyperdrive_id }) => {
168181
const account_id = await agent.getActiveAccountId()
169182
if (!account_id) {
@@ -202,6 +215,12 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) {
202215
{
203216
hyperdrive_id: HyperdriveConfigIdSchema,
204217
},
218+
{
219+
title: 'Get Hyperdrive config',
220+
annotations: {
221+
readOnlyHint: true,
222+
},
223+
},
205224
async ({ hyperdrive_id }) => {
206225
const account_id = await agent.getActiveAccountId()
207226
if (!account_id) {
@@ -252,6 +271,13 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) {
252271
caching_stale_while_revalidate:
253272
HyperdriveCachingStaleWhileRevalidateSchema.optional().nullable(),
254273
},
274+
{
275+
title: 'Edit Hyperdrive config',
276+
annotations: {
277+
readOnlyHint: false,
278+
destructiveHint: false,
279+
},
280+
},
255281
async ({
256282
hyperdrive_id,
257283
name,

packages/mcp-common/src/tools/kv_namespace.tools.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ export function registerKVTools(agent: CloudflareMcpAgent) {
2929
- title: The title of the kv namespace.
3030
`,
3131
{ params: KvNamespacesListParamsSchema.optional() },
32+
{
33+
title: 'List KV namespaces',
34+
annotations: {
35+
readOnlyHint: true,
36+
},
37+
},
3238
async ({ params }) => {
3339
const account_id = await agent.getActiveAccountId()
3440
if (!account_id) {
@@ -80,6 +86,13 @@ export function registerKVTools(agent: CloudflareMcpAgent) {
8086
{
8187
title: KvNamespaceTitleSchema,
8288
},
89+
{
90+
title: 'Create KV namespace',
91+
annotations: {
92+
readOnlyHint: false,
93+
destructiveHint: false,
94+
},
95+
},
8396
async ({ title }) => {
8497
const account_id = await agent.getActiveAccountId()
8598
if (!account_id) {
@@ -118,6 +131,13 @@ export function registerKVTools(agent: CloudflareMcpAgent) {
118131
{
119132
namespace_id: KvNamespaceIdSchema,
120133
},
134+
{
135+
title: 'Delete KV namespace',
136+
annotations: {
137+
readOnlyHint: false,
138+
destructiveHint: true,
139+
},
140+
},
121141
async ({ namespace_id }) => {
122142
const account_id = await agent.getActiveAccountId()
123143
if (!account_id) {
@@ -163,6 +183,12 @@ export function registerKVTools(agent: CloudflareMcpAgent) {
163183
{
164184
namespace_id: KvNamespaceIdSchema,
165185
},
186+
{
187+
title: 'Get KV namespace',
188+
annotations: {
189+
readOnlyHint: true,
190+
},
191+
},
166192
async ({ namespace_id }) => {
167193
const account_id = await agent.getActiveAccountId()
168194
if (!account_id) {
@@ -202,6 +228,13 @@ export function registerKVTools(agent: CloudflareMcpAgent) {
202228
namespace_id: KvNamespaceIdSchema,
203229
title: KvNamespaceTitleSchema,
204230
},
231+
{
232+
title: 'Update KV namespace',
233+
annotations: {
234+
readOnlyHint: false,
235+
destructiveHint: false,
236+
},
237+
},
205238
async ({ namespace_id, title }) => {
206239
const account_id = await agent.getActiveAccountId()
207240
if (!account_id) {

packages/mcp-common/src/tools/r2_bucket.tools.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) {
2121
per_page: PaginationPerPageParam,
2222
start_after: BucketListStartAfterParam,
2323
},
24+
{
25+
title: 'List R2 buckets',
26+
annotations: {
27+
readOnlyHint: true,
28+
},
29+
},
2430
async ({ cursor, direction, name_contains, per_page, start_after }) => {
2531
const account_id = await agent.getActiveAccountId()
2632
if (!account_id) {
@@ -65,6 +71,13 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) {
6571
'r2_bucket_create',
6672
'Create a new r2 bucket in your Cloudflare account',
6773
{ name: BucketNameSchema },
74+
{
75+
title: 'Create R2 bucket',
76+
annotations: {
77+
readOnlyHint: false,
78+
destructiveHint: false,
79+
},
80+
},
6881
async ({ name }) => {
6982
const account_id = await agent.getActiveAccountId()
7083
if (!account_id) {
@@ -101,6 +114,12 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) {
101114
'r2_bucket_get',
102115
'Get details about a specific R2 bucket',
103116
{ name: BucketNameSchema },
117+
{
118+
title: 'Get R2 bucket',
119+
annotations: {
120+
readOnlyHint: true,
121+
},
122+
},
104123
async ({ name }) => {
105124
const account_id = await agent.getActiveAccountId()
106125
if (!account_id) {
@@ -134,6 +153,13 @@ export function registerR2BucketTools(agent: CloudflareMcpAgent) {
134153
'r2_bucket_delete',
135154
'Delete an R2 bucket',
136155
{ name: BucketNameSchema },
156+
{
157+
title: 'Delete R2 bucket',
158+
annotations: {
159+
readOnlyHint: false,
160+
destructiveHint: true,
161+
},
162+
},
137163
async ({ name }) => {
138164
const account_id = await agent.getActiveAccountId()
139165
if (!account_id) {

packages/mcp-common/src/tools/worker.tools.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ export function registerWorkersTools(agent: CloudflareMcpAgent) {
2323
// Tool to list all workers
2424
agent.server.tool(
2525
'workers_list',
26+
{
27+
title: 'List Workers',
28+
annotations: {
29+
readOnlyHint: true,
30+
destructiveHint: false,
31+
},
32+
},
2633
fmt.trim(`
2734
List all Workers in your Cloudflare account.
2835
@@ -90,6 +97,13 @@ export function registerWorkersTools(agent: CloudflareMcpAgent) {
9097
// Tool to get a specific worker's script details
9198
agent.server.tool(
9299
'workers_get_worker',
100+
{
101+
title: 'Get Worker details',
102+
annotations: {
103+
readOnlyHint: true,
104+
destructiveHint: false,
105+
},
106+
},
93107
'Get the details of the Cloudflare Worker.',
94108
{
95109
scriptName: workerNameParam,
@@ -156,6 +170,13 @@ export function registerWorkersTools(agent: CloudflareMcpAgent) {
156170
// Tool to get a specific worker's script content
157171
agent.server.tool(
158172
'workers_get_worker_code',
173+
{
174+
title: 'Get Worker code',
175+
annotations: {
176+
readOnlyHint: true,
177+
destructiveHint: false,
178+
},
179+
},
159180
'Get the source code of a Cloudflare Worker. Note: This may be a bundled version of the worker.',
160181
{ scriptName: workerNameParam },
161182
async (params) => {

packages/mcp-common/src/tools/zone.tools.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ export function registerZoneTools(agent: CloudflareMcpAgent) {
88
// Tool to list all zones under an account
99
agent.server.tool(
1010
'zones_list',
11+
{
12+
title: 'List zones',
13+
annotations: {
14+
readOnlyHint: true,
15+
destructiveHint: false,
16+
},
17+
},
1118
'List all zones under a Cloudflare account',
1219
{
1320
name: z.string().optional().describe('Filter zones by name'),
@@ -80,6 +87,13 @@ export function registerZoneTools(agent: CloudflareMcpAgent) {
8087
// Tool to get zone details by ID
8188
agent.server.tool(
8289
'zone_details',
90+
{
91+
title: 'Get zone details',
92+
annotations: {
93+
readOnlyHint: true,
94+
destructiveHint: false,
95+
},
96+
},
8397
'Get details for a specific Cloudflare zone',
8498
{
8599
zoneId: z.string().describe('The ID of the zone to get details for'),

0 commit comments

Comments
 (0)