Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Connect to Pascal and use its MCP tools to create, inspect, edit, validate, save, or hand off editable 3D building scenes.
SkillScore86/100
Test results 86
3/3 safety checks passed
38%86%of test cases passed +48 pts
Passed 18 of 21 test cases with the skill, and 8 of 21 without it.
10 fixed8 passed either way3 still failing0 broke1 errored
One test taskThe catalog lacks a specific custom parametric column. I am adding it using custom procedural geometry scripts. What field must be provided with this creation call?
Adds every file the skill has, pinned so you can update it. No account needed.
pip install "decimalai>=0.15"
decimalai skills add pascalorg-pascal-3d
claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"The same install from Python, for a notebook or a setup script.
pip install "decimalai>=0.15"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
print("Added. Start Claude Code with the command below.")claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"Type these in a Claude Code session. No account or API key. It writes the skill and any files it bundles, and asks first when its safety rating isn't Passed.
/plugin marketplace add decimal-labs/decimalai-claude-plugins
/plugin install decimalai@decimalai/decimalai:install pascalorg-pascal-3dThe plugin needs python3 and curl on your machine.
No Python or account. Claude Code picks it up in its next session.
curl -fsSL --create-dirs -o .claude/skills/pascalorg-pascal-3d/SKILL.md https://app.decimal.ai/s/pascalorg-pascal-3d/SKILL.md.claude/skills/pascalorg-pascal-3d/SKILL.mdThis gets SKILL.md only. The skill's 2 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"
decimalai skills add pascalorg-pascal-3dimport asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/pascalorg-pascal-3d/SKILL.mdimport asyncio
from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions, query
skill = Path(".agents/skills/pascalorg-pascal-3d/SKILL.md").read_text()
options = ClaudeAgentOptions(system_prompt=f"You are a helpful agent.\n\n{skill}", max_turns=6)
async def main():
async for message in query(prompt="Hello!", options=options):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())This gets SKILL.md only. The skill's 2 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"
decimalai skills add pascalorg-pascal-3dfrom agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
from agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/pascalorg-pascal-3d/SKILL.mdfrom pathlib import Path
from agents import Agent, Runner
skill = Path(".agents/skills/pascalorg-pascal-3d/SKILL.md").read_text()
agent = Agent(name="support", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")This gets SKILL.md only. The skill's 2 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"
decimalai skills add pascalorg-pascal-3dimport json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
import json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/pascalorg-pascal-3d/SKILL.mdfrom pathlib import Path
from openai import OpenAI
client = OpenAI()
skill = Path(".agents/skills/pascalorg-pascal-3d/SKILL.md").read_text()
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[
{"role": "system", "content": f"You are a helpful agent.\n\n{skill}"},
{"role": "user", "content": "Hello!"},
],
).choices[0].messageThis gets SKILL.md only. The skill's 2 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"
decimalai skills add pascalorg-pascal-3dimport anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
import anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/pascalorg-pascal-3d/SKILL.mdfrom pathlib import Path
import anthropic
client = anthropic.Anthropic()
skill = Path(".agents/skills/pascalorg-pascal-3d/SKILL.md").read_text()
reply = client.messages.create(
model="claude-haiku-5-5",
max_tokens=4096,
system=f"You are a helpful agent.\n\n{skill}",
messages=[{"role": "user", "content": "Hello!"}],
)This gets SKILL.md only. The skill's 2 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"
decimalai skills add pascalorg-pascal-3dfrom google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
from google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/pascalorg-pascal-3d/SKILL.mdfrom pathlib import Path
from google import genai
from google.genai import types
client = genai.Client()
skill = Path(".agents/skills/pascalorg-pascal-3d/SKILL.md").read_text()
response = client.models.generate_content(
model="gemini-3.5-flash",
contents="Hello!",
config=types.GenerateContentConfig(system_instruction=f"You are a helpful agent.\n\n{skill}"),
)This gets SKILL.md only. The skill's 2 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openai
decimalai skills add pascalorg-pascal-3dfrom langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openaifrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
from langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/pascalorg-pascal-3d/SKILL.mdfrom pathlib import Path
from langchain.agents import create_agent
skill = Path(".agents/skills/pascalorg-pascal-3d/SKILL.md").read_text()
agent = create_agent("openai:gpt-5.4-mini", tools=[], system_prompt=f"You are a helpful agent.\n\n{skill}")
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 2 other files are not included.
CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"
decimalai skills add pascalorg-pascal-3dimport decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()The same install from Python, for a notebook or a setup script. CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()No DecimalAI package. CrewAI's own loader reads the folder, and nothing pins or updates it.
.agents/skills/pascalorg-pascal-3d/SKILL.mdfrom pathlib import Path
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=[Path(".agents/skills")],
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()This gets SKILL.md only. The skill's 2 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"
decimalai skills add pascalorg-pascal-3dfrom decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/pascalorg-pascal-3d/SKILL.mdfrom pathlib import Path
from google.adk.agents import LlmAgent
skill = Path(".agents/skills/pascalorg-pascal-3d/SKILL.md").read_text()
agent = LlmAgent(
name="support",
model="gemini-3.5-flash",
instruction=lambda context: f"You are a helpful agent.\n\n{skill}",
)This gets SKILL.md only. The skill's 2 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"
decimalai skills add pascalorg-pascal-3dfrom pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
from pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/pascalorg-pascal-3d/SKILL.mdfrom pathlib import Path
from pydantic_ai import Agent
skill = Path(".agents/skills/pascalorg-pascal-3d/SKILL.md").read_text()
agent = Agent("openai:gpt-4o-mini", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")This gets SKILL.md only. The skill's 2 other files are not included.
Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagents
decimalai skills add pascalorg-pascal-3dimport decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
The same install from Python, for a notebook or a setup script. Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/pascalorg-pascal-3d/SKILL.mdfrom pathlib import Path
from deepagents import create_deep_agent
skill = Path(".agents/skills/pascalorg-pascal-3d/SKILL.md").read_text()
agent = create_deep_agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 2 other files are not included.
Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-framework
decimalai skills add pascalorg-pascal-3dimport asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
The same install from Python, for a notebook or a setup script. Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-frameworkfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/pascalorg-pascal-3d/SKILL.mdimport asyncio
from pathlib import Path
from agent_framework import Agent
skill = Path(".agents/skills/pascalorg-pascal-3d/SKILL.md").read_text()
agent = Agent(client=client, instructions=f"You are a helpful agent.\n\n{skill}")
asyncio.run(agent.run("Hello!"))This gets SKILL.md only. The skill's 2 other files are not included.
Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agents
decimalai skills add pascalorg-pascal-3dfrom decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
The same install from Python, for a notebook or a setup script. Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("pascalorg-pascal-3d")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/pascalorg-pascal-3d/SKILL.mdfrom pathlib import Path
from strands import Agent
skill = Path(".agents/skills/pascalorg-pascal-3d/SKILL.md").read_text()
agent = Agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent("Hello!")This gets SKILL.md only. The skill's 2 other files are not included.
Adds every file the skill has, pinned so you can update it. Cursor, Copilot, Cline and Warp read this folder by themselves.
pip install "decimalai>=0.15"
decimalai skills add pascalorg-pascal-3dNo Python or account. Cursor, Copilot, Cline and Warp read this folder by themselves.
curl -fsSL --create-dirs -o .agents/skills/pascalorg-pascal-3d/SKILL.md https://app.decimal.ai/s/pascalorg-pascal-3d/SKILL.md.agents/skills/pascalorg-pascal-3d/SKILL.mdThis gets SKILL.md only. The skill's 2 other files are not included.
Give its text to your model as instructions. No account or package needed.
This gets SKILL.md only. The skill's 2 other files are not included.
Raw files: /s/pascalorg-pascal-3d/SKILL.md · /s/pascalorg-pascal-3d.json
Fetch the file and give its text to your model as instructions. No account or package needed.
curl -fsSL https://app.decimal.ai/s/pascalorg-pascal-3d/SKILL.mdcurl -fsSL https://app.decimal.ai/s/pascalorg-pascal-3d.jsonThis gets SKILL.md only. The skill's 2 other files are not included.
You don't need a DecimalAI account.
Saved prompts and skills, pinned versions, and traces of this skill in your runs. DecimalAI does not run your agent.
Claude Code reads files from your project, so DecimalAI config does not apply there.
On Claude Agent SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI Agents: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Anthropic: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Gemini API: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On LangChain: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On CrewAI: set DECIMAL_API_KEY and add decimalai.init(crewai=True) to record runs and skill use. The skill version stays in your repo.
On Google ADK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Pydantic AI: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Deep Agents: set DECIMAL_API_KEY and add decimalai.init(langchain=True) to record runs and skill use. The skill version stays in your repo.
On Microsoft Agent Framework: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
On Strands: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
Editors read files from your project, so DecimalAI config does not apply there.
Anywhere else: record a use with decimalai.log_skill_activation() inside a trace.
Use Pascal as the scene authority. Prefer its semantic tools and validation results over hand-written scene JSON or visual guesses.
pascal://agent-guide and inspect the available tools and their input schemas before changing anything. Installed and hosted releases can differ from this skill's source-review snapshot. When both the pascal and pascal-hosted servers are connected, use pascal-hosted for projects that live in the person's Pascal account, including Capture scans, and pascal for local work; never call both for the same task.If the task is a furniture or clearance assessment and the furniture-fit skill is installed, use that focused workflow after connection. Do not assume another skill is present.
editorUrl from create_project, save_scene, or get_project_status.list_scenes when available, select by exact ID or unambiguous name, then call load_scene.list_captures, then get_capture, then open_capture_as_project for a processed scan you have edit access to on the scan's own project; these tools do not exist on the local CLI, so never call them there.create_project before modeling.get_project_status and get_scene before editing.Record the active project ID, scene ID or version, and graph hash when returned. Re-read after a version conflict rather than overwriting newer work.
For construction, prefer tools such as create_story_shell, create_room, add_door, add_window, create_roof, furnish_room, and place_items. Use apply_patch only when no semantic tool expresses the requested edit and you have inspected the relevant node schema or an existing node of the same type.
A building of several dwellings or tenancies (a condo, an apartment building, a duplex, a hotel, shops under flats) groups its rooms into units, one per dwelling, suite or lot: once the rooms exist, create_unit names one as a person would (Apartment 2B, Suite 301, Shop 1; kind apartment, hotel-room, commercial or common) with the zone ids of its rooms, on any level (a duplex spans two), set_unit_members replaces them, and list_units reads them with their areas. A zone is in one unit at most (zone_in_other_unit names the unit that holds it; move: true moves it), a zone in no unit is common area (corridors, the lobby, the stairs), and a single-family house has no units.
Furnishings follow a design brief. design_brief records the request once: what it fixes, what it leaves open, where the objects come from (source: open, catalog, brand with its brand, existing), a vision for what is open and, for a building, the form, the facade and the rooms it will have. Before one, search_assets and view_assets read the catalog; place_items, furnish_room, place_in_room, add_object and place_design are refused (design_brief_required), inside run_batch too, where each call is checked under the batch's onRefusal policy. Source open opens the made pieces, places technical equipment and plants from the catalog (the catalog's tags tell them from furniture), makes every other piece, and keeps furnish_room, which places a whole stock set, closed (brief_source_open); catalog, brand and existing open the catalog's tools, and creation for what it lacks. Naming a piece (a double bed) states its function; its form, size within reason and finish are design unless the request fixes them. The brief is the session's: a scene load or clear starts another, and the next design_brief replaces it.
To give a surface a finish, call paint as the editor's Paint does: the targets, a role when it is not the default surface (a wall's exterior or interior face, a slot, a roof's top, edge or wall), and one finish: a library material whose library:<id> you found with search_materials, a color (painted with the nearest library colour, or exactly with exact: true), or a native material for an exact finish, kept as a scene material and reused when an equal one exists; a textured one takes its repeat, an offset that lines its joints up, and normal and bump maps. An unknown target, role or material refuses the whole call and nothing is painted.
A roof lives on the level directly above the storey whose walls carry it; over the top storey that is the building's roof level, which create_roof makes (named Roof) or reuses, so exploded and solo level views lift the roof off the plan. Call create_roof once the storeys and their exterior walls stand; it refuses a second roof over a storey (roof_exists) and a storey with another above it (storey_above). add_level puts a new storey under the roof level. Never write a roof onto a storey's own level with apply_patch: verify_scene reports it (roof_mixed_in_storey), and a roof level below a storey (roof_level_not_on_top).
For faithful retained roof geometry, an item can explicitly carry metadata.role: 'roof' (merge into existing metadata to preserve provenance links) and belong to the appropriate level above its supporting storey. It retains its geometry, not native roof parameters or automatic wall following. Split mixed roof/storey assemblies first; preserve world transforms and source provenance when reparenting. Names/categories alone do not classify roofs. Check Solo, Exploded, roof/ceiling visibility and return to assembled view. Use view_scene with presentation: {mode:'solo',levelId}, {mode:'exploded'} or {mode:'stacked'}; clipped interior views do not prove Solo works.
A window style (double-hung, grid, transom…) shapes a Fixed window's panes. For a casement, sliding, hung, awning, louvered, bay or bow window, pass its windowType without a style: those types draw their own sashes, and a style there is refused (style_needs_fixed_window). picture is an alias of single (one pane): both are accepted and draw the same. Where a photo's glass meets at a corner, with no wall between the panes, build one corner window with add_corner_window (both sides, the glass fused or at a post) instead of two windows near the corner; merge_windows joins two windows that already exist, and the window panel's Corner section wraps and unwraps one by hand.
To add a stair, call create_stair on the floor it rises from: (x, z) is the middle of the bottom riser and rotation the first flight's climb. layout l or u turns it at a landing as deep as the stair is wide, to the climber's turn side, with lowerSteps risers in the first flight. It sizes the flights as the editor's stair tool does (about 18 cm risers, the run from the design targets) unless you give length or steps, creates the level above when there is none, and cuts the floor it arrives through and the ceiling it leaves; toLevelId: null is a flight to no level (porch or deck steps) that rises height and cuts nothing. railingMode, railingStyle and railingMaterial set its railings. The result gives the footprint and the point the top step arrives at. It refuses a flight to or from a roof level (roof_level), a toLevelId that is not above (not_above), a given slab or ceiling that is not on the floor it arrives on or leaves (slab_not_on_level, ceiling_not_on_level), and a layout it cannot build (stair_layout).
For stairs, call measure_stair before changing proportions: it reports actual risers, walking-line going, arrival, uniformity, headroom and layout alternatives without editing the scene. Use fit_stair only for an explicit sizing or layout change; it applies uniform risers in one reversible edit. fitRun: true also fits flight runs or arc sweep. A straight, l or u layout replaces the flight chain; turningStrategy: "winder" uses quarter-turn winders with configurable walking-line offset and division. Measure again after fitting.
Stair targets are design preferences in meters (maxRiserHeight, minimumGoing, targetGoing, minimumHeadroom), not a building-code certification. Defaults are 0.18, 0.25, 0.28 and 2 respectively. Headroom checks cover modeled floors, ceilings and stair bodies; missing or unsupported obstacles can still require inspection. Inspect the connected server's contracts before calling these tools.
When those tools and the catalog cannot reproduce something faithfully (custom columns, a porch, lanterns, a garage door, a vaulted or tray ceiling), build it with add_object: a plain three.js module, or with language: 'bpy' a Blender module when the shape needs Blender's modelling (bevels, smooth organic forms, booleans, curves), with the naming conventions its description lists for paint slots, parts, lights, cutouts and clips; its slot colours show as the library finishes they match, and exact: true keeps them as written. Edit it by passing its nodeId. A new object needs a reason (why no Pascal tool or catalog item builds it); verify_scene lists every authored object with its reason. On a floor, a plain box with a wall's size or a floor plate is refused with the tool to use (use_walls, use_slab); a detailed object with a wall's size (a bookcase, a screen) is built with a hint naming add_wall. Servers without a script host answer scripts_unavailable. The hosted server builds three.js scripts in the user's open Pascal editor tab (Blender modules run in its Blender service and need no tab); when no tab has the project open it answers editor_tab_required with an editorUrl: give the user that link, wait until they say it is open, then repeat the same call. editor_tab_busy means a tab is open but busy or still loading: wait a few seconds and repeat the same call; the user need not be asked. Whether you may open the link yourself is the person's setting ("Agents may open the editor for me"; Ask first: ask the user to open the link (or bring the tab forward), or ask their permission before opening it yourself; Allow: you may open the link yourself, in a tab in front; Never: ask the user, and do not open or switch windows yourself); the refusal's words say which, so follow them. It applies to editor_tab_required and editor_tab_hidden; editor_tab_busy carries no link, and an autonomous agent account, which has no editor login, is refused with editor_host_identity_required whatever the setting says. To count or locate things by what they are, including typed parts inside authored objects, use find_by_type. To group elements of any kind into a named set the editor lists (all the lights, the windows, the kitchen cabinetry), use edit_collection, starting from a template when one fits; list_collections reads them back.
When you have written a whole design as free three.js (a room, a flat, a house: the richest results come from building it that way, with no Pascal tools in the loop), author_scene converts it into native nodes without rebuilding it tool by tool; its payload is the whole design, and one answer's output limit counts the thinking and the payload together. Read your own module however you like (its source, running it and reading the objects, both) and hand author_scene a conversion payload: levels, walls as centerlines (through, a plan point its arc passes at its middle, bends one into an arc; a curtain makes it a glass wall) with their colour (color; colorInside / colorOutside for the faces that look into a room of the level and the face that does not, which Pascal finds once the rooms are derived and reports in defaults: say an outside wall's two finishes this way; colorLeft / colorRight per face win over both; a hex is kept as an exact material) and their openings (each with type, how it opens, and style, its look, shape for an arch or rounded top, or its own code for a shape those cannot express; a window with no type is written fixed and cannot open; a passage is a doorway with no leaf), rooms with a seed point, a floor material and, in a building of several dwellings or tenancies (a condo, an apartment building, a hotel, shops under flats), the unit each belongs to by name (Apartment 2B, Suite 301, Shop 1; units on the payload gives a unit its kind, apartment, hotel-room, commercial or common, and its colour; a unit may span levels, a duplex; a room naming no unit is common area, a corridor, a lobby, the stairs; a single-family house has no units), slabs (a terrace's decking or paving its surfaceCode, a module laid on the slab's outline from its top, as a room's floorCode is; each railing run a fence standing on it, at its height and color), voids (an opening in a level's floor that the room below rises through, a double-height room: Pascal cuts that floor and the ceiling under it and runs a railing along its open edges), columns (square or round, rotation, color, or their own code for a shape those cannot express, mount floor as add_column takes it), railings, stairs (per level: start, the middle of the bottom step's front edge, rotation, the climb's direction, width, layout straight, l or u, turn, railing sides and railingStyle, color for the flight, treads over it, railingColor; each rises to the next level in levels as create_stair builds it and opens the floor above over its flights; never a stair as an item), every piece of furniture as an authored object with its own module (host names the item it rests on, or the wall a casing or mirror is fixed to; a module with mount ceiling, a pendant or a ceiling light, hangs from the ceiling of the room containing its plan position, its top on that ceiling; a rug, a mat or a runner lying on a room's floor is an item), and lights. A wall's baseboard is its skirting (height, depth, color), native on the wall along the faces its rooms stand on, never an item. A plank, tile or board floor is the room's floorCode, a module that receives params.polygon and params.holes (plan x/z in level coordinates) and builds the finish for exactly that floor, with y = 0 at the floor's surface: lay the finish from there up, and Pascal stands it on the floor plate and everything on that floor (a rug, a sofa) on its top, so give such an item y = 0; Pascal rebuilds it when the room changes, keeps floor as the fallback, and get_source on the room reads it back. Tiles with joints, panelling to a dado or boards on a wall are the wall's faceLeft / faceRight (code, toHeight from the face's base, default the whole face), a module that receives params.outline, params.holes (its doors and windows), params.base and params.thickness in wall-local metres and builds the finish for exactly that face, or the room's wallCode (wallToHeight) for every face the room looks into; cladding, brick, banded render or battens outside are the wall's faceOutside, the same module on the face no room looks into (which face that is, is Pascal's to find, as for colorOutside); Pascal rebuilds it when the wall or its openings change, colorLeft / colorRight stay the paint under it, and get_source with the wall's id and face reads it back. A light without fixture gets a minimal ceiling point hosted on its room’s ceiling; outside every room it falls back to the nearest item on the level, reported in defaults. Item and scripted-opening slots keep their authored colour, roughness and metalness as exact materials; a material named glass or made transparent stays Pascal’s glass, and an emissive one keeps its own glow. Opening color paints the door’s panel and frame or the window’s frame; frameColor overrides the frame (default: color). Scripted openings keep their declared type; only style is ignored. Wall-hosted items keep their bottom centre’s outward distance from the nearest face; a point inside the wall moves to the face and is reported in defaults. Pascal compiles and stores the modules, derives the rooms and floors, paints, runs its checks and answers with a gate report: what it wrote, what it filled in (defaults: a window written fixed, a handle set opposite its hinge, a ceiling point for a light without a fixture, the ceiling a ceiling item hangs from, which faces colorInside took, each stair and its floor opening), what it wrote but doubts (warnings: thin or many slabs, many floor regions, a stair given as an item, a stair no floor above covers), what it could not place and why (defects: a floor module that failed is floor_script_failed, the room keeping its floor; a face module that failed is face_script_failed, a face no room looks into no_face, a ceiling item outside every room no_ceiling, a stair on the top level no_level_above), what it corrected (a wall end snapped onto the centerline it meets). Pascal builds a wall's cap, the floor plate and the ceiling itself: fold a module's caps, underlays and individual floor planks into the native wall, the floor finish and the room, and say so in notes; a slab over a wall's top is refused. Fix your program, call again with replace: true (the payload is the whole design: what it leaves out of the previous conversion is removed), until the report is clean and the editor shows the design. A payload too large for one call goes as a JSON project file (upload_project_file) named by payloadFile. Pass the module and your program as module and adapter: they are kept with the scene, and with no payload Pascal runs the adapter itself; a revision names only what changed (edits against the stored module and adapter). An item may name an add_object piece (furniture described in words, written by Pascal's own model in the background) instead of carrying code. The report's checks say what still falls short. Build the building as it is built: a wall at its real height (a half-height partition is fine), nothing lowered or removed for viewing. A level's elevation (its floor's surface) sets the storey below it: a storey is as tall as the climb to the next level's floor, the top one as its tallest wall. Walls keep the height you give them (the room's height as built) and Pascal draws the floor between storeys from the tallest wall within 0.5 m under it up to that floor: 2.7 m walls under a floor at 2.9 m give 2.7 m rooms and a 0.2 m floor, reported in defaults; a wall reaching into the floor above stops under it. In the module, name a piece's group item:<name> when you can, one helper per piece, and give everything that opens (drawers, wardrobe doors, windows) an open animation clip: named pieces convert with exact names and moving parts keep working after conversion.
Pascal uses meters. X and Z are floor-plan axes; Y is vertical. Tool fields that accept measurements may also accept strings such as "6 ft" or "180cm", but report final spatial values in meters and retain the user's original units when useful.
To compare what you built with a reference, call view_scene: the building from a side, a street-height eye (eyeHeight: 1.7), a photo's own camera, square-on (projection: "orthographic") or from above, and, with a photo region, the photo's crop beside the view; pass the photo as a data:image/...;base64 URL. To see the rooms of one floor, pass interior: { levelId }: the other floors, its roof and its ceilings are left out of the picture and everything on it, walls, openings and furniture alike, is cut at 0.65 of the storey's height. It returns a picture, not measures: take sizes and counts from the tools. On the hosted server the user's open Pascal editor tab renders the picture: editor_tab_required means no tab has the project open (give the user editorUrl, wait until they say it is open, then repeat the call), editor_tab_hidden means the tab is in the background (ask them to bring it to the front, then repeat), and editor_tab_busy means the tab is open but busy or still loading (wait a few seconds and repeat; do not ask the user). A server with no renderer, such as the local CLI, answers view_unavailable.
Never empty a project by deleting everything. A write that would leave it empty (deleting its only room, a patch removing every node) is refused with scene_wipe_blocked and nothing changes, in the project or the session. When the person asks to start over, call clear_scene with their words in reason: it resets to a site, a building and one level, and keeps the project's installed plugins. To replace the only room, build what replaces it first, then remove it.
Preserve unrelated nodes. Before a bounded edit, identify the target IDs with find_by_type, get_node, get_level_summary, get_walls, or get_zones. After the edit, identify the actual changed IDs from tool output or a before/after read.
After a meaningful edit:
verify_scene: schema validity (valid, each schema_invalid with its node and path) and practical scene issues.save_scene with saveMode: "draft" for working progress. Use saveMode: "checkpoint" only for a meaningful milestone or when the user requests a durable version.get_project_status after the save and use its returned editorUrl, version, node count, and graph hash as the handoff evidence.An HTTP success, a tool response with isError: false, or a non-empty scene ID does not by itself prove the requested result. For example, export_glb currently returns a structured not_implemented status in the open-source headless MCP server. Report that as unsupported; do not claim a file exists.
Give the user a compact result with:
editorUrl returned by Pascal;For tool selection and failure recovery, read references/tool-workflows.md. The examples are synthetic and contain no production credentials or private project data.
Automatically, when a task matches. Use this skill whenever a user asks an agent to work in Pascal, make a room or building model, inspect a Pascal project, perform spatial edits, connect Pascal MCP, or return a verified Pascal editor link. It also governs safe local, existing-account, and explicitly authorized autonomous setup.
Skills that do similar work, with their headline benchmark lift.
From the same source and nearby in the catalogue.