Skip to content

Commit e7780b1

Browse files
refactor(mcp): unify header setting between CLI and Worker implementations (#486)
* refactor(mcp): unify header setting between CLI and Worker implementations * chore: cleanup TS worker config * feat: add destructiveHint to self-targeting tools * fix: env.ENABLE_OUTPUT_SCHEMAS support * chore: cleanup setting mcp headers * chore: cleanup
1 parent b26e3cf commit e7780b1

File tree

9 files changed

+192
-74
lines changed

9 files changed

+192
-74
lines changed

mcp-worker/src/apiClient.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { UserProps, DevCycleJWTClaims } from './types'
22
import { IDevCycleApiClient } from '../../src/mcp/api/interface'
33
import { getErrorMessage, ensureError } from '../../src/mcp/utils/api'
4-
import { setDVCReferrer } from '../../src/api/apiClient'
4+
import { setMCPHeaders, setMCPToolCommand } from '../../src/mcp/utils/headers'
55

66
/**
77
* Interface for state management - allows McpAgent or other state managers
@@ -20,9 +20,15 @@ export class WorkerApiClient implements IDevCycleApiClient {
2020
private props: UserProps,
2121
private env: Env,
2222
private stateManager?: IStateManager,
23-
private version: string = 'unknown',
2423
) {}
2524

25+
/**
26+
* Initialize MCP headers with version - should be called once at server startup
27+
*/
28+
static initializeMCPHeaders(version: string): void {
29+
setMCPHeaders(version)
30+
}
31+
2632
/**
2733
* Execute an API operation with OAuth token authentication and consistent logging
2834
*/
@@ -45,8 +51,7 @@ export class WorkerApiClient implements IDevCycleApiClient {
4551
}
4652

4753
// Set MCP analytics headers for this specific tool operation
48-
// Caller is 'mcp' to distinguish from CLI and other callers; command is the tool name
49-
setDVCReferrer(operationName, this.version, 'mcp')
54+
setMCPToolCommand(operationName)
5055

5156
console.log(`Worker MCP ${operationName}:`, {
5257
args,

mcp-worker/src/index.ts

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import OAuthProvider from '@cloudflare/workers-oauth-provider'
22
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
3+
import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'
4+
import type { ZodRawShape } from 'zod'
35
import workerVersion from './version'
46
import { McpAgent } from 'agents/mcp'
57
import { createAuthApp, createTokenExchangeCallback } from './auth'
@@ -9,7 +11,7 @@ import { WorkerApiClient } from './apiClient'
911
import { registerAllToolsWithServer } from '../../src/mcp/tools/index'
1012

1113
// Import types
12-
import { DevCycleMCPServerInstance } from '../../src/mcp/server'
14+
import type { DevCycleMCPServerInstance } from '../../src/mcp/server'
1315
import { handleToolError } from '../../src/mcp/utils/errorHandling'
1416

1517
import { registerProjectSelectionTools } from './projectSelectionTools'
@@ -48,12 +50,14 @@ export class DevCycleMCP extends McpAgent<Env, DevCycleMCPState, UserProps> {
4850
* Initialize the MCP server with tools and handlers
4951
*/
5052
async init() {
53+
// Initialize MCP headers with version once at startup
54+
WorkerApiClient.initializeMCPHeaders(this.version)
55+
5156
// Initialize the Worker-specific API client with OAuth tokens and state management
5257
this.apiClient = new WorkerApiClient(
5358
this.props,
5459
this.env,
5560
this, // Pass the McpAgent instance for state management
56-
this.version,
5761
)
5862

5963
console.log('Initializing DevCycle MCP Worker', {
@@ -62,26 +66,50 @@ export class DevCycleMCP extends McpAgent<Env, DevCycleMCPState, UserProps> {
6266
hasProject: await this.apiClient.hasProjectKey(),
6367
})
6468

69+
// Check environment variable for output schema support
70+
const enableOutputSchemas =
71+
(this.env.ENABLE_OUTPUT_SCHEMAS as string) === 'true'
72+
if (enableOutputSchemas) {
73+
console.log('DevCycle MCP Worker - Output Schemas: ENABLED')
74+
}
75+
6576
// Create an adapter to make the worker's McpServer compatible with the CLI registration pattern
6677
const serverAdapter: DevCycleMCPServerInstance = {
6778
registerToolWithErrorHandling: (
6879
name: string,
6980
config: {
7081
description: string
71-
annotations?: any
72-
inputSchema?: any
73-
outputSchema?: any
82+
annotations?: ToolAnnotations
83+
inputSchema?: ZodRawShape
84+
outputSchema?: ZodRawShape
7485
},
75-
handler: (args: any) => Promise<any>,
86+
handler: (args: unknown) => Promise<unknown>,
7687
) => {
88+
// Conditionally include output schema based on environment variable
89+
const toolConfig: {
90+
description: string
91+
annotations?: ToolAnnotations
92+
inputSchema?: ZodRawShape
93+
outputSchema?: ZodRawShape
94+
} = {
95+
description: config.description,
96+
annotations: config.annotations,
97+
inputSchema: config.inputSchema,
98+
}
99+
100+
if (enableOutputSchemas && config.outputSchema) {
101+
toolConfig.outputSchema = config.outputSchema
102+
}
103+
77104
this.server.registerTool(
78105
name,
79-
{
80-
description: config.description,
81-
annotations: config.annotations,
82-
inputSchema: config.inputSchema || {},
83-
},
84-
async (args: any) => {
106+
// TypeScript workaround: The MCP SDK's registerTool has complex generic constraints
107+
// that cause "Type instantiation is excessively deep" errors when used with our
108+
// adapter pattern. The types are correct at runtime, but TS can't verify them.
109+
toolConfig as Parameters<
110+
typeof this.server.registerTool
111+
>[1],
112+
async (args: unknown) => {
85113
try {
86114
const result = await handler(args)
87115
return {

mcp-worker/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"compilerOptions": {
44
"outDir": "./dist",
55
"rootDir": "../",
6+
"noEmit": true,
67
"types": [
78
"node"
89
],

0 commit comments

Comments
 (0)