Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions apps/workers-bindings/evals/hyperdrive.eval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,42 @@ import { describeEval } from 'vitest-evals'

import { checkFactuality } from '@repo/eval-tools/src/scorers'
import { eachModel } from '@repo/eval-tools/src/test-models'
import { HYPERDRIVE_TOOLS } from '@repo/mcp-common/src/tools/hyperdrive'

import { initializeClient, runTask } from './utils' // Assuming utils.ts will exist here

const HYPERDRIVE_NAME = 'neon-test-hyperdrive'
const HYPERDRIVE_DATABASE = 'neondb'
const HYPERDRIVE_HOST = 'ep-late-cell-a4fm3g5p-pooler.us-east-1.aws.neon.tech'
const HYPERDRIVE_PORT = 5432
const HYPERDRIVE_USER = 'neondb_owner'
const HYPERDRIVE_PASSWORD = 'my-test-password'
// TODO: Add test for creating hyperdrive config with the following params once we can securely pass parameters to the tool. See: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/382
// const HYPERDRIVE_NAME = 'neon-test-hyperdrive'
// const HYPERDRIVE_DATABASE = 'neondb'
// const HYPERDRIVE_HOST = 'ep-late-cell-a4fm3g5p-pooler.us-east-1.aws.neon.tech'
// const HYPERDRIVE_PORT = 5432
// const HYPERDRIVE_USER = 'neondb_owner'
// const HYPERDRIVE_PASSWORD = 'my-test-password'

eachModel('$modelName', ({ model }) => {
describeEval('Hyperdrive Tool Evaluations', {
data: async () => [
{
input: `Create a new Hyperdrive configuration with the name "${HYPERDRIVE_NAME}" and the database "${HYPERDRIVE_DATABASE}" and the host "${HYPERDRIVE_HOST}" and the port "${HYPERDRIVE_PORT}" and the user "${HYPERDRIVE_USER}" and the password "${HYPERDRIVE_PASSWORD}".`,
expected:
'The hyperdrive_configs_create tool should be called to create a new hyperdrive configuration.',
input: `List my hyperdrive configurations.`,
expected: `The ${HYPERDRIVE_TOOLS.hyperdrive_configs_list} tool should be called to list my hyperdrive configurations.`,
},
],
task: async (input: string) => {
const client = await initializeClient(/* Pass necessary mocks/config */)
const { promptOutput, toolCalls } = await runTask(client, model, input)

const toolCall = toolCalls.find((call) => call.toolName === 'hyperdrive_config_create')
expect(toolCall, 'Tool hyperdrive_configs_create was not called').toBeDefined()
const toolCall = toolCalls.find(
(call) => call.toolName === HYPERDRIVE_TOOLS.hyperdrive_configs_list
)
expect(
toolCall,
`Tool ${HYPERDRIVE_TOOLS.hyperdrive_configs_list} was not called`
).toBeDefined()

return promptOutput
},
scorers: [checkFactuality],
threshold: 1,
timeout: 60000, // 60 seconds
timeout: 60000,
})
})
153 changes: 78 additions & 75 deletions packages/mcp-common/src/tools/hyperdrive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@
HyperdriveListParamPerPageSchema,
HyperdriveOriginDatabaseSchema,
HyperdriveOriginHostSchema,
HyperdriveOriginPasswordSchema,

Check warning on line 16 in packages/mcp-common/src/tools/hyperdrive.ts

View workflow job for this annotation

GitHub Actions / test (20)

'HyperdriveOriginPasswordSchema' is defined but never used

Check warning on line 16 in packages/mcp-common/src/tools/hyperdrive.ts

View workflow job for this annotation

GitHub Actions / test (20)

'HyperdriveOriginPasswordSchema' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 16 in packages/mcp-common/src/tools/hyperdrive.ts

View workflow job for this annotation

GitHub Actions / test (22)

'HyperdriveOriginPasswordSchema' is defined but never used

Check warning on line 16 in packages/mcp-common/src/tools/hyperdrive.ts

View workflow job for this annotation

GitHub Actions / test (22)

'HyperdriveOriginPasswordSchema' is defined but never used. Allowed unused vars must match /^_/u
HyperdriveOriginPortSchema,
HyperdriveOriginSchemeSchema,
HyperdriveOriginUserSchema,
} from '../types/hyperdrive'

export const HYPERDRIVE_TOOLS = {
hyperdrive_configs_list: 'hyperdrive_configs_list',
hyperdrive_config_create: 'hyperdrive_config_create',
hyperdrive_config_delete: 'hyperdrive_config_delete',
hyperdrive_config_get: 'hyperdrive_config_get',
hyperdrive_config_edit: 'hyperdrive_config_edit',
}

/**
* Registers Hyperdrive tools with the Cloudflare MCP Agent.
* @param agent The Cloudflare MCP Agent instance.
Expand All @@ -28,7 +36,7 @@
* Tool to list Hyperdrive configurations.
*/
agent.server.tool(
'hyperdrive_configs_list',
HYPERDRIVE_TOOLS.hyperdrive_configs_list,
'List Hyperdrive configurations in your Cloudflare account',
{
page: HyperdriveListParamPageSchema.nullable(),
Expand Down Expand Up @@ -77,81 +85,82 @@
}
)

// TODO: Once elicitation is available in MCP as a way to securely pass parameters, re-enable this tool. See: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/382
/**
* Tool to create a Hyperdrive configuration.
*/
agent.server.tool(
'hyperdrive_config_create',
'Create a new Hyperdrive configuration in your Cloudflare account',
{
name: HyperdriveConfigNameSchema,
database: HyperdriveOriginDatabaseSchema,
host: HyperdriveOriginHostSchema,
port: HyperdriveOriginPortSchema,
scheme: HyperdriveOriginSchemeSchema,
user: HyperdriveOriginUserSchema,
password: HyperdriveOriginPasswordSchema,
caching_disabled: HyperdriveCachingDisabledSchema.nullable(),
caching_max_age: HyperdriveCachingMaxAgeSchema.nullable(),
caching_stale_while_revalidate: HyperdriveCachingStaleWhileRevalidateSchema.nullable(),
},
async ({
name,
database,
host,
port,
scheme,
user,
password,
caching_disabled = undefined,
caching_max_age = undefined,
caching_stale_while_revalidate = undefined,
}) => {
const account_id = await agent.getActiveAccountId()
if (!account_id) {
return MISSING_ACCOUNT_ID_RESPONSE
}
try {
const origin = { database, host, port, scheme, user, password }
const caching: Record<string, any> = {}
if (caching_disabled !== undefined) caching.disabled = caching_disabled
if (caching_max_age !== undefined) caching.max_age = caching_max_age
if (caching_stale_while_revalidate !== undefined)
caching.stale_while_revalidate = caching_stale_while_revalidate
// agent.server.tool(
// HYPERDRIVE_TOOLS.hyperdrive_config_create,
// 'Create a new Hyperdrive configuration in your Cloudflare account',
// {
// name: HyperdriveConfigNameSchema,
// database: HyperdriveOriginDatabaseSchema,
// host: HyperdriveOriginHostSchema,
// port: HyperdriveOriginPortSchema,
// scheme: HyperdriveOriginSchemeSchema,
// user: HyperdriveOriginUserSchema,
// password: HyperdriveOriginPasswordSchema,
// caching_disabled: HyperdriveCachingDisabledSchema.nullable(),
// caching_max_age: HyperdriveCachingMaxAgeSchema.nullable(),
// caching_stale_while_revalidate: HyperdriveCachingStaleWhileRevalidateSchema.nullable(),
// },
// async ({
// name,
// database,
// host,
// port,
// scheme,
// user,
// password,
// caching_disabled = undefined,
// caching_max_age = undefined,
// caching_stale_while_revalidate = undefined,
// }) => {
// const account_id = await agent.getActiveAccountId()
// if (!account_id) {
// return MISSING_ACCOUNT_ID_RESPONSE
// }
// try {
// const origin = { database, host, port, scheme, user, password }
// const caching: Record<string, any> = {}
// if (caching_disabled !== undefined) caching.disabled = caching_disabled
// if (caching_max_age !== undefined) caching.max_age = caching_max_age
// if (caching_stale_while_revalidate !== undefined)
// caching.stale_while_revalidate = caching_stale_while_revalidate

const client = getCloudflareClient(agent.props.accessToken)
const hyperdriveConfig = await client.hyperdrive.configs.create({
account_id,
name,
origin,
...(Object.keys(caching).length > 0 && { caching }),
})
return {
content: [
{
type: 'text',
text: JSON.stringify(hyperdriveConfig),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error creating Hyperdrive config: ${error instanceof Error ? error.message : String(error)}`,
},
],
}
}
}
)
// const client = getCloudflareClient(agent.props.accessToken)
// const hyperdriveConfig = await client.hyperdrive.configs.create({
// account_id,
// name,
// origin,
// ...(Object.keys(caching).length > 0 && { caching }),
// })
// return {
// content: [
// {
// type: 'text',
// text: JSON.stringify(hyperdriveConfig),
// },
// ],
// }
// } catch (error) {
// return {
// content: [
// {
// type: 'text',
// text: `Error creating Hyperdrive config: ${error instanceof Error ? error.message : String(error)}`,
// },
// ],
// }
// }
// }
// )

/**
* Tool to delete a Hyperdrive configuration.
*/
agent.server.tool(
'hyperdrive_config_delete',
HYPERDRIVE_TOOLS.hyperdrive_config_delete,
'Delete a Hyperdrive configuration in your Cloudflare account',
{
hyperdrive_id: HyperdriveConfigIdSchema,
Expand Down Expand Up @@ -189,7 +198,7 @@
* Tool to get a specific Hyperdrive configuration.
*/
agent.server.tool(
'hyperdrive_config_get',
HYPERDRIVE_TOOLS.hyperdrive_config_get,
'Get details of a specific Hyperdrive configuration in your Cloudflare account',
{
hyperdrive_id: HyperdriveConfigIdSchema,
Expand Down Expand Up @@ -229,7 +238,7 @@
* Tool to edit (PATCH) a Hyperdrive configuration.
*/
agent.server.tool(
'hyperdrive_config_edit',
HYPERDRIVE_TOOLS.hyperdrive_config_edit,
'Edit (patch) a Hyperdrive configuration in your Cloudflare account',
{
hyperdrive_id: HyperdriveConfigIdSchema,
Expand All @@ -239,7 +248,6 @@
port: HyperdriveOriginPortSchema.optional().nullable(),
scheme: HyperdriveOriginSchemeSchema.optional().nullable(),
user: HyperdriveOriginUserSchema.optional().nullable(),
password: HyperdriveOriginPasswordSchema.optional().nullable(),
caching_disabled: HyperdriveCachingDisabledSchema.optional().nullable(),
caching_max_age: HyperdriveCachingMaxAgeSchema.optional().nullable(),
caching_stale_while_revalidate:
Expand All @@ -253,7 +261,6 @@
port,
scheme,
user,
password,
caching_disabled,
caching_max_age,
caching_stale_while_revalidate,
Expand All @@ -269,7 +276,6 @@
if (port) originPatch.port = port
if (scheme) originPatch.scheme = scheme
if (user) originPatch.user = user
if (password) originPatch.password = password

const cachingPatch: Record<string, any> = {}
if (caching_disabled) cachingPatch.disabled = caching_disabled
Expand Down Expand Up @@ -318,7 +324,4 @@
}
}
)

// Note: client.hyperdrive.configs.update (PUT) was requested but doesn't exist in the SDK.
// The SDK provides client.hyperdrive.configs.edit (PATCH) which is implemented above.
}
Loading