Skip to main content
Glama
mcp-abacus

mcp-abacus

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault

No arguments

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}

Tools

Functions exposed to the LLM to take actions

NameDescription
infoA

Report mcp-abacus server availability, version, and environment information.

toolsets lists the opt-in tool groups active in this build. It is EMPTY today — every tool registers unconditionally — and is reserved for the future toolset gating (SA.1); it is reported now so a client can read the active set once that lands.

helpA

Return mcp-abacus reference text for one section, to drive the evaluator.

Sections: 'types' (the numeric types this build supports), 'language' (the expression grammar — operators, precedence, literal forms), 'functions' (the callable functions and their argument counts), 'solver' (the solver tool — solving / optimising one variable over a bracket), and 'fit' (the curve_fit tool — fitting curve forms to paired (x, y) data). section is restricted to these five names — advertised as a schema enum — so any other value is rejected with the valid list.

search_filter, when given, narrows the section to the lines that contain it as a case-insensitive substring; a filter that matches nothing says so. details expands the 'functions' section into per-function cards (signature, arity, description).

calculateA

Evaluate an expression (or short program) in one numeric type; return value + precision.

Use `calculate` when you want the VALUE of an expression. To instead see WHERE a
surprising answer rounded or overflowed — the per-node parse tree with each
sub-result — use `analyze`; to find the variable value(s) that drive an
expression to a root or extremum, use `solver`. All three share this expression
language and `mode`/`min_fixed_point_precision` arguments.

`mode` is the numeric type the WHOLE calculation runs in — every intermediate
result behaves exactly as that type would, so float rounding, fixed-point
scale, and rational exactness each show through. Modes:
  fixed-point   (default) exact scaled integer; money / ERC-20-safe; alias decimal
  floating-point  IEEE-754 double; ~15-17 sig. digits; aliases float64, double, float, ieee754
  rational        exact numerator/denominator; no irrationals; aliases fraction, frac
  complex         a+bi over two fixed-point parts; imaginary unit `1i` (e.g. 3+4i);
                  exact + - *, rounds / and transcendentals; no ordering/bitwise/solver

Grammar. Binary `+ - * / // %`; unary prefix `+ - ~`; `**` is POWER,
right-assoc, binds tighter than unary minus: -2**2 == -(2**2). Bitwise
`& | ^` (^ is XOR, NOT power) and `~` (NOT) work in EVERY type, on its own
stored bits (float's 64-bit IEEE pattern, fixed-point's mantissa, rational's
numerator/denominator). Both operands
of a binary op must share ONE type — there is no implicit promotion. Group
with `( )`. Functions: call as `name(arg, ...)` — e.g. `sqrt`, `sin`, `sum`;
each argument evaluates in the active type. For the full set and their
argument counts call `help('functions')`. Constants: `pi` and `e` are usable
bare (no parentheses), e.g. `2*pi`; assigning to them is an error. Literals: decimals
`12 3.14 .5 1e3 2.5e-4`; base integers
`0x1F 0b1010 0o17`; fixed-point `M@D == M x 10^-D`, where M MUST be
base-prefixed (0x/0o/0b) — a DECIMAL mantissa is INVALID: both `123@2` and
`123.45@2` error; write a decimal value as its digits (e.g. 123.45), never
with `@`. (`0x59682F00@9` = 1.5, `0xDE0B6B3A7640000@18` = 1 ETH.)

Variables & multi-line programs. Assign with `name = expr` (name is an
identifier `[A-Za-z_][A-Za-z0-9_]*`); a bare `name` reads it back, and reading
a name that was never assigned is an error. An assignment is itself an
expression — its value is the right-hand side — so `x = 2 + 3` returns 5 and
also binds `x`. Pass SEVERAL statements as one `expression` by separating them
with NEWLINES (`

): they run top to bottom sharing one variable scope, so a later line sees earlier bindings. EVERY bare-expression line is answered, in source order, in the valuesarray (below); assignment lines run silently for their bindings and are NOT echoed — except the final line, which is always the program's result and so is always echoed. When more than one line is answered, the top-level valueis a multi-line transcript, one = per answered line; a single answered line keepsvalueas just that result. E.g. "x = 10 y = x * 2 y + 1"returns 21 (onlyy + 1is a bare line), while "x = 10 (x - 1) / (x + 1) (x + 1) / 100.0"answers both divisions. Scope lasts for the one call only — bindings do not carry over to the nextcalculate`.

Returns a dict: `value` is the result rendered as a string and ANNOTATED with
its precision verdict — "(exact)" when the result is the true value, else
"(inexact, rounded to N decimals)" — so e.g. `/` cannot silently mislead by
looking exact when it rounded. `value_hex_dump` is that same value in hex (the
bit-backed representation): fixed-point as M@D (mantissa in whole-byte hex,
`@scale` dropped at scale 0), floating-point as the raw 64-bit IEEE-754
pattern, and NULL in rational mode (a numerator/denominator pair has no single
integer to dump). `mode` is the RESOLVED numeric type the call ran in — always
the canonical name even when you passed an alias (e.g. "double" reads back as
"floating-point"), so the reply stands on its own. The exactness/scale facts
are also returned as separate fields: `exact` (bool — did the mode hold the
true value) and `precision` (the fixed-point decimal scale, or null when the
mode has none). NOTE: floating-point conservatively reports `exact: false` for
every result today, including ones a double holds exactly. `values` is the per-line
breakdown: an array with one object per answered line, in source order, each
`{source, value, value_hex_dump, exact, precision, offered_precision}` — `source` is
the re-rendered expression and the other fields mirror the top-level ones for that one
line. The LAST entry is the program's result, so its fields equal the top-level
`value_hex_dump`/`exact`/`precision`/`offered_precision` (and, for a single answered
line, the top-level `value`). On failure `value`/`value_hex_dump`/`mode`/`exact`/
`precision`/`offered_precision`/`values` are null and `error` carries a plain,
self-contained message — what went wrong (a malformed expression, a domain error,
or an unknown mode with the valid list). It reads as prose, not a log line; only
the inexact-abort diagnostic names its source line, and in prose. On success
`error` is null. For the full reference call `help`.

`min_fixed_point_precision` floors the fixed-point result at that many decimal
places: every operand is held at no fewer than that many fractional digits, so
a `/` that would otherwise round at scale 0 keeps more decimals. It is valid
ONLY in fixed-point mode (the other modes have no decimal scale) and must be a
non-negative integer; either violation is an `error`. Omit it (null) for no
floor.

`inexact_handling` chooses what happens when a result is INEXACT:
  continue-and-report  (default) evaluate normally and let the verdict surface
                       in `value`/`exact`; never reject. Aliases: continue, report.
  abort-on-inexact     stop and FAIL the moment any sub-result is inexact. The
                       call returns no value — `error` instead carries a
                       diagnostic naming the source line and laying out the
                       operation that went inexact in computed VALUES (e.g.
                       `1.00 / 3.00 = 0.33`), then how to enable inexact
                       calculations if you do want the rounded answer. Use it
                       when an approximate answer is unacceptable and you want to
                       be told precisely what and where, rather than silently
                       trusting a rounded figure. Aliases: abort, strict, exact-only.
An unknown value is an `error` listing the valid choices. Note floating-point
reports every result inexact, so abort-on-inexact there fails on the first value.

`offered_precision` is a what-if nudge, present (non-null) ONLY on an inexact
fixed-point result when you did NOT pass min_fixed_point_precision: it shows the
SAME expression at a few more decimals so you see the digits the rounding hid.
It is a nested object `{mode, min_fixed_point_precision, value, value_hex_dump,
exact}` mirroring the top-level reply — its `mode` is always "fixed-point", its
min_fixed_point_precision is the argument to pass to GET that fuller value, its
`value` is what you'd get back (annotated with its OWN precision verdict, since
the offered value may itself still be inexact — e.g. 10/3 never terminates),
and `value_hex_dump` is that offered value in hex. It is NOT the answer to the
call you made (that stays in the top-level `value`); it is null whenever there
is nothing to offer. Each `values` entry carries its OWN `offered_precision` under
the same gate, so every answered line steers independently.
analyzeA

Evaluate an expression and return its AST as an indented tree of sub-results.

Same arguments and evaluation as calculate — mode (fixed-point default, floating-point, rational) and min_fixed_point_precision behave identically — but instead of one final value this returns the WHOLE parse tree, each node annotated with the Value it computed in that mode. Reach for it to see WHERE a surprising answer comes from: which sub-expression rounded, overflowed, or lost precision, rather than only the rounded result. For just the final value use calculate; to find the variable value(s) that drive an expression to a root or extremum, use solver.

tree is a multi-line string, one node per line, indented by depth (root last- applied operator at the top, literals at the leaves). Each line is <OPCODE/LITERAL "lexeme"> Value = <value> (<type>[<scale>], <exact|inexact>) followed by ·-separated per-mode details: the value in hex (fixed-point as M@D with whole-byte digits, @<scale> dropped at scale 0; float as raw IEEE-754 bits), or a rational's decimal approximation. The <scale> is the fixed-point decimal scale (omitted for modes without one). A fixed-point node that ROUNDED its result also carries a final · rounding <residual> ≈ <approx> fragment: the exact signed residual stored − true (a fraction, bounded by half a unit in the last place) and its decimal approximation. It is named rounding, NOT error, on purpose — the reply's top-level error field is the failure channel, so a rounding label keeps "this node rounded" from being misread as "this node failed". For example (1 + 1/2) * 3 in fixed-point:

BINARY_MUL Value = 3 (fixed-point[0], inexact) · hex 0x03
  BINARY_ADD Value = 1 (fixed-point[0], inexact) · hex 0x01
    LITERAL "1" Value = 1 (fixed-point[0], exact) · hex 0x01
    BINARY_DIV Value = 0 (fixed-point[0], inexact) · hex 0x00 · rounding -1/2 ≈ -0.5
      LITERAL "1" Value = 1 (fixed-point[0], exact) · hex 0x01
      LITERAL "2" Value = 2 (fixed-point[0], exact) · hex 0x02
  LITERAL "3" Value = 3 (fixed-point[0], exact) · hex 0x03

— the 1/2 = 0 leaf (inexact, scale 0) makes plain that fixed-point rounded the half away (its rounding -1/2 is the exact half discarded), so the product is 3, not 4.5, and every node above it inherits the inexactness. Those ancestors show no rounding fragment: they introduced no rounding of their own, only carried the leaf's. (Raise min_fixed_point_precision, or use a different mode, to keep those digits.)

On success tree is the rendering and error is null; on a bad mode, an invalid min_fixed_point_precision, or a malformed/erroring expression, tree is null and error carries the message (the same messages calculate returns).

solverA

Find the value(s) of one or more variables that drive an expression to a root or extremum.

solver takes the SAME expression language as calculate — every operator, function, literal form, and (crucially) multi-line programs with name = expr assignments — but instead of evaluating the expression it SEARCHES for the value of the unknown(s) that drive the expression to the chosen objective:

  • "find-root" (default): find where the expression equals zero. Write an equation f = g as the expression f - g and find its root.

  • "find-minimum" / "find-maximum": find where the expression reaches its smallest / largest value within the bracket(s).

There are two input forms for the unknowns:

  • SINGLE: variable + lower + upper — one unknown searched over the bracket [lower, upper] (lower must be below upper). This is the default golden-section engine. variable may be OMITTED when the expression has exactly one free name; the solver then auto-detects it (e.g. n in 12*n - (450 + 3*n)), needing only lower + upper.

  • MULTIPLE: variables — a dict mapping each unknown name to its [lower, upper] bracket, e.g. {"x": [0, 5], "y": [-4, 2]}. This needs a multivariate engine (algorithm="nelder-mead", "powell" or "bfgs"), which searches all the unknowns jointly. Give exactly one of the two forms. Each unknown must OCCUR in the expression and must NOT be assigned by it; every OTHER name is a constant the program sets via an assignment line (e.g. "r = 0.05\np = 1000\np * (1 + r)**n - 2000" solving for n with r, p fixed). A name that is neither an unknown nor assigned is an error.

objective (optional) names what to search for — "find-root", "find-minimum", or "find-maximum"; omitted, it defaults to "find-root". (The older spellings solve, minimise/maximise and their min/max and American forms are accepted too.)

algorithm (optional) names the search engine — "golden-section-search" (the default, single-variable), "brent-parabolic" (single-variable too, parabolic interpolation with a golden-section fallback — usually faster on smooth extrema), "bisection", "ridders", "brent-dekker", "chandrupatla" or "secant" (single-variable, find-root ONLY — all five bracket a sign change and need not have straddling endpoints, since they scan the bracket for one; bisection halves the bracket, ridders takes a faster exponential-fit step, brent-dekker interpolates inverse-quadratically with a bisection fallback — the usual library default for a bracketed root — chandrupatla admits that same interpolation under a sharper test, which keeps it at bisection's speed on a repeated root where brent-dekker slows to about a third of it, and secant simply chases the chord through the last two points: the leanest of the five on a simple root, but the slowest on a repeated one), "newton-raphson" or "halley" (single-variable, find-root ONLY as well, but the root engines that bracket NOTHING: they step along the expression's own derivatives — newton along the tangent, x - f(x)/f'(x), halley along a tangent hyperbola that carries the curvature too and so converges cubically for the same work — starting from the best point of a coarse scan of the bracket, with the derivatives taken numerically and every step fenced back into [lower, upper]. Fastest of all on a smooth root, and the only engines that reach a root which merely TOUCHES zero without crossing, where the sign-change engines report "no sign change"), or one of the three MULTIVARIATE engines — "nelder-mead" (a bounds-clamped downhill simplex), "powell" (derivative-free too: each iteration sweeps a full bounded line minimisation along every one of n directions, and re-aims that direction set at the sweep's own net displacement, so it follows a diagonal valley the simplex can only feel out — usually the fewest evaluations of the three on a smooth objective) or "bfgs" (quasi-Newton gradient descent, find-minimum / find-maximum only). The single-variable engines solve only the SINGLE form; the variables form requires a multivariate one. (golden, brent, bisect, ridder, brent-root, newton, simplex, powells and a few other spellings are accepted too — note bare brent names the PARABOLIC MINIMISER, while Brent's root method is brent-dekker.)

mode and min_fixed_point_precision behave as in calculate — the search runs in that numeric type and the found value is reported in it — with ONE solver-only rule: in fixed-point mode (the default) a search at scale 0 would floor the variable to whole numbers and miss any non-integer solution, so when min_fixed_point_precision is omitted it DEFAULTS to 9 (sub-unit resolution) rather than 0. Pass an explicit value for more/fewer decimals, or 0 to search integers only; the other modes (floating-point / rational) resolve sub-unit values natively and take no floor. See calculate and help for the shared grammar and modes; if a found value or objective looks off, analyze shows the per-node parse tree of the expression (with the unknowns substituted) to reveal where it rounded or overflowed.

The search is bounded by a hard 2-second time limit. If it has not converged by then it stops and reports the best value reached so far (a find-root that has not got close enough to zero in that time is reported as no-solution, naming the limit).

Returns a dict: solutions is a list of {variable, solution, solution_hex_dump}, one per unknown in input order — each solution rendered and marked "(approximate)" (the search locates it to a tolerance, never exactly), with its bit-backed hex. For the SINGLE form the scalar variable / solution / solution_hex_dump are also set (the one unknown); for the MULTIPLE form those scalars are null and solutions carries every value. value is the EXPRESSION evaluated at that solution, annotated with its own precision verdict (near zero for find-root, the extremum otherwise), and value_hex_dump its hex. mode is the resolved numeric type; exact and precision describe value exactly as in calculate. objective echoes the resolved objective, algorithm names the engine used, and iterations is how many search steps it took. On any failure — a bad mode/precision, a malformed expression, an invalid request (no/both input forms, empty bracket, unknown not in the expression, golden-section asked for multiple unknowns, unknown objective or algorithm), or no solution in the bracket — every data field is null and error carries the message (a no-solution error reports the closest |expr| it reached); on success error is null.

curve_fitA

Fit curve(s) to paired (x, y) data and report each fitted equation with its error.

Reach for curve_fit when you have observations and want an EQUATION that describes them; use calculate to evaluate an equation you already have, or solver to find the input that drives a known equation to a target. For example, x=[1, 1.5, 2], y=[2, 5.8, 8.9] comes back with the line 6.9*x - 4.78… ranked first by its residual error, so you can paste that equation straight into calculate to predict new points.

curve_fit takes the observations as two equal-length lists — x and y — and estimates, for every curve form it knows, the parameter values that best match the data in the least-squares sense, reporting each form's fitted equation and the residual error (the sum of squared residuals). The curve library holds the straight line a*x + b, the quadratic a*x**2 + b*x + c, the cubic a*x**3 + b*x**2 + c*x + d, the power law a*x**b, the exponential a*exp(b*x), the logarithm a + b*ln(x), the square root a*sqrt(x) + b, the reciprocal a/x + b, the sinusoid a*sin(b*x + c) + d, the gaussian a*exp(-(x-b)**2/(2*c**2)), the saturation x/(a*x + b) (Michaelis-Menten) and the hyperbolic 1/(a*x + b) (44.2.1-44.2.12, the complete library). Every form but the sinusoid is fitted in CLOSED FORM — the polynomials and the affine logarithm/square-root/reciprocal forms by the exact normal equations, the power and exponential laws by a log-linearisation (ln y = ln a + b·ln x for the power, ln y = ln a + b·x for the exponential), the gaussian by Caruana's method (logs to the quadratic ln y = p2·x**2 + p1·x + p0, fitted by the normal equations, then the peak/centre/width are recovered from its coefficients), and the saturation and hyperbolic by a reciprocal-line transform (the double-reciprocal Lineweaver-Burk line 1/y = a + b·(1/x) for the saturation, the line 1/y = a·x + b for the hyperbolic). The sinusoid alone has no closed-form solution, so it is fitted by an ITERATIVE frequency search: it is non-linear only in the frequency b, so for each candidate b the best amplitude/phase/offset is a linear sub-fit, and a coarse scan over a data-derived frequency range (then a golden-section refinement) picks the b minimising the residual. A form that cannot fit the data is dropped, not fatal: the power and logarithm need x > 0, the square root x >= 0, the reciprocal x != 0, the exponential, power and gaussian y > 0 (and the gaussian needs bell-shaped, downward-parabola data), the saturation x != 0 and y != 0, the hyperbolic y != 0; the power, exponential, logarithm, square-root, sinusoid and gaussian forms also drop in rational mode, which cannot represent their irrational logs/roots/sines/exp (the saturation and hyperbolic, being pure reciprocals, stay exact in rational); and a polynomial needs enough distinct x (the sinusoid four). The forms that fit are ranked by their residual error — least error first — and only the best three are returned (fewer when fewer forms fit).

mode and min_fixed_point_precision behave as in calculate — the whole fit runs in that numeric type, so the parameters and error are exact in rational, rounded at the scale in fixed-point, and native doubles in floating-point. As in the solver, a fixed-point fit at scale 0 would round every fitted parameter to a whole number, so when min_fixed_point_precision is omitted it DEFAULTS to 9 (sub-unit resolution); pass an explicit value for more/fewer decimals. Complex mode is not supported (the fit is real-valued).

Returns a dict: fits is the ranked list of fitted forms (best first, at most three), each {form, equation, parameters, fit_error, fit_error_hex_dump, exact, precision} — form names the curve, equation is the model with its parameters substituted (over the variable x, so it can be pasted into calculate), parameters is a list of {name, value, value_hex_dump} for each fitted coefficient (each value annotated with its own precision verdict), and fit_error is the residual error annotated the same way with its hex dump; that fit's own exact and precision describe its error. mode is the resolved numeric type, and the top-level exact / precision describe the BEST fit's error (the headline number), as in solver. On any failure — a bad mode/precision, mismatched or too-short data, or data that cannot support the form (every x equal, so a line has no slope) — fits/mode/exact/precision are null and error carries the message; on success error is null.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription
abacus-reference-indexIndex of the available reference sections; read abacus://reference/{section} for one section's full text.

TDQS

A4.2/5.0

Scored across 6 tools

Disambiguation5/5

Each tool has a clearly distinct action: calculate evaluates, analyze returns the parse tree, solver finds roots/extrema, curve_fit fits curves, help returns reference text, and info reports server state. The descriptions explicitly cross-reference one another ('for just the final value use calculate; to find variable values use solver'), making boundaries unambiguous even though they share an expression language.

Naming Consistency3/5

Names mix conventions: verbs (calculate, analyze), nouns (info, solver, help), and snake_case (curve_fit). All are lowercase and readable, but there is no single predictable verb_noun pattern, so an agent cannot infer a naming rule.

Tool Count5/5

Six tools is well-scoped for a numeric evaluation server, with each tool earning its place: value evaluation, AST debugging, solving, curve fitting, and two support tools (help, info). No redundant or filler tools.

Completeness4/5

The surface covers the full lifecycle of its domain: evaluate (calculate), inspect/debug (analyze), solve (solver), fit (curve_fit), plus discovery (help) and status (info). Minor gaps exist—no explicit unit conversion or matrix/statistics operations—but core numeric workflows are fully covered.

Maintenance

ActivityMaintained
ResponsivenessNo issues