diff --git a/.changeset/long-spies-peel.md b/.changeset/long-spies-peel.md new file mode 100644 index 00000000..928288aa --- /dev/null +++ b/.changeset/long-spies-peel.md @@ -0,0 +1,8 @@ +--- +'workers-observability': minor +'workers-bindings': minor +'docs-vectorize': minor +'@repo/mcp-common': minor +--- + +Add search docs tool to bindings and obs servers diff --git a/apps/docs-vectorize/src/docs-vectorize.app.ts b/apps/docs-vectorize/src/docs-vectorize.app.ts index 9d15de6c..1f93b690 100644 --- a/apps/docs-vectorize/src/docs-vectorize.app.ts +++ b/apps/docs-vectorize/src/docs-vectorize.app.ts @@ -2,10 +2,9 @@ import { McpAgent } from 'agents/mcp' import { createApiHandler } from '@repo/mcp-common/src/api-handler' import { getEnv } from '@repo/mcp-common/src/env' +import { registerPrompts } from '@repo/mcp-common/src/prompts/docs-vectorize.prompts' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' - -import { registerPrompts } from './prompts/docs-vectorize.prompts' -import { registerDocsTools } from './tools/docs-vectorize.tools' +import { registerDocsTools } from '@repo/mcp-common/src/tools/docs-vectorize.tools' import type { Env } from './docs-vectorize.context' @@ -33,7 +32,7 @@ export class CloudflareDocumentationMCP extends McpAgent { } async init() { - registerDocsTools(this) + registerDocsTools(this, this.env) registerPrompts(this) } } diff --git a/apps/workers-bindings/src/bindings.app.ts b/apps/workers-bindings/src/bindings.app.ts index a4ba5d50..8dcfc5b1 100644 --- a/apps/workers-bindings/src/bindings.app.ts +++ b/apps/workers-bindings/src/bindings.app.ts @@ -8,10 +8,12 @@ import { } from '@repo/mcp-common/src/cloudflare-oauth-handler' import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { registerPrompts } from '@repo/mcp-common/src/prompts/docs-vectorize.prompts' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' import { registerD1Tools } from '@repo/mcp-common/src/tools/d1.tools' +import { registerDocsTools } from '@repo/mcp-common/src/tools/docs-vectorize.tools' import { registerHyperdriveTools } from '@repo/mcp-common/src/tools/hyperdrive.tools' import { registerKVTools } from '@repo/mcp-common/src/tools/kv_namespace.tools' import { registerR2BucketTools } from '@repo/mcp-common/src/tools/r2_bucket.tools' @@ -77,6 +79,10 @@ export class WorkersBindingsMCP extends McpAgent USER_DETAILS: DurableObjectNamespace MCP_METRICS: AnalyticsEngineDataset - DEV_DISABLE_OAUTH: string - DEV_CLOUDFLARE_API_TOKEN: string - DEV_CLOUDFLARE_EMAIL: string CLOUDFLARE_API_TOKEN: string OPENAI_API_KEY: string AI_GATEWAY_TOKEN: string CLOUDFLARE_ACCOUNT_ID: string AI_GATEWAY_ID: string AI: Ai + VECTORIZE: VectorizeIndex + DEV_DISABLE_OAUTH: string + DEV_CLOUDFLARE_API_TOKEN: string + DEV_CLOUDFLARE_EMAIL: string } diff --git a/apps/workers-bindings/wrangler.jsonc b/apps/workers-bindings/wrangler.jsonc index f54af12a..e171a91e 100644 --- a/apps/workers-bindings/wrangler.jsonc +++ b/apps/workers-bindings/wrangler.jsonc @@ -45,6 +45,15 @@ "CLOUDFLARE_CLIENT_ID": "", "CLOUDFLARE_CLIENT_SECRET": "" }, + "ai": { + "binding": "AI" + }, + "vectorize": [ + { + "binding": "VECTORIZE", + "index_name": "docs-bge-base" + } + ], "workers_dev": false, "preview_urls": false, "analytics_engine_datasets": [ @@ -77,6 +86,15 @@ "id": "4258ce2dee98428db6c9870eb5097f26" } ], + "ai": { + "binding": "AI" + }, + "vectorize": [ + { + "binding": "VECTORIZE", + "index_name": "docs-bge-base" + } + ], "vars": { "ENVIRONMENT": "staging", "MCP_SERVER_NAME": "workers-bindings-staging", @@ -117,6 +135,15 @@ "MCP_SERVER_NAME": "workers-bindings", "MCP_SERVER_VERSION": "1.0.0" }, + "ai": { + "binding": "AI" + }, + "vectorize": [ + { + "binding": "VECTORIZE", + "index_name": "docs-bge-base" + } + ], "analytics_engine_datasets": [ { "binding": "MCP_METRICS", diff --git a/apps/workers-observability/src/workers-observability.app.ts b/apps/workers-observability/src/workers-observability.app.ts index 69d5bdc5..79f04f63 100644 --- a/apps/workers-observability/src/workers-observability.app.ts +++ b/apps/workers-observability/src/workers-observability.app.ts @@ -8,10 +8,12 @@ import { } from '@repo/mcp-common/src/cloudflare-oauth-handler' import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' import { getEnv } from '@repo/mcp-common/src/env' +import { registerPrompts } from '@repo/mcp-common/src/prompts/docs-vectorize.prompts' import { RequiredScopes } from '@repo/mcp-common/src/scopes' import { initSentryWithUser } from '@repo/mcp-common/src/sentry' import { CloudflareMCPServer } from '@repo/mcp-common/src/server' import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' +import { registerDocsTools } from '@repo/mcp-common/src/tools/docs-vectorize.tools' import { registerWorkersTools } from '@repo/mcp-common/src/tools/worker.tools' import { MetricsTracker } from '../../../packages/mcp-observability/src' @@ -83,6 +85,10 @@ export class ObservabilityMCP extends McpAgent { // Register Cloudflare Workers logs tools registerObservabilityTools(this) + + // Add docs tools + registerDocsTools(this, this.env) + registerPrompts(this) } async getActiveAccountId() { diff --git a/apps/workers-observability/src/workers-observability.context.ts b/apps/workers-observability/src/workers-observability.context.ts index 3f70d82f..1054481d 100644 --- a/apps/workers-observability/src/workers-observability.context.ts +++ b/apps/workers-observability/src/workers-observability.context.ts @@ -15,6 +15,8 @@ export interface Env { SENTRY_ACCESS_CLIENT_SECRET: string GIT_HASH: string SENTRY_DSN: string + AI: Ai + VECTORIZE: VectorizeIndex DEV_DISABLE_OAUTH: string DEV_CLOUDFLARE_API_TOKEN: string DEV_CLOUDFLARE_EMAIL: string diff --git a/apps/workers-observability/wrangler.jsonc b/apps/workers-observability/wrangler.jsonc index 45c64255..2eaa2c95 100644 --- a/apps/workers-observability/wrangler.jsonc +++ b/apps/workers-observability/wrangler.jsonc @@ -35,6 +35,15 @@ "id": "DEV_KV" } ], + "ai": { + "binding": "AI" + }, + "vectorize": [ + { + "binding": "VECTORIZE", + "index_name": "docs-bge-base" + } + ], "vars": { "ENVIRONMENT": "development", "MCP_SERVER_NAME": "PLACEHOLDER", @@ -83,6 +92,15 @@ "MCP_SERVER_NAME": "workers-observability-staging", "MCP_SERVER_VERSION": "1.0.0" }, + "ai": { + "binding": "AI" + }, + "vectorize": [ + { + "binding": "VECTORIZE", + "index_name": "docs-bge-base" + } + ], "analytics_engine_datasets": [ { "binding": "MCP_METRICS", @@ -112,6 +130,15 @@ "id": "753f27a19ef94d7dbd49de05588ca890" } ], + "ai": { + "binding": "AI" + }, + "vectorize": [ + { + "binding": "VECTORIZE", + "index_name": "docs-bge-base" + } + ], "vars": { "ENVIRONMENT": "production", "GIT_HASH": "OVERRIDEN_DURING_DEPLOYMENT", diff --git a/apps/docs-vectorize/src/prompts/docs-vectorize.prompts.ts b/packages/mcp-common/src/prompts/docs-vectorize.prompts.ts similarity index 79% rename from apps/docs-vectorize/src/prompts/docs-vectorize.prompts.ts rename to packages/mcp-common/src/prompts/docs-vectorize.prompts.ts index 986326e3..d8ff45d3 100644 --- a/apps/docs-vectorize/src/prompts/docs-vectorize.prompts.ts +++ b/packages/mcp-common/src/prompts/docs-vectorize.prompts.ts @@ -1,10 +1,10 @@ -import type { CloudflareDocumentationMCP } from '../docs-vectorize.app' +import type { CloudflareMcpAgentNoAccount } from '../types/cloudflare-mcp-agent.types' /** * Registers developer-platform-related prompts with the MCP server * @param agent The MCP server instance */ -export function registerPrompts(agent: CloudflareDocumentationMCP) { +export function registerPrompts(agent: CloudflareMcpAgentNoAccount) { agent.server.prompt( 'workers-prompt-full', 'Detailed prompt for generating Cloudflare Workers code (and other developer platform products) from https://developers.cloudflare.com/workers/prompt.txt', diff --git a/apps/docs-vectorize/src/tools/docs-vectorize.tools.ts b/packages/mcp-common/src/tools/docs-vectorize.tools.ts similarity index 91% rename from apps/docs-vectorize/src/tools/docs-vectorize.tools.ts rename to packages/mcp-common/src/tools/docs-vectorize.tools.ts index e1ffc5ea..ce5f7a41 100644 --- a/apps/docs-vectorize/src/tools/docs-vectorize.tools.ts +++ b/packages/mcp-common/src/tools/docs-vectorize.tools.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import type { CloudflareDocumentationMCP } from '../docs-vectorize.app' +import type { CloudflareMcpAgentNoAccount } from '../types/cloudflare-mcp-agent.types' + +interface RequiredEnv { + AI: Ai + VECTORIZE: VectorizeIndex +} // Always return 10 results for simplicity, don't make it configurable const TOP_K = 10 @@ -9,7 +14,7 @@ const TOP_K = 10 * Registers the docs search tool with the MCP server * @param agent The MCP server instance */ -export function registerDocsTools(agent: CloudflareDocumentationMCP) { +export function registerDocsTools(agent: CloudflareMcpAgentNoAccount, env: RequiredEnv) { agent.server.tool( 'search_cloudflare_documentation', `Search the Cloudflare documentation. @@ -26,7 +31,7 @@ export function registerDocsTools(agent: CloudflareDocumentationMCP) { query: z.string(), }, async ({ query }) => { - const results = await queryVectorize(agent.env.AI, agent.env.VECTORIZE, query, TOP_K) + const results = await queryVectorize(env.AI, env.VECTORIZE, query, TOP_K) const resultsAsXml = results .map((result) => { return ` diff --git a/packages/mcp-common/src/types/cloudflare-mcp-agent.types.ts b/packages/mcp-common/src/types/cloudflare-mcp-agent.types.ts index 1e2e9c0f..fdcc19c7 100644 --- a/packages/mcp-common/src/types/cloudflare-mcp-agent.types.ts +++ b/packages/mcp-common/src/types/cloudflare-mcp-agent.types.ts @@ -13,8 +13,13 @@ type McpAgentWithoutServer = Omit< 'server' > -export interface CloudflareMcpAgent extends McpAgentWithoutServer { +export interface CloudflareMcpAgentNoAccount + extends McpAgentWithoutServer { server: CloudflareMCPServer +} + +export interface CloudflareMcpAgent + extends CloudflareMcpAgentNoAccount { setActiveAccountId(accountId: string): Promise getActiveAccountId(): Promise }