Skip to content
Felspar
Esc
↑↓navigate↵open⌘Jpreview
On this page

MCP tools

Felspar's remote MCP server - connection, the four agent tools and the seven authoring tools, with inputs, results and errors.

Connection

Address https://mcp.felspar.ai/mcp
Transport Streamable HTTP, stateless JSON-RPC: one JSON response per message
Protocol versions 2025-11-25, 2025-06-18, 2025-03-26
Agents authenticate with Authorization: Bearer <agent key>
People authenticate with OAuth sign-in with their Felspar login (the client opens it on first use)

The credential is checked on every message, including initialize. A missing, unknown or revoked key answers 401, so a client fails to connect rather than failing each tool.

Agents and people see different tools, never each other’s. An agent key gets the four agent tools. A person’s sign-in gets the seven authoring tools. Each tool calls the same REST route as an HTTP client would, with the caller’s own credential, so access, release choice and receipts are identical.

Errors

A refused or failed call is a tool result with isError: true, never a decision. Refusals carry the same error, path, issues[] and fix as the REST API, so the model can read and correct them. Arguments that don’t match a tool’s schema return invalid_arguments with each issue’s path and problem.

Tool hints

Every tool declares destructiveHint: false and openWorldHint: false: nothing deletes, overwrites or reaches outside your workspace. Tools that record something new (add_current_version, propose_pack, get_job_decision, check_output) declare readOnlyHint: false; the rest are read-only.

Agent tools

list_jobs

List the jobs this agent key may run, so an agent that wasn’t told which job to run can pick one. No inputs. Each job comes from its released pack:

Field Meaning
id The job ID
purpose Why the job exists and what it resolves, quoted
useWhen, notFor When the policy says to use the job, and when not, quoted
subject What each case is about: type, definition (quoted), identifier and example; or null

A job is listed only when get_job_requirements would serve it to this key: it is registered for the job, followed on Latest or pinned, with a live release. An agent picks the job whose purpose and use-when fit the task and whose not-for doesn’t; when none fits, it says so instead of forcing one. Over REST, GET /v0/jobs returns the job IDs.

get_job_requirements

Read what a company job needs before deciding: its purpose and subject, its questions, what each means (quoted from the policy), what evidence counts, the possible answers and outcomes, and request limits. Call it once per job; it changes only when a new version is released.

Input Type Meaning
job string, required The job ID, such as triage-ticket
detail "compact" or "full" compact (default) is enough to gather evidence; full adds the decision rules

Returns the job’s brief from the release this agent uses:

  • purpose, useWhen, notFor and owner: what the job is for, quoted, first;
  • subject: what each case is about (type, quoted definition, identifier, example, match), its related records (related[], each with relationship, cardinality, required and quoted definitions), and send: in plain words, exactly which ID to send and how to send related ones;
  • identity: how each case gets its ID (mode: provided, fields, content or issued), send in plain words, and the fields or question it uses;
  • questions[]: key, type (enum, boolean, number, date), meaning (quoted, with its locator), answer (what to send and how it’s read: send, valueFormat, interpretation, threshold, applies, dependsOn, appliesWhen), answers[] (each with meaning, inclusions, exclusions, examples, edgeCases) and whereToLook[]; a question about a related record carries about (relationship, subject);
  • outcomes[]: id, meaning, nextStep (quoted), the outputs that outcome asks the agent to write, and escalate when it hands off;
  • escalations: the hand-offs the policy names (routes[], each id with its quote), which one the unresolved path and the attempt limit use, and the rule in words;
  • whenUnresolved: what to do when nothing is decided (quoted);
  • attempts: max, withProgress and the rule in words;
  • guidance.gather: how to gather evidence so one request is enough;
  • input: for this tool, { tool: "get_job_decision", jsonSchema, note }, the exact arguments to send;
  • limits: the facts limits;
  • release: the release ID and digests;
  • rules (with detail: "full"), each with its quoted text.

get_job_decision

Get the company’s decision for one case. Send, for each question, the exact words your sources say and where they came from. Use only the outcome Felspar returns.

Input Type Meaning
job string, required The job ID
entity_id string The case’s ID, as the brief’s identity.send and subject.send say. Required when the job uses the record’s own ID (provided): a ticket number, a contract ID, a web domain; for an older job with no subject, the record’s own stable ID. Left out for fields and content jobs, and on the first decision of an issued job; after that, the case.id Felspar returned. Reuse it for every attempt on the case.
identity object Only for a job that identifies cases by fields: each field’s value by name, as the brief’s identity.fields lists them, such as { "email": "sam@example.com", "requisition": "REQ-114" }
related object Only when a question is about a related record: that record’s ID under its relationship name, such as { "counterparty": "northwind.com" }; a list of IDs for a many relationship
facts object, required One entry per question key: an excerpt { text, source: { class, id, locator, observedAt }, about? }, or a list of up to 3 excerpts from different places. For a many relationship, about names the related ID the excerpt comes from.

Felspar works out the case ID by the job’s identity mode, normalizes it and each related ID by its subject’s match rule, picks the release (Latest or the agent’s pin) and assigns each call its own execution ID. The result is the same as the REST decision result: status, reason, and result with outcome, explanation, retry, attempts, nextStep, outputs, interpretation, case, subject, escalation, missing, receipt and executionObjectId.

  • case: { id, mode } for a job released with identity modes, where mode is provided, fields, content or issued. Send case.id as entity_id for every later decision on the same case.

  • subject: { type, id, related[] }, with the normalized IDs and, for each related record, the questions it supplied evidence for.

  • escalation: { id, trigger, why } when the release names a hand-off for this result. trigger is outcome, unresolved or attempt_limit. Report the id exactly and hand the case off; don’t ask again. See Subjects and escalations.

  • missing: with status gap, the required related records the request lacked, such as ["missing: counterparty"].

A wrong or missing case ID is refused with one of the identity refusals, such as entity_id_required or unknown_case_id.

check_output

Check text the agent wrote for one of its own decisions (a ticket reply, an email) before it is sent or saved.

Input Type Meaning
job string, required The same job ID sent to get_job_decision
execution_id string, required The executionId of the decision this text is for
text string, required The exact text, in full
output string Which output this is, when the outcome asks for more than one

Returns pass, revise (each finding with the policy sentence it quotes and what to fix), review_required or unavailable, and on pass a receipt bound to exactly this text. Any edit needs a new check. A pass means the text follows the released rules; it is not approval to send. Refusals include output_not_required (the outcome asks for no such output; it lists the ones it does), decision_not_decided and no_output_checks. Each check counts as one decision toward rate limits. Over REST: POST /v0/jobs/{job}/output-checks.

Receipt verification is deliberately not an MCP tool. The code that acts calls POST /v0/executions/{id}/verify with the same key.

Authoring tools

Available to a signed-in member, approver or owner. None of them can approve or release.

get_pack_guide

How to write a pack draft: the format, the rules Felspar enforces and a small example. No inputs. Read it before the first check_pack. Same content as Pack draft format.

list_sources

The workspace’s connected policy documents. No inputs. Each entry:

Field Meaning
sourceObjectId The source’s ID, used by the other tools
provider google-drive or notion
revision The document version that was added
access Whether you can read it now: allowed, or why not
title Only when you can read it
watch Only when you can read it: state (current, changed, stopped, pending), checkedAt, changedAt, reason
boundAt When it was added

read_source

The exact text of one added source: the version that was added, which may be older than the document now.

Input Meaning
source_object_id From list_sources

Quote meanings from this text word for word; Felspar refuses any quote it can’t find.

add_current_version

When a document is changed, capture its current text as a new source, read with your own connection. Returns the new sourceObjectId; read, draft and propose from that one. Live decisions don’t change until a new proposal is released.

Input Meaning
source_object_id The source whose document to re-read

This is the only way an MCP sign-in can add a source. Text the assistant writes is never accepted as a source (source_text_not_allowed).

check_pack

Check a pack draft against its source without saving anything.

Input Meaning
source_object_id The source the draft quotes
draft A felspar.pack-draft object: v2, or v1 and v0

Returns status:

  • invalid, with problems[] (each a path and message) and warnings;
  • tests_failed, with tests and warnings;
  • needs_definitions, with blocking (count), tests and warnings;
  • ready, with tests, warnings and artifactDigest.

When the draft revises a released job and removes an escalation ID, subject type or relationship name, or changes the job’s subject, the result also carries breaking: { changes[], agents }. Report each change to the person: code that acts on results may depend on it.

propose_pack

Submit a checked draft as a proposal for review. Same inputs as check_pack. Returns status: "proposed" with the proposal, tests, warnings, breaking when it applies, a review link to the web app and authority: "unapproved". Refuses with status: "needs_definitions" while any blocking finding remains. Agents keep using the current release until an approver approves and releases on the web app.

backtest

Re-decide the job’s saved cases (the last 30 days) under a proposal and under the current release, before anyone approves it.

Input Type Meaning
proposal_id string, required From propose_pack
reinterpret boolean Re-classify only the quotes whose definitions changed. Run without it first to see the estimate.
execution_ids string[] Only these saved cases, up to 50
limit integer How many saved cases, newest first: 1 to 50, default 25

Returns each case as unchanged, changed (current outcome to proposal outcome, with each side’s escalation), unresolved (with the questions still open and why), needs_reinterpretation (with an estimate) or unavailable; counts of each; escalationChanged, the number of cases whose escalation differs; breaking when the proposal removes something callers may depend on; a total estimate; and agents: those that ran the cases and those following the job on Latest or pinned. Calls no model unless reinterpret is true. Writes nothing. Counts as 10 decisions toward rate limits. See Review and backtest.

Was this page helpful?