god-code 0.5.4__tar.gz → 0.5.5__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.
- {god_code-0.5.4 → god_code-0.5.5}/CLAUDE.md +10 -0
- {god_code-0.5.4 → god_code-0.5.5}/PKG-INFO +3 -1
- {god_code-0.5.4 → god_code-0.5.5}/README.md +55 -1
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/cli.py +24 -1
- god_code-0.5.5/godot_agent/mcp_server.py +289 -0
- {god_code-0.5.4 → god_code-0.5.5}/pyproject.toml +4 -1
- {god_code-0.5.4 → god_code-0.5.5}/tests/test_package_compatibility.py +2 -2
- {god_code-0.5.4 → god_code-0.5.5}/.github/workflows/publish.yml +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/.gitignore +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/AGENTS.md +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/CHANGELOG.md +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/CONTRIBUTING.md +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/LICENSE +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/agents/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/agents/configs.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/agents/dispatcher.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/agents/results.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/godot/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/godot/collision_planner.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/godot/consistency_checker.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/godot/dependency_graph.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/godot/gdscript_linter.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/godot/impact_analysis.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/godot/pattern_advisor.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/godot/project.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/godot/resource_validator.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/godot/scene_parser.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/godot/scene_writer.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/godot/tscn_validator.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/llm/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/llm/adapters/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/llm/adapters/anthropic.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/llm/adapters/base.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/llm/adapters/openai.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/llm/client.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/llm/streaming.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/llm/types.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/llm/vision.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/prompts/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/prompts/assembler.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/prompts/build_discipline.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/prompts/godot_playbook.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/prompts/image_templates.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/prompts/knowledge_selector.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/prompts/skill_library.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/prompts/skill_selector.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/prompts/system.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/py.typed +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/auth.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/config.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/context_manager.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/design_memory.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/engine.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/error_loop.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/events.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/gameplay_reviewer.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/modes.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/oauth.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/playtest_harness.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/providers.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/quality_gate.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/reviewer.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/runtime_bridge.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/scenario_specs/hud_feedback.json +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/scenario_specs/player_movement.json +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/scenario_specs/scene_transition.json +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/runtime/session.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/security/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/security/classifier.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/security/hooks.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/security/policies.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/security/protected_paths.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/security/tool_pipeline.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/testing/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/testing/scenario_runner.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/analysis_tools.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/base.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/editor_bridge.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/file_ops.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/git.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/godot_cli.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/image_gen.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/list_dir.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/memory_tool.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/registry.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/scene_tools.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/screenshot.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/script_tools.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/search.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/shell.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tools/web_search.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tui/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tui/display.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/godot_agent/tui/input_handler.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/agents/test_dispatcher.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/agents/test_playtest_analyst.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/e2e/test_planner_worker_reviewer_flow.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/e2e/test_policy_enforcement.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/e2e/test_scenario_runner.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/godot/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/godot/test_collision_planner.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/godot/test_consistency.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/godot/test_dependency_graph.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/godot/test_impact_analysis.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/godot/test_linter.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/godot/test_pattern_advisor.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/godot/test_project.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/godot/test_resource_validator.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/godot/test_scene_parser.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/godot/test_scene_writer.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/godot/test_tscn_validator.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/llm/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/llm/test_adapters.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/llm/test_client.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/llm/test_vision.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/prompts/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/prompts/test_knowledge_selector.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/prompts/test_prompt_assembler.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/prompts/test_skill_selector.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/prompts/test_system_prompt.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_config.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_context_manager.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_design_memory.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_engine.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_error_loop.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_gameplay_reviewer.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_mode_restrictions.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_multi_agent_flow.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_playtest_harness.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_quality_gate.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_reviewer.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_runtime_bridge.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/runtime/test_session.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/security/test_classifier.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/security/test_hooks.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/security/test_permissions.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/security/test_tool_pipeline.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/test_cli_config_flow.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/test_e2e.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/test_runtime_switch_commands.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/__init__.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/test_analysis_tools.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/test_editor_bridge.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/test_file_ops.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/test_git.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/test_godot_cli.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/test_list_dir.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/test_memory_tool.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/test_registry.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/test_scene_tools.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/test_script_tools.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/test_search.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tools/test_shell.py +0 -0
- {god_code-0.5.4 → god_code-0.5.5}/tests/tui/test_input_handler.py +0 -0
|
@@ -144,6 +144,16 @@ Smart compression at 75% (787K tokens):
|
|
|
144
144
|
|
|
145
145
|
## Development Rules
|
|
146
146
|
|
|
147
|
+
### CRITICAL: Python 3.9 Compatibility
|
|
148
|
+
`pyproject.toml` MUST keep `requires-python = ">=3.9"`. macOS ships Python 3.9 via Xcode — users run `pip install god-code` with it. Never change this.
|
|
149
|
+
|
|
150
|
+
Rules:
|
|
151
|
+
- No `dataclass(slots=True)` — Python 3.10+ only
|
|
152
|
+
- No `match`/`case` statements — Python 3.10+ only
|
|
153
|
+
- Every `.py` file MUST have `from __future__ import annotations` as first import
|
|
154
|
+
- Use `eval_type_backport` dependency for pydantic `str | None` on 3.9
|
|
155
|
+
- **Before every release**: verify `grep "requires-python" pyproject.toml` shows `>=3.9`
|
|
156
|
+
|
|
147
157
|
### Adding a New Tool
|
|
148
158
|
1. Create in `godot_agent/tools/your_tool.py` inheriting `BaseTool`
|
|
149
159
|
2. Define `Input`/`Output` as pydantic `BaseModel` (all fields must have defaults for strict mode)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: god-code
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.5
|
|
4
4
|
Summary: AI coding agent specialized for Godot game development
|
|
5
5
|
Project-URL: Homepage, https://github.com/chuisiufai/god-code
|
|
6
6
|
Project-URL: Repository, https://github.com/chuisiufai/god-code
|
|
@@ -19,3 +19,5 @@ Provides-Extra: dev
|
|
|
19
19
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
20
20
|
Requires-Dist: pytest-mock>=3.14; extra == 'dev'
|
|
21
21
|
Requires-Dist: pytest>=8.2; extra == 'dev'
|
|
22
|
+
Provides-Extra: mcp
|
|
23
|
+
Requires-Dist: mcp[cli]>=1.12; extra == 'mcp'
|
|
@@ -33,7 +33,7 @@ cd god-code
|
|
|
33
33
|
pip install -e ".[dev]"
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Requires Python 3.
|
|
36
|
+
Requires Python 3.9+.
|
|
37
37
|
|
|
38
38
|
## Quick Start
|
|
39
39
|
|
|
@@ -190,6 +190,60 @@ God Code executes tools on your local machine. The LLM decides which tools to ca
|
|
|
190
190
|
|
|
191
191
|
**API keys**: Stored in `~/.config/god-code/config.json` with `600` permissions. Never committed to git.
|
|
192
192
|
|
|
193
|
+
## MCP Server (for Claude Code / Codex / AI Agents)
|
|
194
|
+
|
|
195
|
+
God Code can run as an MCP (Model Context Protocol) server, exposing 12 Godot tools directly to AI agents. **No LLM middleman, zero token cost** — tools run locally.
|
|
196
|
+
|
|
197
|
+
### Install
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
pip install god-code[mcp]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Configure in Claude Code
|
|
204
|
+
|
|
205
|
+
Add to `~/.claude.json` or Claude Desktop config:
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"mcpServers": {
|
|
210
|
+
"god-code": {
|
|
211
|
+
"command": "god-code",
|
|
212
|
+
"args": ["mcp", "--project", "/path/to/your/godot/project"]
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Available MCP Tools
|
|
219
|
+
|
|
220
|
+
| Tool | Description |
|
|
221
|
+
|------|-------------|
|
|
222
|
+
| `validate_project` | Run Godot headless validation, return errors/warnings |
|
|
223
|
+
| `validate_tscn` | Check .tscn format, optionally auto-fix ordering |
|
|
224
|
+
| `lint_script` | GDScript style, naming, type annotation checks |
|
|
225
|
+
| `check_consistency` | Cross-file collision/signal/resource consistency |
|
|
226
|
+
| `plan_collision` | Generate standard 8-layer collision config |
|
|
227
|
+
| `analyze_dependencies` | Build project-wide dependency graph |
|
|
228
|
+
| `suggest_patterns` | Object pool, component, state machine suggestions |
|
|
229
|
+
| `parse_scene` | Parse .tscn into structured node tree |
|
|
230
|
+
| `project_info` | Read project.godot metadata |
|
|
231
|
+
| `godot_knowledge` | Query Godot 4.4 Playbook (17 knowledge sections) |
|
|
232
|
+
| `generate_sprite` | AI pixel art generation + post-processing |
|
|
233
|
+
| `validate_resources` | Check all res:// paths exist |
|
|
234
|
+
|
|
235
|
+
### How it works
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
Claude Code / Codex
|
|
239
|
+
↓ (MCP protocol over stdio)
|
|
240
|
+
god-code mcp process (local)
|
|
241
|
+
↓ (direct function calls)
|
|
242
|
+
Godot analysis tools (no LLM needed)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The AI agent gets Godot-native intelligence without you paying for an extra LLM layer.
|
|
246
|
+
|
|
193
247
|
## Architecture
|
|
194
248
|
|
|
195
249
|
```
|
|
@@ -702,7 +702,7 @@ def _run_setup_wizard(config_path: Path | None = None) -> None:
|
|
|
702
702
|
click.echo()
|
|
703
703
|
|
|
704
704
|
|
|
705
|
-
_VERSION = "0.5.
|
|
705
|
+
_VERSION = "0.5.5"
|
|
706
706
|
|
|
707
707
|
|
|
708
708
|
def _check_update() -> None:
|
|
@@ -1657,5 +1657,28 @@ def info(project: str):
|
|
|
1657
1657
|
click.echo(f" {name} -> {path}")
|
|
1658
1658
|
|
|
1659
1659
|
|
|
1660
|
+
@main.command("mcp")
|
|
1661
|
+
@click.option("--project", "-p", default=".", help="Path to Godot project root")
|
|
1662
|
+
def mcp_command(project: str):
|
|
1663
|
+
"""Start MCP server (for Claude Code, Codex, and other AI agents).
|
|
1664
|
+
|
|
1665
|
+
Exposes god-code's Godot tools via Model Context Protocol over stdio.
|
|
1666
|
+
No LLM needed — tools run locally, zero token cost.
|
|
1667
|
+
|
|
1668
|
+
Configure in Claude Code:
|
|
1669
|
+
\b
|
|
1670
|
+
{
|
|
1671
|
+
"mcpServers": {
|
|
1672
|
+
"god-code": {
|
|
1673
|
+
"command": "god-code",
|
|
1674
|
+
"args": ["mcp", "--project", "/path/to/project"]
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
"""
|
|
1679
|
+
from godot_agent.mcp_server import run_mcp_server
|
|
1680
|
+
run_mcp_server(project_path=project)
|
|
1681
|
+
|
|
1682
|
+
|
|
1660
1683
|
if __name__ == "__main__":
|
|
1661
1684
|
main()
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""MCP Server — exposes god-code's Godot tools to AI agents (Claude Code, Codex, etc).
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
god-code mcp --project /path/to/godot/project
|
|
5
|
+
|
|
6
|
+
Runs as a local stdio process. No backend server needed.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from mcp.server.fastmcp import FastMCP
|
|
18
|
+
from pydantic import BaseModel, Field
|
|
19
|
+
|
|
20
|
+
log = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
mcp = FastMCP("god-code") #
|
|
23
|
+
|
|
24
|
+
# Project root — set via CLI arg
|
|
25
|
+
_project_root: Path | None = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def set_mcp_project_root(path: Path) -> None:
|
|
29
|
+
global _project_root
|
|
30
|
+
_project_root = path.resolve()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _root() -> Path:
|
|
34
|
+
return _project_root or Path.cwd()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ─── Validation Tools ─────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
@mcp.tool()
|
|
40
|
+
async def validate_project(project_path: str = "") -> dict:
|
|
41
|
+
"""Run Godot headless validation on the project. Returns errors and warnings.
|
|
42
|
+
|
|
43
|
+
If project_path is empty, uses the configured project root.
|
|
44
|
+
"""
|
|
45
|
+
from godot_agent.runtime.error_loop import validate_project as _validate, format_validation_for_llm
|
|
46
|
+
root = project_path or str(_root())
|
|
47
|
+
# Read godot_path from config
|
|
48
|
+
godot_path = "godot"
|
|
49
|
+
config_file = Path.home() / ".config" / "god-code" / "config.json"
|
|
50
|
+
if config_file.exists():
|
|
51
|
+
cfg = json.loads(config_file.read_text())
|
|
52
|
+
godot_path = cfg.get("godot_path", "godot")
|
|
53
|
+
result = await _validate(root, godot_path, timeout=30)
|
|
54
|
+
return {
|
|
55
|
+
"success": result.success,
|
|
56
|
+
"errors": [{"file": e.file, "line": e.line, "message": e.message} for e in result.errors],
|
|
57
|
+
"warnings": [{"file": w.file, "line": w.line, "message": w.message} for w in result.warnings],
|
|
58
|
+
"summary": format_validation_for_llm(result),
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@mcp.tool()
|
|
63
|
+
def validate_tscn(file_path: str, auto_fix: bool = False) -> dict:
|
|
64
|
+
"""Validate a .tscn scene file format. Optionally auto-fix ordering issues.
|
|
65
|
+
|
|
66
|
+
Checks: sub_resource ordering, load_steps count, duplicate nodes, resource references.
|
|
67
|
+
"""
|
|
68
|
+
from godot_agent.godot.tscn_validator import validate_tscn as _validate, validate_and_fix
|
|
69
|
+
text = Path(file_path).read_text(errors="replace")
|
|
70
|
+
if auto_fix:
|
|
71
|
+
fixed_text, issues = validate_and_fix(text)
|
|
72
|
+
if fixed_text != text:
|
|
73
|
+
Path(file_path).write_text(fixed_text)
|
|
74
|
+
return {
|
|
75
|
+
"fixed": fixed_text != text,
|
|
76
|
+
"remaining_issues": [{"line": i.line, "severity": i.severity, "message": i.message} for i in issues],
|
|
77
|
+
}
|
|
78
|
+
issues = _validate(text)
|
|
79
|
+
return {
|
|
80
|
+
"issues": [{"line": i.line, "severity": i.severity, "message": i.message} for i in issues],
|
|
81
|
+
"has_errors": any(i.severity == "error" for i in issues),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ─── Code Quality Tools ──────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
@mcp.tool()
|
|
88
|
+
def lint_script(file_path: str) -> dict:
|
|
89
|
+
"""Lint a GDScript file for naming conventions, code ordering, type annotations, and anti-patterns.
|
|
90
|
+
|
|
91
|
+
Based on Godot 4.4 style guide.
|
|
92
|
+
"""
|
|
93
|
+
from godot_agent.godot.gdscript_linter import lint_gdscript, format_lint_report
|
|
94
|
+
text = Path(file_path).read_text(errors="replace")
|
|
95
|
+
issues = lint_gdscript(text, filename=Path(file_path).name)
|
|
96
|
+
return {
|
|
97
|
+
"issues": [{"line": i.line, "severity": i.severity, "rule": i.rule, "message": i.message} for i in issues],
|
|
98
|
+
"error_count": sum(1 for i in issues if i.severity == "error"),
|
|
99
|
+
"warning_count": sum(1 for i in issues if i.severity == "warning"),
|
|
100
|
+
"report": format_lint_report(issues, Path(file_path).name),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@mcp.tool()
|
|
105
|
+
def check_consistency(project_path: str = "") -> dict:
|
|
106
|
+
"""Check cross-file consistency: collision layers, signal names, resource paths, group names.
|
|
107
|
+
|
|
108
|
+
Scans all .gd and .tscn files in the project.
|
|
109
|
+
"""
|
|
110
|
+
from godot_agent.godot.consistency_checker import check_consistency as _check, format_consistency_report
|
|
111
|
+
root = Path(project_path) if project_path else _root()
|
|
112
|
+
issues = _check(root)
|
|
113
|
+
return {
|
|
114
|
+
"issues": [{"file": i.file, "line": i.line, "severity": i.severity, "message": i.message} for i in issues],
|
|
115
|
+
"error_count": sum(1 for i in issues if i.severity == "error"),
|
|
116
|
+
"report": format_consistency_report(issues),
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# ─── Collision & Architecture Tools ──────────────────────────────
|
|
121
|
+
|
|
122
|
+
@mcp.tool()
|
|
123
|
+
def plan_collision(entities: list[str]) -> dict:
|
|
124
|
+
"""Generate standard collision layer/mask config for game entities.
|
|
125
|
+
|
|
126
|
+
Standard scheme: Player=1, Enemy=2, Terrain=3, PlayerBullet=4, EnemyBullet=5, Pickup=6, Trigger=7, Interactable=8.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
entities: List of entity types (e.g., ["player", "enemy", "player_bullet", "enemy_bullet"])
|
|
130
|
+
"""
|
|
131
|
+
from godot_agent.godot.collision_planner import plan_game_collisions, format_collision_plan
|
|
132
|
+
configs = plan_game_collisions(entities)
|
|
133
|
+
return {
|
|
134
|
+
"configs": [
|
|
135
|
+
{"entity": c.entity_type, "layer": c.layer, "layer_bitmask": c.layer_bitmask,
|
|
136
|
+
"mask_layers": c.mask_layers, "mask_bitmask": c.mask_bitmask, "gdscript": c.to_gdscript()}
|
|
137
|
+
for c in configs
|
|
138
|
+
],
|
|
139
|
+
"plan": format_collision_plan(configs),
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@mcp.tool()
|
|
144
|
+
def analyze_dependencies(project_path: str = "") -> dict:
|
|
145
|
+
"""Build a dependency graph of scenes, scripts, and resources in the project."""
|
|
146
|
+
from godot_agent.godot.dependency_graph import build_dependency_graph
|
|
147
|
+
root = Path(project_path) if project_path else _root()
|
|
148
|
+
graph = build_dependency_graph(root)
|
|
149
|
+
return {
|
|
150
|
+
"node_count": len(graph.nodes),
|
|
151
|
+
"main_scene": graph.main_scene,
|
|
152
|
+
"autoloads": graph.autoloads,
|
|
153
|
+
"orphans": graph.orphans(),
|
|
154
|
+
"summary": graph.format_summary(),
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@mcp.tool()
|
|
159
|
+
def suggest_patterns(project_path: str = "") -> dict:
|
|
160
|
+
"""Analyze project scripts and suggest design patterns (object pool, component, state machine)."""
|
|
161
|
+
from godot_agent.godot.pattern_advisor import analyze_project, format_advice
|
|
162
|
+
root = Path(project_path) if project_path else _root()
|
|
163
|
+
advice = analyze_project(root)
|
|
164
|
+
return {
|
|
165
|
+
"suggestions": [
|
|
166
|
+
{"file": a.file, "pattern": a.pattern, "severity": a.severity, "message": a.message}
|
|
167
|
+
for a in advice
|
|
168
|
+
],
|
|
169
|
+
"report": format_advice(advice),
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# ─── Scene Tools ─────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
@mcp.tool()
|
|
176
|
+
def parse_scene(file_path: str) -> dict:
|
|
177
|
+
"""Parse a .tscn scene file and return its node tree, resources, and connections."""
|
|
178
|
+
from godot_agent.godot.scene_parser import parse_tscn
|
|
179
|
+
text = Path(file_path).read_text(errors="replace")
|
|
180
|
+
scene = parse_tscn(text)
|
|
181
|
+
return {
|
|
182
|
+
"format": scene.format,
|
|
183
|
+
"load_steps": scene.load_steps,
|
|
184
|
+
"ext_resources": [{"type": r.type, "path": r.path, "id": r.id} for r in scene.ext_resources],
|
|
185
|
+
"nodes": [
|
|
186
|
+
{"name": n.name, "type": n.type, "parent": n.parent,
|
|
187
|
+
"properties": n.properties}
|
|
188
|
+
for n in scene.nodes
|
|
189
|
+
],
|
|
190
|
+
"connections": [
|
|
191
|
+
{"signal": c.signal, "from": c.from_node, "to": c.to_node, "method": c.method}
|
|
192
|
+
for c in scene.connections
|
|
193
|
+
],
|
|
194
|
+
"node_paths": scene.node_paths(),
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@mcp.tool()
|
|
199
|
+
def project_info(project_path: str = "") -> dict:
|
|
200
|
+
"""Read project.godot and return project name, version, autoloads, resolution, renderer."""
|
|
201
|
+
from godot_agent.godot.project import parse_project_godot
|
|
202
|
+
root = Path(project_path) if project_path else _root()
|
|
203
|
+
proj = parse_project_godot(root / "project.godot")
|
|
204
|
+
return {
|
|
205
|
+
"name": proj.name,
|
|
206
|
+
"version": proj.version,
|
|
207
|
+
"main_scene": proj.main_scene,
|
|
208
|
+
"viewport_width": proj.viewport_width,
|
|
209
|
+
"viewport_height": proj.viewport_height,
|
|
210
|
+
"renderer": proj.renderer,
|
|
211
|
+
"autoloads": proj.autoloads,
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# ─── Knowledge Tools ─────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
@mcp.tool()
|
|
218
|
+
def godot_knowledge(topic: str) -> dict:
|
|
219
|
+
"""Query the Godot 4.4 Playbook for best practices, patterns, and API references.
|
|
220
|
+
|
|
221
|
+
Topics: collision, physics, signal, animation, ui, input, performance, resource, autoload, shader, navigation, etc.
|
|
222
|
+
"""
|
|
223
|
+
from godot_agent.prompts.knowledge_selector import select_sections
|
|
224
|
+
sections = select_sections(topic, max_sections=3)
|
|
225
|
+
return {
|
|
226
|
+
"sections": [{"title": t, "content": c} for t, c in sections],
|
|
227
|
+
"topic": topic,
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# ─── Sprite Generation ───────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
@mcp.tool()
|
|
234
|
+
async def generate_sprite(
|
|
235
|
+
subject: str,
|
|
236
|
+
output_path: str,
|
|
237
|
+
size: int = 32,
|
|
238
|
+
style: str = "pixel_modern",
|
|
239
|
+
facing: str = "front",
|
|
240
|
+
category: str = "character",
|
|
241
|
+
extra: str = "",
|
|
242
|
+
) -> dict:
|
|
243
|
+
"""Generate a pixel art sprite using AI with automatic post-processing.
|
|
244
|
+
|
|
245
|
+
Pipeline: AI generation → chroma key (green → transparent) → auto-crop → nearest-neighbor resize.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
subject: What to draw (e.g., "fire mage casting spell", "health potion")
|
|
249
|
+
output_path: Where to save the PNG file
|
|
250
|
+
size: Target pixel size (8, 16, 24, 32, 48, 64, 128)
|
|
251
|
+
style: pixel_8bit, pixel_16bit, pixel_modern, chibi, minimal
|
|
252
|
+
facing: front, left, right, back
|
|
253
|
+
category: character, enemy, boss, item, projectile, ui_icon, effect
|
|
254
|
+
"""
|
|
255
|
+
from godot_agent.tools.image_gen import GenerateSpriteTool
|
|
256
|
+
tool = GenerateSpriteTool()
|
|
257
|
+
result = await tool.execute(tool.Input(
|
|
258
|
+
subject=subject, output_path=output_path, size=size,
|
|
259
|
+
style=style, facing=facing, category=category, extra=extra,
|
|
260
|
+
))
|
|
261
|
+
if result.error:
|
|
262
|
+
return {"error": result.error}
|
|
263
|
+
return {
|
|
264
|
+
"path": result.output.path,
|
|
265
|
+
"width": result.output.width,
|
|
266
|
+
"height": result.output.height,
|
|
267
|
+
"prompt_used": result.output.prompt_used,
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
# ─── Resource Validation ─────────────────────────────────────────
|
|
272
|
+
|
|
273
|
+
@mcp.tool()
|
|
274
|
+
def validate_resources(file_path: str, project_path: str = "") -> dict:
|
|
275
|
+
"""Check that all ext_resource paths in a .tscn file actually exist on disk."""
|
|
276
|
+
from godot_agent.godot.resource_validator import validate_resources as _validate
|
|
277
|
+
root = Path(project_path) if project_path else _root()
|
|
278
|
+
issues = _validate(Path(file_path), project_root=root)
|
|
279
|
+
return {
|
|
280
|
+
"valid": len(issues) == 0,
|
|
281
|
+
"missing": issues,
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def run_mcp_server(project_path: str | None = None) -> None:
|
|
286
|
+
"""Entry point called by CLI: god-code mcp [--project PATH]"""
|
|
287
|
+
if project_path:
|
|
288
|
+
set_mcp_project_root(Path(project_path))
|
|
289
|
+
mcp.run(transport="stdio")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "god-code"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.5"
|
|
4
4
|
description = "AI coding agent specialized for Godot game development"
|
|
5
5
|
requires-python = ">=3.9"
|
|
6
6
|
license = {text = "GPL-3.0-or-later"}
|
|
@@ -16,6 +16,9 @@ dependencies = [
|
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
[project.optional-dependencies]
|
|
19
|
+
mcp = [
|
|
20
|
+
"mcp[cli]>=1.12",
|
|
21
|
+
]
|
|
19
22
|
dev = [
|
|
20
23
|
"pytest>=8.2",
|
|
21
24
|
"pytest-asyncio>=0.23",
|
|
@@ -86,8 +86,8 @@ def _module_uses_dataclass_slots(module: ast.Module) -> bool:
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
def test_requires_python_matches_documented_floor() -> None:
|
|
89
|
-
assert _declared_min_python() == (3,
|
|
90
|
-
assert "
|
|
89
|
+
assert _declared_min_python() == (3, 9)
|
|
90
|
+
assert "Python 3.9+" in README_PATH.read_text(encoding="utf-8")
|
|
91
91
|
|
|
92
92
|
|
|
93
93
|
def test_declared_python_floor_covers_runtime_features() -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|