Skip to content

Commit f5837d3

Browse files
committed
fix: Fix TypeScript errors in MCP implementation
1 parent c915fee commit f5837d3

File tree

3 files changed

+166
-150
lines changed

3 files changed

+166
-150
lines changed

packages/agent/src/core/mcp/index.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
/**
22
* Model Context Protocol (MCP) Integration
3-
*
3+
*
44
* This module provides integration with the Model Context Protocol (MCP),
55
* allowing MyCoder to use context from MCP-compatible servers.
6-
*
6+
*
77
* Uses the official MCP SDK: https://www.npmjs.com/package/@modelcontextprotocol/sdk
88
*/
99

10-
import * as MCP from '@modelcontextprotocol/sdk';
11-
12-
export { MCP };
13-
1410
/**
1511
* Configuration for MCP in mycoder.config.js
1612
*/
@@ -36,4 +32,4 @@ export interface McpServerConfig {
3632
/** Authentication token */
3733
token: string;
3834
};
39-
}
35+
}

packages/agent/src/tools/getTools.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { McpConfig } from '../core/mcp/index.js';
12
import { Tool } from '../core/types.js';
23

34
// Import tools
@@ -8,18 +9,16 @@ import { agentStartTool } from './interaction/agentStart.js';
89
import { userPromptTool } from './interaction/userPrompt.js';
910
import { fetchTool } from './io/fetch.js';
1011
import { textEditorTool } from './io/textEditor.js';
12+
import { createMcpTool } from './mcp.js';
1113
import { listBackgroundToolsTool } from './system/listBackgroundTools.js';
1214
import { respawnTool } from './system/respawn.js';
1315
import { sequenceCompleteTool } from './system/sequenceComplete.js';
1416
import { shellMessageTool } from './system/shellMessage.js';
1517
import { shellStartTool } from './system/shellStart.js';
1618
import { sleepTool } from './system/sleep.js';
17-
import { McpTool } from './mcp.js';
1819

1920
// Import these separately to avoid circular dependencies
2021

21-
import { McpConfig } from '../core/mcp/types.js';
22-
2322
interface GetToolsOptions {
2423
userPrompt?: boolean;
2524
mcpConfig?: McpConfig;
@@ -52,8 +51,8 @@ export function getTools(options?: GetToolsOptions): Tool[] {
5251

5352
// Add MCP tool if we have any servers configured
5453
if (mcpConfig.servers && mcpConfig.servers.length > 0) {
55-
const mcpTool = new McpTool(mcpConfig);
56-
tools.push(mcpTool as unknown as Tool);
54+
const mcpTool = createMcpTool(mcpConfig);
55+
tools.push(mcpTool);
5756
}
5857

5958
return tools;

packages/agent/src/tools/mcp.ts

Lines changed: 159 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -7,170 +7,191 @@
77
* Uses the official MCP SDK: https://www.npmjs.com/package/@modelcontextprotocol/sdk
88
*/
99

10-
import { MCP, McpConfig, McpServerConfig } from '../core/mcp';
11-
import { Tool } from './tool';
12-
import { ToolContext } from './types';
10+
import { z } from 'zod';
11+
import { zodToJsonSchema } from 'zod-to-json-schema';
1312

14-
/**
15-
* Parameters for listResources method
16-
*/
17-
interface ListResourcesParams {
18-
/** Optional server name to filter resources by */
19-
server?: string;
20-
}
13+
import { Tool } from '../core/types.js';
14+
import { McpConfig } from '../core/mcp/index.js';
2115

22-
/**
23-
* Parameters for getResource method
24-
*/
25-
interface GetResourceParams {
26-
/** URI of the resource to fetch */
27-
uri: string;
28-
}
16+
// Parameters for listResources method
17+
const listResourcesSchema = z.object({
18+
server: z
19+
.string()
20+
.optional()
21+
.describe('Optional server name to filter resources by'),
22+
});
23+
24+
// Parameters for getResource method
25+
const getResourceSchema = z.object({
26+
uri: z
27+
.string()
28+
.describe('URI of the resource to fetch in the format "scheme://path"'),
29+
});
30+
31+
// Return type for listResources
32+
const listResourcesReturnSchema = z.array(
33+
z.object({
34+
uri: z.string(),
35+
metadata: z.record(z.unknown()).optional(),
36+
})
37+
);
38+
39+
// Return type for getResource
40+
const getResourceReturnSchema = z.string();
41+
42+
type ListResourcesParams = z.infer<typeof listResourcesSchema>;
43+
type GetResourceParams = z.infer<typeof getResourceSchema>;
44+
type ListResourcesReturn = z.infer<typeof listResourcesReturnSchema>;
45+
type GetResourceReturn = z.infer<typeof getResourceReturnSchema>;
46+
47+
// Map to store MCP clients
48+
const mcpClients = new Map<string, any>();
2949

3050
/**
31-
* MCP Tool for interacting with MCP servers
51+
* Create a new MCP tool with the specified configuration
52+
* @param config MCP configuration
53+
* @returns The MCP tool
3254
*/
33-
export class McpTool extends Tool {
34-
private clients: Map<string, MCP.Client> = new Map();
55+
export function createMcpTool(config: McpConfig): Tool {
56+
// We'll import the MCP SDK dynamically to avoid TypeScript errors
57+
// This is a temporary solution until we can properly add type declarations
58+
const mcpSdk = require('@modelcontextprotocol/sdk');
3559

36-
/**
37-
* Create a new MCP tool
38-
* @param config MCP configuration
39-
*/
40-
constructor(private config: McpConfig = { servers: [], defaultResources: [] }) {
41-
super({
42-
name: 'mcp',
43-
description: 'Interact with Model Context Protocol (MCP) servers to retrieve resources',
44-
schema: {
45-
listResources: {
46-
description: 'List available resources from MCP servers',
47-
parameters: {
48-
server: {
49-
type: 'string',
50-
description: 'Optional server name to filter resources by',
51-
required: false,
52-
},
53-
},
54-
},
55-
getResource: {
56-
description: 'Fetch a resource from an MCP server',
57-
parameters: {
58-
uri: {
59-
type: 'string',
60-
description: 'URI of the resource to fetch in the format "scheme://path"',
61-
required: true,
62-
},
63-
},
64-
},
65-
},
66-
});
67-
68-
// Initialize MCP clients for each configured server
69-
this.initializeClients();
70-
}
60+
// Initialize MCP clients for each configured server
61+
mcpClients.clear();
7162

72-
/**
73-
* Initialize MCP clients for each configured server
74-
*/
75-
private initializeClients(): void {
76-
if (!this.config.servers || this.config.servers.length === 0) {
77-
return;
78-
}
79-
80-
for (const server of this.config.servers) {
63+
if (config.servers && config.servers.length > 0) {
64+
for (const server of config.servers) {
8165
try {
82-
const clientOptions: MCP.ClientOptions = {
66+
let clientOptions: any = {
8367
baseURL: server.url,
8468
};
8569

8670
// Add authentication if configured
8771
if (server.auth && server.auth.type === 'bearer') {
88-
clientOptions.headers = {
89-
Authorization: `Bearer ${server.auth.token}`,
72+
clientOptions = {
73+
...clientOptions,
74+
headers: {
75+
Authorization: `Bearer ${server.auth.token}`,
76+
},
9077
};
9178
}
9279

93-
const client = new MCP.Client(clientOptions);
94-
this.clients.set(server.name, client);
80+
const client = new mcpSdk.Client(clientOptions);
81+
mcpClients.set(server.name, client);
9582
} catch (error) {
9683
console.error(`Failed to initialize MCP client for server ${server.name}:`, error);
9784
}
9885
}
9986
}
10087

101-
/**
102-
* List available resources from MCP servers
103-
* @param params Optional parameters
104-
* @param _context Tool context
105-
* @returns List of available resources
106-
*/
107-
async listResources(
108-
params: ListResourcesParams = {},
109-
_context: ToolContext,
110-
): Promise<MCP.Resource[]> {
111-
const resources: MCP.Resource[] = [];
88+
// Define the MCP tool
89+
return {
90+
name: 'mcp',
91+
description: 'Interact with Model Context Protocol (MCP) servers to retrieve resources',
92+
parameters: z.discriminatedUnion('method', [
93+
z.object({
94+
method: z.literal('listResources'),
95+
params: listResourcesSchema.optional(),
96+
}),
97+
z.object({
98+
method: z.literal('getResource'),
99+
params: getResourceSchema,
100+
}),
101+
]),
102+
parametersJsonSchema: zodToJsonSchema(
103+
z.discriminatedUnion('method', [
104+
z.object({
105+
method: z.literal('listResources'),
106+
params: listResourcesSchema.optional(),
107+
}),
108+
z.object({
109+
method: z.literal('getResource'),
110+
params: getResourceSchema,
111+
}),
112+
])
113+
),
114+
returns: z.union([listResourcesReturnSchema, getResourceReturnSchema]),
115+
returnsJsonSchema: zodToJsonSchema(z.union([listResourcesReturnSchema, getResourceReturnSchema])),
112116

113-
// If a specific server is requested, only check that server
114-
if (params.server) {
115-
const client = this.clients.get(params.server);
116-
if (client) {
117-
try {
118-
const serverResources = await client.resources();
119-
resources.push(...serverResources);
120-
} catch (error) {
121-
console.error(`Failed to fetch resources from server ${params.server}:`, error);
122-
}
117+
execute: async ({ method, params }, { logger }) => {
118+
// Extract the server name from a resource URI
119+
function getServerNameFromUri(uri: string): string | undefined {
120+
const match = uri.match(/^([^:]+):\/\//);
121+
return match ? match[1] : undefined;
123122
}
124-
} else {
125-
// Otherwise, check all servers
126-
for (const [serverName, client] of this.clients.entries()) {
127-
try {
128-
const serverResources = await client.resources();
129-
resources.push(...serverResources);
130-
} catch (error) {
131-
console.error(`Failed to fetch resources from server ${serverName}:`, error);
123+
124+
if (method === 'listResources') {
125+
// List available resources from MCP servers
126+
const resources: any[] = [];
127+
const serverFilter = params?.server;
128+
129+
// If a specific server is requested, only check that server
130+
if (serverFilter) {
131+
const client = mcpClients.get(serverFilter);
132+
if (client) {
133+
try {
134+
logger.verbose(`Fetching resources from server: ${serverFilter}`);
135+
const serverResources = await client.resources();
136+
resources.push(...(serverResources as any[]));
137+
} catch (error) {
138+
logger.error(`Failed to fetch resources from server ${serverFilter}:`, error);
139+
}
140+
} else {
141+
logger.warn(`Server not found: ${serverFilter}`);
142+
}
143+
} else {
144+
// Otherwise, check all servers
145+
for (const [serverName, client] of mcpClients.entries()) {
146+
try {
147+
logger.verbose(`Fetching resources from server: ${serverName}`);
148+
const serverResources = await client.resources();
149+
resources.push(...(serverResources as any[]));
150+
} catch (error) {
151+
logger.error(`Failed to fetch resources from server ${serverName}:`, error);
152+
}
153+
}
132154
}
155+
156+
return resources;
157+
} else if (method === 'getResource') {
158+
// Fetch a resource from an MCP server
159+
const uri = params.uri;
160+
161+
// Parse the URI to determine which server to use
162+
const serverName = getServerNameFromUri(uri);
163+
if (!serverName) {
164+
throw new Error(`Could not determine server from URI: ${uri}`);
165+
}
166+
167+
const client = mcpClients.get(serverName);
168+
if (!client) {
169+
throw new Error(`Server not found: ${serverName}`);
170+
}
171+
172+
// Use the MCP SDK to fetch the resource
173+
logger.verbose(`Fetching resource: ${uri}`);
174+
const resource = await client.resource(uri);
175+
return resource.content;
133176
}
134-
}
177+
178+
throw new Error(`Unknown method: ${method}`);
179+
},
135180

136-
return resources;
137-
}
138-
139-
/**
140-
* Fetch a resource from an MCP server
141-
* @param params Parameters
142-
* @param _context Tool context
143-
* @returns The resource content
144-
*/
145-
async getResource(
146-
params: GetResourceParams,
147-
_context: ToolContext,
148-
): Promise<string> {
149-
// Parse the URI to determine which server to use
150-
const serverName = this.getServerNameFromUri(params.uri);
151-
if (!serverName) {
152-
throw new Error(`Could not determine server from URI: ${params.uri}`);
153-
}
154-
155-
const client = this.clients.get(serverName);
156-
if (!client) {
157-
throw new Error(`Server not found: ${serverName}`);
158-
}
181+
logParameters: (params, { logger }) => {
182+
if (params.method === 'listResources') {
183+
logger.verbose(`Listing MCP resources${params.params?.server ? ` from server: ${params.params.server}` : ''}`);
184+
} else if (params.method === 'getResource') {
185+
logger.verbose(`Fetching MCP resource: ${params.params.uri}`);
186+
}
187+
},
159188

160-
// Use the MCP SDK to fetch the resource
161-
const resource = await client.resource(params.uri);
162-
return resource.content;
163-
}
164-
165-
/**
166-
* Extract the server name from a resource URI
167-
* @param uri Resource URI in the format 'scheme://path'
168-
* @returns The server name or undefined if not found
169-
* @private
170-
*/
171-
private getServerNameFromUri(uri: string): string | undefined {
172-
// For simplicity, we'll use the first part of the URI as the server name
173-
const match = uri.match(/^([^:]+):\/\//);
174-
return match ? match[1] : undefined;
175-
}
189+
logReturns: (result, { logger }) => {
190+
if (Array.isArray(result)) {
191+
logger.verbose(`Found ${result.length} MCP resources`);
192+
} else {
193+
logger.verbose(`Retrieved MCP resource content (${result.length} characters)`);
194+
}
195+
},
196+
};
176197
}

0 commit comments

Comments
 (0)