Skip to main content
Glama
beel-es

BeeL MCP server

Official
by beel-es

beel_patch_company

Update editable company fields like legal name, address, and bank details for BeeL Spanish e-invoicing. Includes AEAT census validation for name changes and test-credential limits on live companies.

Instructions

Updates the editable fields of a company; the set is the one UpdateCompanyRequest declares.

  • Immutable fields: nif, entity_type and legal_form, once set. Sending one of them with a different value answers 422 with a code that names the field; sending the value it already has is not a change.

  • legal_name: changing it requires the NIF to pass an AEAT census re-validation. For a legal entity (LEGAL_ENTITY) the census identifies the company by its NIF alone: the name is not verified, so the name sent cannot make it fail. For an INDIVIDUAL the name must match the one the census holds for that NIF.

  • Census not answering: if the AEAT census cannot be reached, the change is not rejected. The response is 200 with the new legal_name stored, and BeeL repeats the census check in the background. The outcome of that check is not part of this resource: no field of CompanyData carries it. To know what the census says about a NIF and a name, ask POST /v1/nif/validate.

  • Addresses: a Spanish postal code (country_code omitted or ES) must have 5 digits, in address and in legal_representative.address; otherwise 422 POSTAL_CODE_INVALID_ES. Other countries' postal codes are free-form.

Test credentials on a Live company

Once the company is activated in Live, a test credential may only write the fields that affect how the invoice looks: logo_url, invoice_accent_color, invoice_template_type, invoice_language, email_language and additional_info. Any other field describes the real business — fiscal address, legal representative, bank details, contact data, IAE, activity start date, payment term — and answers 422 FISCAL_IDENTITY_LIVE_ONLY from Test, since the company is a single record shared by both modes. A company not activated in Live accepts the whole body from Test, and sending a field its current value is never a change.

What comes back

The 200 returns CompanyData with every field this request accepts, under the same name and the same type — so the response is the confirmation of what was stored, and a later GET says the same. A field you never set comes back absent, which means "nothing stored", not "hidden".

Two things live outside this body and keep their own reads: the invoice series (GET /v1/companies/{company_id}/series) and the rendering block, which is also served on its own by GET /v1/companies/{company_id}/invoice-customization.

Endpoint: PATCH /v1/companies/{company_id}

⚠️ Read before calling:

  • Which company an operation acts on, and how that is selected. (resource: beel://guardrails/multi-nif)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
bodyYes
company_idYesUnique identifier (UUID) of the company the operation acts on — its identifier, not its NIF. It is the only source of context: the account that owns it is derived from it, and the `BeeL-Active-Company` header plays no part. A company you do not reach answers `403`, and so does a company that does not exist, so the existence of a company in another account is never disclosed.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed14 schema fields changedv0.9.0
    • changedInput schema / $defs / Address / description
      Previous value: -"Address you send when you create or update a company, a customer or an onboarding.\nThe street number is mandatory here: an address without it is rejected with `422`.\n\nAddresses you read back are described by their own schema, and do not guarantee the\nstreet number: records registered before it was collected have none.\n"New value: +"Address you send when you create or update a company, a customer or an onboarding.\n\nAddresses you read back are described by their own schema.\n"
    • changedInput schema / $defs / Address / properties / city / pattern
      Previous value: -"^[a-zA-Z0-9À-ÿ\\u0100-\\u017F\\u00B7\\u2018\\u2019\\u0060\\u00B4\\s\\.,\\-\\/'ºª()]+$"New value: +"^[a-zA-Z0-9À-ÿ\\u0100-\\u017F\\u00B7\\u2018\\u2019\\u0060\\u00B4\\s\\.,\\-\\/'ºª°:;\"()&#]+$"
    • changedInput schema / $defs / Address / properties / country / description
      Previous value: -"Country - Latin characters only.\nOmitted, the address is stored as `España`.\n"New value: +"Country of the address, as its ISO 3166-1 alpha-2 code (`GB`) or its official name\nin Spanish, English or Catalan (`Reino Unido`, `United Kingdom`, `Regne Unit`;\ncase and accents are ignored). Anything else, such as `UK`, is rejected with\n`422 COUNTRY_CODE_REQUIRED`: send `country_code` instead. If it names a different\ncountry than `country_code`, `422 COUNTRY_CODE_MISMATCH` (`España` alone yields to\na foreign `country_code`: it was the old default). What is stored and\nreturned is always the Spanish name derived from the resulting code, never the\ntext sent. With neither field present, the address is Spanish (`España`).\n"
    • changedInput schema / $defs / Address / properties / country / pattern
      Previous value: -"^[a-zA-Z0-9À-ÿ\\u0100-\\u017F\\u00B7\\u2018\\u2019\\u0060\\u00B4\\s\\.,\\-\\/'ºª]+$"New value: +"^[a-zA-Z0-9À-ÿ\\u0100-\\u017F\\u00B7\\u2018\\u2019\\u0060\\u00B4\\s\\.,\\-\\/'ºª°:;\"()&#]+$"
    • changedInput schema / $defs / Address / properties / country_code / description
      Previous value: -"ISO 3166-1 alpha-2 country code.\nOmitted, the address is stored as `ES`.\n"New value: +"ISO 3166-1 alpha-2 country code: the canonical field that decides the country of\nthe address. It must be a real country code (`GB`, not `UK`); otherwise\n`422 COUNTRY_CODE_REQUIRED`. When it is omitted, the code comes from `country`\n(see there). With neither field present, the address is stored as `ES`.\n"
    • changedInput schema / $defs / Address / properties / number / description
      Previous value: -"Street number"New value: +"Street number. Optional: omit it when the address has none, or when `street` already\ncarries the address in full.\n"
    • changedInput schema / $defs / Address / properties / province / pattern
      Previous value: -"^[a-zA-Z0-9À-ÿ\\u0100-\\u017F\\u00B7\\u2018\\u2019\\u0060\\u00B4\\s\\.,\\-\\/'ºª]+$"New value: +"^[a-zA-Z0-9À-ÿ\\u0100-\\u017F\\u00B7\\u2018\\u2019\\u0060\\u00B4\\s\\.,\\-\\/'ºª°:;\"()&#]+$"
    • changedInput schema / $defs / Address / properties / street / pattern
      Previous value: -"^[a-zA-Z0-9À-ÿ\\u0100-\\u017F\\u00B7\\s\\.,\\-\\/'ºª°:;\"()&#]+$"New value: +"^[a-zA-Z0-9À-ÿ\\u0100-\\u017F\\u00B7\\u2018\\u2019\\u0060\\u00B4\\s\\.,\\-\\/'ºª°:;\"()&#]+$"
    • changedInput schema / $defs / Address / required
      Previous value: -[
      -  "street",
      -  "number",
      -  "postal_code",
      -  "city",
      -  "province"
      -]New value: +[
      +  "street",
      +  "postal_code",
      +  "city",
      +  "province"
      +]
    • changedInput schema / $defs / Language / description
      Previous value: -"Supported languages"New value: +"Supported languages: `es` Spanish, `en` English, `ca` Catalan.\n"
    • removedInput schema / $defs / Phone
      Removed value: -{
      -  "description": "Phone number. Allows digits, spaces, dashes, parentheses, and optional leading +",
      -  "maxLength": 20,
      -  "minLength": 9,
      -  "pattern": "^[+]?[0-9\\s\\-\\(\\)]+$",
      -  "type": "string"
      -}
    • addedInput schema / $defs / PhoneInput
      Added value: +{
      +  "description": "A phone number as this API accepts it: 9 to 20 characters, and only digits, spaces,\ndashes, parentheses and an optional leading `+`. Every request that takes a phone number\nuses this schema.\n\nIt is `Phone` plus the rules enforced on input. A value this schema accepts always\nsatisfies `Phone`, so anything you send here is something a response can return.\n",
      +  "maxLength": 20,
      +  "minLength": 9,
      +  "pattern": "^[+]?[0-9\\s\\-\\(\\)]+$",
      +  "type": "string"
      +}
    • removedInput schema / $defs / UpdateCompanyRequest / properties / phone / allOf
      Removed value: -[
      -  {
      -    "$ref": "#/$defs/Phone"
      -  },
      -  {
      -    "anyOf": [
      -      {},
      -      {
      -        "type": "null"
      -      }
      -    ]
      -  }
      -]
    • addedInput schema / $defs / UpdateCompanyRequest / properties / phone / anyOf
      Added value: +[
      +  {
      +    "allOf": [
      +      {
      +        "$ref": "#/$defs/PhoneInput"
      +      }
      +    ]
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
  2. Addedv0.5.0

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only give the generic write/open-world profile, yet the description discloses the concrete behaviour that matters: which fields answer 422 with which codes, that a 422 FISCAL_IDENTITY_LIVE_ONLY blocks Test writes on Live companies, that a failed census does not reject the change (200 with background retry), and that unset fields return absent rather than a default. This is exactly the extra context annotations cannot carry. idempotentHint=false is not contradicted: the census re-validation is a background side effect, so repeat calls are not guaranteed no-ops.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Headers and bolding make it scannable and the key immutability constraint is front-loaded. However, substantial content duplicates the schema descriptions — the immutable-field rule, the Test/Live writable set, and the entire 'What comes back' section reappear almost verbatim inside UpdateCompanyRequest and its field descriptions, so several sentences do not earn their place at the top level.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a PATCH with a large nested body and no output schema, the description covers everything an agent needs: writability by credential type, immutability, census dependency, error codes, address validation, and the shape of the response plus which parts live outside it. Nothing material for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With only 50% schema-description coverage, the description carries real weight: it enumerates the immutable fields (nif, entity_type, legal_form), the six Test-writable fields, and the address rule (5-digit Spanish postal codes in both `address` and `legal_representative.address`, else 422 POSTAL_CODE_INVALID_ES). It leaves the remaining field-level semantics to the referenced UpdateCompanyRequest schema rather than restating them, so it is a strong 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb+resource+scope: 'Updates the editable fields of a company; the set is the one `UpdateCompanyRequest` declares.' This distinguishes it from the many sibling patchers (beel_patch_customer, beel_patch_product, beel_update_invoice_customization) and names the endpoint explicitly. An agent can identify the operation without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives unusually rich when/when-not context: which fields are immutable, that changing legal_name triggers census re-validation, and that a Test credential on a Live company may only write six presentation fields. It also routes to alternatives (POST /v1/nif/validate, GET .../invoice-customization, GET .../series). It stops short of an explicit 'use this instead of X when Y' statement against sibling write tools, so it is a 4 rather than a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Deploy Server

Other Tools