odoorpc-agent-skill
OdooRPC Agent Skill
This skill gives an AI coding agent a safe, reusable workflow for connecting to Odoo with Python and OdooRPC.
It is intentionally not tied to Claude Code. Any agent that can read SKILL.md and run local scripts can use it: Claude Code, OpenCode, Codex, Manus, Cursor-like agents, local terminal agents, or other Agent Skills-compatible clients.
The skill is meant to be used actively. If the user asks about Odoo records, Odoo fields, Odoo business documents, Odoo connector data, Odoo settings, Odoo errors, Odoo version differences, or Odoo database state, do not treat this as a generic explanation task only. First consider whether a configured Odoo profile can answer the question with real data.
Proactive invocation policy
Use this skill whenever the request is about Odoo and one of these is true:
- The user wants to look up real Odoo data, such as customers, vendors, contacts, products, variants, quotations, sale orders, purchase orders, invoices, bills, payments, stock, pickings, deliveries, receipts, tasks, projects, users, companies, or modules.
- The user asks why something in Odoo looks wrong, such as a company hierarchy, wrong partner address, missing product, wrong inventory number, missing order, connector import issue, failed synchronization, access-right issue, or model/field error.
- The user provides Odoo connection information and expects the agent to save it and connect later.
- The user asks for Odoo model names, field names, record IDs, domains, counts, or examples based on their database.
- The user asks the agent to create, update, archive, deactivate, or delete Odoo records.
- The user asks for Odoo version-aware behavior or says the system is Odoo 16, 17, 18, 19, Odoo Online, Odoo.sh, or a custom deployment.
- The user is working on a connector, migration, data sync, import/export, automation, or integration that depends on Odoo records.
- The user is doing local Odoo development or operations, including
odoo.confdiagnosis, database selection, addons path detection, module documentation, module manifest work, Odoo.sh backup restore, filestore recovery, or admin/account repair.
Do not wait for the user to explicitly say “use OdooRPC”. If an Odoo profile exists and the user asks a database-specific Odoo question, use read-only commands to inspect the live system before answering, unless the user explicitly says not to connect.
Common trigger words and entities
Treat these as strong signals to use or consider this skill:
- English:
Odoo,Odoo.sh,Odoo Online,partner,customer,vendor,contact,lead,opportunity,quotation,sale order,purchase order,invoice,bill,payment,journal entry,product,variant,stock,inventory,picking,delivery,receipt,warehouse,route,lot,serial,project,task,user,company,access rights,record rule,module,connector,import,sync,XML-RPC,JSON-RPC,odoorpc. - Chinese:
客户,联系人,供应商,报价单,销售订单,采购订单,发票,账单,付款,会计凭证,产品,规格,变体,库存,调拨,出库,入库,仓库,批次,序列号,项目,任务,用户,公司,多公司,权限,记录规则,模块,连接器,导入,同步,接口,字段,模型,数据库,模块介绍页,恢复备份,附件,文件存储,管理员密码,登录密码.
Default behavior by task type
| User intent | Agent behavior |
|---|---|
| “查一下 / 看一下 / 为什么显示这样” | Use read-only commands if a profile exists. Inspect model, fields, and sample records before explaining. |
| “这个字段叫什么 / 这个模型有什么字段” | Use fields against the relevant model. Do not guess if the database is reachable. |
| “有多少 / 哪些记录 / 列出” | Use count or search-read with a small --limit. |
| “帮我建一个记录” | Prepare a dry-run create; execute only with explicit user instruction and --confirm CREATE. |
| “帮我改成 / 修一下数据” | Search/read exact targets, show IDs and before snapshot, then dry-run update. Execute only with explicit instruction and --confirm UPDATE. |
| “删掉 / 清理掉” | Prefer archive/deactivate when possible. Delete only after exact IDs, before snapshot, explicit instruction, --execute, and --confirm DELETE. |
| “连接这个 Odoo” | Save or update a named profile in ~/.config/odoorpc/config.yaml, then test login and detect version. |
| “以后都用这个库 / 这个客户系统” | Set the profile as default only if the user implies it should become the default. |
| “看一下 odoo.conf / 当前库 / addons 路径” | Inspect the active config file, distinguish active values from commented examples, and report database/addons/data_dir clearly. |
| “重置 admin 登录密码” | First distinguish Odoo login password from admin_passwd and PostgreSQL password; prefer odoo-bin shell + ORM. |
| “生成模块介绍页 / README 转 description” | Confirm the target is an Odoo module, inspect __manifest__.py, and create or update static/description/index.html. |
| “恢复 Odoo.sh 备份” | Identify backup type, confirm target database and filestore/data_dir, prefer safe restore paths, and warn about non-neutralized backups. |
Core rule
Use the bundled scripts first. Do not write ad-hoc OdooRPC code unless the scripts are insufficient.
Preferred command style from the skill directory:
uv run scripts/odoo_config.py --help
uv run scripts/odoo_query.py --help
uv run scripts/odoo_mutate.py --help
Fallback when uv is unavailable:
python3 -m pip install --user "odoorpc>=0.10,<1" "PyYAML>=6,<7"
python3 scripts/odoo_config.py --help
python3 scripts/odoo_query.py --help
python3 scripts/odoo_mutate.py --help
Configuration location
Connection profiles are stored under the user directory:
~/.config/odoorpc/config.yaml
For compatibility with common typos, the scripts also detect ~/.config/odoorpc/config.ymal; however, agents should create and update config.yaml as the canonical file.
The config file supports multiple Odoo connections:
default_profile: local
profiles:
local:
host: 127.0.0.1
port: 8069
protocol: jsonrpc
database: odoo
username: admin
password: admin
timeout: 30
odoo_version: "19.0"
production:
host: odoo.example.com
port: 443
protocol: jsonrpc+ssl
database: prod_db
username: admin@example.com
password: "paste-api-key-or-password-here"
timeout: 30
odoo_version: "19.0"
Supported protocols usually include jsonrpc, jsonrpc+ssl, xmlrpc, and xmlrpc+ssl.
odoo_version is optional but strongly recommended. Store the major/minor Odoo version as a string such as "16.0", "17.0", "18.0", or "19.0". Agents should use it to choose version-aware model fields, workflows, and documentation assumptions. If the user provides the Odoo version with their connection details, save it in the profile. If the user does not know the version, connect once and run scripts/odoo_config.py detect-version --profile <name> --save.
Saving user-provided connection config
When the user provides Odoo connection details and asks the agent to connect, save them automatically into a named profile unless the user explicitly says not to save.
Use scripts/odoo_config.py set-profile; never manually echo secrets into shell history when avoidable. Prefer --password-stdin for passwords/API keys:
printf '%s' 'SECRET_VALUE' | uv run scripts/odoo_config.py set-profile \
--profile customer-dev \
--host odoo.example.com \
--port 443 \
--protocol jsonrpc+ssl \
--database customer_db \
--username admin@example.com \
--password-stdin \
--odoo-version 19.0 \
--set-default
If the user provides multiple Odoo systems, ask or infer a clear profile name such as local, dev, staging, production, customer-a-prod, or nordic-match-prod. If the user does not provide a name, create a short descriptive one from the host and purpose.
If the user did not provide the Odoo version, detect and save it after the profile is created:
uv run scripts/odoo_config.py detect-version --profile customer-dev --save
The config script sets restrictive permissions where supported:
~/.config/odoorpc:0700~/.config/odoorpc/config.yaml:0600
Available scripts
scripts/odoo_config.py— create/list/show/remove local Odoo connection profiles and detect/save Odoo server versions.scripts/odoo_query.py— read-only Odoo operations: test login, inspect fields, search/read, count, and method calls marked as read-only.scripts/odoo_mutate.py— guarded create/update/delete operations with dry-run defaults, snapshots, confirmation phrases, deny rules, and JSON output.scripts/odoo_common.py— shared helper library used by the above scripts.
All scripts output JSON on stdout and diagnostics on stderr. Prefer --limit to keep output small.
Read-only workflow
Use this workflow for inspection, troubleshooting, and reporting.
1. List configured profiles
uv run scripts/odoo_config.py list
2. Test login and show configured/detected Odoo version
uv run scripts/odoo_query.py test --profile local
To persist the detected server version into the profile config:
uv run scripts/odoo_config.py detect-version --profile local --save
3. Inspect a model
uv run scripts/odoo_query.py fields --profile local --model res.partner --fields name,email,phone,is_company
4. Search and read records
uv run scripts/odoo_query.py search-read \
--profile local \
--model res.partner \
--domain-json '[["is_company", "=", true]]' \
--fields name,email,phone \
--limit 10
Use JSON domains only. Do not use Python eval for domains.
Practical Odoo playbooks
Partner/customer/vendor lookup
Use res.partner. Start with name, display_name, email, phone, mobile, is_company, parent_id, company_id, country_id, vat, customer_rank, and supplier_rank.
uv run scripts/odoo_query.py search-read \
--profile local \
--model res.partner \
--domain-json '[["name", "ilike", "ACME"]]' \
--fields name,display_name,email,phone,is_company,parent_id,company_id,country_id,customer_rank,supplier_rank \
--limit 10
Sale quotation/order lookup
Use sale.order. Start with name, partner_id, state, date_order, amount_total, currency_id, company_id, user_id, and invoice_status.
uv run scripts/odoo_query.py search-read \
--profile local \
--model sale.order \
--domain-json '[["name", "ilike", "S"]]' \
--fields name,partner_id,state,date_order,amount_total,currency_id,company_id,user_id,invoice_status \
--limit 10
Product and variant lookup
Use product.template for product-level information and product.product for variants. Start with name, default_code, barcode, active, sale_ok, purchase_ok, type, categ_id, list_price, standard_price, and qty_available when available.
uv run scripts/odoo_query.py search-read \
--profile local \
--model product.product \
--domain-json '[["default_code", "ilike", "SKU"]]' \
--fields name,display_name,default_code,barcode,active,sale_ok,purchase_ok,type,categ_id,list_price,standard_price,qty_available \
--limit 10
Inventory/stock lookup
Use stock.quant for on-hand quantities, stock.picking for transfers, and stock.move for stock moves. Stock mutation is high risk; default to read-only diagnostics.
uv run scripts/odoo_query.py search-read \
--profile local \
--model stock.picking \
--domain-json '[["name", "ilike", "WH"]]' \
--fields name,partner_id,picking_type_id,location_id,location_dest_id,state,scheduled_date,origin,company_id \
--limit 10
Invoice/bill lookup
Use account.move. Accounting documents are protected. Read them freely when authorized, but do not post, cancel, reset, delete, or change them unless the user gives precise instructions and accepts business/legal risk.
uv run scripts/odoo_query.py search-read \
--profile local \
--model account.move \
--domain-json '[["name", "ilike", "INV"]]' \
--fields name,partner_id,move_type,state,payment_state,invoice_date,amount_total,currency_id,company_id \
--limit 10
Company hierarchy and multi-company questions
Use res.company and res.partner. For a company record, inspect name, parent_id, partner_id, country_id, and related partner address fields. Do not mutate company_id, company_ids, parent_id, or company address fields without explicit approval.
uv run scripts/odoo_query.py search-read \
--profile local \
--model res.company \
--domain-json '[["name", "ilike", "Schaeffler"]]' \
--fields name,parent_id,partner_id,country_id,company_registry,vat \
--limit 10
Access-right and security diagnostics
Use res.users, ir.model.access, and ir.rule in read-only mode. Never change groups or record rules unless the user explicitly asks for an admin/security change and the impact is reviewed.
Create workflow
Creating records is state-changing and must be deliberate.
Default behavior is dry-run:
uv run scripts/odoo_mutate.py create \
--profile local \
--model res.partner \
--values-json '{"name":"Demo Customer","email":"demo@example.com"}'
Actual creation requires --execute and a confirmation phrase:
uv run scripts/odoo_mutate.py create \
--profile local \
--model res.partner \
--values-json '{"name":"Demo Customer","email":"demo@example.com"}' \
--execute \
--confirm CREATE
Strict update rules
Updating existing records is high risk. Follow all rules below.
- Never update records based only on a fuzzy name. First search/read and show the user the target IDs and key fields.
- Always read a before-snapshot of the exact IDs and fields being changed.
- Default to dry-run. Show the proposed diff before executing.
- Actual update requires all of these:
- User explicitly requested the update.
- Exact model and IDs are known.
--executeis passed.--confirm UPDATEis passed.
- Do not update more than 20 records in one command unless the user explicitly requests a bulk operation and the agent has explained the blast radius.
- Avoid updates that trigger mail/tracking unless required. Use
--quiet-mailwhen appropriate. - Never update protected models/fields unless the user gives a very explicit administrator-level instruction and accepts the risk.
- When the user asks for a broad fix, first translate it into exact model, exact domain, exact record IDs, exact fields, and exact new values.
- Prefer normal Odoo business workflows over direct field edits when a business document has workflow state, but only execute workflow methods after explicit authorization.
Dry-run update:
uv run scripts/odoo_mutate.py update \
--profile local \
--model res.partner \
--ids 12,13 \
--values-json '{"category_id":[[6,0,[3]]]}'
Execute update:
uv run scripts/odoo_mutate.py update \
--profile local \
--model res.partner \
--ids 12,13 \
--values-json '{"category_id":[[6,0,[3]]]}' \
--execute \
--confirm UPDATE \
--quiet-mail
Strict delete rules
Deletion is the most dangerous operation.
- Never delete by default.
- Prefer archiving/deactivation when the model supports
active. - Never delete invoices, accounting entries, stock moves/pickings, posted documents, users, companies, or configuration records unless the user gives a precise, written instruction and you have verified the legal/business impact.
- Always read a before-snapshot of the exact IDs.
- Actual delete requires all of these:
- Exact model and IDs are known.
- User explicitly asked to delete, not “clean up” or “remove from view”.
--executeis passed.--confirm DELETEis passed.- Number of records is within the script limit.
- If uncertain, stop and ask for confirmation instead of deleting.
- For duplicate records, first propose merge/archive/deactivate where appropriate instead of unlink.
Dry-run delete:
uv run scripts/odoo_mutate.py delete \
--profile local \
--model res.partner \
--ids 99
Execute delete:
uv run scripts/odoo_mutate.py delete \
--profile local \
--model res.partner \
--ids 99 \
--execute \
--confirm DELETE
Protected models and fields
Local Odoo development and operations playbooks
Use these playbooks when the task is about a local/self-hosted Odoo deployment, module source tree, database restore, or server configuration rather than only live RPC record lookup.
Global local-ops rules
- Distinguish Odoo concepts before acting. For “admin password”, decide whether the user means the Odoo login user password,
admin_passwd, or PostgreSQLdb_user/db_password. - Read the active instance configuration first. For database-affecting work, inspect
odoo.confand identifydb_name,db_host,db_port,db_user,db_password,addons_path, anddata_dirwhere available. - Prefer Odoo framework operations. Use
odoo-bin shell+ ORM for Odoo data changes when possible; avoid hand-writing password hashes or blindly editing business tables. - Keep changes minimal. Only modify the requested database, user, module, or config entry; do not opportunistically change unrelated passwords, modules, or settings.
- Report clear boundaries. Say what changed, what did not change, and which database/module/user/config file was targeted.
Reset Odoo login user admin password
Apply when the user asks to reset the Odoo backend login user admin, not the database user and not the Odoo database manager master password.
Required checks:
- Confirm the target is the Odoo application login user
admin. - Confirm the active database from
odoo.confor the running command/container. - Verify a
res.usersrecord withlogin='admin'exists. - Use
odoo-bin shell+ ORM; only fall back to direct SQL when the framework path is impossible and the user understands the risk.
Preferred ORM snippet:
user = env['res.users'].search([('login', '=', 'admin')], limit=1)
assert user, 'admin user not found'
user.write({'password': '<NEW_PASSWORD>'})
env.cr.commit()
print(f'password updated for user id={user.id} login={user.login}')
Report using this shape:
已处理当前 Odoo 数据库:<DB_NAME>
- 目标用户:admin
- 已执行操作:将 Odoo 登录密码改为 <REDACTED_OR_USER_PROVIDED_VALUE>
- 未修改:admin_passwd / PostgreSQL 用户密码
- 建议验证:使用 admin 登录 Odoo 页面
Generate or maintain module description page
Apply when the user asks to generate an Odoo module introduction page, convert a module README into an app description page, maintain module documentation, or create static/description/index.html.
Execution rules:
- Confirm the target is an Odoo module by checking
__manifest__.pyor__openerp__.py. - Prefer existing
README.md,README.rst,docs/, manifest metadata, screenshots, and module source structure as inputs. - Write the final app-store-style page to
static/description/index.htmland create the directory if needed. - Keep the content specific to the module; do not turn a generic repository README into unrelated marketing copy.
- If converting Markdown, use the repository’s existing converter/tooling if present; otherwise use a minimal HTML structure compatible with Odoo module descriptions.
Report using this shape:
已生成/更新 Odoo 模块介绍页:<MODULE_PATH>/static/description/index.html
- 来源文档:<README_OR_GENERATED_FROM_MODULE_STRUCTURE>
- 已覆盖内容:功能介绍、安装/配置、使用方式、注意事项
- 未处理:<KNOWN_GAPS_OR_NONE>
Identify active database and addons paths from odoo.conf
Apply when the user asks which database is running, which config is active, where addons are loaded from, or why Odoo is using the wrong database/module path.
Execution rules:
- Locate the actual config used by the running service/container/command; do not assume a nearby sample file is active.
- Parse only active, uncommented values; do not treat commented examples as configuration.
- Report
db_name,db_host,db_port,db_user,addons_path,data_dir,dbfilter, and relevant service/container command-line overrides. - If multiple configs exist, explain the evidence for which one is active.
- Redact
db_password, API keys, and other secrets.
Report using this shape:
已识别当前 Odoo 配置:<ODOO_CONF_PATH>
- 数据库:<DB_NAME_OR_FILTER>
- 数据库连接:<HOST>:<PORT> / user=<DB_USER>
- addons_path:<PATHS>
- data_dir:<DATA_DIR_OR_DEFAULT_NEEDS_CONFIRMATION>
- 注意事项:<MULTI_DB_OR_DBFILTER_OR_OVERRIDES>
Restore Odoo.sh backup and configure odoo.conf
Apply when the user asks to restore an Odoo.sh backup locally or on a server, configure odoo.conf after restore, recover a dump.sql/.dump/zip backup, or align filestore and addons paths.
Required checks:
- Identify the backup type: standard Odoo zip,
dump.sql, PostgreSQL custom.dump, or anofsbackup without real filestore content. - Confirm the target database name before overwriting or creating anything.
- Confirm
data_dir; filestore must live under<data_dir>/filestore/<DB_NAME>/. - Confirm
addons_pathincludes all custom and enterprise modules required by the restored database. - Warn that non-neutralized restores may trigger emails, scheduled actions, webhooks, payments, or external connectors.
Preferred restore paths:
python odoo-bin db load <DB_NAME> <BACKUP_ZIP> --force
python odoo-bin db load <DB_NAME> <BACKUP_ZIP> --force --neutralize
createdb -U <DB_USER> <DB_NAME>
psql -U <DB_USER> -d <DB_NAME> -f dump.sql
pg_restore --no-owner -U <DB_USER> -d <DB_NAME> backup.dump
Windows fallback:
- If
db loadcannot read a local path on Windows, do not conclude the backup is corrupt. - Fall back by extracting
dump.sqlor.dump, creating the database, restoring withpsql/pg_restore, then copying real filestore content if present.
nofs backup rule:
- Treat filename/content signs of
nofsor an emptyfilestore/as a successful database-only backup with missing attachments. - Clearly tell the user that images, attachments, documents, and binary assets may be unavailable even if SQL restore succeeds.
Default database naming rule:
<project_name>_<YYYYMMDDHHMMSS>
If renaming after restore, use a filestore-aware process and update odoo.conf db_name; do not rename PostgreSQL only and leave filestore/config stale.
Validation checklist:
- Target database was created or overwritten intentionally.
odoo.confpoints to the restored database and correctdata_dir.- Odoo can load the registry, ideally via
odoo-bin shell --no-http --stop-after-initor an equivalent startup check. - Attachments/images/documents work when filestore is present.
- Missing filestore is documented as a known limitation for
nofsbackups. - Missing addons/version mismatches are reported explicitly.
Report using this shape:
已处理 Odoo 备份恢复:<BACKUP_PATH>
- 恢复目标数据库:<DB_NAME>
- 恢复方式:db load / psql / pg_restore
- filestore 状态:已恢复 / 缺失 / 待确认
- 已更新配置:db_name, db_host, db_port, db_user, db_password, addons_path, data_dir[, dbfilter]
- 当前风险:<RISKS>
- 建议验证:启动 Odoo,打开目标数据库并检查附件与模块加载情况
Local-ops must not do
- Do not confuse
admin_passwdwith the Odoo login useradminpassword. - Do not confuse PostgreSQL
db_user/db_passwordwith Odoo user credentials. - Do not write
res_users.passwordhashes by hand unless ORM is impossible. - Do not silently modify several credential systems at once.
- Do not delete, deactivate, or recreate users without explicit authorization.
- Do not announce restore success until database,
odoo.conf,data_dir, and filestore expectations are aligned. - Do not ignore Odoo version, enterprise addons, or custom addons compatibility.
- Do not overwrite an existing local database without explicit confirmation.
- Do not assume SQL restore means attachments and images are available.
- Do not forget to update
odoo.confafter restoring or renaming a database.
Treat these as protected. Read-only access is allowed; mutation requires exceptional user authorization and manual review.
Protected models include:
res.usersres.companyir.config_parameterir.model.accessir.ruleir.module.moduleaccount.moveaccount.move.linestock.movestock.pickingstock.quantpayment.*account.*stock.*ir.*
Protected fields include:
passwordgroups_idcompany_idcompany_idsparent_idon companiesactiveon users/companies- accounting state fields such as
state,payment_state,move_type - stock state/location fields such as
state,location_id,location_dest_id,quantity,reserved_quantity,inventory_quantity - any field containing
token,secret,key,password, orcredential
The mutation script enforces a denylist by default. Do not bypass it casually.
Notification and tracking safety
When doing batch creates/updates that could trigger chatter messages or emails, prefer quiet context:
{
"mail_create_nosubscribe": true,
"mail_notrack": true,
"tracking_disable": true,
"mail_auto_subscribe_no_notify": true
}
Use --quiet-mail in scripts/odoo_mutate.py to apply this context.
Output handling
- Scripts print JSON to stdout.
- Errors should be treated as actionable diagnostics.
- Do not paste secrets into the final answer.
- Redact passwords/API keys when summarizing config.
- Save snapshots generated by mutation scripts; they are rollback/audit aids, not a full database backup.
- When discussing behavior or writing custom code, mention whether the answer is based on configured
odoo_versionor a detected server version.
When scripts are insufficient
If a task requires a custom Odoo method call, first inspect the model and method behavior. For non-mutating calls, use scripts/odoo_query.py call-readonly only when the method is known to be safe. For mutating business workflows such as confirming sale orders, validating pickings, posting invoices, reconciling payments, changing stock quantities, cancelling documents, resetting posted documents, installing modules, changing access rules, or modifying users/companies, do not call raw methods until the user explicitly authorizes the workflow and the agent explains the expected effect.
Agent response style when using this skill
- For read-only lookups, summarize what was checked: profile, model, domain, fields, and key result.
- For configuration, say which profile was saved and whether login/version detection succeeded. Never reveal the password or API key.
- For dry-run mutations, show the target IDs, before values, proposed values, and exact command needed to execute.
- For executed mutations, show the final status, affected IDs, and snapshot path if available.
- If the Odoo server is unreachable or credentials fail, provide the actionable error and the profile name; do not invent database results.
More from imhansiy/my-skills
localtunnel-auto-expose
Automatically exposes local services using LocalTunnel via npx and provides the public URL along with the access password. Use when starting a new service or when external web access is requested.
13cliproxy-manager
Unified management skill for CLIProxyAPI, enabling AI to dynamically update service configurations (API Keys, model aliases, proxy rules) and monitor usage statistics.
7odoo-dev-assistant
Odoo 开发与运维辅助技能。用于处理 Odoo 模块开发、配置排查、数据库操作、账号与权限修复、文档生成等任务。当前已内置重置 Odoo 登录用户 admin 密码、生成模块介绍页文档、识别当前运行数据库与 addons 路径、恢复 Odoo.sh 备份并配置 odoo.conf 等工作流,后续可继续扩展更多 Odoo 场景。
2plantuml-image-generator
Triggered when the user requests an image (e.g., architecture, flowchart, sequence diagram) or needs a technical illustration inserted into a document. This skill dynamically generates and inserts images using the official PlantUML public API.
1hexo-blog-manager
Handles the creation workflow for Hexo blog posts and the uploading of images/covers to GitHub_Oss using the jsDelivr CDN. Use exclusively for publishing or updating Hexo blog content with images.
1cliproxyapi-manager-skill
>-
1