unity-agent-tools 0.5.0 → 0.7.0
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.
- package/dist/server.mjs +170 -2
- package/package.json +1 -1
package/dist/server.mjs
CHANGED
|
@@ -31237,7 +31237,7 @@ async function handleAIQuery(request) {
|
|
|
31237
31237
|
}
|
|
31238
31238
|
}
|
|
31239
31239
|
function buildFullPrompt(userPrompt, context, language, projectPath) {
|
|
31240
|
-
let prompt = "You are a Unity development expert assistant embedded in a Unity Editor plugin. You can read, create, modify, and delete files in the Unity project. You have expertise in shaders (HLSL/ShaderLab), C# scripts, materials, textures, and all Unity workflows. You can also diagnose and fix Unity errors that prevent the project from compiling or running. Do NOT ask the user for file paths or project paths \u2014 the working directory is already set to the Unity project root. When fixing errors: read the relevant source files, understand the root cause, apply the fix, and explain what you changed. Answer clearly and concisely. When the user asks you to modify or create files, do it directly.\n";
|
|
31240
|
+
let prompt = "You are a Unity development expert assistant embedded in a Unity Editor plugin. You can read, create, modify, and delete files in the Unity project. You have expertise in shaders (HLSL/ShaderLab), C# scripts, materials, textures, and all Unity workflows. You can also diagnose and fix Unity errors that prevent the project from compiling or running. You can generate images using the generate_image tool (powered by Google Nano Banana / Gemini Image). When the user asks to create a texture, sprite, icon, or any visual asset, use the generate_image tool with a detailed prompt. The generated image will appear in the Unity Editor where the user can save it to their project. Do NOT ask the user for file paths or project paths \u2014 the working directory is already set to the Unity project root. When fixing errors: read the relevant source files, understand the root cause, apply the fix, and explain what you changed. Answer clearly and concisely. When the user asks you to modify or create files, do it directly.\n";
|
|
31241
31241
|
if (projectPath) {
|
|
31242
31242
|
prompt += `Unity project path: ${projectPath}
|
|
31243
31243
|
`;
|
|
@@ -31792,6 +31792,167 @@ function registerListProjectFilesTool(server, bridge) {
|
|
|
31792
31792
|
});
|
|
31793
31793
|
}
|
|
31794
31794
|
|
|
31795
|
+
// build/gemini-handler.js
|
|
31796
|
+
async function generateImage(request) {
|
|
31797
|
+
const { apiKey, model, prompt, referenceImage } = request;
|
|
31798
|
+
if (!apiKey) {
|
|
31799
|
+
return {
|
|
31800
|
+
success: false,
|
|
31801
|
+
error: "Gemini API key not configured. Please set it in AI Chat > Settings."
|
|
31802
|
+
};
|
|
31803
|
+
}
|
|
31804
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
|
|
31805
|
+
const parts = [];
|
|
31806
|
+
if (referenceImage) {
|
|
31807
|
+
parts.push({
|
|
31808
|
+
inlineData: {
|
|
31809
|
+
mimeType: "image/png",
|
|
31810
|
+
data: referenceImage
|
|
31811
|
+
}
|
|
31812
|
+
});
|
|
31813
|
+
}
|
|
31814
|
+
parts.push({
|
|
31815
|
+
text: prompt
|
|
31816
|
+
});
|
|
31817
|
+
const body = {
|
|
31818
|
+
contents: [
|
|
31819
|
+
{
|
|
31820
|
+
parts
|
|
31821
|
+
}
|
|
31822
|
+
],
|
|
31823
|
+
generationConfig: {
|
|
31824
|
+
responseModalities: ["TEXT", "IMAGE"]
|
|
31825
|
+
}
|
|
31826
|
+
};
|
|
31827
|
+
try {
|
|
31828
|
+
console.error(`[NanoBanana] Generating image with ${model}: "${prompt.substring(0, 60)}..."`);
|
|
31829
|
+
const response = await fetch(url, {
|
|
31830
|
+
method: "POST",
|
|
31831
|
+
headers: { "Content-Type": "application/json" },
|
|
31832
|
+
body: JSON.stringify(body)
|
|
31833
|
+
});
|
|
31834
|
+
if (!response.ok) {
|
|
31835
|
+
const errorText = await response.text();
|
|
31836
|
+
let errorMsg = `Gemini API error (${response.status})`;
|
|
31837
|
+
try {
|
|
31838
|
+
const errorJson = JSON.parse(errorText);
|
|
31839
|
+
errorMsg = errorJson.error?.message || errorMsg;
|
|
31840
|
+
} catch {
|
|
31841
|
+
errorMsg += `: ${errorText.substring(0, 200)}`;
|
|
31842
|
+
}
|
|
31843
|
+
return { success: false, error: errorMsg };
|
|
31844
|
+
}
|
|
31845
|
+
const data = await response.json();
|
|
31846
|
+
let imageBase64;
|
|
31847
|
+
let description;
|
|
31848
|
+
const candidates = data.candidates;
|
|
31849
|
+
if (candidates && candidates.length > 0) {
|
|
31850
|
+
const parts2 = candidates[0].content?.parts;
|
|
31851
|
+
if (parts2) {
|
|
31852
|
+
for (const part of parts2) {
|
|
31853
|
+
if (part.inlineData?.data) {
|
|
31854
|
+
imageBase64 = part.inlineData.data;
|
|
31855
|
+
}
|
|
31856
|
+
if (part.text) {
|
|
31857
|
+
description = part.text;
|
|
31858
|
+
}
|
|
31859
|
+
}
|
|
31860
|
+
}
|
|
31861
|
+
}
|
|
31862
|
+
if (!imageBase64) {
|
|
31863
|
+
return {
|
|
31864
|
+
success: false,
|
|
31865
|
+
error: "Gemini returned no image data. The model may not support image generation, or the prompt was filtered.",
|
|
31866
|
+
...description ? { description } : {}
|
|
31867
|
+
};
|
|
31868
|
+
}
|
|
31869
|
+
console.error("[NanoBanana] Image generated successfully.");
|
|
31870
|
+
return {
|
|
31871
|
+
success: true,
|
|
31872
|
+
imageBase64,
|
|
31873
|
+
description: description || "Generated image"
|
|
31874
|
+
};
|
|
31875
|
+
} catch (err) {
|
|
31876
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
31877
|
+
console.error(`[NanoBanana] Error: ${msg}`);
|
|
31878
|
+
return { success: false, error: `Gemini request failed: ${msg}` };
|
|
31879
|
+
}
|
|
31880
|
+
}
|
|
31881
|
+
|
|
31882
|
+
// build/tools/generate-image.js
|
|
31883
|
+
var geminiConfig = {
|
|
31884
|
+
apiKey: "",
|
|
31885
|
+
model: "gemini-2.5-flash-preview-image-generation",
|
|
31886
|
+
referenceImage: void 0
|
|
31887
|
+
};
|
|
31888
|
+
function registerGenerateImageTool(server, bridge) {
|
|
31889
|
+
server.tool("generate_image", "Generate an image using Google's Nano Banana (Gemini Image Generation). Use this when the user asks to create, generate, or make an image, texture, sprite, icon, or visual asset. The generated image will be displayed in the Unity Editor's AI Chat window where the user can save it to their project. You should describe what you're generating and call this tool with a detailed prompt.", {
|
|
31890
|
+
prompt: external_exports.string().describe("Detailed image generation prompt. Be specific about style, colors, composition, and content. For game textures, include terms like 'seamless', 'tileable', 'PBR', etc."),
|
|
31891
|
+
useReferenceImage: external_exports.boolean().optional().describe("Whether to include the user's reference image (if one is set in the UI). Default: true if available.")
|
|
31892
|
+
}, async ({ prompt, useReferenceImage }) => {
|
|
31893
|
+
if (!geminiConfig.apiKey) {
|
|
31894
|
+
return {
|
|
31895
|
+
content: [
|
|
31896
|
+
{
|
|
31897
|
+
type: "text",
|
|
31898
|
+
text: "Error: Gemini API key is not configured. The user needs to set it in AI Chat > Settings panel."
|
|
31899
|
+
}
|
|
31900
|
+
],
|
|
31901
|
+
isError: true
|
|
31902
|
+
};
|
|
31903
|
+
}
|
|
31904
|
+
try {
|
|
31905
|
+
const shouldUseRef = useReferenceImage !== false;
|
|
31906
|
+
const refImage = shouldUseRef && geminiConfig.referenceImage ? geminiConfig.referenceImage : void 0;
|
|
31907
|
+
const result = await generateImage({
|
|
31908
|
+
apiKey: geminiConfig.apiKey,
|
|
31909
|
+
model: geminiConfig.model,
|
|
31910
|
+
prompt,
|
|
31911
|
+
referenceImage: refImage
|
|
31912
|
+
});
|
|
31913
|
+
if (result.success && result.imageBase64) {
|
|
31914
|
+
bridge.sendRaw({
|
|
31915
|
+
method: "image/generated",
|
|
31916
|
+
imageData: result.imageBase64,
|
|
31917
|
+
description: result.description || `Generated: ${prompt.substring(0, 60)}`
|
|
31918
|
+
});
|
|
31919
|
+
return {
|
|
31920
|
+
content: [
|
|
31921
|
+
{
|
|
31922
|
+
type: "text",
|
|
31923
|
+
text: `Image generated successfully and sent to the Unity Editor.
|
|
31924
|
+
Model: ${geminiConfig.model}
|
|
31925
|
+
Prompt: "${prompt}"
|
|
31926
|
+
` + (result.description ? `Description: ${result.description}
|
|
31927
|
+
` : "") + `The user can now preview and save it to their project from the AI Chat window.`
|
|
31928
|
+
}
|
|
31929
|
+
]
|
|
31930
|
+
};
|
|
31931
|
+
} else {
|
|
31932
|
+
return {
|
|
31933
|
+
content: [
|
|
31934
|
+
{
|
|
31935
|
+
type: "text",
|
|
31936
|
+
text: `Image generation failed: ${result.error || "Unknown error"}`
|
|
31937
|
+
}
|
|
31938
|
+
],
|
|
31939
|
+
isError: true
|
|
31940
|
+
};
|
|
31941
|
+
}
|
|
31942
|
+
} catch (err) {
|
|
31943
|
+
return {
|
|
31944
|
+
content: [
|
|
31945
|
+
{
|
|
31946
|
+
type: "text",
|
|
31947
|
+
text: `Image generation error: ${err instanceof Error ? err.message : String(err)}`
|
|
31948
|
+
}
|
|
31949
|
+
],
|
|
31950
|
+
isError: true
|
|
31951
|
+
};
|
|
31952
|
+
}
|
|
31953
|
+
});
|
|
31954
|
+
}
|
|
31955
|
+
|
|
31795
31956
|
// build/resources/pipeline-info.js
|
|
31796
31957
|
function registerPipelineInfoResource(server, bridge) {
|
|
31797
31958
|
server.resource("pipeline-info", "unity://pipeline/info", {
|
|
@@ -31920,7 +32081,7 @@ function registerEditorPlatformResource(server, bridge) {
|
|
|
31920
32081
|
async function main() {
|
|
31921
32082
|
const server = new McpServer({
|
|
31922
32083
|
name: "unity-agent-tools",
|
|
31923
|
-
version: "0.
|
|
32084
|
+
version: "0.7.0"
|
|
31924
32085
|
});
|
|
31925
32086
|
const bridge = new UnityBridge("ws://localhost:8090");
|
|
31926
32087
|
const lspClient = new ShaderLspClient();
|
|
@@ -31937,6 +32098,7 @@ async function main() {
|
|
|
31937
32098
|
registerReadProjectFileTool(server, bridge);
|
|
31938
32099
|
registerWriteProjectFileTool(server, bridge);
|
|
31939
32100
|
registerListProjectFilesTool(server, bridge);
|
|
32101
|
+
registerGenerateImageTool(server, bridge);
|
|
31940
32102
|
registerPipelineInfoResource(server, bridge);
|
|
31941
32103
|
registerShaderIncludesResource(server, bridge);
|
|
31942
32104
|
registerShaderKeywordsResource(server, bridge);
|
|
@@ -31950,6 +32112,12 @@ async function main() {
|
|
|
31950
32112
|
console.error("[UnityAgent] Invalid AI query: missing id or prompt");
|
|
31951
32113
|
return;
|
|
31952
32114
|
}
|
|
32115
|
+
if (params.geminiApiKey) {
|
|
32116
|
+
geminiConfig.apiKey = params.geminiApiKey;
|
|
32117
|
+
geminiConfig.model = params.geminiModel || geminiConfig.model;
|
|
32118
|
+
geminiConfig.referenceImage = params.referenceImage || void 0;
|
|
32119
|
+
console.error(`[NanoBanana] Config updated: model=${geminiConfig.model}, hasRef=${!!geminiConfig.referenceImage}`);
|
|
32120
|
+
}
|
|
31953
32121
|
console.error(`[UnityAgent] AI query received (id=${id}): ${params.prompt.substring(0, 80)}...`);
|
|
31954
32122
|
try {
|
|
31955
32123
|
const result = await handleAIQuery({
|
package/package.json
CHANGED