mycode-aiagent 0.4.0__tar.gz → 0.4.2__tar.gz

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.
Files changed (27) hide show
  1. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/PKG-INFO +38 -13
  2. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/README.md +37 -12
  3. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/__init__.py +1 -2
  4. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/backends/__init__.py +3 -7
  5. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/cli.py +19 -9
  6. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/mcp_client.py +26 -0
  7. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/mycode_aiagent.egg-info/PKG-INFO +38 -13
  8. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/mycode_aiagent.egg-info/SOURCES.txt +2 -2
  9. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/pyproject.toml +1 -1
  10. mycode_aiagent-0.4.2/tests/test_server.py +138 -0
  11. mycode_aiagent-0.4.0/my_code/backends/ricky_backend.py +0 -15
  12. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/__main__.py +0 -0
  13. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/analyzer.py +0 -0
  14. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/backends/base.py +0 -0
  15. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/backends/claude_backend.py +0 -0
  16. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/backends/mcp_backend.py +0 -0
  17. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/backends/openai_backend.py +0 -0
  18. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/generator.py +0 -0
  19. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/server.py +0 -0
  20. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/utils/__init__.py +0 -0
  21. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/my_code/utils/prompts.py +0 -0
  22. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/mycode_aiagent.egg-info/dependency_links.txt +0 -0
  23. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/mycode_aiagent.egg-info/entry_points.txt +0 -0
  24. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/mycode_aiagent.egg-info/requires.txt +0 -0
  25. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/mycode_aiagent.egg-info/top_level.txt +0 -0
  26. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/setup.cfg +0 -0
  27. {mycode_aiagent-0.4.0 → mycode_aiagent-0.4.2}/tests/test_library.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mycode-aiagent
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Style-aware code generation — analyze any codebase and generate new code that matches its style
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/RyanAbbottData/MyCode
@@ -90,14 +90,15 @@ MyCode delegates inference to a pluggable backend. Choose one based on what you
90
90
 
91
91
  | Backend | Flag | Requirement |
92
92
  |---|---|---|
93
- | Local LLM | `--backend llama` (default) | MCP server running at `localhost:8000` |
94
- | Anthropic Claude | `--backend claude` | `ANTHROPIC_API_KEY` env var or `--api-key` |
93
+ | Anthropic Claude | `--backend claude` (default) | `ANTHROPIC_API_KEY` env var or `--api-key` |
95
94
  | OpenAI | `--backend openai` | `OPENAI_API_KEY` env var or `--api-key` |
96
- | Custom MCP server | `--backend mcp` | Any MCP server at `--mcp-url` |
95
+ | Any MCP server | `--backend mcp` | An MCP server at `--mcp-url` (MyCode server or local LLM wrapper) |
96
+
97
+ When `--backend mcp` is used with `analyze` or `generate`, the CLI delegates the entire operation to the running MyCode server — it calls the server's `analyze_codebase` or `generate_code` tool directly instead of running the pipeline locally. This is the recommended way to use MyCode in a multi-project or agentic setup.
97
98
 
98
99
  ### Setting up a local LLM
99
100
 
100
- The `llama` backend expects an MCP server at `http://localhost:8000/mcp` that exposes two tools: one for code generation and one for analysis. Any MCP-compatible wrapper around a local model will work. Here is a recommended setup using [llama.cpp](https://github.com/ggerganov/llama.cpp):
101
+ The `mcp` backend can connect to any MCP server, including a local LLM wrapper. The server must expose two tools: one for code generation and one for analysis (tool name must contain `"analyze"`). Here is a recommended setup using [llama.cpp](https://github.com/ggerganov/llama.cpp):
101
102
 
102
103
  **1. Download a model**
103
104
 
@@ -124,7 +125,7 @@ python -m llama_cpp.server \
124
125
 
125
126
  **3. Wrap it with an MCP server**
126
127
 
127
- The `llama` backend communicates over MCP, not directly with the llama.cpp HTTP API. You need a thin MCP wrapper that exposes two tools:
128
+ The `mcp` backend communicates over MCP, not directly with the llama.cpp HTTP API. You need a thin MCP wrapper that exposes two tools:
128
129
  - A **code generation tool** (name must not contain `"analyze"`)
129
130
  - An **analysis tool** (name must contain `"analyze"`)
130
131
 
@@ -166,13 +167,13 @@ python llm_mcp_server.py
166
167
  **4. Point MyCode at it**
167
168
 
168
169
  ```bash
169
- my-code --backend llama --ricky-url http://localhost:8000/mcp analyze .
170
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp analyze .
170
171
  ```
171
172
 
172
173
  Or set a custom URL if your server runs on a different port:
173
174
 
174
175
  ```bash
175
- my-code --backend llama --ricky-url http://localhost:9000/mcp analyze .
176
+ my-code --backend mcp --mcp-url http://localhost:9000/mcp analyze .
176
177
  ```
177
178
 
178
179
  ---
@@ -203,6 +204,11 @@ The profile is saved to `style_profile.json` by default. Specify a different pat
203
204
  my-code analyze ./path/to/codebase --profile ./profiles/my_team.json
204
205
  ```
205
206
 
207
+ Delegating to a running MyCode server:
208
+ ```bash
209
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp analyze ./path/to/codebase
210
+ ```
211
+
206
212
  ### Step 2 — Generate code
207
213
 
208
214
  ```bash
@@ -220,6 +226,9 @@ my-code --backend claude generate "write a binary search function"
220
226
 
221
227
  # Override the model
222
228
  my-code --backend claude --model claude-sonnet-4-6 generate "write a rate limiter"
229
+
230
+ # Delegate to a running MyCode server
231
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp generate "write a rate limiter"
223
232
  ```
224
233
 
225
234
  ---
@@ -230,11 +239,10 @@ my-code --backend claude --model claude-sonnet-4-6 generate "write a rate limite
230
239
  my-code [OPTIONS] COMMAND
231
240
 
232
241
  Options:
233
- --backend {llama,claude,openai,mcp} AI backend to use (default: llama)
242
+ --backend {claude,openai,mcp} AI backend to use (default: claude)
234
243
  --api-key TEXT API key for claude/openai backends
235
244
  --model TEXT Override the default model
236
- --ricky-url TEXT Local LLM MCP server URL (default: http://localhost:8000/mcp)
237
- --mcp-url TEXT Custom MCP server URL (default: http://localhost:8001/mcp)
245
+ --mcp-url TEXT MCP server URL (default: http://localhost:8001/mcp)
238
246
  --profile TEXT Path to style profile JSON (default: style_profile.json)
239
247
 
240
248
  Commands:
@@ -364,6 +372,24 @@ my-code stop --pid-file mycode-8080.pid
364
372
  my-code stop --pid-file mycode-8081.pid
365
373
  ```
366
374
 
375
+ ### Using the server from the CLI
376
+
377
+ Start a MyCode server as a daemon, then point `analyze` and `generate` at it with `--backend mcp`. The CLI calls the server's tools directly — the server handles all analysis and generation using whichever LLM it was started with.
378
+
379
+ ```bash
380
+ # Start the server (uses a local LLM at port 8001 as its brain)
381
+ my-code --backend mcp --mcp-url http://localhost:8001/mcp serve --daemon --port 8000
382
+
383
+ # Analyze a codebase via the server
384
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp analyze ./my_project
385
+
386
+ # Generate code via the server (loads style_profile.json locally and sends it)
387
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp generate "write a retry decorator"
388
+
389
+ # Stop the server
390
+ my-code stop
391
+ ```
392
+
367
393
  ### Connecting from an MCP consumer
368
394
 
369
395
  Add the printed snippet to your consumer's MCP config file (e.g. `.mcp.json`):
@@ -443,12 +469,11 @@ Both test suites use a `MockBackend` — no live AI backend required.
443
469
  my_code/
444
470
  ├── analyzer.py # StyleAnalyzer — scans files, builds style profile
445
471
  ├── generator.py # generate_code() — formats prompt and calls backend
446
- ├── mcp_client.py # Generic MCP client (Streamable HTTP transport)
472
+ ├── mcp_client.py # MCPClient (raw LLM wrapper) + MyCodeClient (server delegation)
447
473
  ├── server.py # MCPServer — exposes analyze/generate as MCP tools
448
474
  ├── cli.py # CLI entry point (my-code command)
449
475
  ├── backends/
450
476
  │ ├── base.py # AIBackend abstract base class
451
- │ ├── ricky_backend.py # Local LLM backend (connects via MCP)
452
477
  │ ├── claude_backend.py
453
478
  │ ├── openai_backend.py
454
479
  │ └── mcp_backend.py # Generic MCP server backend
@@ -61,14 +61,15 @@ MyCode delegates inference to a pluggable backend. Choose one based on what you
61
61
 
62
62
  | Backend | Flag | Requirement |
63
63
  |---|---|---|
64
- | Local LLM | `--backend llama` (default) | MCP server running at `localhost:8000` |
65
- | Anthropic Claude | `--backend claude` | `ANTHROPIC_API_KEY` env var or `--api-key` |
64
+ | Anthropic Claude | `--backend claude` (default) | `ANTHROPIC_API_KEY` env var or `--api-key` |
66
65
  | OpenAI | `--backend openai` | `OPENAI_API_KEY` env var or `--api-key` |
67
- | Custom MCP server | `--backend mcp` | Any MCP server at `--mcp-url` |
66
+ | Any MCP server | `--backend mcp` | An MCP server at `--mcp-url` (MyCode server or local LLM wrapper) |
67
+
68
+ When `--backend mcp` is used with `analyze` or `generate`, the CLI delegates the entire operation to the running MyCode server — it calls the server's `analyze_codebase` or `generate_code` tool directly instead of running the pipeline locally. This is the recommended way to use MyCode in a multi-project or agentic setup.
68
69
 
69
70
  ### Setting up a local LLM
70
71
 
71
- The `llama` backend expects an MCP server at `http://localhost:8000/mcp` that exposes two tools: one for code generation and one for analysis. Any MCP-compatible wrapper around a local model will work. Here is a recommended setup using [llama.cpp](https://github.com/ggerganov/llama.cpp):
72
+ The `mcp` backend can connect to any MCP server, including a local LLM wrapper. The server must expose two tools: one for code generation and one for analysis (tool name must contain `"analyze"`). Here is a recommended setup using [llama.cpp](https://github.com/ggerganov/llama.cpp):
72
73
 
73
74
  **1. Download a model**
74
75
 
@@ -95,7 +96,7 @@ python -m llama_cpp.server \
95
96
 
96
97
  **3. Wrap it with an MCP server**
97
98
 
98
- The `llama` backend communicates over MCP, not directly with the llama.cpp HTTP API. You need a thin MCP wrapper that exposes two tools:
99
+ The `mcp` backend communicates over MCP, not directly with the llama.cpp HTTP API. You need a thin MCP wrapper that exposes two tools:
99
100
  - A **code generation tool** (name must not contain `"analyze"`)
100
101
  - An **analysis tool** (name must contain `"analyze"`)
101
102
 
@@ -137,13 +138,13 @@ python llm_mcp_server.py
137
138
  **4. Point MyCode at it**
138
139
 
139
140
  ```bash
140
- my-code --backend llama --ricky-url http://localhost:8000/mcp analyze .
141
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp analyze .
141
142
  ```
142
143
 
143
144
  Or set a custom URL if your server runs on a different port:
144
145
 
145
146
  ```bash
146
- my-code --backend llama --ricky-url http://localhost:9000/mcp analyze .
147
+ my-code --backend mcp --mcp-url http://localhost:9000/mcp analyze .
147
148
  ```
148
149
 
149
150
  ---
@@ -174,6 +175,11 @@ The profile is saved to `style_profile.json` by default. Specify a different pat
174
175
  my-code analyze ./path/to/codebase --profile ./profiles/my_team.json
175
176
  ```
176
177
 
178
+ Delegating to a running MyCode server:
179
+ ```bash
180
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp analyze ./path/to/codebase
181
+ ```
182
+
177
183
  ### Step 2 — Generate code
178
184
 
179
185
  ```bash
@@ -191,6 +197,9 @@ my-code --backend claude generate "write a binary search function"
191
197
 
192
198
  # Override the model
193
199
  my-code --backend claude --model claude-sonnet-4-6 generate "write a rate limiter"
200
+
201
+ # Delegate to a running MyCode server
202
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp generate "write a rate limiter"
194
203
  ```
195
204
 
196
205
  ---
@@ -201,11 +210,10 @@ my-code --backend claude --model claude-sonnet-4-6 generate "write a rate limite
201
210
  my-code [OPTIONS] COMMAND
202
211
 
203
212
  Options:
204
- --backend {llama,claude,openai,mcp} AI backend to use (default: llama)
213
+ --backend {claude,openai,mcp} AI backend to use (default: claude)
205
214
  --api-key TEXT API key for claude/openai backends
206
215
  --model TEXT Override the default model
207
- --ricky-url TEXT Local LLM MCP server URL (default: http://localhost:8000/mcp)
208
- --mcp-url TEXT Custom MCP server URL (default: http://localhost:8001/mcp)
216
+ --mcp-url TEXT MCP server URL (default: http://localhost:8001/mcp)
209
217
  --profile TEXT Path to style profile JSON (default: style_profile.json)
210
218
 
211
219
  Commands:
@@ -335,6 +343,24 @@ my-code stop --pid-file mycode-8080.pid
335
343
  my-code stop --pid-file mycode-8081.pid
336
344
  ```
337
345
 
346
+ ### Using the server from the CLI
347
+
348
+ Start a MyCode server as a daemon, then point `analyze` and `generate` at it with `--backend mcp`. The CLI calls the server's tools directly — the server handles all analysis and generation using whichever LLM it was started with.
349
+
350
+ ```bash
351
+ # Start the server (uses a local LLM at port 8001 as its brain)
352
+ my-code --backend mcp --mcp-url http://localhost:8001/mcp serve --daemon --port 8000
353
+
354
+ # Analyze a codebase via the server
355
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp analyze ./my_project
356
+
357
+ # Generate code via the server (loads style_profile.json locally and sends it)
358
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp generate "write a retry decorator"
359
+
360
+ # Stop the server
361
+ my-code stop
362
+ ```
363
+
338
364
  ### Connecting from an MCP consumer
339
365
 
340
366
  Add the printed snippet to your consumer's MCP config file (e.g. `.mcp.json`):
@@ -414,12 +440,11 @@ Both test suites use a `MockBackend` — no live AI backend required.
414
440
  my_code/
415
441
  ├── analyzer.py # StyleAnalyzer — scans files, builds style profile
416
442
  ├── generator.py # generate_code() — formats prompt and calls backend
417
- ├── mcp_client.py # Generic MCP client (Streamable HTTP transport)
443
+ ├── mcp_client.py # MCPClient (raw LLM wrapper) + MyCodeClient (server delegation)
418
444
  ├── server.py # MCPServer — exposes analyze/generate as MCP tools
419
445
  ├── cli.py # CLI entry point (my-code command)
420
446
  ├── backends/
421
447
  │ ├── base.py # AIBackend abstract base class
422
- │ ├── ricky_backend.py # Local LLM backend (connects via MCP)
423
448
  │ ├── claude_backend.py
424
449
  │ ├── openai_backend.py
425
450
  │ └── mcp_backend.py # Generic MCP server backend
@@ -2,7 +2,7 @@ __version__ = "0.3.0"
2
2
 
3
3
  from .analyzer import StyleAnalyzer
4
4
  from .generator import generate_code
5
- from .backends import AIBackend, ClaudeBackend, OpenAIBackend, RickyBackend, MCPBackend, make_backend
5
+ from .backends import AIBackend, ClaudeBackend, OpenAIBackend, MCPBackend, make_backend
6
6
  from .server import MCPServer
7
7
 
8
8
 
@@ -17,7 +17,6 @@ __all__ = [
17
17
  "AIBackend",
18
18
  "ClaudeBackend",
19
19
  "OpenAIBackend",
20
- "RickyBackend",
21
20
  "MCPBackend",
22
21
  "make_backend",
23
22
  "MCPServer",
@@ -3,16 +3,14 @@ import os
3
3
  from .base import AIBackend
4
4
  from .claude_backend import ClaudeBackend
5
5
  from .openai_backend import OpenAIBackend
6
- from .ricky_backend import RickyBackend
7
6
  from .mcp_backend import MCPBackend
8
7
 
9
- __all__ = ["AIBackend", "ClaudeBackend", "OpenAIBackend", "RickyBackend", "MCPBackend", "make_backend"]
8
+ __all__ = ["AIBackend", "ClaudeBackend", "OpenAIBackend", "MCPBackend", "make_backend"]
10
9
 
11
10
 
12
11
  def make_backend(
13
- backend: str = "llama",
12
+ backend: str = "claude",
14
13
  api_key: str | None = None,
15
- ricky_url: str = "http://localhost:8000/mcp",
16
14
  mcp_url: str = "http://localhost:8001/mcp",
17
15
  model: str | None = None,
18
16
  timeout: int = 120,
@@ -29,6 +27,4 @@ def make_backend(
29
27
  return OpenAIBackend(api_key=key, **{"model": model} if model else {})
30
28
  if backend == "mcp":
31
29
  return MCPBackend(url=mcp_url, timeout=timeout)
32
- if backend == "llama":
33
- return RickyBackend(url=ricky_url, timeout=timeout)
34
- raise ValueError(f"Unknown backend {backend!r}. Choose: llama, claude, openai, mcp")
30
+ raise ValueError(f"Unknown backend {backend!r}. Choose: claude, openai, mcp")
@@ -2,8 +2,8 @@
2
2
  Style-aware code generation agent.
3
3
 
4
4
  Usage:
5
- my-code [--backend llama|claude|openai] analyze <dir>
6
- my-code [--backend llama|claude|openai] --api-key <key> generate "<task>"
5
+ my-code [--backend claude|openai|mcp] analyze <dir>
6
+ my-code [--backend claude|openai|mcp] --api-key <key> generate "<task>"
7
7
  """
8
8
 
9
9
  import argparse
@@ -17,6 +17,7 @@ from pathlib import Path
17
17
  from .analyzer import StyleAnalyzer
18
18
  from .backends import make_backend
19
19
  from .generator import generate_code
20
+ from .mcp_client import MyCodeClient
20
21
  from .server import MCPServer
21
22
 
22
23
  DEFAULT_PROFILE = Path("style_profile.json")
@@ -27,10 +28,16 @@ def cmd_analyze(args):
27
28
  if not root.exists():
28
29
  sys.exit(f"Error: directory not found: {root}")
29
30
 
31
+ if args.backend == "mcp":
32
+ client = MyCodeClient(args.mcp_url, args.timeout)
33
+ print(f"Analyzing codebase at {root} via MyCode server ...")
34
+ profile = client.analyze_codebase(str(root))
35
+ StyleAnalyzer.save_profile(profile, Path(args.profile))
36
+ return
37
+
30
38
  backend = make_backend(
31
39
  backend=args.backend,
32
40
  api_key=args.api_key,
33
- ricky_url=args.ricky_url,
34
41
  mcp_url=args.mcp_url,
35
42
  model=args.model,
36
43
  timeout=args.timeout,
@@ -52,10 +59,16 @@ def cmd_generate(args):
52
59
  "Run 'analyze' first."
53
60
  )
54
61
 
62
+ if args.backend == "mcp":
63
+ profile = json.loads(profile_path.read_text(encoding="utf-8"))
64
+ client = MyCodeClient(args.mcp_url, args.timeout)
65
+ print("Generating code ...\n")
66
+ print(client.generate_code(args.task, profile=profile))
67
+ return
68
+
55
69
  backend = make_backend(
56
70
  backend=args.backend,
57
71
  api_key=args.api_key,
58
- ricky_url=args.ricky_url,
59
72
  mcp_url=args.mcp_url,
60
73
  model=args.model,
61
74
  timeout=args.timeout,
@@ -72,7 +85,6 @@ def _spawn_daemon(args):
72
85
  cmd = [
73
86
  sys.executable, "-m", "my_code",
74
87
  "--backend", args.backend,
75
- "--ricky-url", args.ricky_url,
76
88
  "--mcp-url", args.mcp_url,
77
89
  "--timeout", str(args.timeout),
78
90
  "--profile", args.profile,
@@ -112,7 +124,6 @@ def cmd_serve(args):
112
124
  backend = make_backend(
113
125
  backend=args.backend,
114
126
  api_key=args.api_key,
115
- ricky_url=args.ricky_url,
116
127
  mcp_url=args.mcp_url,
117
128
  model=args.model,
118
129
  timeout=args.timeout,
@@ -123,12 +134,11 @@ def cmd_serve(args):
123
134
  def main():
124
135
  parser = argparse.ArgumentParser(description="Style-aware code agent")
125
136
  parser.add_argument(
126
- "--backend", default="llama", choices=["llama", "claude", "openai", "mcp"],
127
- help="AI backend to use (default: llama)",
137
+ "--backend", default="claude", choices=["claude", "openai", "mcp"],
138
+ help="AI backend to use (default: claude)",
128
139
  )
129
140
  parser.add_argument("--api-key", default=None, help="API key (claude/openai); falls back to env var")
130
141
  parser.add_argument("--model", default=None, help="Override default model for claude/openai backends")
131
- parser.add_argument("--ricky-url", default="http://localhost:8000/mcp", help="Ricky MCP server URL (llama backend)")
132
142
  parser.add_argument("--mcp-url", default="http://localhost:8001/mcp", help="MCP server URL (mcp backend)")
133
143
  parser.add_argument("--timeout", type=int, default=120, help="Request timeout in seconds (default: 120)")
134
144
  parser.add_argument("--profile", default=str(DEFAULT_PROFILE))
@@ -137,3 +137,29 @@ class MCPClient:
137
137
  "arguments": {self._analyze_arg: prompt},
138
138
  })
139
139
  return self._extract_text(result)
140
+
141
+
142
+ class MyCodeClient(MCPClient):
143
+ """Client for a running MyCode MCP server. Calls semantic tools directly."""
144
+
145
+ def __init__(self, url: str = "http://localhost:8000/mcp", timeout: int = 120):
146
+ self.url = url
147
+ self._timeout = timeout
148
+ self._session_id = None
149
+ self._initialize()
150
+
151
+ def analyze_codebase(self, path: str, save_to: str | None = None) -> dict:
152
+ args = {"path": path}
153
+ if save_to:
154
+ args["save_to"] = save_to
155
+ result = self._post("tools/call", {"name": "analyze_codebase", "arguments": args})
156
+ return json.loads(self._extract_text(result))
157
+
158
+ def generate_code(self, task: str, profile: dict | None = None, profile_path: str | None = None) -> str:
159
+ args = {"task": task}
160
+ if profile is not None:
161
+ args["profile"] = profile
162
+ elif profile_path:
163
+ args["profile_path"] = profile_path
164
+ result = self._post("tools/call", {"name": "generate_code", "arguments": args})
165
+ return self._extract_text(result)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mycode-aiagent
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Style-aware code generation — analyze any codebase and generate new code that matches its style
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/RyanAbbottData/MyCode
@@ -90,14 +90,15 @@ MyCode delegates inference to a pluggable backend. Choose one based on what you
90
90
 
91
91
  | Backend | Flag | Requirement |
92
92
  |---|---|---|
93
- | Local LLM | `--backend llama` (default) | MCP server running at `localhost:8000` |
94
- | Anthropic Claude | `--backend claude` | `ANTHROPIC_API_KEY` env var or `--api-key` |
93
+ | Anthropic Claude | `--backend claude` (default) | `ANTHROPIC_API_KEY` env var or `--api-key` |
95
94
  | OpenAI | `--backend openai` | `OPENAI_API_KEY` env var or `--api-key` |
96
- | Custom MCP server | `--backend mcp` | Any MCP server at `--mcp-url` |
95
+ | Any MCP server | `--backend mcp` | An MCP server at `--mcp-url` (MyCode server or local LLM wrapper) |
96
+
97
+ When `--backend mcp` is used with `analyze` or `generate`, the CLI delegates the entire operation to the running MyCode server — it calls the server's `analyze_codebase` or `generate_code` tool directly instead of running the pipeline locally. This is the recommended way to use MyCode in a multi-project or agentic setup.
97
98
 
98
99
  ### Setting up a local LLM
99
100
 
100
- The `llama` backend expects an MCP server at `http://localhost:8000/mcp` that exposes two tools: one for code generation and one for analysis. Any MCP-compatible wrapper around a local model will work. Here is a recommended setup using [llama.cpp](https://github.com/ggerganov/llama.cpp):
101
+ The `mcp` backend can connect to any MCP server, including a local LLM wrapper. The server must expose two tools: one for code generation and one for analysis (tool name must contain `"analyze"`). Here is a recommended setup using [llama.cpp](https://github.com/ggerganov/llama.cpp):
101
102
 
102
103
  **1. Download a model**
103
104
 
@@ -124,7 +125,7 @@ python -m llama_cpp.server \
124
125
 
125
126
  **3. Wrap it with an MCP server**
126
127
 
127
- The `llama` backend communicates over MCP, not directly with the llama.cpp HTTP API. You need a thin MCP wrapper that exposes two tools:
128
+ The `mcp` backend communicates over MCP, not directly with the llama.cpp HTTP API. You need a thin MCP wrapper that exposes two tools:
128
129
  - A **code generation tool** (name must not contain `"analyze"`)
129
130
  - An **analysis tool** (name must contain `"analyze"`)
130
131
 
@@ -166,13 +167,13 @@ python llm_mcp_server.py
166
167
  **4. Point MyCode at it**
167
168
 
168
169
  ```bash
169
- my-code --backend llama --ricky-url http://localhost:8000/mcp analyze .
170
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp analyze .
170
171
  ```
171
172
 
172
173
  Or set a custom URL if your server runs on a different port:
173
174
 
174
175
  ```bash
175
- my-code --backend llama --ricky-url http://localhost:9000/mcp analyze .
176
+ my-code --backend mcp --mcp-url http://localhost:9000/mcp analyze .
176
177
  ```
177
178
 
178
179
  ---
@@ -203,6 +204,11 @@ The profile is saved to `style_profile.json` by default. Specify a different pat
203
204
  my-code analyze ./path/to/codebase --profile ./profiles/my_team.json
204
205
  ```
205
206
 
207
+ Delegating to a running MyCode server:
208
+ ```bash
209
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp analyze ./path/to/codebase
210
+ ```
211
+
206
212
  ### Step 2 — Generate code
207
213
 
208
214
  ```bash
@@ -220,6 +226,9 @@ my-code --backend claude generate "write a binary search function"
220
226
 
221
227
  # Override the model
222
228
  my-code --backend claude --model claude-sonnet-4-6 generate "write a rate limiter"
229
+
230
+ # Delegate to a running MyCode server
231
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp generate "write a rate limiter"
223
232
  ```
224
233
 
225
234
  ---
@@ -230,11 +239,10 @@ my-code --backend claude --model claude-sonnet-4-6 generate "write a rate limite
230
239
  my-code [OPTIONS] COMMAND
231
240
 
232
241
  Options:
233
- --backend {llama,claude,openai,mcp} AI backend to use (default: llama)
242
+ --backend {claude,openai,mcp} AI backend to use (default: claude)
234
243
  --api-key TEXT API key for claude/openai backends
235
244
  --model TEXT Override the default model
236
- --ricky-url TEXT Local LLM MCP server URL (default: http://localhost:8000/mcp)
237
- --mcp-url TEXT Custom MCP server URL (default: http://localhost:8001/mcp)
245
+ --mcp-url TEXT MCP server URL (default: http://localhost:8001/mcp)
238
246
  --profile TEXT Path to style profile JSON (default: style_profile.json)
239
247
 
240
248
  Commands:
@@ -364,6 +372,24 @@ my-code stop --pid-file mycode-8080.pid
364
372
  my-code stop --pid-file mycode-8081.pid
365
373
  ```
366
374
 
375
+ ### Using the server from the CLI
376
+
377
+ Start a MyCode server as a daemon, then point `analyze` and `generate` at it with `--backend mcp`. The CLI calls the server's tools directly — the server handles all analysis and generation using whichever LLM it was started with.
378
+
379
+ ```bash
380
+ # Start the server (uses a local LLM at port 8001 as its brain)
381
+ my-code --backend mcp --mcp-url http://localhost:8001/mcp serve --daemon --port 8000
382
+
383
+ # Analyze a codebase via the server
384
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp analyze ./my_project
385
+
386
+ # Generate code via the server (loads style_profile.json locally and sends it)
387
+ my-code --backend mcp --mcp-url http://localhost:8000/mcp generate "write a retry decorator"
388
+
389
+ # Stop the server
390
+ my-code stop
391
+ ```
392
+
367
393
  ### Connecting from an MCP consumer
368
394
 
369
395
  Add the printed snippet to your consumer's MCP config file (e.g. `.mcp.json`):
@@ -443,12 +469,11 @@ Both test suites use a `MockBackend` — no live AI backend required.
443
469
  my_code/
444
470
  ├── analyzer.py # StyleAnalyzer — scans files, builds style profile
445
471
  ├── generator.py # generate_code() — formats prompt and calls backend
446
- ├── mcp_client.py # Generic MCP client (Streamable HTTP transport)
472
+ ├── mcp_client.py # MCPClient (raw LLM wrapper) + MyCodeClient (server delegation)
447
473
  ├── server.py # MCPServer — exposes analyze/generate as MCP tools
448
474
  ├── cli.py # CLI entry point (my-code command)
449
475
  ├── backends/
450
476
  │ ├── base.py # AIBackend abstract base class
451
- │ ├── ricky_backend.py # Local LLM backend (connects via MCP)
452
477
  │ ├── claude_backend.py
453
478
  │ ├── openai_backend.py
454
479
  │ └── mcp_backend.py # Generic MCP server backend
@@ -12,7 +12,6 @@ my_code/backends/base.py
12
12
  my_code/backends/claude_backend.py
13
13
  my_code/backends/mcp_backend.py
14
14
  my_code/backends/openai_backend.py
15
- my_code/backends/ricky_backend.py
16
15
  my_code/utils/__init__.py
17
16
  my_code/utils/prompts.py
18
17
  mycode_aiagent.egg-info/PKG-INFO
@@ -21,4 +20,5 @@ mycode_aiagent.egg-info/dependency_links.txt
21
20
  mycode_aiagent.egg-info/entry_points.txt
22
21
  mycode_aiagent.egg-info/requires.txt
23
22
  mycode_aiagent.egg-info/top_level.txt
24
- tests/test_library.py
23
+ tests/test_library.py
24
+ tests/test_server.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mycode-aiagent"
7
- version = "0.4.0"
7
+ version = "0.4.2"
8
8
  description = "Style-aware code generation — analyze any codebase and generate new code that matches its style"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -0,0 +1,138 @@
1
+ """
2
+ Tests for the MyCode MCP server.
3
+ Spins up a real ThreadingHTTPServer against a MockBackend and exercises the protocol.
4
+ No live AI backend required.
5
+ """
6
+ import json
7
+ import socket
8
+ import threading
9
+ import unittest
10
+ from pathlib import Path
11
+
12
+ import requests
13
+
14
+ from my_code.server import MCPServer
15
+ from my_code import AIBackend
16
+
17
+ # ── Mock backend (same contract as the one in test_library.py) ────────────────
18
+
19
+ MOCK_PROFILE = {
20
+ "naming": {"functions": "snake_case", "classes": "PascalCase"},
21
+ "comments": {"docstring_style": "plain"},
22
+ "representative_snippets": ["def foo(x: int) -> str:\n return str(x)"],
23
+ }
24
+
25
+ class MockBackend(AIBackend):
26
+ max_file_chars = 6000
27
+
28
+ def ask_to_analyze(self, prompt: str) -> str:
29
+ return json.dumps(MOCK_PROFILE)
30
+
31
+ def ask_for_code(self, prompt: str) -> str:
32
+ return "def hello(name: str) -> str:\n return f'Hello, {name}'"
33
+
34
+
35
+ def _free_port() -> int:
36
+ with socket.socket() as s:
37
+ s.bind(("127.0.0.1", 0))
38
+ return s.getsockname()[1]
39
+
40
+
41
+ def _post(url: str, method: str, params: dict, session_id: str | None = None) -> requests.Response:
42
+ headers = {
43
+ "Content-Type": "application/json",
44
+ "Accept": "application/json, text/event-stream",
45
+ }
46
+ if session_id:
47
+ headers["Mcp-Session-Id"] = session_id
48
+ return requests.post(
49
+ url,
50
+ json={"jsonrpc": "2.0", "id": "1", "method": method, "params": params},
51
+ headers=headers,
52
+ timeout=10,
53
+ )
54
+
55
+
56
+ def _parse(resp: requests.Response) -> dict:
57
+ for line in resp.text.splitlines():
58
+ if line.startswith("data:"):
59
+ return json.loads(line[5:].strip())
60
+ raise AssertionError(f"No SSE data line in response: {resp.text[:300]}")
61
+
62
+
63
+ class TestMCPServer(unittest.TestCase):
64
+ @classmethod
65
+ def setUpClass(cls):
66
+ port = _free_port()
67
+ cls.url = f"http://127.0.0.1:{port}/mcp"
68
+ server = MCPServer(MockBackend(), host="127.0.0.1", port=port)
69
+ cls.httpd = server.start()
70
+ t = threading.Thread(target=cls.httpd.serve_forever, daemon=True)
71
+ t.start()
72
+
73
+ @classmethod
74
+ def tearDownClass(cls):
75
+ cls.httpd.shutdown()
76
+
77
+ def test_initialize(self):
78
+ resp = _post(self.url, "initialize", {
79
+ "protocolVersion": "2024-11-05",
80
+ "capabilities": {},
81
+ "clientInfo": {"name": "test", "version": "1"},
82
+ })
83
+ self.assertEqual(resp.status_code, 200)
84
+ self.assertIn("Mcp-Session-Id", resp.headers)
85
+ msg = _parse(resp)
86
+ self.assertIn("result", msg)
87
+ self.assertEqual(msg["result"]["serverInfo"]["name"], "mycode")
88
+
89
+ def test_tools_list(self):
90
+ resp = _post(self.url, "tools/list", {})
91
+ self.assertEqual(resp.status_code, 200)
92
+ msg = _parse(resp)
93
+ tools = {t["name"] for t in msg["result"]["tools"]}
94
+ self.assertEqual(tools, {"analyze_codebase", "generate_code"})
95
+
96
+ def test_analyze_codebase(self):
97
+ path = str(Path(__file__).parent.parent / "my_code")
98
+ resp = _post(self.url, "tools/call", {
99
+ "name": "analyze_codebase",
100
+ "arguments": {"path": path},
101
+ })
102
+ self.assertEqual(resp.status_code, 200)
103
+ msg = _parse(resp)
104
+ text = msg["result"]["content"][0]["text"]
105
+ profile = json.loads(text)
106
+ self.assertIn("naming", profile)
107
+
108
+ def test_generate_code(self):
109
+ resp = _post(self.url, "tools/call", {
110
+ "name": "generate_code",
111
+ "arguments": {"task": "write a greeting function", "profile": MOCK_PROFILE},
112
+ })
113
+ self.assertEqual(resp.status_code, 200)
114
+ msg = _parse(resp)
115
+ code = msg["result"]["content"][0]["text"]
116
+ self.assertTrue(len(code) > 0)
117
+ self.assertIn("def", code)
118
+
119
+ def test_unknown_method(self):
120
+ resp = _post(self.url, "nonexistent/method", {})
121
+ self.assertEqual(resp.status_code, 200)
122
+ msg = _parse(resp)
123
+ self.assertIn("error", msg)
124
+ self.assertEqual(msg["error"]["code"], -32601)
125
+
126
+ def test_unknown_tool(self):
127
+ resp = _post(self.url, "tools/call", {
128
+ "name": "does_not_exist",
129
+ "arguments": {},
130
+ })
131
+ self.assertEqual(resp.status_code, 200)
132
+ msg = _parse(resp)
133
+ self.assertIn("error", msg)
134
+ self.assertEqual(msg["error"]["code"], -32602)
135
+
136
+
137
+ if __name__ == "__main__":
138
+ unittest.main()
@@ -1,15 +0,0 @@
1
- from .base import AIBackend
2
- from ..mcp_client import MCPClient
3
-
4
-
5
- class RickyBackend(AIBackend):
6
- max_file_chars: int = 1500
7
-
8
- def __init__(self, url: str = "http://localhost:8000/mcp", timeout: int = 120):
9
- self._client = MCPClient(url=url, timeout=timeout)
10
-
11
- def ask_for_code(self, prompt: str) -> str:
12
- return self._client.ask_for_code(prompt)
13
-
14
- def ask_to_analyze(self, prompt: str) -> str:
15
- return self._client.ask_to_analyze(prompt)
File without changes