auto-coder 0.1.215__py3-none-any.whl → 0.1.218__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of auto-coder might be problematic. Click here for more details.

@@ -7,7 +7,8 @@ import re
7
7
  from pydantic import BaseModel, Field
8
8
  from loguru import logger
9
9
  import mcp.types as mcp_types
10
-
10
+ import os
11
+ import time
11
12
 
12
13
  class McpToolCall(BaseModel):
13
14
  server_name: str = Field(..., description="The name of the MCP server")
@@ -102,9 +103,11 @@ class McpExecutor:
102
103
  info.append(server_info)
103
104
 
104
105
  return "\n\n".join(info)
106
+
107
+
105
108
 
106
109
  @byzerllm.prompt()
107
- def mcp_prompt(self) -> str:
110
+ def mcp_prompt(self,query:str) -> str:
108
111
  """
109
112
  TOOL USE
110
113
 
@@ -194,358 +197,19 @@ class McpExecutor:
194
197
  When a server is connected, you can use the server's tools via the `use_mcp_tool` tool, and access the server's resources via the `access_mcp_resource` tool.
195
198
 
196
199
  {{ connected_servers_info }}
197
-
198
- ## Creating an MCP Server
199
-
200
- The user may ask you something along the lines of "add a tool" that does some function, in other words to create an MCP server that provides tools and resources that may connect to external APIs for example. You have the ability to create an MCP server and add it to a configuration file that will then expose the tools and resources for you to use with `use_mcp_tool` and `access_mcp_resource`.
201
-
202
- When creating MCP servers, it's important to understand that they operate in a non-interactive environment. The server cannot initiate OAuth flows, open browser windows, or prompt for user input during runtime. All credentials and authentication tokens must be provided upfront through environment variables in the MCP settings configuration. For example, Spotify's API uses OAuth to get a refresh token for the user, but the MCP server cannot initiate this flow. While you can walk the user through obtaining an application client ID and secret, you may have to create a separate one-time setup script (like get-refresh-token.js) that captures and logs the final piece of the puzzle: the user's refresh token (i.e. you might run the script using execute_command which would open a browser for authentication, and then log the refresh token so that you can see it in the command output for you to use in the MCP settings configuration).
203
-
204
- Unless the user specifies otherwise, new MCP servers should be created in: ${await mcpHub.getMcpServersPath()}
205
-
206
- ### Example MCP Server
207
-
208
- For example, if the user wanted to give you the ability to retrieve weather information, you could create an MCP server that uses the OpenWeather API to get weather information, add it to the MCP settings configuration file, and then notice that you now have access to new tools and resources in the system prompt that you might use to show the user your new capabilities.
209
-
210
- The following example demonstrates how to build an MCP server that provides weather data functionality. While this example shows how to implement resources, resource templates, and tools, in practice you should prefer using tools since they are more flexible and can handle dynamic parameters. The resource and resource template implementations are included here mainly for demonstration purposes of the different MCP capabilities, but a real weather server would likely just expose tools for fetching weather data. (The following steps are for macOS)
211
-
212
- 1. Use the `create-typescript-server` tool to bootstrap a new project in the default MCP servers directory:
213
-
214
- ```bash
215
- cd ${await mcpHub.getMcpServersPath()}
216
- npx @modelcontextprotocol/create-server weather-server
217
- cd weather-server
218
- # Install dependencies
219
- npm install axios
220
- ```
221
-
222
- This will create a new project with the following structure:
223
-
224
- ```
225
- weather-server/
226
- ├── package.json
227
- {
228
- ...
229
- "type": "module", // added by default, uses ES module syntax (import/export) rather than CommonJS (require/module.exports) (Important to know if you create additional scripts in this server repository like a get-refresh-token.js script)
230
- "scripts": {
231
- "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
232
- ...
233
- }
234
- ...
235
- }
236
- ├── tsconfig.json
237
- └── src/
238
- └── weather-server/
239
- └── index.ts # Main server implementation
240
- ```
241
-
242
- 2. Replace `src/index.ts` with the following:
243
-
244
- ```typescript
245
- #!/usr/bin/env node
246
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
247
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
248
- import {
249
- CallToolRequestSchema,
250
- ErrorCode,
251
- ListResourcesRequestSchema,
252
- ListResourceTemplatesRequestSchema,
253
- ListToolsRequestSchema,
254
- McpError,
255
- ReadResourceRequestSchema,
256
- } from '@modelcontextprotocol/sdk/types.js';
257
- import axios from 'axios';
258
-
259
- const API_KEY = process.env.OPENWEATHER_API_KEY; // provided by MCP config
260
- if (!API_KEY) {
261
- throw new Error('OPENWEATHER_API_KEY environment variable is required');
262
- }
263
-
264
- interface OpenWeatherResponse {
265
- main: {
266
- temp: number;
267
- humidity: number;
268
- };
269
- weather: [{ description: string }];
270
- wind: { speed: number };
271
- dt_txt?: string;
272
- }
273
-
274
- const isValidForecastArgs = (
275
- args: any
276
- ): args is { city: string; days?: number } =>
277
- typeof args === 'object' &&
278
- args !== null &&
279
- typeof args.city === 'string' &&
280
- (args.days === undefined || typeof args.days === 'number');
281
-
282
- class WeatherServer {
283
- private server: Server;
284
- private axiosInstance;
285
-
286
- constructor() {
287
- this.server = new Server(
288
- {
289
- name: 'example-weather-server',
290
- version: '0.1.0',
291
- },
292
- {
293
- capabilities: {
294
- resources: {},
295
- tools: {},
296
- },
297
- }
298
- );
299
-
300
- this.axiosInstance = axios.create({
301
- baseURL: 'http://api.openweathermap.org/data/2.5',
302
- params: {
303
- appid: API_KEY,
304
- units: 'metric',
305
- },
306
- });
307
-
308
- this.setupResourceHandlers();
309
- this.setupToolHandlers();
310
-
311
- // Error handling
312
- this.server.onerror = (error) => console.error('[MCP Error]', error);
313
- process.on('SIGINT', async () => {
314
- await this.server.close();
315
- process.exit(0);
316
- });
317
- }
318
-
319
- // MCP Resources represent any kind of UTF-8 encoded data that an MCP server wants to make available to clients, such as database records, API responses, log files, and more. Servers define direct resources with a static URI or dynamic resources with a URI template that follows the format `[protocol]://[host]/[path]`.
320
- private setupResourceHandlers() {
321
- // For static resources, servers can expose a list of resources:
322
- this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
323
- resources: [
324
- // This is a poor example since you could use the resource template to get the same information but this demonstrates how to define a static resource
325
- {
326
- uri: `weather://San Francisco/current`, // Unique identifier for San Francisco weather resource
327
- name: `Current weather in San Francisco`, // Human-readable name
328
- mimeType: 'application/json', // Optional MIME type
329
- // Optional description
330
- description:
331
- 'Real-time weather data for San Francisco including temperature, conditions, humidity, and wind speed',
332
- },
333
- ],
334
- }));
335
-
336
- // For dynamic resources, servers can expose resource templates:
337
- this.server.setRequestHandler(
338
- ListResourceTemplatesRequestSchema,
339
- async () => ({
340
- resourceTemplates: [
341
- {
342
- uriTemplate: 'weather://{city}/current', // URI template (RFC 6570)
343
- name: 'Current weather for a given city', // Human-readable name
344
- mimeType: 'application/json', // Optional MIME type
345
- description: 'Real-time weather data for a specified city', // Optional description
346
- },
347
- ],
348
- })
349
- );
350
-
351
- // ReadResourceRequestSchema is used for both static resources and dynamic resource templates
352
- this.server.setRequestHandler(
353
- ReadResourceRequestSchema,
354
- async (request) => {
355
- const match = request.params.uri.match(
356
- /^weather:\/\/([^/]+)\/current$/
357
- );
358
- if (!match) {
359
- throw new McpError(
360
- ErrorCode.InvalidRequest,
361
- `Invalid URI format: ${request.params.uri}`
362
- );
363
- }
364
- const city = decodeURIComponent(match[1]);
365
-
366
- try {
367
- const response = await this.axiosInstance.get(
368
- 'weather', // current weather
369
- {
370
- params: { q: city },
371
- }
372
- );
373
-
374
- return {
375
- contents: [
376
- {
377
- uri: request.params.uri,
378
- mimeType: 'application/json',
379
- text: JSON.stringify(
380
- {
381
- temperature: response.data.main.temp,
382
- conditions: response.data.weather[0].description,
383
- humidity: response.data.main.humidity,
384
- wind_speed: response.data.wind.speed,
385
- timestamp: new Date().toISOString(),
386
- },
387
- null,
388
- 2
389
- ),
390
- },
391
- ],
392
- };
393
- } catch (error) {
394
- if (axios.isAxiosError(error)) {
395
- throw new McpError(
396
- ErrorCode.InternalError,
397
- `Weather API error: ${
398
- error.response?.data.message ?? error.message
399
- }`
400
- );
401
- }
402
- throw error;
403
- }
404
- }
405
- );
406
- }
407
-
408
- /* MCP Tools enable servers to expose executable functionality to the system. Through these tools, you can interact with external systems, perform computations, and take actions in the real world.
409
- * - Like resources, tools are identified by unique names and can include descriptions to guide their usage. However, unlike resources, tools represent dynamic operations that can modify state or interact with external systems.
410
- * - While resources and tools are similar, you should prefer to create tools over resources when possible as they provide more flexibility.
411
- */
412
- private setupToolHandlers() {
413
- this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
414
- tools: [
415
- {
416
- name: 'get_forecast', // Unique identifier
417
- description: 'Get weather forecast for a city', // Human-readable description
418
- inputSchema: {
419
- // JSON Schema for parameters
420
- type: 'object',
421
- properties: {
422
- city: {
423
- type: 'string',
424
- description: 'City name',
425
- },
426
- days: {
427
- type: 'number',
428
- description: 'Number of days (1-5)',
429
- minimum: 1,
430
- maximum: 5,
431
- },
432
- },
433
- required: ['city'], // Array of required property names
434
- },
435
- },
436
- ],
437
- }));
438
-
439
- this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
440
- if (request.params.name !== 'get_forecast') {
441
- throw new McpError(
442
- ErrorCode.MethodNotFound,
443
- `Unknown tool: ${request.params.name}`
444
- );
445
- }
446
-
447
- if (!isValidForecastArgs(request.params.arguments)) {
448
- throw new McpError(
449
- ErrorCode.InvalidParams,
450
- 'Invalid forecast arguments'
451
- );
452
- }
453
-
454
- const city = request.params.arguments.city;
455
- const days = Math.min(request.params.arguments.days || 3, 5);
456
-
457
- try {
458
- const response = await this.axiosInstance.get<{
459
- list: OpenWeatherResponse[];
460
- }>('forecast', {
461
- params: {
462
- q: city,
463
- cnt: days * 8,
464
- },
465
- });
466
-
467
- return {
468
- content: [
469
- {
470
- type: 'text',
471
- text: JSON.stringify(response.data.list, null, 2),
472
- },
473
- ],
474
- };
475
- } catch (error) {
476
- if (axios.isAxiosError(error)) {
477
- return {
478
- content: [
479
- {
480
- type: 'text',
481
- text: `Weather API error: ${
482
- error.response?.data.message ?? error.message
483
- }`,
484
- },
485
- ],
486
- isError: true,
487
- };
488
- }
489
- throw error;
490
- }
491
- });
492
- }
493
-
494
- async run() {
495
- const transport = new StdioServerTransport();
496
- await this.server.connect(transport);
497
- console.error('Weather MCP server running on stdio');
498
- }
499
- }
500
-
501
- const server = new WeatherServer();
502
- server.run().catch(console.error);
503
- ```
504
-
505
- (Remember: This is just an example–you may use different dependencies, break the implementation up into multiple files, etc.)
506
-
507
- 3. Build and compile the executable JavaScript file
508
-
509
- ```bash
510
- npm run build
511
- ```
512
-
513
- 4. Whenever you need an environment variable such as an API key to configure the MCP server, walk the user through the process of getting the key. For example, they may need to create an account and go to a developer dashboard to generate the key. Provide step-by-step instructions and URLs to make it easy for the user to retrieve the necessary information. Then use the ask_followup_question tool to ask the user for the key, in this case the OpenWeather API key.
514
-
515
- 5. Install the MCP Server by adding the MCP server configuration to the settings file located at '${await mcpHub.getMcpSettingsFilePath()}'. The settings file may have other MCP servers already configured, so you would read it first and then add your new server to the existing `mcpServers` object.
516
-
517
- ```json
518
- {
519
- "mcpServers": {
520
- ...,
521
- "weather": {
522
- "command": "node",
523
- "args": ["/path/to/weather-server/build/index.js"],
524
- "env": {
525
- "OPENWEATHER_API_KEY": "user-provided-api-key"
526
- }
527
- },
528
- }
529
- }
530
- ```
531
-
532
- (Note: the user may also ask you to install the MCP server to the Claude desktop app, in which case you would read then modify `~/Library/Application\ Support/Claude/claude_desktop_config.json` on macOS for example. It follows the same format of a top level `mcpServers` object.)
533
-
534
- 6. After you have edited the MCP settings configuration file, the system will automatically run all the servers and expose the available tools and resources in the 'Connected MCP Servers' section.
535
-
536
- 7. Now that you have access to these new tools and resources, you may suggest ways the user can command you to invoke them - for example, with this new weather tool now available, you can invite the user to ask "what's the weather in San Francisco?"
537
-
538
- ## Editing MCP Servers
539
-
540
- The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: {{ server_names }}, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use replace_in_file to make changes to the files.
541
-
542
- However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server.
543
-
200
+
544
201
  # MCP Servers Are Not Always Necessary
545
202
 
546
203
  The user may not always request the use or creation of MCP servers. Instead, they might provide tasks that can be completed with existing tools. While using the MCP SDK to extend your capabilities can be useful, it's important to understand that this is just one specialized type of task you can accomplish. You should only implement MCP servers when the user explicitly requests it (e.g., "add a tool that...").
547
204
 
548
205
  Remember: The MCP documentation and example provided above are to help you understand and work with existing MCP servers or create new ones when requested by the user. You already have access to tools and capabilities that can be used to accomplish a wide range of tasks.
206
+
207
+ 用户的问题是:
208
+
209
+ {{query}}
210
+
211
+ 请选择前面罗列的 MCP Servers 信息,并且按规定格式来选择前面罗列的 MCP Servers,并选择合适的工具来回答。
212
+ 注意,如果没有合适的工具,你可以直接回答没有可用工具。
549
213
  """
550
214
  return {
551
215
  "connected_servers_info": self.get_connected_servers_info(),
@@ -600,34 +264,20 @@ class McpExecutor:
600
264
 
601
265
  return results
602
266
 
603
- async def run(self, conversations: List[Dict[str, Any]]):
604
- new_conversations = [{
605
- "role": "user",
606
- "content": self.mcp_prompt.prompt()
607
- }, {
608
- "role": "assistant",
609
- "content": "I have read the tools usage instructions."
610
- }] + conversations
611
-
267
+ async def run(self, conversations: List[Dict[str, Any]]):
612
268
  all_tool_results = []
613
269
 
270
+ query = conversations[-1]["content"]
271
+ new_conversations = conversations[0:-1]+[{
272
+ "role": "user",
273
+ "content": self.mcp_prompt.prompt(query=query)
274
+ }]
275
+
614
276
  v = self.llm.chat_oai(conversations=new_conversations)
615
- content = v[0].output
277
+ content = v[0].output
278
+
616
279
  tools = await self.extract_mcp_calls(content)
617
- # while tools:
618
- # results = await self.execute_mcp_tools(tools)
619
- # all_tool_results += results
620
- # str_results = "\n\n".join(self.format_mcp_result(result) for result in results)
621
- # new_conversations += [{
622
- # "role": "assistant",
623
- # "content": f"Tool results: {str_results}"
624
- # },{
625
- # "role": "user",
626
- # "content": "continue"
627
- # }]
628
- # v = self.llm.chat_oai(conversations=new_conversations)
629
- # content = v[0].output
630
- # tools = await self.extract_mcp_calls(content)
280
+
631
281
  if tools:
632
282
  results = await self.execute_mcp_tools(tools)
633
283
  all_tool_results += results
@@ -646,12 +296,10 @@ class McpExecutor:
646
296
  """
647
297
  if result is None:
648
298
  return "(No result)"
649
- # if isinstance(result, mcp_types.CallToolResult):
650
- # for content in result.contents:
651
- # if isinstance(content, mcp_types.TextContent):
652
- # return content.text
653
- # if isinstance(result, mcp_types.ReadResourceResult):
654
- # return result.contents
299
+ if isinstance(result, mcp_types.CallToolResult):
300
+ if len(result.content) ==1:
301
+ return result.content[0].text
302
+
655
303
  return json.dumps(result.model_dump(), indent=2, ensure_ascii=False)
656
304
 
657
305
  async def execute_mcp_tools(self, tools: List[Union[McpToolCall, McpResourceAccess]]) -> List[Any]:
autocoder/index/index.py CHANGED
@@ -96,7 +96,7 @@ class IndexManager:
96
96
 
97
97
  ```json
98
98
  {
99
- "relevant_score": 0-10, // 相关分数
99
+ "relevant_score": 0-10,
100
100
  "reason": "这是相关的原因..."
101
101
  }
102
102
  ```
autocoder/rag/types.py CHANGED
@@ -0,0 +1,77 @@
1
+
2
+ import os
3
+ import json
4
+ import time
5
+ import pydantic
6
+ from typing import Dict, Any, Optional, List
7
+ import psutil
8
+ import glob
9
+
10
+ class RAGServiceInfo(pydantic.BaseModel):
11
+ host: str
12
+ port: int
13
+ model: str
14
+ args: Dict[str, Any]
15
+ _pid: int
16
+ _timestamp:str
17
+
18
+ def save(self):
19
+ # Get home directory in a cross-platform way
20
+ home_dir = os.path.expanduser("~")
21
+ rag_dir = os.path.join(home_dir, ".auto-coder", "rags")
22
+ os.makedirs(rag_dir, exist_ok=True)
23
+
24
+ # Generate filename
25
+ filename = f"{self.host}_{self.port}.json"
26
+ filepath = os.path.join(rag_dir, filename)
27
+
28
+ # Save to JSON
29
+ with open(filepath, "w", encoding="utf-8") as f:
30
+ json.dump(self.model_dump(), f, ensure_ascii=False, indent=2)
31
+
32
+ @classmethod
33
+ def load(cls, host: str, port: int) -> Optional["RAGServiceInfo"]:
34
+ """Load RAGServiceInfo from file"""
35
+ home_dir = os.path.expanduser("~")
36
+ rag_dir = os.path.join(home_dir, ".auto-coder", "rags")
37
+ filename = f"{host}_{port}.json"
38
+ filepath = os.path.join(rag_dir, filename)
39
+
40
+ if not os.path.exists(filepath):
41
+ return None
42
+
43
+ with open(filepath, "r", encoding="utf-8") as f:
44
+ data = json.load(f)
45
+ return cls(**data)
46
+
47
+ def is_alive(self) -> bool:
48
+ """Check if the RAG service process is still running using psutil"""
49
+ try:
50
+ process = psutil.Process(self._pid)
51
+ return process.is_running()
52
+ except psutil.NoSuchProcess:
53
+ return False
54
+ except psutil.AccessDenied:
55
+ # Process exists but we don't have permission to access it
56
+ return True
57
+
58
+ @classmethod
59
+ def load_all(cls) -> List["RAGServiceInfo"]:
60
+ """Load all RAGServiceInfo from ~/.auto-coder/rags directory"""
61
+ home_dir = os.path.expanduser("~")
62
+ rag_dir = os.path.join(home_dir, ".auto-coder", "rags")
63
+
64
+ if not os.path.exists(rag_dir):
65
+ return []
66
+
67
+ service_infos = []
68
+ for filepath in glob.glob(os.path.join(rag_dir, "*.json")):
69
+ try:
70
+ with open(filepath, "r", encoding="utf-8") as f:
71
+ data = json.load(f)
72
+ service_info = cls(**data)
73
+ service_infos.append(service_info)
74
+ except Exception as e:
75
+ continue
76
+
77
+ return service_infos
autocoder/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.215"
1
+ __version__ = "0.1.218"