#
tokens: 7404/50000 21/21 files
lines: on (toggle) GitHub
raw markdown copy reset
# 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 | 
```