diff --git a/apps/workers-bindings/evals/hyperdrive.eval.ts b/apps/workers-bindings/evals/hyperdrive.eval.ts index f9aa8d42..d51e6c05 100644 --- a/apps/workers-bindings/evals/hyperdrive.eval.ts +++ b/apps/workers-bindings/evals/hyperdrive.eval.ts @@ -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, }) }) diff --git a/packages/mcp-common/src/tools/hyperdrive.ts b/packages/mcp-common/src/tools/hyperdrive.ts index 967a6d10..14d3baa8 100644 --- a/packages/mcp-common/src/tools/hyperdrive.ts +++ b/packages/mcp-common/src/tools/hyperdrive.ts @@ -19,6 +19,14 @@ import { 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. @@ -28,7 +36,7 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { * 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(), @@ -77,81 +85,82 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { } ) + // 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 = {} - 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 = {} + // 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, @@ -189,7 +198,7 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { * 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, @@ -229,7 +238,7 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { * 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, @@ -239,7 +248,6 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { 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: @@ -253,7 +261,6 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { port, scheme, user, - password, caching_disabled, caching_max_age, caching_stale_while_revalidate, @@ -269,7 +276,6 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { if (port) originPatch.port = port if (scheme) originPatch.scheme = scheme if (user) originPatch.user = user - if (password) originPatch.password = password const cachingPatch: Record = {} if (caching_disabled) cachingPatch.disabled = caching_disabled @@ -318,7 +324,4 @@ export function registerHyperdriveTools(agent: CloudflareMcpAgent) { } } ) - - // 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. }