Skip to content

Commit 43bb754

Browse files
authored
Merge pull request #59 from cloudflare/kv-refactor
feat: update KV tools
2 parents 48a91be + 6f41143 commit 43bb754

File tree

4 files changed

+154
-132
lines changed

4 files changed

+154
-132
lines changed

packages/mcp-common/src/api/kv.ts

Lines changed: 0 additions & 76 deletions
This file was deleted.

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

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import { type CallToolResult } from '@modelcontextprotocol/sdk/types.js'
22
import { z } from 'zod'
33

4-
import {
5-
handleKVNamespaceCreate,
6-
handleKVNamespaceDelete,
7-
handleKVNamespaceGet,
8-
handleKVNamespacesList,
9-
handleKVNamespaceUpdate,
10-
} from '../api/kv'
114
import { getCloudflareClient } from '../cloudflare-api'
125
import { type CloudflareMcpAgent } from '../types/cloudflare-mcp-agent'
6+
import {
7+
KvNamespaceIdSchema,
8+
KvNamespacesListParamsSchema,
9+
KvNamespaceTitleSchema,
10+
} from '../types/kv_namespace'
1311

14-
const kvNamespaceTitle = z.string().describe('The title of the kv namespace')
15-
const kvNamespaceId = z.string().describe('The id of the kv namespace')
12+
// Define the standard response for missing account ID
1613
const MISSING_ACCOUNT_ID_RESPONSE = {
1714
content: [
1815
{
@@ -23,21 +20,27 @@ const MISSING_ACCOUNT_ID_RESPONSE = {
2320
} satisfies CallToolResult
2421

2522
export function registerKVTools(agent: CloudflareMcpAgent) {
23+
/**
24+
* Tool to list KV namespaces.
25+
*/
2626
agent.server.tool(
2727
'kv_namespaces_list',
2828
'List all of the kv namespaces in your Cloudflare account',
29-
{},
30-
async () => {
29+
{ params: KvNamespacesListParamsSchema.optional() },
30+
async ({ params }) => {
3131
const account_id = agent.getActiveAccountId()
3232
if (!account_id) {
3333
return MISSING_ACCOUNT_ID_RESPONSE
3434
}
3535
try {
36-
const namespaces = await handleKVNamespacesList({
37-
client: getCloudflareClient(agent.props.accessToken),
36+
const client = getCloudflareClient(agent.props.accessToken)
37+
const response = await client.kv.namespaces.list({
3838
account_id,
39+
...params,
3940
})
4041

42+
const namespaces = response.result ?? []
43+
4144
return {
4245
content: [
4346
{
@@ -54,29 +57,31 @@ export function registerKVTools(agent: CloudflareMcpAgent) {
5457
content: [
5558
{
5659
type: 'text',
57-
text: `Error listing KV namespaces: ${error instanceof Error && error.message}`,
60+
text: `Error listing KV namespaces: ${error instanceof Error ? error.message : String(error)}`,
5861
},
5962
],
6063
}
6164
}
6265
}
6366
)
6467

68+
/**
69+
* Tool to create a KV namespace.
70+
*/
6571
agent.server.tool(
6672
'kv_namespace_create',
6773
'Create a new kv namespace in your Cloudflare account',
68-
{ title: kvNamespaceTitle },
74+
{
75+
title: KvNamespaceTitleSchema,
76+
},
6977
async ({ title }) => {
7078
const account_id = agent.getActiveAccountId()
7179
if (!account_id) {
7280
return MISSING_ACCOUNT_ID_RESPONSE
7381
}
7482
try {
75-
const namespace = await handleKVNamespaceCreate({
76-
client: getCloudflareClient(agent.props.accessToken),
77-
account_id,
78-
title: title,
79-
})
83+
const client = getCloudflareClient(agent.props.accessToken)
84+
const namespace = await client.kv.namespaces.create({ account_id, title })
8085
return {
8186
content: [
8287
{
@@ -90,34 +95,36 @@ export function registerKVTools(agent: CloudflareMcpAgent) {
9095
content: [
9196
{
9297
type: 'text',
93-
text: `Error creating KV namespace: ${error instanceof Error && error.message}`,
98+
text: `Error creating KV namespace: ${error instanceof Error ? error.message : String(error)}`,
9499
},
95100
],
96101
}
97102
}
98103
}
99104
)
100105

106+
/**
107+
* Tool to delete a KV namespace.
108+
*/
101109
agent.server.tool(
102110
'kv_namespace_delete',
103111
'Delete a kv namespace in your Cloudflare account',
104-
{ namespace_id: kvNamespaceId },
112+
{
113+
namespace_id: KvNamespaceIdSchema,
114+
},
105115
async ({ namespace_id }) => {
106116
const account_id = agent.getActiveAccountId()
107117
if (!account_id) {
108118
return MISSING_ACCOUNT_ID_RESPONSE
109119
}
110120
try {
111-
const namespace = await handleKVNamespaceDelete({
112-
client: getCloudflareClient(agent.props.accessToken),
113-
account_id,
114-
namespace_id,
115-
})
121+
const client = getCloudflareClient(agent.props.accessToken)
122+
const result = await client.kv.namespaces.delete(namespace_id, { account_id })
116123
return {
117124
content: [
118125
{
119126
type: 'text',
120-
text: JSON.stringify(namespace),
127+
text: JSON.stringify(result ?? { success: true }),
121128
},
122129
],
123130
}
@@ -126,29 +133,31 @@ export function registerKVTools(agent: CloudflareMcpAgent) {
126133
content: [
127134
{
128135
type: 'text',
129-
text: `Error deleting KV namespace: ${error instanceof Error && error.message}`,
136+
text: `Error deleting KV namespace: ${error instanceof Error ? error.message : String(error)}`,
130137
},
131138
],
132139
}
133140
}
134141
}
135142
)
136143

144+
/**
145+
* Tool to get details of a specific KV namespace.
146+
*/
137147
agent.server.tool(
138148
'kv_namespace_get',
139-
'Get a kv namespace in your Cloudflare account',
140-
{ namespace_id: kvNamespaceId },
149+
'Get details of a kv namespace in your Cloudflare account',
150+
{
151+
namespace_id: KvNamespaceIdSchema,
152+
},
141153
async ({ namespace_id }) => {
142154
const account_id = agent.getActiveAccountId()
143155
if (!account_id) {
144156
return MISSING_ACCOUNT_ID_RESPONSE
145157
}
146158
try {
147-
const namespace = await handleKVNamespaceGet({
148-
client: getCloudflareClient(agent.props.accessToken),
149-
account_id,
150-
namespace_id,
151-
})
159+
const client = getCloudflareClient(agent.props.accessToken)
160+
const namespace = await client.kv.namespaces.get(namespace_id, { account_id })
152161
return {
153162
content: [
154163
{
@@ -162,38 +171,40 @@ export function registerKVTools(agent: CloudflareMcpAgent) {
162171
content: [
163172
{
164173
type: 'text',
165-
text: `Error getting KV namespace: ${error instanceof Error && error.message}`,
174+
text: `Error getting KV namespace: ${error instanceof Error ? error.message : String(error)}`,
166175
},
167176
],
168177
}
169178
}
170179
}
171180
)
172181

182+
/**
183+
* Tool to update the title of a KV namespace.
184+
*/
173185
agent.server.tool(
174186
'kv_namespace_update',
175-
'Update a kv namespace in your Cloudflare account',
187+
'Update the title of a kv namespace in your Cloudflare account',
176188
{
177-
namespace_id: kvNamespaceId,
178-
title: kvNamespaceTitle,
189+
namespace_id: KvNamespaceIdSchema,
190+
title: KvNamespaceTitleSchema,
179191
},
180192
async ({ namespace_id, title }) => {
181193
const account_id = agent.getActiveAccountId()
182194
if (!account_id) {
183195
return MISSING_ACCOUNT_ID_RESPONSE
184196
}
185197
try {
186-
const namespaceUpdateResponse = await handleKVNamespaceUpdate({
187-
client: getCloudflareClient(agent.props.accessToken),
198+
const client = getCloudflareClient(agent.props.accessToken)
199+
const result = await client.kv.namespaces.update(namespace_id, {
188200
account_id,
189-
namespace_id,
190201
title,
191202
})
192203
return {
193204
content: [
194205
{
195206
type: 'text',
196-
text: JSON.stringify(namespaceUpdateResponse),
207+
text: JSON.stringify(result ?? { success: true }),
197208
},
198209
],
199210
}
@@ -202,7 +213,7 @@ export function registerKVTools(agent: CloudflareMcpAgent) {
202213
content: [
203214
{
204215
type: 'text',
205-
text: `Error updating KV namespace: ${error instanceof Error && error.message}`,
216+
text: `Error updating KV namespace: ${error instanceof Error ? error.message : String(error)}`,
206217
},
207218
],
208219
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { z } from 'zod'
2+
3+
import type {
4+
Namespace,
5+
NamespaceCreateParams,
6+
NamespaceDeleteParams,
7+
NamespaceGetParams,
8+
NamespaceListParams,
9+
NamespaceUpdateParams,
10+
} from 'cloudflare/resources/kv.mjs'
11+
12+
/**
13+
* Zod schema for a KV namespace ID.
14+
*/
15+
export const KvNamespaceIdSchema: z.ZodType<Namespace['id']> = z
16+
.string()
17+
.describe('The ID of the KV namespace')
18+
19+
/**
20+
* Zod schema for a KV namespace title.
21+
*/
22+
export const KvNamespaceTitleSchema: z.ZodType<Namespace['title']> = z
23+
.string()
24+
.describe('The human-readable name/title of the KV namespace')
25+
26+
/**
27+
* Zod schema for the optional parameters when listing KV namespaces.
28+
*/
29+
export const KvNamespacesListParamsSchema: z.ZodType<Omit<NamespaceListParams, 'account_id'>> = z
30+
.object({
31+
direction: z
32+
.enum(['asc', 'desc'])
33+
.optional()
34+
.describe('Direction to order namespaces (asc/desc)'),
35+
order: z.enum(['id', 'title']).optional().describe('Field to order namespaces by (id/title)'),
36+
page: z.number().int().positive().optional().describe('Page number of results (starts at 1)'),
37+
per_page: z
38+
.number()
39+
.int()
40+
.min(1)
41+
.max(100)
42+
.optional()
43+
.describe('Number of namespaces per page (1-100)'),
44+
})
45+
.describe('Optional parameters for listing KV namespaces')
46+
47+
/**
48+
* Zod schema for parameters needed to create a KV namespace.
49+
*/
50+
export const KvNamespaceCreateParamsSchema: z.ZodType<Omit<NamespaceCreateParams, 'account_id'>> = z
51+
.object({
52+
title: KvNamespaceTitleSchema,
53+
})
54+
.describe('Parameters for creating a KV namespace')
55+
56+
/**
57+
* Zod schema for parameters needed to delete a KV namespace.
58+
*/
59+
export const KvNamespaceDeleteParamsSchema: z.ZodType<Omit<NamespaceDeleteParams, 'account_id'>> = z
60+
.object({
61+
namespace_id: KvNamespaceIdSchema,
62+
})
63+
.describe('Parameters for deleting a KV namespace')
64+
65+
/**
66+
* Zod schema for parameters needed to get a KV namespace.
67+
*/
68+
export const KvNamespaceGetParamsSchema: z.ZodType<Omit<NamespaceGetParams, 'account_id'>> = z
69+
.object({
70+
namespace_id: KvNamespaceIdSchema,
71+
})
72+
.describe('Parameters for getting a KV namespace')
73+
74+
/**
75+
* Zod schema for parameters needed to update a KV namespace.
76+
*/
77+
export const KvNamespaceUpdateParamsSchema: z.ZodType<Omit<NamespaceUpdateParams, 'account_id'>> = z
78+
.object({
79+
namespace_id: KvNamespaceIdSchema,
80+
title: KvNamespaceTitleSchema,
81+
})
82+
.describe('Parameters for updating a KV namespace')

0 commit comments

Comments
 (0)