god-code 0.5.5__tar.gz → 0.6.1__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.5 → god_code-0.6.1}/PKG-INFO +3 -3
- {god_code-0.5.5 → god_code-0.6.1}/README.md +24 -5
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/cli.py +53 -1
- god_code-0.6.1/godot_agent/mcp_server.py +393 -0
- {god_code-0.5.5 → god_code-0.6.1}/pyproject.toml +3 -3
- god_code-0.6.1/skills/god-code-setup/SKILL.md +123 -0
- god_code-0.5.5/godot_agent/mcp_server.py +0 -289
- {god_code-0.5.5 → god_code-0.6.1}/.github/workflows/publish.yml +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/.gitignore +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/AGENTS.md +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/CHANGELOG.md +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/CLAUDE.md +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/CONTRIBUTING.md +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/LICENSE +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/agents/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/agents/configs.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/agents/dispatcher.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/agents/results.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/godot/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/godot/collision_planner.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/godot/consistency_checker.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/godot/dependency_graph.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/godot/gdscript_linter.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/godot/impact_analysis.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/godot/pattern_advisor.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/godot/project.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/godot/resource_validator.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/godot/scene_parser.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/godot/scene_writer.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/godot/tscn_validator.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/llm/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/llm/adapters/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/llm/adapters/anthropic.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/llm/adapters/base.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/llm/adapters/openai.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/llm/client.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/llm/streaming.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/llm/types.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/llm/vision.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/prompts/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/prompts/assembler.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/prompts/build_discipline.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/prompts/godot_playbook.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/prompts/image_templates.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/prompts/knowledge_selector.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/prompts/skill_library.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/prompts/skill_selector.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/prompts/system.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/py.typed +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/auth.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/config.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/context_manager.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/design_memory.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/engine.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/error_loop.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/events.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/gameplay_reviewer.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/modes.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/oauth.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/playtest_harness.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/providers.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/quality_gate.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/reviewer.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/runtime_bridge.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/scenario_specs/hud_feedback.json +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/scenario_specs/player_movement.json +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/scenario_specs/scene_transition.json +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/runtime/session.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/security/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/security/classifier.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/security/hooks.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/security/policies.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/security/protected_paths.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/security/tool_pipeline.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/testing/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/testing/scenario_runner.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/analysis_tools.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/base.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/editor_bridge.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/file_ops.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/git.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/godot_cli.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/image_gen.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/list_dir.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/memory_tool.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/registry.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/scene_tools.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/screenshot.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/script_tools.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/search.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/shell.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tools/web_search.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tui/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tui/display.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/godot_agent/tui/input_handler.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/agents/test_dispatcher.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/agents/test_playtest_analyst.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/e2e/test_planner_worker_reviewer_flow.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/e2e/test_policy_enforcement.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/e2e/test_scenario_runner.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/godot/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/godot/test_collision_planner.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/godot/test_consistency.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/godot/test_dependency_graph.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/godot/test_impact_analysis.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/godot/test_linter.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/godot/test_pattern_advisor.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/godot/test_project.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/godot/test_resource_validator.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/godot/test_scene_parser.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/godot/test_scene_writer.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/godot/test_tscn_validator.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/llm/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/llm/test_adapters.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/llm/test_client.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/llm/test_vision.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/prompts/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/prompts/test_knowledge_selector.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/prompts/test_prompt_assembler.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/prompts/test_skill_selector.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/prompts/test_system_prompt.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_config.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_context_manager.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_design_memory.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_engine.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_error_loop.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_gameplay_reviewer.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_mode_restrictions.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_multi_agent_flow.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_playtest_harness.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_quality_gate.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_reviewer.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_runtime_bridge.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/runtime/test_session.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/security/test_classifier.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/security/test_hooks.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/security/test_permissions.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/security/test_tool_pipeline.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/test_cli_config_flow.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/test_e2e.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/test_package_compatibility.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/test_runtime_switch_commands.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/__init__.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/test_analysis_tools.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/test_editor_bridge.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/test_file_ops.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/test_git.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/test_godot_cli.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/test_list_dir.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/test_memory_tool.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/test_registry.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/test_scene_tools.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/test_script_tools.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/test_search.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tools/test_shell.py +0 -0
- {god_code-0.5.5 → god_code-0.6.1}/tests/tui/test_input_handler.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: god-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: AI coding agent specialized for Godot game development
|
|
5
|
-
Project-URL: Homepage, https://github.com/
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
5
|
+
Project-URL: Homepage, https://github.com/888wing/god-code
|
|
6
|
+
Project-URL: Repository, https://github.com/888wing/god-code
|
|
7
7
|
Author: chuisiufai
|
|
8
8
|
License: GPL-3.0-or-later
|
|
9
9
|
License-File: LICENSE
|
|
@@ -25,16 +25,35 @@ AI coding agent specialized for Godot 4.4 game development. Unlike generic codin
|
|
|
25
25
|
pip install god-code
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
With MCP support (for Claude Code / Codex integration):
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
|
|
32
|
-
cd god-code
|
|
33
|
-
pip install -e ".[dev]"
|
|
31
|
+
pip install god-code[mcp]
|
|
34
32
|
```
|
|
35
33
|
|
|
36
34
|
Requires Python 3.9+.
|
|
37
35
|
|
|
36
|
+
### Claude Code Skill (one-click setup)
|
|
37
|
+
|
|
38
|
+
If you use Claude Code, install the god-code skill for automated setup:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Copy the skill to your Claude Code skills directory
|
|
42
|
+
mkdir -p ~/.claude/skills/god-code-setup
|
|
43
|
+
curl -sL https://raw.githubusercontent.com/888wing/god-code/main/skills/god-code-setup/SKILL.md \
|
|
44
|
+
-o ~/.claude/skills/god-code-setup/SKILL.md
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Then in Claude Code, just say: **"install god-code and configure MCP"** — it will handle everything automatically.
|
|
48
|
+
|
|
49
|
+
### From Source
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git clone https://github.com/888wing/god-code.git
|
|
53
|
+
cd god-code
|
|
54
|
+
pip install -e ".[dev,mcp]"
|
|
55
|
+
```
|
|
56
|
+
|
|
38
57
|
## Quick Start
|
|
39
58
|
|
|
40
59
|
### 1. Configure API key (BYOK — Bring Your Own Key)
|
|
@@ -192,7 +211,7 @@ God Code executes tools on your local machine. The LLM decides which tools to ca
|
|
|
192
211
|
|
|
193
212
|
## MCP Server (for Claude Code / Codex / AI Agents)
|
|
194
213
|
|
|
195
|
-
God Code can run as an MCP (Model Context Protocol) server, exposing
|
|
214
|
+
God Code can run as an MCP (Model Context Protocol) server, exposing 20 Godot tools directly to AI agents. **No LLM middleman, zero token cost** — tools run locally.
|
|
196
215
|
|
|
197
216
|
### Install
|
|
198
217
|
|
|
@@ -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.
|
|
705
|
+
_VERSION = "0.6.1"
|
|
706
706
|
|
|
707
707
|
|
|
708
708
|
def _check_update() -> None:
|
|
@@ -1680,5 +1680,57 @@ def mcp_command(project: str):
|
|
|
1680
1680
|
run_mcp_server(project_path=project)
|
|
1681
1681
|
|
|
1682
1682
|
|
|
1683
|
+
@main.command()
|
|
1684
|
+
def tools():
|
|
1685
|
+
"""List all available MCP tools with descriptions."""
|
|
1686
|
+
from rich.console import Console
|
|
1687
|
+
from rich.table import Table
|
|
1688
|
+
from rich.panel import Panel
|
|
1689
|
+
|
|
1690
|
+
console = Console()
|
|
1691
|
+
|
|
1692
|
+
try:
|
|
1693
|
+
from godot_agent.mcp_server import mcp as mcp_instance
|
|
1694
|
+
mcp_tools = mcp_instance._tool_manager._tools
|
|
1695
|
+
except Exception:
|
|
1696
|
+
console.print("[red]MCP not available. Install with: pip install god-code[mcp][/]")
|
|
1697
|
+
return
|
|
1698
|
+
|
|
1699
|
+
t = Table(show_header=True, padding=(0, 1))
|
|
1700
|
+
t.add_column("#", style="dim", width=3)
|
|
1701
|
+
t.add_column("Tool", style="green bold")
|
|
1702
|
+
t.add_column("Description", style="dim")
|
|
1703
|
+
|
|
1704
|
+
for i, (name, tool) in enumerate(sorted(mcp_tools.items()), 1):
|
|
1705
|
+
desc = (tool.description or "").split("\n")[0][:80]
|
|
1706
|
+
t.add_row(str(i), name, desc)
|
|
1707
|
+
|
|
1708
|
+
console.print(Panel(t, title=f"[cyan]God Code MCP Tools ({len(mcp_tools)})[/]", border_style="cyan"))
|
|
1709
|
+
console.print()
|
|
1710
|
+
console.print("[dim]Use these tools via MCP in Claude Code, or directly in god-code chat.[/]")
|
|
1711
|
+
|
|
1712
|
+
|
|
1713
|
+
@main.command("update-skill")
|
|
1714
|
+
def update_skill():
|
|
1715
|
+
"""Download/update the god-code-setup skill for Claude Code."""
|
|
1716
|
+
from pathlib import Path
|
|
1717
|
+
import httpx
|
|
1718
|
+
|
|
1719
|
+
skill_dir = Path.home() / ".claude" / "skills" / "god-code-setup"
|
|
1720
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
1721
|
+
skill_file = skill_dir / "SKILL.md"
|
|
1722
|
+
url = "https://raw.githubusercontent.com/888wing/god-code/main/skills/god-code-setup/SKILL.md"
|
|
1723
|
+
|
|
1724
|
+
click.echo(f"Downloading skill from {url}...")
|
|
1725
|
+
try:
|
|
1726
|
+
resp = httpx.get(url, timeout=10, follow_redirects=True)
|
|
1727
|
+
resp.raise_for_status()
|
|
1728
|
+
skill_file.write_text(resp.text)
|
|
1729
|
+
click.secho(f"Skill updated: {skill_file}", fg="green")
|
|
1730
|
+
click.echo("Restart Claude Code to activate.")
|
|
1731
|
+
except Exception as e:
|
|
1732
|
+
click.secho(f"Failed: {e}", fg="red")
|
|
1733
|
+
|
|
1734
|
+
|
|
1683
1735
|
if __name__ == "__main__":
|
|
1684
1736
|
main()
|
|
@@ -0,0 +1,393 @@
|
|
|
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
|
+
12 analysis tools + 8 write/execute tools = full game development capability.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from mcp.server.fastmcp import FastMCP
|
|
17
|
+
|
|
18
|
+
log = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
mcp = FastMCP("god-code")
|
|
21
|
+
|
|
22
|
+
_project_root: Path | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def set_mcp_project_root(path: Path) -> None:
|
|
26
|
+
global _project_root
|
|
27
|
+
_project_root = path.resolve()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _root() -> Path:
|
|
31
|
+
return _project_root or Path.cwd()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _godot_path() -> str:
|
|
35
|
+
config_file = Path.home() / ".config" / "god-code" / "config.json"
|
|
36
|
+
if config_file.exists():
|
|
37
|
+
return json.loads(config_file.read_text()).get("godot_path", "godot")
|
|
38
|
+
return "godot"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
42
|
+
# ANALYSIS TOOLS (read-only, zero side effects)
|
|
43
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
44
|
+
|
|
45
|
+
@mcp.tool()
|
|
46
|
+
async def validate_project(project_path: str = "") -> dict:
|
|
47
|
+
"""Run Godot headless validation. Returns errors and warnings."""
|
|
48
|
+
from godot_agent.runtime.error_loop import validate_project as _validate, format_validation_for_llm
|
|
49
|
+
root = project_path or str(_root())
|
|
50
|
+
result = await _validate(root, _godot_path(), timeout=30)
|
|
51
|
+
return {
|
|
52
|
+
"success": result.success,
|
|
53
|
+
"errors": [{"file": e.file, "line": e.line, "message": e.message} for e in result.errors],
|
|
54
|
+
"warnings": [{"file": w.file, "line": w.line, "message": w.message} for w in result.warnings],
|
|
55
|
+
"summary": format_validation_for_llm(result),
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@mcp.tool()
|
|
60
|
+
def validate_tscn(file_path: str, auto_fix: bool = False) -> dict:
|
|
61
|
+
"""Validate .tscn format. Optionally auto-fix ordering issues (sub_resource before node, load_steps count)."""
|
|
62
|
+
from godot_agent.godot.tscn_validator import validate_tscn as _validate, validate_and_fix
|
|
63
|
+
text = Path(file_path).read_text(errors="replace")
|
|
64
|
+
if auto_fix:
|
|
65
|
+
fixed_text, issues = validate_and_fix(text)
|
|
66
|
+
if fixed_text != text:
|
|
67
|
+
Path(file_path).write_text(fixed_text)
|
|
68
|
+
return {"fixed": fixed_text != text, "remaining_issues": [str(i) for i in issues]}
|
|
69
|
+
issues = _validate(text)
|
|
70
|
+
return {"issues": [str(i) for i in issues], "has_errors": any(i.severity == "error" for i in issues)}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@mcp.tool()
|
|
74
|
+
def lint_script(file_path: str) -> dict:
|
|
75
|
+
"""Lint GDScript for naming, ordering, type annotations, and anti-patterns."""
|
|
76
|
+
from godot_agent.godot.gdscript_linter import lint_gdscript, format_lint_report
|
|
77
|
+
text = Path(file_path).read_text(errors="replace")
|
|
78
|
+
issues = lint_gdscript(text, filename=Path(file_path).name)
|
|
79
|
+
return {
|
|
80
|
+
"report": format_lint_report(issues, Path(file_path).name),
|
|
81
|
+
"error_count": sum(1 for i in issues if i.severity == "error"),
|
|
82
|
+
"warning_count": sum(1 for i in issues if i.severity == "warning"),
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@mcp.tool()
|
|
87
|
+
def check_consistency(project_path: str = "") -> dict:
|
|
88
|
+
"""Check cross-file consistency: collision layers, signals, resource paths, groups."""
|
|
89
|
+
from godot_agent.godot.consistency_checker import check_consistency as _check, format_consistency_report
|
|
90
|
+
root = Path(project_path) if project_path else _root()
|
|
91
|
+
issues = _check(root)
|
|
92
|
+
return {"report": format_consistency_report(issues), "error_count": sum(1 for i in issues if i.severity == "error")}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@mcp.tool()
|
|
96
|
+
def plan_collision(entities: list[str]) -> dict:
|
|
97
|
+
"""Generate standard collision layer/mask config. Entities: player, enemy, player_bullet, enemy_bullet, terrain, pickup, trigger, interactable."""
|
|
98
|
+
from godot_agent.godot.collision_planner import plan_game_collisions, format_collision_plan
|
|
99
|
+
configs = plan_game_collisions(entities)
|
|
100
|
+
return {
|
|
101
|
+
"configs": [{"entity": c.entity_type, "layer": c.layer, "bitmask": c.layer_bitmask,
|
|
102
|
+
"mask_layers": c.mask_layers, "mask_bitmask": c.mask_bitmask, "gdscript": c.to_gdscript()}
|
|
103
|
+
for c in configs],
|
|
104
|
+
"plan": format_collision_plan(configs),
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@mcp.tool()
|
|
109
|
+
def analyze_dependencies(project_path: str = "") -> dict:
|
|
110
|
+
"""Build project dependency graph (scenes → scripts → resources)."""
|
|
111
|
+
from godot_agent.godot.dependency_graph import build_dependency_graph
|
|
112
|
+
graph = build_dependency_graph(Path(project_path) if project_path else _root())
|
|
113
|
+
return {"summary": graph.format_summary(), "autoloads": graph.autoloads, "orphans": graph.orphans()}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@mcp.tool()
|
|
117
|
+
def suggest_patterns(project_path: str = "") -> dict:
|
|
118
|
+
"""Suggest design patterns: object pool, component, state machine."""
|
|
119
|
+
from godot_agent.godot.pattern_advisor import analyze_project, format_advice
|
|
120
|
+
advice = analyze_project(Path(project_path) if project_path else _root())
|
|
121
|
+
return {"report": format_advice(advice), "count": len(advice)}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@mcp.tool()
|
|
125
|
+
def parse_scene(file_path: str) -> dict:
|
|
126
|
+
"""Parse .tscn into structured data: nodes, resources, connections, node paths."""
|
|
127
|
+
from godot_agent.godot.scene_parser import parse_tscn
|
|
128
|
+
scene = parse_tscn(Path(file_path).read_text(errors="replace"))
|
|
129
|
+
return {
|
|
130
|
+
"nodes": [{"name": n.name, "type": n.type, "parent": n.parent, "properties": n.properties} for n in scene.nodes],
|
|
131
|
+
"ext_resources": [{"type": r.type, "path": r.path, "id": r.id} for r in scene.ext_resources],
|
|
132
|
+
"connections": [{"signal": c.signal, "from": c.from_node, "to": c.to_node, "method": c.method} for c in scene.connections],
|
|
133
|
+
"node_paths": scene.node_paths(),
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@mcp.tool()
|
|
138
|
+
def project_info(project_path: str = "") -> dict:
|
|
139
|
+
"""Read project.godot: name, version, autoloads, resolution, renderer."""
|
|
140
|
+
from godot_agent.godot.project import parse_project_godot
|
|
141
|
+
proj = parse_project_godot((Path(project_path) if project_path else _root()) / "project.godot")
|
|
142
|
+
return {"name": proj.name, "version": proj.version, "main_scene": proj.main_scene,
|
|
143
|
+
"resolution": f"{proj.viewport_width}x{proj.viewport_height}", "autoloads": proj.autoloads}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@mcp.tool()
|
|
147
|
+
def godot_knowledge(topic: str) -> dict:
|
|
148
|
+
"""Query Godot 4.4 Playbook. Topics: collision, physics, signal, animation, ui, input, performance, resource, autoload, shader, etc."""
|
|
149
|
+
from godot_agent.prompts.knowledge_selector import select_sections
|
|
150
|
+
sections = select_sections(topic, max_sections=3)
|
|
151
|
+
return {"sections": [{"title": t, "content": c} for t, c in sections]}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@mcp.tool()
|
|
155
|
+
def validate_resources(file_path: str, project_path: str = "") -> dict:
|
|
156
|
+
"""Check all ext_resource paths in a .tscn exist on disk."""
|
|
157
|
+
from godot_agent.godot.resource_validator import validate_resources as _validate
|
|
158
|
+
issues = _validate(Path(file_path), project_root=Path(project_path) if project_path else _root())
|
|
159
|
+
return {"valid": len(issues) == 0, "missing": issues}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
163
|
+
# WRITE TOOLS (modify files with format protection)
|
|
164
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
165
|
+
|
|
166
|
+
@mcp.tool()
|
|
167
|
+
def write_scene(file_path: str, content: str) -> dict:
|
|
168
|
+
"""Write a .tscn file with automatic format validation and fix.
|
|
169
|
+
|
|
170
|
+
Validates the content before writing. Auto-fixes sub_resource ordering and load_steps count.
|
|
171
|
+
Use this instead of writing .tscn files directly to prevent format errors.
|
|
172
|
+
"""
|
|
173
|
+
from godot_agent.godot.tscn_validator import validate_and_fix
|
|
174
|
+
fixed_text, issues = validate_and_fix(content)
|
|
175
|
+
errors = [i for i in issues if i.severity == "error"]
|
|
176
|
+
if errors:
|
|
177
|
+
return {"written": False, "errors": [str(e) for e in errors]}
|
|
178
|
+
Path(file_path).parent.mkdir(parents=True, exist_ok=True)
|
|
179
|
+
Path(file_path).write_text(fixed_text, encoding="utf-8")
|
|
180
|
+
return {"written": True, "path": file_path, "auto_fixed": fixed_text != content,
|
|
181
|
+
"warnings": [str(i) for i in issues if i.severity == "warning"]}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@mcp.tool()
|
|
185
|
+
def add_scene_node(file_path: str, parent: str, name: str, node_type: str,
|
|
186
|
+
properties: dict[str, str] | None = None) -> dict:
|
|
187
|
+
"""Add a node to an existing .tscn scene file.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
file_path: Path to .tscn file
|
|
191
|
+
parent: Parent node path ("." for root's child, "NodeName" for deeper)
|
|
192
|
+
name: New node name (PascalCase)
|
|
193
|
+
node_type: Godot node type (Node2D, Sprite2D, Area2D, Label, etc.)
|
|
194
|
+
properties: Optional property dict (e.g., {"position": "Vector2(100, 200)"})
|
|
195
|
+
"""
|
|
196
|
+
from godot_agent.godot.scene_writer import add_node
|
|
197
|
+
from godot_agent.godot.tscn_validator import validate_and_fix
|
|
198
|
+
text = Path(file_path).read_text(errors="replace")
|
|
199
|
+
new_text = add_node(text, parent=parent, name=name, type=node_type, properties=properties)
|
|
200
|
+
fixed, issues = validate_and_fix(new_text)
|
|
201
|
+
Path(file_path).write_text(fixed, encoding="utf-8")
|
|
202
|
+
return {"added": True, "node": f"{name} ({node_type})", "parent": parent,
|
|
203
|
+
"warnings": [str(i) for i in issues if i.severity == "warning"]}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@mcp.tool()
|
|
207
|
+
def set_scene_property(file_path: str, node_name: str, key: str, value: str) -> dict:
|
|
208
|
+
"""Set or update a property on a node in a .tscn file.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
file_path: Path to .tscn file
|
|
212
|
+
node_name: Name of the node to modify
|
|
213
|
+
key: Property name (e.g., "position", "text", "color", "visible")
|
|
214
|
+
value: Property value as Godot string (e.g., "Vector2(10, 20)", '"Hello"', "true")
|
|
215
|
+
"""
|
|
216
|
+
from godot_agent.godot.scene_writer import set_node_property
|
|
217
|
+
text = Path(file_path).read_text(errors="replace")
|
|
218
|
+
new_text = set_node_property(text, node_name=node_name, key=key, value=value)
|
|
219
|
+
Path(file_path).write_text(new_text, encoding="utf-8")
|
|
220
|
+
return {"set": True, "node": node_name, "property": f"{key} = {value}"}
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@mcp.tool()
|
|
224
|
+
def add_signal_connection(file_path: str, signal_name: str, from_node: str,
|
|
225
|
+
to_node: str, method: str) -> dict:
|
|
226
|
+
"""Add a signal connection to a .tscn file.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
signal_name: Signal name (e.g., "pressed", "body_entered", "timeout")
|
|
230
|
+
from_node: Source node path
|
|
231
|
+
to_node: Target node path
|
|
232
|
+
method: Handler method name (e.g., "_on_button_pressed")
|
|
233
|
+
"""
|
|
234
|
+
from godot_agent.godot.scene_writer import add_connection
|
|
235
|
+
text = Path(file_path).read_text(errors="replace")
|
|
236
|
+
new_text = add_connection(text, signal_name=signal_name, from_node=from_node,
|
|
237
|
+
to_node=to_node, method=method)
|
|
238
|
+
Path(file_path).write_text(new_text, encoding="utf-8")
|
|
239
|
+
return {"connected": True, "signal": signal_name, "from": from_node, "to": to_node, "method": method}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@mcp.tool()
|
|
243
|
+
def write_script(file_path: str, content: str, lint: bool = True) -> dict:
|
|
244
|
+
"""Write a GDScript file with optional lint validation.
|
|
245
|
+
|
|
246
|
+
Use this instead of writing .gd files directly. Runs the linter and returns issues.
|
|
247
|
+
"""
|
|
248
|
+
Path(file_path).parent.mkdir(parents=True, exist_ok=True)
|
|
249
|
+
Path(file_path).write_text(content, encoding="utf-8")
|
|
250
|
+
result = {"written": True, "path": file_path}
|
|
251
|
+
if lint:
|
|
252
|
+
from godot_agent.godot.gdscript_linter import lint_gdscript
|
|
253
|
+
issues = lint_gdscript(content, filename=Path(file_path).name)
|
|
254
|
+
result["lint_errors"] = sum(1 for i in issues if i.severity == "error")
|
|
255
|
+
result["lint_warnings"] = sum(1 for i in issues if i.severity == "warning")
|
|
256
|
+
if issues:
|
|
257
|
+
result["lint_issues"] = [str(i) for i in issues[:10]]
|
|
258
|
+
return result
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
262
|
+
# EXECUTION TOOLS (run Godot, take screenshots)
|
|
263
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
264
|
+
|
|
265
|
+
@mcp.tool()
|
|
266
|
+
async def run_gut_tests(test_script: str = "", project_path: str = "") -> dict:
|
|
267
|
+
"""Run GUT tests in headless mode. Returns pass/fail results.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
test_script: Specific test script (e.g., "res://tests/test_battle.gd"). Empty = run all.
|
|
271
|
+
project_path: Godot project root. Empty = use configured root.
|
|
272
|
+
"""
|
|
273
|
+
import asyncio
|
|
274
|
+
from godot_agent.tools.godot_cli import build_gut_command, parse_godot_output
|
|
275
|
+
root = project_path or str(_root())
|
|
276
|
+
cmd = build_gut_command(_godot_path(), test_script or None)
|
|
277
|
+
proc = await asyncio.create_subprocess_exec(
|
|
278
|
+
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, cwd=root)
|
|
279
|
+
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=120)
|
|
280
|
+
output = stdout.decode(errors="replace") + "\n" + stderr.decode(errors="replace")
|
|
281
|
+
report = parse_godot_output(output)
|
|
282
|
+
return {
|
|
283
|
+
"exit_code": proc.returncode or 0,
|
|
284
|
+
"passed": proc.returncode == 0,
|
|
285
|
+
"errors": [{"file": e.file, "line": e.line, "message": e.message} for e in report.errors],
|
|
286
|
+
"output_tail": output[-500:],
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@mcp.tool()
|
|
291
|
+
async def screenshot_scene(scene_path: str, output_path: str = "", delay_ms: int = 1000,
|
|
292
|
+
project_path: str = "") -> dict:
|
|
293
|
+
"""Take a screenshot of a Godot scene using headless rendering.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
scene_path: res:// path to the scene (e.g., "res://scenes/main.tscn")
|
|
297
|
+
output_path: Where to save PNG. Empty = temp file.
|
|
298
|
+
delay_ms: Wait time before capture (ms). Default 1000.
|
|
299
|
+
"""
|
|
300
|
+
import asyncio
|
|
301
|
+
import tempfile
|
|
302
|
+
from godot_agent.tools.godot_cli import build_screenshot_script
|
|
303
|
+
from godot_agent.llm.vision import encode_image
|
|
304
|
+
|
|
305
|
+
root = project_path or str(_root())
|
|
306
|
+
if not output_path:
|
|
307
|
+
output_path = str(Path(tempfile.mkdtemp()) / "screenshot.png")
|
|
308
|
+
|
|
309
|
+
script = build_screenshot_script(scene_path, output_path, delay_ms)
|
|
310
|
+
script_path = str(Path(tempfile.mkdtemp()) / "capture.gd")
|
|
311
|
+
Path(script_path).write_text(script)
|
|
312
|
+
|
|
313
|
+
proc = await asyncio.create_subprocess_exec(
|
|
314
|
+
_godot_path(), "--headless", "-s", script_path,
|
|
315
|
+
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, cwd=root)
|
|
316
|
+
await asyncio.wait_for(proc.communicate(), timeout=30)
|
|
317
|
+
|
|
318
|
+
if not Path(output_path).exists():
|
|
319
|
+
return {"success": False, "error": "Screenshot not created. Scene may have failed to load."}
|
|
320
|
+
|
|
321
|
+
b64 = encode_image(output_path)
|
|
322
|
+
return {"success": True, "path": output_path, "image_base64": b64}
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@mcp.tool()
|
|
326
|
+
async def run_scene(scene_path: str = "", project_path: str = "", timeout_seconds: int = 5) -> dict:
|
|
327
|
+
"""Run a Godot scene briefly to verify it loads without errors.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
scene_path: res:// path. Empty = main scene from project.godot.
|
|
331
|
+
timeout_seconds: How long to let it run before stopping (default 5).
|
|
332
|
+
"""
|
|
333
|
+
import asyncio
|
|
334
|
+
root = project_path or str(_root())
|
|
335
|
+
cmd = [_godot_path(), "--headless"]
|
|
336
|
+
if scene_path:
|
|
337
|
+
cmd.extend(["--scene", scene_path])
|
|
338
|
+
cmd.append("--quit-after")
|
|
339
|
+
cmd.append(str(timeout_seconds))
|
|
340
|
+
|
|
341
|
+
proc = await asyncio.create_subprocess_exec(
|
|
342
|
+
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, cwd=root)
|
|
343
|
+
try:
|
|
344
|
+
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout_seconds + 10)
|
|
345
|
+
output = stdout.decode(errors="replace") + "\n" + stderr.decode(errors="replace")
|
|
346
|
+
except asyncio.TimeoutError:
|
|
347
|
+
proc.kill()
|
|
348
|
+
output = "Timed out"
|
|
349
|
+
|
|
350
|
+
from godot_agent.runtime.error_loop import parse_godot_output
|
|
351
|
+
errors = parse_godot_output(output)
|
|
352
|
+
return {
|
|
353
|
+
"loaded": not any(e.level == "ERROR" for e in errors),
|
|
354
|
+
"errors": [{"file": e.file, "line": e.line, "message": e.message} for e in errors if e.level == "ERROR"],
|
|
355
|
+
"exit_code": proc.returncode,
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
360
|
+
# SPRITE GENERATION
|
|
361
|
+
# ═══════════════════════════════════════════════════════════════════
|
|
362
|
+
|
|
363
|
+
@mcp.tool()
|
|
364
|
+
async def generate_sprite(
|
|
365
|
+
subject: str, output_path: str, size: int = 32, style: str = "pixel_modern",
|
|
366
|
+
facing: str = "front", category: str = "character", extra: str = "",
|
|
367
|
+
) -> dict:
|
|
368
|
+
"""Generate pixel art sprite with AI. Pipeline: generate → chroma key → crop → resize.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
subject: What to draw (e.g., "fire mage", "health potion", "slime enemy")
|
|
372
|
+
output_path: Where to save PNG
|
|
373
|
+
size: Pixel size (8/16/24/32/48/64/128)
|
|
374
|
+
style: pixel_8bit, pixel_16bit, pixel_modern, chibi, minimal
|
|
375
|
+
facing: front, left, right, back
|
|
376
|
+
category: character, enemy, boss, item, projectile, ui_icon, effect
|
|
377
|
+
"""
|
|
378
|
+
from godot_agent.tools.image_gen import GenerateSpriteTool
|
|
379
|
+
tool = GenerateSpriteTool()
|
|
380
|
+
result = await tool.execute(tool.Input(
|
|
381
|
+
subject=subject, output_path=output_path, size=size,
|
|
382
|
+
style=style, facing=facing, category=category, extra=extra))
|
|
383
|
+
if result.error:
|
|
384
|
+
return {"error": result.error}
|
|
385
|
+
return {"path": result.output.path, "width": result.output.width,
|
|
386
|
+
"height": result.output.height, "prompt_used": result.output.prompt_used}
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def run_mcp_server(project_path: str | None = None) -> None:
|
|
390
|
+
"""Entry point: god-code mcp [--project PATH]"""
|
|
391
|
+
if project_path:
|
|
392
|
+
set_mcp_project_root(Path(project_path))
|
|
393
|
+
mcp.run(transport="stdio")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "god-code"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.1"
|
|
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"}
|
|
@@ -29,8 +29,8 @@ dev = [
|
|
|
29
29
|
god-code = "godot_agent.cli:main"
|
|
30
30
|
|
|
31
31
|
[project.urls]
|
|
32
|
-
Homepage = "https://github.com/
|
|
33
|
-
Repository = "https://github.com/
|
|
32
|
+
Homepage = "https://github.com/888wing/god-code"
|
|
33
|
+
Repository = "https://github.com/888wing/god-code"
|
|
34
34
|
|
|
35
35
|
[tool.hatch.build.targets.wheel]
|
|
36
36
|
packages = ["godot_agent"]
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: god-code-setup
|
|
3
|
+
description: Install and configure God Code — the AI coding agent for Godot 4.4 game development. Use when the user wants to set up god-code, configure MCP server for Claude Code, or start a Godot project with AI assistance. Triggers on keywords like god-code, godot agent, godot mcp, godot ai, setup godot tools.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# God Code Setup
|
|
7
|
+
|
|
8
|
+
Automated installation and MCP server configuration for God Code — the Godot 4.4 AI development toolkit.
|
|
9
|
+
|
|
10
|
+
## What This Does
|
|
11
|
+
|
|
12
|
+
1. Installs god-code from PyPI (with MCP support)
|
|
13
|
+
2. Configures MCP server so Claude Code gets 12 Godot tools
|
|
14
|
+
3. Verifies everything works
|
|
15
|
+
|
|
16
|
+
## Step 1: Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install god-code[mcp]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
If pip fails on macOS (externally-managed-environment):
|
|
23
|
+
```bash
|
|
24
|
+
pip install --user god-code[mcp]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Verify:
|
|
28
|
+
```bash
|
|
29
|
+
god-code --version
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Step 2: Configure API Key (if not already set)
|
|
33
|
+
|
|
34
|
+
Run the setup wizard:
|
|
35
|
+
```bash
|
|
36
|
+
god-code setup
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or set manually:
|
|
40
|
+
```bash
|
|
41
|
+
mkdir -p ~/.config/god-code
|
|
42
|
+
cat > ~/.config/god-code/config.json << 'EOF'
|
|
43
|
+
{
|
|
44
|
+
"api_key": "YOUR_API_KEY_HERE",
|
|
45
|
+
"model": "gpt-5.4",
|
|
46
|
+
"godot_path": "/Applications/Godot.app/Contents/MacOS/Godot"
|
|
47
|
+
}
|
|
48
|
+
EOF
|
|
49
|
+
chmod 600 ~/.config/god-code/config.json
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Step 3: Configure MCP Server for Claude Code
|
|
53
|
+
|
|
54
|
+
Add god-code as an MCP server so Claude Code gets Godot tools directly:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Find the Claude Code config location
|
|
58
|
+
CONFIG_DIR="${HOME}/.claude"
|
|
59
|
+
mkdir -p "$CONFIG_DIR"
|
|
60
|
+
|
|
61
|
+
# Check if settings file exists, create or update
|
|
62
|
+
SETTINGS_FILE="$CONFIG_DIR/settings.json"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The MCP server config that needs to be added:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"mcpServers": {
|
|
70
|
+
"god-code": {
|
|
71
|
+
"command": "god-code",
|
|
72
|
+
"args": ["mcp", "--project", "."]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
For project-specific setup, the `--project` arg should point to the Godot project root.
|
|
79
|
+
|
|
80
|
+
## Step 4: Verify MCP Connection
|
|
81
|
+
|
|
82
|
+
After configuring, Claude Code will have these tools available:
|
|
83
|
+
|
|
84
|
+
| Tool | Description |
|
|
85
|
+
|------|-------------|
|
|
86
|
+
| `validate_project` | Run Godot headless validation |
|
|
87
|
+
| `validate_tscn` | Check .tscn format + auto-fix |
|
|
88
|
+
| `lint_script` | GDScript quality checks |
|
|
89
|
+
| `check_consistency` | Cross-file consistency |
|
|
90
|
+
| `plan_collision` | Standard collision layers |
|
|
91
|
+
| `analyze_dependencies` | Project dependency graph |
|
|
92
|
+
| `suggest_patterns` | Design pattern suggestions |
|
|
93
|
+
| `parse_scene` | Parse .tscn structure |
|
|
94
|
+
| `project_info` | Read project.godot |
|
|
95
|
+
| `godot_knowledge` | Query Godot Playbook |
|
|
96
|
+
| `generate_sprite` | AI pixel art generation |
|
|
97
|
+
| `validate_resources` | Check res:// paths |
|
|
98
|
+
|
|
99
|
+
## Usage After Setup
|
|
100
|
+
|
|
101
|
+
### As CLI (interactive chat):
|
|
102
|
+
```bash
|
|
103
|
+
god-code
|
|
104
|
+
cd ~/Projects/my-godot-game
|
|
105
|
+
# Start chatting with the agent
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### As MCP Server (Claude Code uses tools directly):
|
|
109
|
+
Claude Code automatically calls god-code tools when working on Godot projects. No extra commands needed.
|
|
110
|
+
|
|
111
|
+
### Key CLI Commands:
|
|
112
|
+
- `/mode [apply|plan|explain|review|fix]` — change interaction mode
|
|
113
|
+
- `/provider [name]` — switch LLM provider
|
|
114
|
+
- `/settings` — view/change all settings
|
|
115
|
+
- `/usage` — token usage and cost
|
|
116
|
+
|
|
117
|
+
## Troubleshooting
|
|
118
|
+
|
|
119
|
+
**god-code not found**: Make sure pip installed to a directory in your PATH. Try `python3 -m pip install --user god-code[mcp]`.
|
|
120
|
+
|
|
121
|
+
**MCP tools not appearing**: Restart Claude Code after adding the MCP config. Check `god-code mcp --help` works.
|
|
122
|
+
|
|
123
|
+
**Godot validation fails**: Set the correct Godot path in config: `god-code setup` or `/set godot_path /path/to/godot`.
|