Metadata-Version: 2.1
Name: odoo-addon-ai_tool_read
Version: 16.0.1.0.0.7
Summary: Generic ai_tool to read any model/field, with a per-field opt-out flag
Home-page: https://github.com/OCA/ai
Author: Quartile,Odoo Community Association (OCA)
Author-email: support@odoo-community.org
License: AGPL-3
Classifier: Programming Language :: Python
Classifier: Framework :: Odoo
Classifier: Framework :: Odoo :: 16.0
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
Requires-Python: >=3.10
Requires-Dist: odoo-addon-ai-tool<16.1dev,>=16.0dev
Requires-Dist: odoo<16.1dev,>=16.0a

.. image:: https://odoo-community.org/readme-banner-image
   :target: https://odoo-community.org/get-involved?utm_source=readme
   :alt: Odoo Community Association

==============
AI Tool - Read
==============

.. 
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   !! This file is generated by oca-gen-addon-readme !!
   !! changes will be overwritten.                   !!
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   !! source digest: sha256:51f52c734a0386c988e501e9b1068a728716ad92c2505aea605baa475cbc7066
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
    :target: https://odoo-community.org/page/development-status
    :alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
    :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
    :alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fai-lightgray.png?logo=github
    :target: https://github.com/OCA/ai/tree/16.0/ai_tool_read
    :alt: OCA/ai
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
    :target: https://translation.odoo-community.org/projects/ai-16-0/ai-16-0-ai_tool_read
    :alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
    :target: https://runboat.odoo-community.org/builds?repo=OCA/ai&target_branch=16.0
    :alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Adds ``ai_tool`` tools to let an AI agent read any model and field it is
allowed to access, while keeping personal or otherwise sensitive fields
out of the results.

A flag **AI Read Forbidden** (``ai_no_read``) is added to fields. The
tools never return nor describe a forbidden field, nor any field
exposing its value: related fields, including those delegated through
``_inherits``, and computed fields depending on it. Flagging
``res.partner.email`` thus also hides ``email_formatted``,
``email_normalized`` and ``res.users.email``.

Three tools are provided:

- **list_models**: lists the models the current user is allowed to read.
- **get_fields**: describes a model's readable fields (label, type,
  help, selection options and relation target), and lists the forbidden
  ones under ``hidden``.
- **search_read**: searches and reads records of a model. It returns the
  stored fields unless a field list is given, relational fields as
  record IDs, and dates in UTC. Results are paged with ``limit`` and
  ``offset``, and ``has_more`` tells whether more records match. A field
  whose computation is refused for the user is left out and listed in
  ``inaccessible_fields``, and a record the search finds but the user
  cannot read (as ``ir.attachment`` allows) is left out without notice,
  as record rules would.

The tools run with the calling user's own access rights (no ``sudo``),
so Odoo ACLs, record rules and field ``groups`` still apply. Connect the
agent as a user with no more rights than it needs: an administrator sees
every field restricted by ``groups``.

Besides forbidden fields, the results leave out binary fields, fields
pointing to a model the user cannot read (reading them would fail), and
display names computed on the fly, including the one Odoo adds to
many2one values: they are built from other records' fields, which may be
forbidden there.

The flag keeps values out of the results, not out of searches: see
*Known issues*.

**Table of contents**

.. contents::
   :local:

Configuration
=============

To forbid a field, tick **AI Read Forbidden** on it. As the
administrator, either:

- Go to *Settings > Technical > Database Structure > Fields*, find the
  field and tick the flag (the **AI Read Forbidden** filter and list
  column help you review what is already flagged), or
- Open the model in *Settings > Technical > Database Structure > Models*
  and tick the flag on the line of the field, in the *Fields* tab.

The flag applies to base fields as well as custom ones, and takes effect
immediately. A flag set on a mixin's field, such as ``email_normalized``
on ``mail.thread.blacklist``, applies to every model inheriting it.

A forbidden field takes along the fields that expose its value through
their definition, on any model: related fields, the fields a model
delegates through ``_inherits``, and computed fields that depend on it.
The ``hidden`` list returned by the **get_fields** tool shows the result
on a model. A value copied into another record when that record is
written is not followed (see *Known issues*): flag such fields as well.

This errs on the safe side: a computed field is hidden as soon as it
depends on a forbidden field, even when its value does not contain it.
Forbidding ``res.partner.email`` also hides ``is_blacklisted``, a yes/no
computed from the address, and ``hr.employee.mobile_phone``, computed by
the same method as the work email. For the same reason, flag the fields
that hold the sensitive values themselves (email, phone, address), not
fields that many others are computed from, such as ``parent_id`` or
``company_id``: those would hide far more than intended.

Fields restricted to a user group (``groups=`` in their definition) are
already left out of the results for a user outside that group, without
any flag, and are not reported in the ``hidden`` list either. **AI Read
Forbidden** is only needed for fields that the user is otherwise allowed
to read.

Usage
=====

The tools are registered as ``ai.tool`` records and are callable through
any ``ai_tool`` integration: ``ai_oca_mcp``, for instance, exposes them
to MCP clients such as Claude Desktop. This module provides the tools
themselves; how they are reached depends on the transport module
installed.

A typical agent workflow:

1. Call ``list_models`` to discover the models the bound user may read.
   Each entry gives the technical ``model`` name (e.g. ``res.partner``)
   to pass to the other tools.

2. Call ``get_fields`` with ``model`` to learn what each readable field
   means (label, type, help, selection options, relation target), and
   which fields are listed under ``hidden``.

3. Call ``search_read`` to fetch records:

   - ``model`` (required): the technical model name.
   - ``domain`` (optional): an Odoo domain as a list, e.g.
     ``[["is_company", "=", true]]``.
   - ``fields`` (optional): an explicit field list; the stored readable
     fields are returned otherwise. An empty list returns ids only.
   - ``limit`` (optional): page size, 200 by default and at most; a
     value of 0 or less, or past the cap, is treated as the maximum.
   - ``offset`` (optional): skip that many matching records.
   - ``order`` (optional): e.g. ``"name asc"``.

   Relational fields come back as record ids, dates and datetimes as
   ISO-8601 strings in UTC. ``has_more`` tells whether another page
   exists under the same domain and order; fields the call could not
   read are listed in ``inaccessible_fields``, while forbidden or
   restricted fields are simply absent from the records.

Example ``search_read`` arguments:

.. code:: json

   {
     "model": "res.partner",
     "domain": [["is_company", "=", true]],
     "fields": ["name", "email", "country_id"],
     "limit": 20,
     "order": "name asc"
   }

Every call executes with the access rights of the calling user — the
API-key owner when reached through ``ai_oca_mcp`` — so Odoo ACLs, record
rules and field ``groups`` apply as usual. Connect the agent as a
dedicated user holding only the rights it needs.

Known issues / Roadmap
======================

The tools keep forbidden values out of their results, not out of the
searches an agent runs: they are not meant to stop an agent that probes
for hidden values on purpose. Each case below still lets a forbidden
value through:

- **Filtering on a forbidden field.**
  ``[("email", "ilike", "@example.com")]`` lists the contacts whose
  address matches, and repeating it on longer fragments rebuilds a whole
  address. The same works on fields restricted by ``groups=``, which
  Odoo does not check when searching, and on search-only fields:
  ``phone_mobile_search`` rebuilds a phone number even when ``phone`` is
  forbidden.
- **Searching a relation by text.**
  ``[("partner_id", "ilike", "@example.com")]`` searches contacts by
  name, email included, and finds the orders of the contacts whose
  address matches.
- **Sorting.** Ordering on a forbidden field, or on a many2one (sorted
  by the related record's display name), reveals the order of the hidden
  values.
- **Copied values.** A value copied into another record is not linked to
  its source: the author's address in a chatter message
  (``mail.message.email_from``) is returned unless that field is flagged
  too.

One more limit, which leaks nothing: a requested field that is
forbidden, binary or restricted is simply missing from the records, with
no reason given; ``get_fields`` tells them apart.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/ai/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/ai/issues/new?body=module:%20ai_tool_read%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Quartile

Contributors
------------

- `Quartile <https://www.quartile.co>`__:

  - Toshikimi Shigenobu

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
   :alt: Odoo Community Association
   :target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-nobuQuartile| image:: https://github.com/nobuQuartile.png?size=40px
    :target: https://github.com/nobuQuartile
    :alt: nobuQuartile

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-nobuQuartile| 

This module is part of the `OCA/ai <https://github.com/OCA/ai/tree/16.0/ai_tool_read>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
