Skip to main content
Glama

Create work package relation

create_work_package_relation

Create a typed relation between two work packages to record dependencies such as blocks, follows, duplicates, or relates. Returns the canonical relation direction and linked work packages.

Instructions

Link two work packages (blocks, follows, duplicates, relates, ...).

Use this to record a dependency the schedule or the reader needs to know about: "ship the client layer follows design sign-off", "this duplicates #4321". Returns the created relation with its id, type, reverse_type, both work packages, lag and description.

Pitfalls. OpenProject stores one canonical direction per pair, so the passive spellings are rewritten on save: creating precedes from A to B comes back as B follows A, with from_work_package and to_work_package swapped. That is the same fact, not an error — read type and reverse_type from the result rather than assuming what you sent. Only one relation may exist between two work packages: a second one answers 409 conflict, and changing it means update_work_package_relation on the existing id. A relation that would close a scheduling cycle is rejected with a validation error. Creating a follows relation can move dates, since OpenProject reschedules the successor.

Cross-references: get_work_package(include=['relations']) lists what a work package is already linked to and produces relation ids; update_work_package_relation edits one; delete_work_package_relation removes it; parent/child hierarchy goes through update_work_package(parent_id=...).

Input Schema

NameRequiredDescriptionDefault
lagNoWorking days to keep between the two work packages. Valid only on 'follows' and 'precedes'; passing it with any other type is refused here before the request is sent.
typeYesHow from_id relates to to_id. 'follows' schedules from_id after to_id, 'precedes' before it; 'blocks'/'blocked' express dependency without scheduling; 'duplicates'/'duplicated', 'includes'/'partof', 'requires'/'required' come in mirrored pairs; 'relates' is the neutral link. Parent/child hierarchy is NOT a relation — set it with update_work_package(parent_id=...).
to_idYesThe other work package. It must be visible to the account and different from from_id.
from_idYesWork package the relation is read from — 'type' describes what this one does to the other. Ids come from search_work_packages or list_work_packages.
descriptionNoOptional note explaining why the two work packages are linked.

Output Schema

NameRequiredDescriptionDefault
idNo
lagNo
typeNo
descriptionNo
reverse_typeNo
to_work_packageNo
from_work_packageNo

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed27 schema fields changedv0.4.0
    • removedOutput schema / description
      Removed value: -"One work-package relation.\n\nThe two endpoints that produce relations — the ``relations`` include of\n``get_work_package`` and the relation write tools — return this same shape.\n``from_work_package``/``to_work_package`` are spelled out because the wire's\n``from``/``to`` read as prepositions rather than as the two ends of an edge."
    • removedOutput schema / properties / description / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / properties / description / default
      Removed value: -null
    • removedOutput schema / properties / description / description
      Removed value: -"Free-text note stored on the relation; null when unset."
    • addedOutput schema / properties / description / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • changedOutput schema / properties / from_work_package / anyOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "description": "A reference to another resource. Canonical output shape.",
      -    "properties": {
      -      "id": {
      -        "anyOf": [
      -          {
      -            "type": "integer"
      -          },
      -          {
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null,
      -        "description": "Resource id."
      -      },
      -      "name": {
      -        "anyOf": [
      -          {
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null,
      -        "description": "Human-readable name."
      -      }
      -    },
      -    "type": "object"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "id": {
      +        "type": [
      +          "integer",
      +          "string",
      +          "null"
      +        ]
      +      },
      +      "name": {
      +        "type": [
      +          "string",
      +          "null"
      +        ]
      +      }
      +    },
      +    "type": "object"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • removedOutput schema / properties / from_work_package / default
      Removed value: -null
    • removedOutput schema / properties / from_work_package / description
      Removed value: -"Work package the relation starts at."
    • removedOutput schema / properties / id / anyOf
      Removed value: -[
      -  {
      -    "type": "integer"
      -  },
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / properties / id / default
      Removed value: -null
    • removedOutput schema / properties / id / description
      Removed value: -"Relation id. Pass it to update_work_package_relation or delete_work_package_relation — it is not a work package id."
    • addedOutput schema / properties / id / type
      Added value: +[
      +  "integer",
      +  "string",
      +  "null"
      +]
    • removedOutput schema / properties / lag / anyOf
      Removed value: -[
      -  {
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / properties / lag / default
      Removed value: -null
    • removedOutput schema / properties / lag / description
      Removed value: -"Working days kept between the predecessor and the successor. Only follows/precedes relations carry one; null everywhere else."
    • addedOutput schema / properties / lag / type
      Added value: +[
      +  "integer",
      +  "null"
      +]
    • removedOutput schema / properties / reverse_type / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / properties / reverse_type / default
      Removed value: -null
    • removedOutput schema / properties / reverse_type / description
      Removed value: -"The same relation read from the 'to' work package: 'follows' <-> 'precedes', 'blocks' <-> 'blocked', 'relates' <-> 'relates'."
    • addedOutput schema / properties / reverse_type / type
      Added value: +[
      +  "string",
      +  "null"
      +]
    • changedOutput schema / properties / to_work_package / anyOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "description": "A reference to another resource. Canonical output shape.",
      -    "properties": {
      -      "id": {
      -        "anyOf": [
      -          {
      -            "type": "integer"
      -          },
      -          {
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null,
      -        "description": "Resource id."
      -      },
      -      "name": {
      -        "anyOf": [
      -          {
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null,
      -        "description": "Human-readable name."
      -      }
      -    },
      -    "type": "object"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "id": {
      +        "type": [
      +          "integer",
      +          "string",
      +          "null"
      +        ]
      +      },
      +      "name": {
      +        "type": [
      +          "string",
      +          "null"
      +        ]
      +      }
      +    },
      +    "type": "object"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • removedOutput schema / properties / to_work_package / default
      Removed value: -null
    • removedOutput schema / properties / to_work_package / description
      Removed value: -"Work package the relation points to."
    • removedOutput schema / properties / type / anyOf
      Removed value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • removedOutput schema / properties / type / default
      Removed value: -null
    • removedOutput schema / properties / type / description
      Removed value: -"Relation type as OpenProject stored it, read from the 'from' work package (e.g. 'follows' means 'from' is scheduled after 'to')."
    • addedOutput schema / properties / type / type
      Added value: +[
      +  "string",
      +  "null"
      +]
  2. First observedv0.1.0

TDQS

A4.6/5.0
Behavior4/5

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

Goes well beyond the annotations: canonical direction rewriting on save (precedes A→B returns as B follows A with swapped from/to), single-relation-per-pair with a 409 on the second attempt, cycle rejection as a validation error, and the date-rescheduling side effect of follows. The only gap is that it doesn't say what permissions/visibility the account needs beyond 'must be visible to the account' in the schema.

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

Conciseness4/5

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

Front-loaded with purpose, then a labelled Pitfalls block and a Cross-references block, so the agent can skim to what it needs. Slightly long and repeats the hierarchy warning in both the description body and the type parameter, but each section carries distinct information.

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?

The description states the return payload (id, type, reverse_type, both work packages, lag, description) even though an output schema exists, and covers the three failure modes (409 duplicate, cycle validation error, direction rewriting) an agent must handle. Nothing needed to call this correctly for a 5-parameter mutation tool 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?

Schema coverage is 100%, so the baseline is 3; the description earns extra by explaining that the returned from/to and type may not match what was sent, which changes how an agent should interpret from_id/to_id/type together. The lag constraint and enum meanings are already fully in the schema, so it does not restate them.

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?

Opens with a specific verb+resource and enumerates the relation kinds (blocks, follows, duplicates, relates), then states the intent: recording a dependency the schedule or reader needs. It is clearly separable from siblings like update_work_package_relation and update_work_package (parent/child) without opening any schema.

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

Usage Guidelines5/5

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

Explicitly says when to use it ('to record a dependency the schedule or the reader needs to know about') with concrete example sentences, and routes the agent elsewhere where appropriate: get_work_package(include=['relations']) to discover existing links, update_work_package_relation to change one, delete_work_package_relation to remove, update_work_package(parent_id=...) for hierarchy.

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