# Directory Structure
```
├── .continue
│ └── prompts
│ └── fastmcp.prompt
├── .continuerc.json
├── .gitignore
├── .python-version
├── .vscode
│ └── settings.json
├── pyproject.toml
├── README.md
├── SPEC-GEMINI.md
├── SPEC.md
├── src
│ └── mcps
│ ├── __init__.py
│ ├── config.py
│ ├── logs.py
│ ├── prompts
│ │ ├── __init__.py
│ │ └── file_prompts.py
│ ├── resources
│ │ ├── __init__.py
│ │ ├── doc_resource.py
│ │ ├── project_resource.py
│ │ └── url_resource.py
│ ├── server.py
│ └── tools
│ ├── __init__.py
│ ├── internet_search.py
│ └── perplexity_search.py
└── uv.lock
```
# Files
--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------
```
1 | 3.12
2 |
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Python-generated files
2 | __pycache__/
3 | *.py[oc]
4 | build/
5 | dist/
6 | wheels/
7 | *.egg-info
8 | .pytest_cache
9 |
10 | # Virtual environments
11 | .venv
12 |
```
--------------------------------------------------------------------------------
/.continuerc.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "experimental": {
3 | "modelContextProtocolServers": [
4 | {
5 | "transport": {
6 | "type": "stdio",
7 | "command": "uv",
8 | "args": [
9 | "run",
10 | "--project",
11 | "/Users/alsmirnov/work/mcp-server-continue",
12 | "mcps"
13 | ],
14 | "env": {
15 | "ROOT": "/Users/alsmirnov/work/mcp-server-continue",
16 | }
17 | }
18 | }
19 | ]
20 | }
21 | }
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 |
2 | # Model Context Protocol ( MCP ) Python server to use with continue.dev
3 | MCP server that exposes a customizable prompt templates, resources, and tools
4 | It uses FastMCP to run as server application.
5 |
6 | Dependencies, build, and run managed by uv tool.
7 |
8 | ## Provided functionality
9 | ### prompts
10 | prompts created from markdown files in `prompts` folder.
11 | Additional content can be added by templating, by variable names in {{variable}} format
12 | Initial list of prompts:
13 | - review code created by another llm
14 | - check code for readability, confirm with *Clean Code* rules
15 | - Use a conversational LLM to hone in on an idea
16 | - wrap out at the end of the brainstorm to save it as `spec.md` file
17 | - test driven development, to create tests from spec
18 | - Draft a detailed, step-by-step blueprint for building project from spec
19 |
20 | ### resources
21 | **NOTE: continue does not understand templates, so resource name should contain all information**
22 | **resouce name left as is in prompt, so it should not confuse llm**
23 | - extract url content as markdown
24 | - full documentation about libraries, preferable from llms-full.txt
25 | - complete project structure and content, created by `CodeWeawer` or `Repomix`
26 |
27 | ### tools
28 | - web search, using `serper` or
29 | - web search results with summary, by `perplexity.io`
30 | - find missed tests
31 | - run unit tests and collect errors
```
--------------------------------------------------------------------------------
/src/mcps/resources/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/src/mcps/tools/__init__.py:
--------------------------------------------------------------------------------
```python
1 |
```
--------------------------------------------------------------------------------
/src/mcps/prompts/__init__.py:
--------------------------------------------------------------------------------
```python
1 | from .file_prompts import setup_prompts
2 |
3 | __all__ = ["setup_prompts"]
4 |
```
--------------------------------------------------------------------------------
/src/mcps/resources/url_resource.py:
--------------------------------------------------------------------------------
```python
1 |
2 | from mcps.config import ServerConfig
3 |
4 |
5 | async def get_resource(encoded_url: str, config: ServerConfig) -> str:
6 | return f"URL resource: {encoded_url}"
```
--------------------------------------------------------------------------------
/src/mcps/resources/doc_resource.py:
--------------------------------------------------------------------------------
```python
1 |
2 | from mcps.config import ServerConfig
3 |
4 |
5 | async def get_resource(library_name: str, config: ServerConfig) -> str:
6 | return f"docs resource: {library_name}"
```
--------------------------------------------------------------------------------
/src/mcps/resources/project_resource.py:
--------------------------------------------------------------------------------
```python
1 |
2 | from mcps.config import ServerConfig
3 |
4 |
5 | async def get_resource(project_name: str, config: ServerConfig) -> str:
6 | return f"project resource: {project_name}"
```
--------------------------------------------------------------------------------
/src/mcps/tools/internet_search.py:
--------------------------------------------------------------------------------
```python
1 | import logging
2 |
3 | from mcps.config import ServerConfig
4 |
5 |
6 | logger = logging.getLogger("mcps")
7 |
8 | async def do_search(query: str, config: ServerConfig) -> str:
9 | """
10 | Performs a search and returns the results. This is a placeholder.
11 | In a real implementation, this would use a search engine API.
12 |
13 | Args:
14 | query: The search query.
15 |
16 | Returns:
17 | The search query string back.
18 | """
19 | logger.info(f"Performing search with query: {query}")
20 | return query
```
--------------------------------------------------------------------------------
/src/mcps/prompts/file_prompts.py:
--------------------------------------------------------------------------------
```python
1 | import logging
2 | from pathlib import Path
3 | from typing import Dict
4 |
5 | from fastmcp import FastMCP
6 |
7 | from mcps.config import ServerConfig
8 |
9 |
10 | def setup_prompts(mcp: FastMCP, config: ServerConfig):
11 | """
12 | Dynamically sets up prompts from the prompts directory.
13 |
14 | Args:
15 | mcp: The FastMCP instance.
16 | config: The server configuration.
17 | """
18 | @mcp.prompt("echo")
19 | def echo_prompt(text: str, workspaceDir: str) -> str:
20 | logging.info(f"Echo prompt called with text: {text}")
21 | logging.info(f"Workspace directory: {workspaceDir}")
22 | return "provide short and concise answer: "+text
```
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
```toml
1 | [project]
2 | name = "mcps"
3 | version = "0.1.0"
4 | description = "Model Context Protocol server for continue.dev"
5 | readme = "README.md"
6 | authors = [
7 | { name = "Alexander Smirnov", email = "[email protected]" }
8 | ]
9 | requires-python = ">=3.12"
10 | dependencies = [
11 | "fastmcp>=0.4.1",
12 | ]
13 | [tool.uv]
14 | package = true
15 |
16 | [project.scripts]
17 | mcps = "mcps:main"
18 |
19 | [build-system]
20 | requires = ["hatchling"]
21 | build-backend = "hatchling.build"
22 |
23 | [tool.hatch.build.targets.wheel]
24 | packages = ["src/mcps"]
25 |
26 | [dependency-groups]
27 | dev = [
28 | "pytest>=8.3.4",
29 | ]
30 |
31 | [tool.pytest.ini_options]
32 | testpaths = ["test"]
33 | pythonpath = ["src/mcps","test"]
34 | asyncio_mode = "auto"
35 |
```
--------------------------------------------------------------------------------
/src/mcps/__init__.py:
--------------------------------------------------------------------------------
```python
1 | import os
2 | import logging
3 | import logging.handlers
4 |
5 | import mcps.server
6 | import mcps.config
7 | from mcps.logs import setup_logging
8 |
9 | # --- Package-level logger setup ---
10 |
11 | # --- End of package-level logger setup ---
12 |
13 |
14 | def main() -> None:
15 | config = mcps.config.create_config() # Use the factory method
16 | server = mcps.server.create_server(config)
17 | # mcp server configures logging in constructor
18 | # configure output to file and remove console handlers
19 | # Disable console output by removing default handlers
20 | setup_logging()
21 | logger = logging.getLogger("mcps")
22 | # Current working directory
23 | logger.info(f"Current working directory: {os.getcwd()}")
24 | # File location
25 | logger.info(f"File location: {__file__}")
26 | # Current package name
27 | logger.info(f"Current package name: {__package__}")
28 |
29 | server.start()
```
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "python.defaultInterpreterPath": ".venv/bin/python",
3 | "python.venvPath": ".venv",
4 | "python.testing.pytestArgs": [
5 | "test"
6 | ],
7 | "python.testing.unittestEnabled": false,
8 | "python.testing.pytestEnabled": true,
9 | "python-envs.defaultEnvManager": "ms-python.python:venv",
10 | "python-envs.defaultPackageManager": "ms-python.python:pip",
11 | "python-envs.pythonProjects": [],
12 | "mcp": {
13 | "inputs": [],
14 | "servers": {
15 | "mcps": {
16 | "command": "uv",
17 | "args": [
18 | "run",
19 | "--project",
20 | "/Users/alsmirnov/work/mcp-server-continue",
21 | "mcps"
22 | ],
23 | "env": {
24 | "ROOT": "/Users/alsmirnov/work/mcp-server-continue",
25 | }
26 | }
27 | }
28 | }
29 | }
```
--------------------------------------------------------------------------------
/src/mcps/logs.py:
--------------------------------------------------------------------------------
```python
1 | import os
2 | import logging
3 | import logging.handlers
4 |
5 | def setup_logging():
6 | """
7 | Set up logging to write to a file in the user's Library/Logs/Mcps directory.
8 | """
9 | log_dir = os.path.expanduser("~/Library/Logs/Mcps")
10 | os.makedirs(log_dir, exist_ok=True)
11 | log_file = os.path.join(log_dir, "mcps.log")
12 |
13 |
14 | file_handler = logging.handlers.RotatingFileHandler(
15 | log_file, maxBytes=10 * 1024 * 1024, backupCount=5
16 | )
17 | file_handler.setLevel(logging.DEBUG)
18 |
19 | formatter = logging.Formatter(
20 | "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
21 | )
22 | file_handler.setFormatter(formatter)
23 | # configure output to file and remove console handlers
24 | # Disable console output by removing default handlers
25 | try:
26 | from rich.logging import RichHandler
27 | # Mcp tries to use rich for logging, if available
28 | for handler in logging.root.handlers[:]:
29 | if isinstance(handler, RichHandler):
30 | logging.root.removeHandler(handler)# Configure logging to write to file
31 | except ImportError:
32 | pass
33 | for handler in logging.root.handlers[:]:
34 | if isinstance(handler, logging.StreamHandler) :
35 | logging.root.removeHandler(handler)
36 | # Configure logging to write to file
37 | logging.basicConfig(
38 | handlers=[file_handler],
39 | level=logging.INFO, # Capture all log levels
40 | force=True # Override any existing logging configuration
41 | )
```
--------------------------------------------------------------------------------
/src/mcps/tools/perplexity_search.py:
--------------------------------------------------------------------------------
```python
1 | from mcps.config import ServerConfig
2 | import httpx
3 |
4 | async def do_search(query: str, config: ServerConfig) -> str:
5 | """
6 | Performs a search and returns the results.
7 | Args:
8 | query: The search query.
9 |
10 | Returns:
11 | The search query string back.
12 | """
13 |
14 | url = "https://api.perplexity.ai/chat/completions"
15 | headers = {
16 | "Authorization": f"Bearer {config.perplexity_api_key}",
17 | "Content-Type": "application/json"
18 | }
19 | payload = {
20 | "model": "sonar",
21 | "messages": [
22 | {"role": "system", "content": "Be precise and concise."},
23 | {"role": "user", "content": query}
24 | ],
25 | "max_tokens": 1000,
26 | "temperature": 0.01,
27 | "top_p": 0.9,
28 | "return_related_questions": False,
29 | "web_search_options": {
30 | "search_context_size": "medium"
31 | }
32 | }
33 |
34 | async with httpx.AsyncClient() as client:
35 | response = await client.post(url, json=payload, headers=headers)
36 | response.raise_for_status()
37 | return format_response_with_citations(response.json())
38 |
39 | def format_response_with_citations(response: dict) -> str:
40 | """
41 | Formats the response from Perplexity.ai to include citations as a markdown list.
42 |
43 | Args:
44 | response: The JSON response from Perplexity.ai.
45 |
46 | Returns:
47 | A formatted string with the content and citations.
48 | """
49 | content = response.get("choices", [{}])[0].get("message", {}).get("content", "No content available")
50 | citations = response.get("citations", [])
51 |
52 | if citations:
53 | citations_md = "\n".join([f"- {url}" for url in citations])
54 | return f"{content}\n\n### Citations\n{citations_md}"
55 | return content
```
--------------------------------------------------------------------------------
/src/mcps/config.py:
--------------------------------------------------------------------------------
```python
1 | # mcps/config.py
2 | from dataclasses import dataclass, field
3 | from pathlib import Path
4 | from typing import Dict
5 | from dotenv import load_dotenv
6 | import os
7 |
8 |
9 | @dataclass
10 | class ServerConfig:
11 | prompts_dir: Path = field(default_factory=lambda: Path(__file__).parent / "prompts")
12 | cache_dir: Path = field(default_factory=lambda: Path(__file__).parent / "cache")
13 | tests_dir: Path = field(default_factory=lambda: Path(__file__).parent / "tests")
14 | library_docs: Dict[str, str] = field(default_factory=dict)
15 | project_paths: Dict[str, str] = field(default_factory=dict)
16 | openai_api_key: str = ""
17 | anthropic_api_key: str = ""
18 | perplexity_api_key: str = ""
19 |
20 | def create_config(
21 | prompts_dir: Path = Path("./prompts"),
22 | cache_dir: Path = Path("./cache"),
23 | tests_dir: Path = Path("./tests"),
24 | library_docs: Dict[str, str] | None = None,
25 | project_paths: Dict[str, str] | None = None,
26 | ) -> ServerConfig:
27 | """
28 | Creates a ServerConfig instance, ensuring directories exist and
29 | handling default values for library_docs and project_paths.
30 | """
31 | # Load environment variables from .env files
32 | for env_path in [
33 | Path(__file__).parent.parent.parent,
34 | Path.home()
35 | ]:
36 | dotenv_path = env_path / ".env"
37 | if dotenv_path.exists():
38 | load_dotenv(dotenv_path)
39 |
40 | # Use provided dictionaries or default to empty dictionaries
41 | library_docs = library_docs if library_docs is not None else {}
42 | project_paths = project_paths if project_paths is not None else {}
43 |
44 | return ServerConfig(
45 | prompts_dir=prompts_dir,
46 | cache_dir=cache_dir,
47 | tests_dir=tests_dir,
48 | library_docs=library_docs,
49 | project_paths=project_paths,
50 | openai_api_key=os.getenv("OPENAI_API_KEY", ""),
51 | anthropic_api_key=os.getenv("ANTHROPIC_API_KEY", ""),
52 | perplexity_api_key=os.getenv("PERPLEXITY_API_KEY", ""),
53 | )
54 |
```
--------------------------------------------------------------------------------
/SPEC.md:
--------------------------------------------------------------------------------
```markdown
1 | # Development Automation Server Specification
2 |
3 | ## Overview
4 | FastMCP server implementation providing development automation tools with focus on TDD and documentation management.
5 |
6 | ## Server Configuration
7 |
8 | ### Directory Structure
9 | mcp-server/
10 | ├── prompts/ # Markdown prompt templates
11 | ├── cache/
12 | │ ├── docs/ # Cached documentation
13 | │ └── search / # Search results
14 | ├── tests/ # Generated test files
15 | └── config/ # Server configuration
16 |
17 |
18 | ### Configuration Parameters
19 | ```python
20 | @dataclass
21 | class ServerConfig:
22 | prompts_dir: Path = Path("./prompts")
23 | cache_dir: Path = Path("./cache")
24 | tests_dir: Path = Path("./tests")
25 | ```
26 | ### Core Components
27 | #### Prompt Templates
28 | test_generator.md - Creates test cases from spec
29 | doc_extractor.md - Formats documentation for caching
30 | spec_parser.md - Extracts requirements from free-form specs
31 | #### Resource Endpoints
32 | "docs://{library_name}" # Get cached library documentation
33 | "spec://{spec_name}" # Get parsed specification
34 | "spec://{spec_name}/tests" # Get generated tests for spec
35 | "url://{encoded_url}" # Get cached URL content as markdown
36 | #### Tools
37 | ```python
38 | @mcp.tool()
39 | def generate_tests(spec_name: str) -> str:
40 | """Generate test cases from a specification file"""
41 |
42 | @mcp.tool()
43 | def validate_tests(spec_name: str) -> str:
44 | """Validate that generated tests match specification requirements"""
45 |
46 | @mcp.tool()
47 | def suggest_test_improvements(test_file: str) -> str:
48 | """Analyze existing tests and suggest improvements for better coverage"""
49 | ```
50 | ### Server Implementation
51 | Core Server Setup
52 | ```python
53 | from mcp.server.fastmcp import FastMCP
54 | from dataclasses import dataclass
55 | from pathlib import Path
56 |
57 | @dataclass
58 | class ServerConfig:
59 | prompts_dir: Path
60 | cache_dir: Path
61 | tests_dir: Path
62 |
63 | @dataclass
64 | class AppContext:
65 | config: ServerConfig
66 |
67 | def create_server(config: ServerConfig) -> FastMCP:
68 | mcp = FastMCP(
69 | "Development Automation Server",
70 | dependencies=["pytest"]
71 | )
72 |
73 | for dir_path in [config.prompts_dir, config.cache_dir, config.tests_dir]:
74 | dir_path.mkdir(parents=True, exist_ok=True)
75 |
76 | return mcp
77 | ```
78 | ### Integration
79 | continue.dev Configuration
80 | ```json
81 | {
82 | "mcpServers": [
83 | {
84 | "name": "Development Automation Server",
85 | "command": "uv",
86 | "args": ["run", "server.py"]
87 | }
88 | ]
89 | }
90 | ```
91 | ### Dependencies
92 | FastMCP
93 | pytest
```
--------------------------------------------------------------------------------
/src/mcps/server.py:
--------------------------------------------------------------------------------
```python
1 | from dataclasses import dataclass
2 | import logging
3 | import os
4 | from pathlib import Path
5 | from typing import Dict
6 |
7 | from mcp import ClientCapabilities, RootsCapability
8 | from mcp.server.session import ServerSession
9 | from mcp.server.fastmcp import FastMCP, Context
10 |
11 | import mcps.prompts as prompts_module
12 | import mcps.resources.url_resource as url_resource
13 | import mcps.resources.doc_resource as doc_resource
14 | import mcps.resources.project_resource as project_resource
15 | import mcps.tools.internet_search as internet_search
16 | import mcps.tools.perplexity_search as perplexity_search
17 | from mcps.config import ServerConfig, create_config # Import from config module
18 |
19 |
20 | logger = logging.getLogger("mcps")
21 | @dataclass
22 | class AppContext:
23 | config: ServerConfig
24 |
25 |
26 | class DevAutomationServer:
27 | def __init__(self, config: ServerConfig):
28 | self.config = config
29 | self.mcp = FastMCP(
30 | "Development Automation Server",
31 | # dependencies=["pytest", "httpx", "beautifulsoup4"], # dependencies for resources/tools
32 | )
33 | self._setup_resources()
34 | self._setup_tools()
35 | self._setup_prompts()
36 |
37 |
38 | def _setup_resources(self):
39 | @self.mcp.resource("url://{encoded_url}")
40 | async def url_resource_handler(encoded_url: str) -> str:
41 | return await url_resource.get_resource(encoded_url, self.config)
42 |
43 | @self.mcp.resource("doc://{library_name}")
44 | async def doc_resource_handler(library_name: str) -> str:
45 | return await doc_resource.get_resource(library_name, self.config)
46 |
47 | @self.mcp.resource("project://{project_name}")
48 | async def project_resource_handler(project_name: str) -> str:
49 | return await project_resource.get_resource(project_name, self.config)
50 | @self.mcp.resource("resource://test", name="test/resource", description="Test project resource")
51 | async def test_resource_handler() -> str:
52 | try:
53 | session: ServerSession = self.mcp.get_context().session
54 | if session.check_client_capability(ClientCapabilities(roots=RootsCapability())) :
55 | result = await session.list_roots()
56 | logger.info(f"Result: {result}")
57 | for root in result.roots:
58 | logger.info(f"Root: {root.name} , {root.uri}")
59 | except Exception as e:
60 | logger.error(f"Error listing roots: {e}")
61 | return "Test project resource"
62 | @self.mcp.resource("documentation://test/docs")
63 | async def test_docs_handler() -> str:
64 | return "Test project documentation"
65 |
66 | def _setup_tools(self):
67 | @self.mcp.tool(name="web_search", description="Search the web for information")
68 | async def web_search(query: str) -> str:
69 | """
70 | Performs a web search using the provided query. Find the most relevant pages
71 | and return summary result.
72 | Args:
73 | query: The search query.
74 | Returns:
75 | The summary of the most relevant search results.
76 | """
77 | try:
78 | session: ServerSession = self.mcp.get_context().session
79 | if session.check_client_capability(ClientCapabilities(roots=RootsCapability())) :
80 | result = await session.list_roots()
81 | logger.info(f"Result: {result}")
82 | for root in result.roots:
83 | logger.info(f"Root: {root.name} , location: {root.uri}")
84 | else:
85 | logger.info("Client does not support roots capability")
86 | # Try to get the roots from the environment variable ROOT
87 | root_value = os.getenv("ROOT")
88 | logger.info(f"ROOT environment variable: {root_value}")
89 | except Exception as e:
90 | logger.error(f"Error listing roots: {e}")
91 | return await perplexity_search.do_search(query, self.config)
92 |
93 | # @self.mcp.tool()
94 | # async def perplexity_summary_search(query: str) -> str:
95 | # return await perplexity_search.do_search(query, self.config)
96 |
97 | def _setup_prompts(self):
98 | # Dynamically register prompts from the prompts directory
99 | prompts_module.setup_prompts(self.mcp, self.config)
100 |
101 | def start(self):
102 | self.mcp.run()
103 |
104 |
105 | def create_server(config: ServerConfig) -> DevAutomationServer:
106 | """
107 | Creates and configures the Development Automation Server.
108 |
109 | Args:
110 | config: The server configuration.
111 |
112 | Returns:
113 | The configured FastMCP server instance.
114 | """
115 | server = DevAutomationServer(config)
116 | return server
117 |
118 |
119 | if __name__ == "__main__":
120 | # Example usage with configuration from the config module
121 | config = create_config() # Use the factory method
122 | server = create_server(config)
123 | server.start()
```
--------------------------------------------------------------------------------
/SPEC-GEMINI.md:
--------------------------------------------------------------------------------
```markdown
1 | # FastMCP Server Project Specification
2 | This document outlines the specification for a FastMCP server designed to provide prompts, resources, and tools to Language Model (LLM) clients, such as continue.dev.
3 |
4 | 1. Prompts
5 | Source: Prompts are stored in Markdown files within a dedicated prompts directory on the server.
6 | Prompt Identification: Each prompt is identified by its filename (without the .md extension). For example, a file named code_review.md corresponds to a prompt named code_review.
7 | Prompt Templating: Prompt files can contain template variables in the format {{variable}}. These variables are placeholders that will be replaced with values provided by the client when requesting a prompt.
8 | Templating Mechanism: Simple string replacement. The server will receive a dictionary of variable names and values from the client and replace all occurrences of {{variable}} with their corresponding values.
9 | Client Interaction (MCP):
10 | Listing Prompts: Clients can use the MCP listPrompts request to get a list of available prompt names. The server will scan the prompts directory and return a list of filenames (without extensions).
11 | Retrieving Prompts: Clients can use the MCP getPrompt request to retrieve a specific prompt. The request must include:
12 | name: The name of the prompt (filename without extension).
13 | arguments: A dictionary where keys are variable names used in the prompt template, and values are the strings to replace the placeholders.
14 | Server Processing: Upon receiving a getPrompt request, the server will:
15 | Locate the Markdown file corresponding to the requested name in the prompts directory.
16 | Read the content of the Markdown file.
17 | Perform template replacement using the provided arguments dictionary.
18 | Return the processed prompt content as a string within the MCP GetPromptResult response.
19 | 2. Resources
20 | The server will provide the following resource types, identified by their URI schemes:
21 |
22 | url: Resource (Fetch URL Content as Markdown)
23 |
24 | URI Format: url:http://<host>/<page> (e.g., url:http://example.com/page)
25 | Functionality:
26 | Extract the URL from the URI (e.g., http://example.com/page).
27 | Use the external service r.jina.ai to fetch and convert the URL content to Markdown by transforming the URL to https://r.jina.ai/<original_url> and making a request.
28 | If the fetched content is plain text, return it as is.
29 | Return the content (Markdown or plain text) as the resource.
30 | Error Handling: Any errors from the r.jina.ai service or the response content itself will be returned as the resource content to the client.
31 | doc: Resource (Library Documentation)
32 |
33 | URI Format: doc://<library_name> (e.g., doc://pandas)
34 | Configuration: The server will have a configuration dictionary (library_docs) mapping library names to URLs of llms.txt files. This dictionary will be initially hardcoded in the Configuration class.
35 | Functionality:
36 | Extract the <library_name> from the URI.
37 | Look up the <library_name> in the library_docs dictionary to get the corresponding llms.txt URL.
38 | Fetch the content from the llms.txt URL.
39 | Return the fetched content (assumed to be plain text, ready for LLM use) as the resource.
40 | Error Handling: If the <library_name> is not found in the library_docs dictionary, the server will return an error to the client.
41 | project: Resource (Project Structure and Content)
42 |
43 | URI Format: project://<project_name> (e.g., project://my_project)
44 | Configuration: The server will have a configuration dictionary (project_paths) mapping project names to local project root folder paths.
45 | External Tool: "CodeWeawer" - assumed to be a command-line tool named codeweawer available in the system's PATH.
46 | Functionality:
47 | Extract the <project_name> from the URI.
48 | Look up the <project_name> in the project_paths dictionary to get the project root folder path.
49 | Execute the codeweawer command in the shell, passing the project root folder path as an argument (e.g., codeweawer /user/projects/my_project).
50 | Capture the standard output (stdout) from the codeweawer command.
51 | Return the captured stdout (plain text project structure) as the resource.
52 | Error Handling:
53 | If the <project_name> is not found in project_paths, return an error.
54 | If the codeweawer command is not found in the system's PATH, return an error.
55 | If the codeweawer command execution fails (non-zero exit code), return an error. In all error cases, an error response will be returned to the client.
56 | 3. Tools
57 | The server will provide the following tools:
58 |
59 | web_search Tool (Web Search using Serper)
60 |
61 | Tool Name: web_search
62 | Argument: query (string, required) - The search query.
63 | Functionality: Uses the serper API to perform a web search using the provided query.
64 | Output: Returns a plain text summary of the search results as a string. If no results are found, returns an empty string.
65 | Error Handling: If the Serper API call fails, returns an error message string to the client. If no search results are found, returns an empty string.
66 | perplexity_summary_search Tool (Summarized Web Search using Perplexity.io)
67 |
68 | Tool Name: perplexity_summary_search
69 | Argument: query (string, required) - The search query.
70 | Functionality: Uses the perplexity.io API to perform a web search and get a summarized response for the query.
71 | Output: Returns the summarized search result as a string. If no summary is available or an error occurs, returns an empty string.
72 | Error Handling: If the Perplexity.io API call fails, returns an error message string to the client. If no summary is available or other issues occur, returns an empty string.
73 |
```