contextlayer-mcp-server 0.1.0__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.
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,186 @@
1
+ """Filesystem detection for ContextLayer. Pure functions, no MCP coupling."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ import os
6
+ from dataclasses import dataclass, field
7
+ from pathlib import Path
8
+ from typing import Iterable
9
+
10
+ MAX_SCAN_DEPTH = 3
11
+ MAX_FILES_READ = 40
12
+ MAX_BYTES_PER_FILE = 8 * 1024
13
+
14
+ IGNORE_DIRS = {
15
+ ".git", "node_modules", "venv", ".venv", "__pycache__",
16
+ "dist", "build", ".next", "target", "vendor", ".mypy_cache",
17
+ ".pytest_cache", "coverage", ".idea", ".vscode",
18
+ }
19
+
20
+ LANGUAGE_MARKERS = {
21
+ "package.json": "Node.js",
22
+ "tsconfig.json": "TypeScript",
23
+ "go.mod": "Go",
24
+ "requirements.txt": "Python",
25
+ "pyproject.toml": "Python",
26
+ "Cargo.toml": "Rust",
27
+ "pom.xml": "Java (Maven)",
28
+ "build.gradle": "Java/Kotlin (Gradle)",
29
+ "Gemfile": "Ruby",
30
+ "composer.json": "PHP",
31
+ }
32
+
33
+ SOURCE_EXTS = {
34
+ ".py", ".ts", ".tsx", ".js", ".jsx", ".go", ".rs",
35
+ ".java", ".kt", ".rb", ".php",
36
+ }
37
+
38
+ STYLE_MARKERS = {
39
+ ".prettierrc": "prettier",
40
+ ".prettierrc.json": "prettier",
41
+ ".eslintrc": "eslint",
42
+ ".eslintrc.json": "eslint",
43
+ "ruff.toml": "ruff",
44
+ ".ruff.toml": "ruff",
45
+ "mypy.ini": "mypy",
46
+ ".golangci.yml": "golangci-lint",
47
+ "rustfmt.toml": "rustfmt",
48
+ }
49
+
50
+
51
+ @dataclass
52
+ class ProjectContext:
53
+ root: str
54
+ languages: list[str] = field(default_factory=list)
55
+ frameworks: list[str] = field(default_factory=list)
56
+ paradigms: list[str] = field(default_factory=list)
57
+ key_files: list[str] = field(default_factory=list)
58
+ style_hints: list[str] = field(default_factory=list)
59
+
60
+ def env_line(self) -> str:
61
+ langs = ", ".join(self.languages) or "General Software Engineering"
62
+ if self.frameworks:
63
+ langs += f" (frameworks: {', '.join(self.frameworks)})"
64
+ return langs
65
+
66
+
67
+ def _walk(root: Path, max_depth: int) -> Iterable[Path]:
68
+ root = root.resolve()
69
+ for dirpath, dirnames, filenames in os.walk(root):
70
+ current = Path(dirpath)
71
+ try:
72
+ depth = len(current.relative_to(root).parts)
73
+ except ValueError:
74
+ continue
75
+ dirnames[:] = [d for d in dirnames if d not in IGNORE_DIRS]
76
+ if depth >= max_depth:
77
+ dirnames[:] = []
78
+ for fn in filenames:
79
+ yield current / fn
80
+
81
+
82
+ def detect_languages_and_frameworks(root: Path) -> tuple[list[str], list[str], list[str]]:
83
+ languages: set[str] = set()
84
+ frameworks: set[str] = set()
85
+ key_files: list[str] = []
86
+
87
+ for path in _walk(root, MAX_SCAN_DEPTH):
88
+ name = path.name
89
+ if name not in LANGUAGE_MARKERS:
90
+ continue
91
+ languages.add(LANGUAGE_MARKERS[name])
92
+ try:
93
+ key_files.append(str(path.relative_to(root)))
94
+ except ValueError:
95
+ key_files.append(str(path))
96
+
97
+ try:
98
+ if name == "package.json":
99
+ pkg = json.loads(path.read_text(encoding="utf-8"))
100
+ deps = {**pkg.get("dependencies", {}), **pkg.get("devDependencies", {})}
101
+ for fw in ("next", "react", "vue", "express", "nestjs", "svelte", "vitest", "jest"):
102
+ if any(fw in d for d in deps):
103
+ frameworks.add(fw)
104
+ elif name == "go.mod":
105
+ txt = path.read_text(encoding="utf-8", errors="ignore")
106
+ for fw, needle in (("gin", "gin-gonic"), ("gRPC", "grpc"), ("echo", "labstack/echo")):
107
+ if needle in txt:
108
+ frameworks.add(fw)
109
+ elif name == "pyproject.toml":
110
+ txt = path.read_text(encoding="utf-8", errors="ignore").lower()
111
+ for fw in ("fastapi", "django", "flask", "pydantic", "pytest", "sqlalchemy"):
112
+ if fw in txt:
113
+ frameworks.add(fw)
114
+ elif name == "Cargo.toml":
115
+ txt = path.read_text(encoding="utf-8", errors="ignore")
116
+ for fw, needle in (("tokio", "tokio"), ("axum", "axum"), ("serde", "serde")):
117
+ if needle in txt:
118
+ frameworks.add(fw)
119
+ except (OSError, json.JSONDecodeError):
120
+ pass
121
+
122
+ return sorted(languages), sorted(frameworks), key_files[:20]
123
+
124
+
125
+ def detect_paradigms(root: Path) -> list[str]:
126
+ hints: set[str] = set()
127
+ read_count = 0
128
+
129
+ for path in _walk(root, MAX_SCAN_DEPTH):
130
+ if read_count >= MAX_FILES_READ:
131
+ break
132
+ if path.suffix not in SOURCE_EXTS:
133
+ continue
134
+ try:
135
+ snippet = path.read_text(encoding="utf-8", errors="ignore")[:MAX_BYTES_PER_FILE]
136
+ except OSError:
137
+ continue
138
+ read_count += 1
139
+
140
+ if path.suffix == ".py":
141
+ if "from typing import" in snippet or "typing import" in snippet:
142
+ hints.add("typed Python")
143
+ if "async def" in snippet:
144
+ hints.add("async/await")
145
+ if "@dataclass" in snippet or "pydantic" in snippet.lower():
146
+ hints.add("dataclasses/pydantic")
147
+ elif path.suffix in {".ts", ".tsx"}:
148
+ if "interface " in snippet or " type " in snippet:
149
+ hints.add("TypeScript strict types")
150
+ if "react" in snippet.lower():
151
+ hints.add("React")
152
+ elif path.suffix == ".go":
153
+ if "interface {" in snippet:
154
+ hints.add("Go interfaces")
155
+ hints.add("explicit error returns")
156
+ elif path.suffix == ".rs":
157
+ if "Result<" in snippet:
158
+ hints.add("Rust Result-based errors")
159
+ elif path.suffix == ".js":
160
+ hints.add("JavaScript")
161
+
162
+ return sorted(hints)
163
+
164
+
165
+ def detect_style_hints(root: Path) -> list[str]:
166
+ hints: list[str] = []
167
+ for marker, label in STYLE_MARKERS.items():
168
+ if (root / marker).exists() and label not in hints:
169
+ hints.append(label)
170
+ return hints
171
+
172
+
173
+ def build_project_context(current_working_dir: str) -> ProjectContext:
174
+ root = Path(current_working_dir)
175
+ if not root.is_dir():
176
+ return ProjectContext(root=str(root))
177
+
178
+ langs, fws, key_files = detect_languages_and_frameworks(root)
179
+ return ProjectContext(
180
+ root=str(root),
181
+ languages=langs,
182
+ frameworks=fws,
183
+ paradigms=detect_paradigms(root),
184
+ key_files=key_files,
185
+ style_hints=detect_style_hints(root),
186
+ )
contextlayer/server.py ADDED
@@ -0,0 +1,26 @@
1
+ """MCP server entry point. Run with: python -m contextlayer.server"""
2
+ from __future__ import annotations
3
+
4
+ from mcp.server.mcpserver import MCPServer
5
+
6
+ from contextlayer.detection import build_project_context
7
+ from contextlayer.wrapper import build_expert_prompt
8
+
9
+ mcp = MCPServer("ContextLayer-SE")
10
+
11
+
12
+ @mcp.tool()
13
+ def analyze_and_wrap_prompt(user_prompt: str, current_working_dir: str) -> str:
14
+ """Detect the project's tech stack, then wrap `user_prompt` in a
15
+ strict senior-engineer schema before the model ever sees it."""
16
+ ctx = build_project_context(current_working_dir)
17
+ return build_expert_prompt(user_prompt, ctx)
18
+
19
+
20
+ def main() -> None:
21
+ import anyio
22
+ anyio.run(mcp.run_stdio_async)
23
+
24
+
25
+ if __name__ == "__main__":
26
+ main()
@@ -0,0 +1,32 @@
1
+ """Prompt wrapper: turns a ProjectContext + user prompt into an expert prompt."""
2
+ from __future__ import annotations
3
+
4
+ from .detection import ProjectContext
5
+
6
+ SE_DIRECTIVES = """\
7
+ CRITICAL OUTPUT DIRECTIVES:
8
+ 1. Do NOT explain basic concepts. Provide direct, production-grade solutions immediately.
9
+ 2. Enforce strict type safety and error handling matching best practices for the detected stack.
10
+ 3. Output code snippets first, followed only by highly specific edge cases or impacted file names.
11
+ 4. Match existing codebase paradigms layout in the working directory.
12
+ 5. Do not introduce new frameworks or dependencies unless explicitly requested."""
13
+
14
+
15
+ def build_expert_prompt(user_prompt: str, ctx: ProjectContext) -> str:
16
+ paradigms = ", ".join(ctx.paradigms) or "none detected"
17
+ style = ", ".join(ctx.style_hints) or "none detected"
18
+ manifests = "\n - ".join(ctx.key_files) or "(no manifest files found)"
19
+
20
+ return f"""ROLE: Principal Systems Architect & Senior Engineer.
21
+ ENVIRONMENT DETECTED: {ctx.env_line()}
22
+
23
+ DETECTED PARADIGMS: {paradigms}
24
+ DETECTED STYLE CONFIG: {style}
25
+ KEY MANIFESTS:
26
+ - {manifests}
27
+
28
+ {SE_DIRECTIVES}
29
+
30
+ USER REQUEST:
31
+ {user_prompt}
32
+ """
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: contextlayer-mcp-server
3
+ Version: 0.1.0
4
+ Summary: Invisible intelligence layer for AI coding tools (MCP)
5
+ Author: Rudra Nalawade
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: mcp>=1.0.0
@@ -0,0 +1,9 @@
1
+ contextlayer/__init__.py,sha256=Pru0BlFBASFCFo7McHdohtKkUtgMPDwbGfyUZlE2_Vw,21
2
+ contextlayer/detection.py,sha256=31ww0wZAcaR8x_QM8yPWGQr7pEcw38BEO3RxCBWTbmI,6297
3
+ contextlayer/server.py,sha256=INT9JykyBubuBCOyUiIxHNrVBBxJFjFj-7I02TnBJwk,742
4
+ contextlayer/wrapper.py,sha256=GzPZs298LALFqgoicnyTc24DA5BIwc_-2zIgjaSrUwg,1168
5
+ contextlayer_mcp_server-0.1.0.dist-info/METADATA,sha256=Y1I5y9_TBRtkCwXVz4IYXQM1JiwntkuCwDQdIYjKMGk,244
6
+ contextlayer_mcp_server-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
7
+ contextlayer_mcp_server-0.1.0.dist-info/entry_points.txt,sha256=EIk9OpDRwWvG11g4_UOo3sEvGmzR3OfRT4vV-2bqVdk,58
8
+ contextlayer_mcp_server-0.1.0.dist-info/top_level.txt,sha256=hmfZnx_fUURJrg6Ftf4AOLOY_kRgc0MRM-q0vPy_bYQ,13
9
+ contextlayer_mcp_server-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ contextlayer = contextlayer.server:main
@@ -0,0 +1 @@
1
+ contextlayer