Skip to main content
Glama

Save Extracted Job

store_extracted_jd
Idempotent

Save a job description you already extracted as structured JSON so it can be scored, matched, and tracked—no extra LLM parsing required.

Instructions

Store a job description the caller already extracted into JSON (the backend makes no LLM call).

extracted_json must match the app's JobExtraction schema: company, title,
role_category, level: "intern"|"entry"|"mid"|"senior"|"lead" (other spellings
map to these; any other level is kept in its own words), employment_type,
work_mode: "remote"|"hybrid"|"on_site" (stored as that key), city/state/country
(the job also stores country_code, the one ISO code they name),
location_raw, salary_min/max, salary_period, salary_currency (ISO 4217; a bare "$"
is no currency, so the job takes its country's, else the home one, and
salary_currency_assumed is true),
work_authorization: "sponsorship_available"|"no_sponsorship"|"citizen_or_gc_required"|"unstated",
opt_accepted: "yes"|"stem_opt_ok"|"no"|"unstated",
years_experience_min/max, skills[{skill_name, skill_category,
requirement_level: "required"|"preferred"|"mentioned"}],
responsibilities[], qualifications[]. ANY other work_authorization or
opt_accepted value silently normalizes to "unstated" with no error.
source is "user" (default) or "agent"; source="agent" marks agent-hunted jobs,
which need an open proposal before the apply helpers act on them. A job
stored with source="user" is not proposal-gated: the server skips the
open-proposal check for any non-agent source. Returns the stored job.

A posting already saved is returned unchanged with already_existed=true,
unless update=true: then this extraction corrects it and the job carries
correction. Already saved means the same raw_text (with none, the same
extraction or the same posting at source_url) or the same company and
requisition_id.

Input Schema

NameRequiredDescriptionDefault
sourceNouser
updateNoTrue: when the posting is already saved, this extraction replaces that job's fields and skills through the same mapping a new capture uses; the job keeps its id, status, applications, proposals, source and raw_text. It overwrites the stored extraction, which the web app can't undo. The saved job is found the way a capture finds it: resend the saved raw_text, or omit raw_text and pass its source_url (new raw_text misses the text match and creates a new job). The response's correction is {changed: the fields whose value changed, "skills" when the skill list did; scores_cleared: the job's match scores deleted because a field scoring reads changed, after which the job is scored again; rescored: true when that succeeded; rescore_failed: "bases" or the application ids it could not score, which score_ats computes later; tailored_applications: tailored resumes, left as written; open_sessions: open gap analyses, left to read as out of date, with their score link cleared; proposals_fit_stale: proposals whose stored fit predates the correction}. A job another copy of Maestro owns is refused (409). False (default): a repeat capture returns the saved job unchanged.
raw_textNo
apply_urlNoThe posting's application address when it differs from source_url (http or https). Stored with the job; find_job_by_url then finds the job by it when the address names the posting (an id in its path or query). Without update, a saved job takes it only when it has none; with update it replaces the stored one.
source_urlNo
extracted_jsonYes

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changedv0.12.0
    • addedInput schema / properties / apply_url
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "The posting's application address when it differs from source_url (http or https). Stored with the job; find_job_by_url then finds the job by it when the address names the posting (an id in its path or query). Without update, a saved job takes it only when it has none; with update it replaces the stored one.",
      +  "title": "Apply Url"
      +}
    • addedInput schema / properties / update
      Added value: +{
      +  "default": false,
      +  "description": "True: when the posting is already saved, this extraction replaces that job's fields and skills through the same mapping a new capture uses; the job keeps its id, status, applications, proposals, source and raw_text. It overwrites the stored extraction, which the web app can't undo. The saved job is found the way a capture finds it: resend the saved raw_text, or omit raw_text and pass its source_url (new raw_text misses the text match and creates a new job). The response's correction is {changed: the fields whose value changed, \"skills\" when the skill list did; scores_cleared: the job's match scores deleted because a field scoring reads changed, after which the job is scored again; rescored: true when that succeeded; rescore_failed: \"bases\" or the application ids it could not score, which score_ats computes later; tailored_applications: tailored resumes, left as written; open_sessions: open gap analyses, left to read as out of date, with their score link cleared; proposals_fit_stale: proposals whose stored fit predates the correction}. A job another copy of Maestro owns is refused (409). False (default): a repeat capture returns the saved job unchanged.",
      +  "title": "Update",
      +  "type": "boolean"
      +}
  2. First observedv0.11.1

TDQS

A4.5/5.0
Behavior5/5

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

Far beyond what the annotations convey: silent normalization of unknown work_authorization/opt_accepted values to "unstated" with no error, currency-assumption flagging, duplicate-detection identity rules (raw_text, extraction, source_url, company+requisition_id), the 409 refusal, and the full correction result shape. The note that update=true overwrites an extraction the web app can't undo is disclosed even though destructiveHint=false — useful context the annotation would otherwise hide, not an outright contradiction of it.

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?

Purpose and the no-LLM constraint are front-loaded, and the single long sentence enumerating the JobExtraction schema is dense but each clause adds real constraint. It is a wall of text for a complex tool, so slightly short of maximum for scannability.

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?

With six parameters, a nested free-form object, and no output schema, the description still covers return values ("Returns the stored job"), the already_existed path, and the correction payload. An agent has everything needed to call this correctly and to predict the response.

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

Parameters5/5

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

Schema coverage is only 33% and extracted_json is an untyped object with additionalProperties, so the description carries the full burden — and does, enumerating every field, every enum, and the fallback semantics of each. source, raw_text, update and source_url behavior are all explained contextually rather than restated.

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

Purpose4/5

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

The opening sentence gives a specific verb and resource ("Store a job description") and rules out the wrong mental model with "the caller already extracted" and "the backend makes no LLM call". It is unambiguous what the tool does, though there is no sibling that stores jobs, so no active differentiation is required.

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?

The description clearly states the two conditions that shape a call: source="agent" vs "user" changes whether the open-proposal gate applies, and update=true vs the default changes duplicate handling. It does not name an alternative tool for pre-save lookup (find_job_by_url) or state outright exclusions, so it stops short of full routing guidance.

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