psychometrics-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@psychometrics-mcpRun a classical item analysis on the exam response file"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
psychometrics-mcp
Classical test theory, differential item functioning (DIF), local item dependence and standard setting, exposed both as a Python library and as an MCP server. Built to replace the ad hoc scripts and spreadsheets used in certification exam review, where the caller is as often an agent as a person.
The library is the product; the MCP server is one interface onto it.
Install
uv sync --frozen # the environment the tests were verified in
pip install -e '.[server,test]' # or resolve fresh: server + tests
pip install -e . # library only: numpy and scipyPython 3.10+. The core depends on numpy and scipy only, so it installs in a
locked-down analysis environment; the mcp dependency is an extra. uv.lock
is committed and pins all 46 packages with hashes; the version specifiers in
pyproject.toml stay floors, so a consumer installing from an index gets a
normal resolution rather than this machine's.
Related MCP server: content-validity-index-mcp
Use as a library
Every example below runs against a file in fixtures/, which is committed, and
the values shown are the ones actually returned -- only the very small p-value
exponents are abbreviated here for width.
from psychometrics import load_response_matrix, compute_classical_stats
matrix, diag = load_response_matrix("fixtures/flawed_form.csv")
stats = compute_classical_stats(matrix, diagnostics=diag)load_response_matrix returns a (ResponseMatrix, Diagnostics) pair, and
every analysis function takes and returns that Diagnostics so caveats
travel with the numbers instead of being reconstructed later.
matrix.summary() describes a dataset without echoing a cell of it:
{'n_examinees': 400, 'n_items': 12,
'missing_policy': 'omit_incorrect_notreached_missing',
'n_omitted_responses': 0, 'n_not_reached_responses': 0,
'pct_cells_blank': 0.0, 'mean_total_score': 6.1375,
'max_not_reached_rate_any_item': 0.0}flawed_form.csv has a mis-keyed first item. Its p-value is unremarkable, so
only the corrected point-biserial exposes it -- which is why that is the value
flags are computed from:
>>> stats["items"][0]
{'item_id': 'item_01', 'n_valid': 400, 'n_missing': 0,
'p_value': 0.4825, 'p_value_ci95': [0.433931, 0.531402],
'point_biserial': -0.438899, # item-rest, corrected
'point_biserial_uncorrected': -0.229897, # item-total, inflated by construction
'biserial': -0.550271, 'discrimination_index_d': -0.546296,
'flags': [{'code': 'negative_discrimination',
'severity': 'investigate',
'reason': 'Higher-scoring examinees did WORSE on this item than '
'lower-scoring ones. At ordinary difficulty this is the '
'canonical signature of a mis-keyed item or of two '
'defensible answers, not of a merely weak item.',
'observed': -0.438899, 'threshold': 0.0,
'next_step': 'Verify the answer key before interpreting any other '
'statistic for this item. Do not recommend deletion '
'until the key has been checked -- a mis-key is a '
'repair, not a retirement.'}]}A flag is an object, not a string: it carries the threshold it crossed and a recommended next step, so a caller reporting it does not have to invent one.
DIF
from psychometrics import load_response_matrix
from psychometrics.dif import flag_dif
matrix, diag = load_response_matrix("fixtures/dif_form.csv", group_column="group")
result = flag_dif(matrix, diagnostics=diag)
comparison = result["comparisons"][0] # focal vs reference, 600 eachitem_02 carries planted uniform DIF of +0.8 on the b scale:
>>> comparison["items"][1]["mantel_haenszel"]
{'n_strata_total': 19, 'n_strata_informative': 18,
'n_reference': 600.0, 'n_focal': 600.0,
'odds_ratio': 2.085446, 'mh_d_dif': -1.72721, 'se_mh_d_dif': 0.328571,
'chi_square': 27.424746, 'p_value': 1.633e-07, 'estimable': True,
'ets': {'category': 'C',
'interpretation': 'large DIF; content review required before operational use',
'direction': 'favours reference group (harder for focal)',
'significant': True, 'significantly_beyond_1.0': True},
'p_value_bh_adjusted': 3.267e-06}The Benjamini-Hochberg adjusted p-value is reported beside the raw one without being asked for, because screening a form is one test per item. The summary block says what the counts do and do not mean:
>>> comparison["summary"]["ets_category_counts"]
{'A': 18, 'B': 1, 'C': 1, 'not_estimable': 0}
>>> comparison["summary"]["items_flagged_c"]
['item_02']The focal group in this file also has a lower mean proficiency, so raw p-values differ on every item. That is impact, not DIF, and no clean item is flagged for it.
Local item dependence
Classical statistics look at one item at a time, so a pair of items measuring the
same thing twice is invisible to them. dependent_pairs_form.csv is the case:
every item passes every per-item check, alpha reads a healthy 0.837, and six of
its 190 pairs are entangled.
from psychometrics import load_response_matrix, compute_local_dependence
matrix, diag = load_response_matrix("fixtures/dependent_pairs_form.csv")
result = compute_local_dependence(matrix, diagnostics=diag)>>> result["flagged_pairs"][0]
{'item_a': 'item_02', 'item_b': 'item_03',
'q3': 0.500011, # Yen's Q3: correlation of the two items' residuals
'q3_star': 0.552522, # Q3 minus this form's mean Q3 -- what is compared
'sd_above_form_mean': 6.431, 'n_examinees_both_items': 600,
'flags': [{'code': 'positive_local_dependence',
'severity': 'investigate',
'reason': 'These two items share variance that the rest of the '
'form does not explain: ...',
'observed': 0.552522, 'threshold': 0.2,
'next_step': 'Read the two items side by side and decide whether '
'one gives away the other, or whether they are the '
'same question twice. ...'}]}The whole Q3 distribution comes back beside the flags, because a flagged pair cannot be read without it -- the same Q3 of 0.25 is unremarkable on a form averaging 0.10 and glaring on one averaging −0.07:
>>> result["distribution"]
{'n_pairs': 190, 'n_estimable': 190, 'n_not_estimable': 0,
'q3_mean': -0.052511, 'q3_sd': 0.085916,
'q3_min': -0.411887, 'q3_max': 0.500011,
'q3_mean_expected_bias': -0.052632}Q3 is biased downward by about −1/(k−1) purely by construction, and that
observed mean sits on the theoretical value to four decimals. Comparing against
the form's own mean rather than a fixed 0.20 is what makes one threshold
transport across form lengths; threshold_reference="absolute" is available for
reconciling with other software, and on a 12-item form it invents five pairs on a
file with no dependence planted in it at all.
>>> result["summary"]["items_in_flagged_pairs_count"]
{'item_02': 1, 'item_03': 1, 'item_05': 1, 'item_06': 1, 'item_09': 1,
'item_10': 1, 'item_16': 3, 'item_17': 1, 'item_18': 1, 'item_19': 1}
>>> result["summary"]["hub_items"]
['item_16']item_16 cues three other items, and it is reported separately from the pair
list because a hub is one item to fix rather than three pairs to triage.
There are deliberately no p-values here. Residualising on a criterion estimated from the same items means Q3's null is not a raw correlation's, and formal inference needs a parametric bootstrap over an IRT calibration this package does not perform. This is an effect-size screen that routes a pair to human content review; it is not an enemy-item check, and which of the two items is the redundant one is not in the number.
Standard setting
Panel ratings are aggregate expert judgements rather than candidate records, so these may be passed inline:
import numpy as np
from psychometrics import run_standard_setting
ratings = np.loadtxt("fixtures/panel_ratings.csv", delimiter=",") # 8 x 15
result = run_standard_setting(ratings.tolist(), method="angoff", sem=1.4)>>> result["cut_score"]
{'raw': 9.506025, 'percent': 63.3735, 'median_based': 9.36905,
'sd_across_panelists': 0.297662, 'standard_error': 0.105239,
'ci95': [9.299759, 9.712291]}
>>> result["agreement"]["icc_single_rater"], result["agreement"]["icc_panel_mean"]
(0.808053, 0.971163)
>>> sorted(result["adjustments"])
['minus_1_sem', 'plus_1_sem', 'rounded_down', 'rounded_nearest',
'rounded_up', 'unrounded']Every rounding and SEM option is returned together, each with its implied pass
rate when score_distribution is supplied, because picking whichever one
produces the pass rate you wanted is the failure this is meant to make visible.
The diagnostics also name the optional arguments that were not supplied and what each omission cost -- no reality check, no chance-level check, no implied pass rate -- so a payload never reads the same whether a check passed or never ran.
Use as an MCP server
psychometrics-mcp # stdio, local onlyRegister it with any MCP client, e.g.:
{
"mcpServers": {
"psychometrics": { "command": "psychometrics-mcp" }
}
}Five tools:
Tool | Purpose |
| Shape, blank breakdown and subgroup sizes. Call first on an unfamiliar file. |
| Per-item difficulty and discrimination, per-form reliability and SEM. |
| Yen's Q3 across every item pair, flagging pairs that share variance the form does not explain. |
| Mantel-Haenszel, standardised p-difference and logistic DIF across every item. |
| Angoff, yes/no Angoff and Bookmark aggregation with the cut's standard error. |
What is deliberately unusual
Refusal is a result. Below a hard sample-size floor these functions raise
InsufficientData rather than return an imprecise number; below an advisory
floor they return the number together with a warning. An agent will format
whatever number it is given into a report, and a person can fail a candidate
on that report, so a number too weak to support the decision is worse than no
number at all. Both InsufficientData and Diagnostics carry to_dict(),
and the server turns a refusal into a structured payload rather than an
exception string.
Every threshold is labelled. Each constant in guardrails.py is marked
either FIELD CONVENTION (a published rule of thumb, cited) or MODELLING ASSUMPTION (ours, and overridable through the overrides dict). A threshold
with no provenance comment is a defect.
Corrected discrimination drives decisions. Both item-rest (corrected) and item-total (uncorrected) point-biserials are reported; flags use the corrected one, because the uncorrected value is spuriously inflated on short forms.
Multiple comparisons are corrected by default. Screening a 65-item form for DIF is 65 simultaneous tests, so Benjamini-Hochberg adjusted p-values are reported alongside the raw ones and the diagnostics say which to use.
Missing data is three things. Omitted (blank mid-form, scored incorrect) is distinguished from not-reached (a trailing run of blanks, excluded, because scoring it wrong confounds difficulty with speededness), and raw-versus-scored input is never guessed at silently.
Variance is ddof=1 everywhere. That is what makes Cronbach's alpha and
KR-20 algebraically identical for dichotomous items, an identity the test
suite asserts.
A DIF flag is not a finding of bias. It means an item behaves differently for two groups of equal overall proficiency, which routes that item to human content review. Flag counts are not counts of biased items.
A dependent pair is not an enemy item. Q3 supplies the statistical half of that question only; whether two items overlap in content, and which of the two is the redundant one, is not in the number. Pairs are also flagged for reasons that have nothing to do with content, such as a shared stimulus or adjacent positions on a speeded form.
Three refusal channels, not one, because the remedies differ.
insufficient_data means collect more data; invalid_input means the same data,
corrected, would work, and the message says what to correct; no_estimable_pairs
means neither would help and the form itself is the problem. A bad value refused
through the wrong channel sends an agent off to recruit more panelists over what
is a units error.
Privacy posture
No network calls, no telemetry. Tools that touch candidate data take file
paths, never inline matrices, so a multi-thousand-examinee response file never
enters a conversation transcript; ResponseMatrix.summary() exists so a
dataset can be described without echoing examinee-level data. Panel ratings
are the exception and may be passed inline, being aggregate expert judgements
rather than candidate records.
Logging
Logging goes to stderr, and that is a transport requirement rather than a
preference: the server speaks JSON-RPC over stdio, so stdout is the protocol
stream and a log line written there is a parse error at the client, not noise.
The only handler here that emits anything names sys.stderr explicitly.
Imported as a library the package attaches nothing that emits -- only a
NullHandler -- so no output appears unless the host application configures
logging itself. Run as a server, main() adds that one stderr handler, at
WARNING by default, which emits refusals and diagnostic warnings:
PSYCHOMETRICS_LOG_LEVEL=INFO psychometrics-mcp # adds the notes
PSYCHOMETRICS_LOG_LEVEL=CRITICAL psychometrics-mcp # effectively silentAn unrecognised value falls back to WARNING and says so rather than refusing to start. Log records carry paths, shapes, counts, thresholds and diagnostic prose -- the same short list that is safe to put in a transcript, and for the same reason. A log file outlives the process and may sit under weaker controls than the response file did, so no cell of examinee data is ever written to one.
Tests
pytest # 236 testsfixtures/ holds the synthetic corpus they and the examples above run against,
regenerated by fixtures/generate.py from a fixed seed. fixtures/README.md
records what is planted in each file and what it should produce, so a change in
those numbers is a change in behaviour that needs explaining.
Not in this version
No IRT calibration, so bookmark requires ordered_item_thetas from an
external calibration and will not substitute classical difficulty for them.
License
MIT
Available Tools
5 toolscompute_classical_statsA
Classical item analysis: difficulty, discrimination and form reliability.
Returns per item: p-value with a Wilson 95% interval, corrected point-biserial (item versus rest score -- the discrimination index to use), uncorrected point-biserial, biserial, the 27% upper-lower D index, and review flags. Returns per form: mean and SD of total score, Cronbach's alpha (identical to KR-20 here by construction), the standard error of measurement, and alpha-if-item-deleted.
REFUSES below 20 examinees and warns below 100.
Read the flags as review triggers, not verdicts. In particular, a negative corrected point-biserial on an item of ordinary difficulty usually means the item is mis-keyed rather than that it is a bad item -- the diagnostics say so explicitly when it occurs, and that warning must be relayed.
| Name | Required | Description | Default |
|---|---|---|---|
| layout | No | 'wide' -- one row per examinee, one column per item. 'long' -- one row per examinee-item response, which requires `id_column` and expects `item_id` and `response` columns. | wide |
| scored | No | True if the cells already hold 0/1 item scores. False if they hold the option each examinee selected (e.g. 'A', 'C'), which requires `answer_key`. This is never inferred: a raw-option file read as scored produces a complete, plausible-looking analysis of nothing. Confirm it with `describe_dataset` before trusting any statistic. | |
| id_column | No | Name of the examinee identifier column. Naming it excludes it from the item columns; leaving it unnamed makes it an extra 'item'. Identifiers are used only for exclusion and are never returned. | |
| answer_key | No | Correct option for each item, in the same order as the item columns. Required when `scored=False`; ignored otherwise. Its length must equal the item count -- a key that is off by one mis-scores every item after the offset, and the result looks like a form-wide item-quality problem. One entry per item, holding the option itself -- not its position, and not the whole key as a single string. | |
| linear_form | No | True if every examinee saw the items in column order, which is what makes a trailing run of blanks readable as not-reached. Set False for adaptive or randomised-order delivery: position is then unknown, so all blanks are treated as omits and a warning says so. | |
| group_column | No | Name of a subgroup column (demographic or otherwise). Naming it excludes it from the item columns and reports subgroup sizes. Optional here; required by `flag_dif`. | |
| response_file | Yes | Path to the response file on disk, read locally. Candidate responses are passed as a path and never inline, so that a multi-thousand-examinee file does not enter the conversation transcript. | |
| missing_policy | No | How blank responses are scored. 'omit_incorrect_notreached_missing' (default) scores a blank mid-form as incorrect -- the examinee had the opportunity -- and excludes a trailing run of blanks as not-reached, because scoring those wrong confounds item difficulty with speededness. 'all_incorrect' scores every blank wrong, which makes end-of-form items look harder and less discriminating in proportion to how speeded the form is; use it only if the scoring rules genuinely penalise unreached items. 'all_missing' excludes every blank, which conditions p-values on having attempted the item and turns items examinees skip when unsure into easy-looking ones. The two non-default policies each raise a warning. | omit_incorrect_notreached_missing |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden, and it does so thoroughly: it discloses that the tool REFUSES below 20 examinees and warns below 100, it explains that flags are review triggers not verdicts, and it warns that a negative corrected punit-biserial on ordinary difficulty usually means mis-keying and that this warning must be relayed. It also warns against interpreting raw options as scored data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than average but every sentence earns its place: a one-line purpose, exact output lists, hard operational thresholds, and a critical interpretation warning. It is front-loaded with the summary before the details and contains no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with eight parameters, an output schema, and sibling tools, this description is remarkably complete: it states inputs implicitly, outputs explicitly, sample-size constraints, warning behavior, and how to interpret edge cases like negative discrimination. The existence of an output schema covers return structure, and nothing needed for safe invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all eight parameters in detail. The description adds useful interpretive context (e.g., corrected punit-biserial is the discrimination index to use; Cronbach's alpha equals KR-20 here), but it does not add new parameter-level meaning that the schema already lacks.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific domain and output class: 'Classical item analysis: difficulty, discrimination and form reliability.' It then enumerates exact per-item and per-form statistics, so an agent knows precisely what the tool computes and can distinguish it from siblings like flag_dif or compute_local_dependence.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description strongly implies when to use the tool – when classical item-quality statistics are needed – and even gives interpretive guidance about flags. However, it never names siblings or states explicit exclusion conditions ('use flag_dif for subgroup DIF', 'use describe_dataset first'), so the routing burden falls on the agent's inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
compute_local_dependenceA
Screen every item pair for local dependence (Yen's Q3) and flag entangled pairs.
Answers a question compute_classical_stats structurally cannot: whether two
items are measuring the same thing twice. Q3 is the correlation between two
items' residuals once the common factor is removed, so it isolates covariance
that the trait does not explain -- one item cueing the answer to another,
a shared stimulus, or near-duplicate content. Every item on a form can pass
every per-item check and the form can post a healthy alpha while a fifth of
its items are entangled; that is the case for running this at all.
Returns the flagged pairs, each with its Q3, its centred Q3*, how many SDs from
the form mean it sits, and a flag object, plus the whole Q3 distribution the
pairs were judged against -- a flag list without its reference distribution
cannot be read. summary.hub_items names items that appear in three or more
flagged pairs, reported separately because a hub is one item to fix rather than
several pairs to triage.
REFUSES below 5 items or 100 examinees, and refuses with no_estimable_pairs
when every pair involves a zero-variance item -- a distinct refusal because
more examinees would not fix it.
Two things this deliberately is not. It is not an enemy-item check: overlapping
content is a content-similarity question, and Q3 supplies only the statistical
half, so a flagged pair is a pair to READ, not a pair to cut. And it is not
inference -- there are no p-values here, for the reason given in method. Do
not present a flagged pair as a significant finding.
| Name | Required | Description | Default |
|---|---|---|---|
| layout | No | 'wide' -- one row per examinee, one column per item. 'long' -- one row per examinee-item response, which requires `id_column` and expects `item_id` and `response` columns. | wide |
| scored | No | True if the cells already hold 0/1 item scores. False if they hold the option each examinee selected (e.g. 'A', 'C'), which requires `answer_key`. This is never inferred: a raw-option file read as scored produces a complete, plausible-looking analysis of nothing. Confirm it with `describe_dataset` before trusting any statistic. | |
| id_column | No | Name of the examinee identifier column. Naming it excludes it from the item columns; leaving it unnamed makes it an extra 'item'. Identifiers are used only for exclusion and are never returned. | |
| threshold | No | How far above the form's mean Q3 a pair must sit to be flagged. The 0.20 default is settled screening practice rather than a derived critical value, so it is adjustable: lowering it toward 0.10 surfaces pairs a content reviewer will mostly dismiss, and raising it toward 0.30 will miss the moderate cueing that item review is best at catching. It is a correlation, so 20 means 20 -- pass 0.2, not 20. | |
| answer_key | No | Correct option for each item, in the same order as the item columns. Required when `scored=False`; ignored otherwise. Its length must equal the item count -- a key that is off by one mis-scores every item after the offset, and the result looks like a form-wide item-quality problem. One entry per item, holding the option itself -- not its position, and not the whole key as a single string. | |
| linear_form | No | True if every examinee saw the items in column order, which is what makes a trailing run of blanks readable as not-reached. Set False for adaptive or randomised-order delivery: position is then unknown, so all blanks are treated as omits and a warning says so. | |
| group_column | No | Name of a subgroup column (demographic or otherwise). Naming it excludes it from the item columns and reports subgroup sizes. Optional here; required by `flag_dif`. | |
| response_file | Yes | Path to the response file on disk, read locally. Candidate responses are passed as a path and never inline, so that a multi-thousand-examinee file does not enter the conversation transcript. | |
| missing_policy | No | How blank responses are scored. 'omit_incorrect_notreached_missing' (default) scores a blank mid-form as incorrect -- the examinee had the opportunity -- and excludes a trailing run of blanks as not-reached, because scoring those wrong confounds item difficulty with speededness. 'all_incorrect' scores every blank wrong, which makes end-of-form items look harder and less discriminating in proportion to how speeded the form is; use it only if the scoring rules genuinely penalise unreached items. 'all_missing' excludes every blank, which conditions p-values on having attempted the item and turns items examinees skip when unsure into easy-looking ones. The two non-default policies each raise a warning. | omit_incorrect_notreached_missing |
| max_pairs_returned | No | Cap on how many flagged pairs come back, largest |Q3*| first. Guards the transcript rather than the analysis: the count of pairs flagged is always reported in full, and `summary.truncated` says when the list is partial. Raising it on a form that flags hundreds of pairs will fill the reply with a form-level problem restated pair by pair. | |
| threshold_reference | No | What `threshold` is measured against. 'centred' (default) compares each pair to this form's own mean Q3, which is what makes one cutoff comparable across forms of different lengths -- Q3 is biased downward by about -1/(n_items - 1) purely by construction. 'absolute' compares the raw Q3 and exists only for reconciling with other software: on a short form it is lopsided, flagging negative pairs for no reason and being nearly impossible to trip positively, so a 12-item form with no dependence at all can return five invented pairs. | centred |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and delivers: refusal conditions ("REFUSES below 5 items or 100 examinees"), a distinct refusal code for an unfixable case ("no_estimable_pairs"), and the return contract including why the reference distribution must accompany the flag list. It also discloses non-behaviors ("there are no p-values here") that prevent misreading the output.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action in the first sentence, then organized into distinct paragraphs on rationale, return semantics, refusals, and exclusions. It is long (three dense paragraphs), but every paragraph carries different information and the stylistic asides ("a pair to READ, not a pair to cut") compactly encode interpretation rules. A minor deduction for overall length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 11-parameter, zero-annotation tool with an output schema, the description covers everything structural fields cannot express: refusal thresholds, the purpose of returning the full Q3 distribution, hub-item aggregation, and the non-inferential reading of flags. Nothing an agent needs to select or invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline of 3 applies. The description adds only marginal parameter context, connecting the threshold to the flagging rule ("how many SDs from the form mean") and echoing threshold_reference='centred' via "centred Q3*", but the per-parameter schema descriptions are the real carrier of meaning (e.g., "20 means 20 -- pass 0.2, not 20").
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb, resource, and method: "Screen every item pair for local dependence (Yen's Q3) and flag entangled pairs." It distinguishes itself from siblings by name ("Answers a question compute_classical_stats structurally cannot") and by exclusion ("not an enemy-item check... not inference"). An agent can tell exactly what this tool computes and how it differs from its siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the alternative and the decision condition separating them: compute_classical_stats for per-item checks, this tool for the pairwise question. It gives the triggering scenario ("a fifth of its items are entangled; that is the case for running this at all") and states what not to do with the output ("Do not present a flagged pair as a significant finding"). No inference is left to the agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
describe_datasetA
Load a response file and describe its shape, blanks and subgroups.
Call this FIRST on any unfamiliar file. It reports counts, the missing-data breakdown (omitted versus not-reached), and subgroup sizes, and it reproduces none of the responses themselves -- so it is safe to summarise in a conversation. Use it to confirm the layout, scoring and column names are right before running an analysis, and to check subgroup sizes before asking for DIF.
Relay the diagnostics warnings verbatim; they describe how blanks were
interpreted, which changes every downstream statistic.
| Name | Required | Description | Default |
|---|---|---|---|
| layout | No | 'wide' -- one row per examinee, one column per item. 'long' -- one row per examinee-item response, which requires `id_column` and expects `item_id` and `response` columns. | wide |
| scored | No | True if the cells already hold 0/1 item scores. False if they hold the option each examinee selected (e.g. 'A', 'C'), which requires `answer_key`. This is never inferred: a raw-option file read as scored produces a complete, plausible-looking analysis of nothing. Confirm it with `describe_dataset` before trusting any statistic. | |
| id_column | No | Name of the examinee identifier column. Naming it excludes it from the item columns; leaving it unnamed makes it an extra 'item'. Identifiers are used only for exclusion and are never returned. | |
| answer_key | No | Correct option for each item, in the same order as the item columns. Required when `scored=False`; ignored otherwise. Its length must equal the item count -- a key that is off by one mis-scores every item after the offset, and the result looks like a form-wide item-quality problem. One entry per item, holding the option itself -- not its position, and not the whole key as a single string. | |
| linear_form | No | True if every examinee saw the items in column order, which is what makes a trailing run of blanks readable as not-reached. Set False for adaptive or randomised-order delivery: position is then unknown, so all blanks are treated as omits and a warning says so. | |
| group_column | No | Name of a subgroup column (demographic or otherwise). Naming it excludes it from the item columns and reports subgroup sizes. Optional here; required by `flag_dif`. | |
| response_file | Yes | Path to the response file on disk, read locally. Candidate responses are passed as a path and never inline, so that a multi-thousand-examinee file does not enter the conversation transcript. | |
| missing_policy | No | How blank responses are scored. 'omit_incorrect_notreached_missing' (default) scores a blank mid-form as incorrect -- the examinee had the opportunity -- and excludes a trailing run of blanks as not-reached, because scoring those wrong confounds item difficulty with speededness. 'all_incorrect' scores every blank wrong, which makes end-of-form items look harder and less discriminating in proportion to how speeded the form is; use it only if the scoring rules genuinely penalise unreached items. 'all_missing' excludes every blank, which conditions p-values on having attempted the item and turns items examinees skip when unsure into easy-looking ones. The two non-default policies each raise a warning. | omit_incorrect_notreached_missing |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it delivers: it discloses that the tool reports counts and missing-data breakdown, reproduces none of the responses themselves, is safe to summarise in conversation, and that diagnostics warnings must be relayed verbatim because they affect every downstream statistic. This goes well beyond what the schema encodes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short paragraphs front-load the purpose, then the usage guidance, then the diagnostics warning. Every sentence carries operational value, and the instruction to relay warnings verbatim earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the 100% schema coverage, an output schema, and a clear description of when and how to use the tool, nothing essential is missing. The description covers the tool's role in the workflow, its safe output characteristics, and the critical warning semantics.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3 and the description need not restate parameter details. It does add workflow context around layout, scoring, and column names, but it does not add parameter-level semantics beyond the rich schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Load a response file and describe its shape, blanks and subgroups') and defines a clear diagnostic scope: counts, missing-data breakdown, and subgroup sizes. It also distinguishes itself from the analytical siblings by positioning itself as the first call before running analysis or asking for DIF.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit usage timing: 'Call this FIRST on any unfamiliar file', 'confirm ... before running an analysis', and 'check subgroup sizes before asking for DIF'. It does not explicitly name sibling tools as alternatives or state when not to use it, but the before/analysis framing provides clear context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
flag_difA
Screen every item for differential item functioning between subgroups.
Runs three methods per item, deliberately not redundant:
Mantel-Haenszel with ETS A/B/C classification -- the operational standard, but it detects UNIFORM DIF ONLY.
Standardised p-difference (Dorans & Kulick) -- same finding on the proportion-correct metric, which panels read without translation.
Logistic regression -- adds the group-by-ability interaction, so it catches NON-UNIFORM DIF that Mantel-Haenszel is structurally blind to. Items flagged by logistic but category A under MH are called out separately; an MH-only screen would have missed them.
Matching is on observed number-correct score. purify=True (default) removes
flagged items from the matching criterion and retests; note that difR in R
defaults to no purification, so numbers differ from a naive difR comparison.
REFUSES when the smaller group is under 50, and warns under 200 because the ETS A/B/C bands were calibrated above that.
CRITICAL FOR REPORTING: a DIF flag is not a finding of bias. It means the item behaves differently for two groups of equal overall proficiency, which routes the item to human content review to decide whether there is a construct-irrelevant reason. Never describe flagged items as biased items, and never report the flag count as a count of biased items. Use the Benjamini-Hochberg adjusted p-values, not the raw ones -- screening a 65-item form is 65 simultaneous tests.
| Name | Required | Description | Default |
|---|---|---|---|
| layout | No | 'wide' -- one row per examinee, one column per item. 'long' -- one row per examinee-item response, which requires `id_column` and expects `item_id` and `response` columns. | wide |
| purify | No | Re-run the analysis with flagged items removed from the matching score, to a fixed point. On by default because a DIF item left in the criterion contaminates the ability match used to test every other item. difR in R defaults to no purification, so leave this on for defensibility and turn it off only to reproduce a difR run. | |
| scored | No | True if the cells already hold 0/1 item scores. False if they hold the option each examinee selected (e.g. 'A', 'C'), which requires `answer_key`. This is never inferred: a raw-option file read as scored produces a complete, plausible-looking analysis of nothing. Confirm it with `describe_dataset` before trusting any statistic. | |
| id_column | No | Name of the examinee identifier column. Naming it excludes it from the item columns; leaving it unnamed makes it an extra 'item'. Identifiers are used only for exclusion and are never returned. | |
| answer_key | No | Correct option for each item, in the same order as the item columns. Required when `scored=False`; ignored otherwise. Its length must equal the item count -- a key that is off by one mis-scores every item after the offset, and the result looks like a form-wide item-quality problem. One entry per item, holding the option itself -- not its position, and not the whole key as a single string. | |
| focal_group | No | Value in `group_column` whose examinees are tested against the reference group. Omit to run every non-reference level in turn; note that the false-discovery correction is applied within each comparison, so multiplicity compounds across them. A value found IN the column, not the column name. | |
| linear_form | No | True if every examinee saw the items in column order, which is what makes a trailing run of blanks readable as not-reached. Set False for adaptive or randomised-order delivery: position is then unknown, so all blanks are treated as omits and a warning says so. | |
| group_column | Yes | Name of the column holding the subgroup membership to test. DIF is a between-group comparison, so there is no default: check the column name and the subgroup sizes with `describe_dataset` first, since the smaller group's size determines whether this analysis can run at all. | |
| response_file | Yes | Path to the response file on disk, read locally. Candidate responses are passed as a path and never inline, so that a multi-thousand-examinee file does not enter the conversation transcript. | |
| missing_policy | No | How blank responses are scored. 'omit_incorrect_notreached_missing' (default) scores a blank mid-form as incorrect -- the examinee had the opportunity -- and excludes a trailing run of blanks as not-reached, because scoring those wrong confounds item difficulty with speededness. 'all_incorrect' scores every blank wrong, which makes end-of-form items look harder and less discriminating in proportion to how speeded the form is; use it only if the scoring rules genuinely penalise unreached items. 'all_missing' excludes every blank, which conditions p-values on having attempted the item and turns items examinees skip when unsure into easy-looking ones. The two non-default policies each raise a warning. | omit_incorrect_notreached_missing |
| reference_group | No | Value in `group_column` to use as the reference (comparison) group. Defaults to the largest group other than the focal one. Which group is reference flips the sign of every DIF statistic, so set it explicitly whenever the direction of the finding will be reported. A value found IN the column, not the column name. | |
| pool_thin_strata | No | Merge adjacent score strata too thin to contribute to the Mantel-Haenszel table. Recovers information at the score extremes on small samples, at the cost of matching examinees less exactly on ability -- the exact match is the assumption the method rests on, so this is off by default. Affects Mantel-Haenszel and the standardised p-difference only; logistic DIF does not stratify. The choice is recorded in `method_notes.matching_criterion`. | |
| effect_size_bands | No | Effect-size thresholds for logistic DIF on the Nagelkerke delta-R-squared scale. 'jodoin_gierl' (default, 0.035/0.070) is recalibrated to align with the ETS A/B/C categories. 'zumbo_thomas' (0.13/0.26) is far more permissive and will flag materially fewer items. Report which one was used -- the choice changes the flag count, which is why it is a parameter rather than a buried constant. | jodoin_gierl |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral disclosure burden and does so thoroughly. It explains that purification changes results relative to difR, that the tool refuses under n=50 and warns under n=200, and that DIF flags must not be described as bias. It also instructs use of Benjamini-Hochberg adjusted p-values because screening many items is a multiple-testing situation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every section earns its place: method rationale, purification behavior, sample-size thresholds, and critical reporting constraints. The bulleted method list and clear warning blocks make the structure easy to scan, and there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the description is remarkably complete: it explains all three methods, their blind spots, matching and purification, operational constraints, and interpretation obligations. An output schema exists, so the description does not need to specify return fields, and the parameter schema covers the remaining operational details.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the parameter semantics baseline is 3. The main description adds useful method-level context, such as purification and matching on number-correct score, but it does not need to explain individual parameters because the schema already documents them in depth.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Screen every item for differential item functioning between subgroups.' This clearly identifies the tool's function and its subgroup-comparison scope, which is enough to distinguish it from siblings like compute_classical_stats and compute_local_dependence 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives strong context about when and how the analysis runs, including purification, sample-size refusals, and reporting cautions. However, it never explicitly says when to choose this tool over sibling tools or mentions alternatives, so the usage guidance is implied rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_standard_settingA
Aggregate standard-setting panel judgements into a cut score with its defensibility record.
Methods: "angoff" (ratings are probabilities that a minimally competent candidate answers each item correctly), "yes_no_angoff" (0/1 judgements), "bookmark" (page placements on an Ordered Item Booklet).
Panel ratings may be passed inline as a panelists-by-items array, or read
from a CSV via ratings_file (one row per panelist, one column per item, no
header). Inline is acceptable here because panel ratings are aggregate expert
judgements, not candidate records.
THE CUT SCORE IS NOT THE OUTPUT. The output is the cut plus its standard error, the panel's ICC agreement, per-panelist severity and noise flags, items with no panel consensus, and round-over-round spread. Report the standard error whenever you report the cut: differences smaller than about two standard errors are inside sampling noise, and panels routinely argue over gaps well inside that.
SUPPLY empirical_p_values WHENEVER YOU HAVE THEM. Content experts
systematically overestimate how easy items are, because they know the answer.
The reality check catches a wholesale miscalibration that would otherwise
produce a cut implying an unusable pass rate, and identifies the specific
items to re-discuss.
bookmark requires ordered_item_thetas from an IRT calibration; this
version of the package does not fit IRT models, and classical item difficulty
is not a substitute for them.
REFUSES below 3 panelists (the cut's standard error is not interpretable) and warns below 8. Also refuses ratings outside [0, 1] rather than guessing that they are percentages.
| Name | Required | Description | Default |
|---|---|---|---|
| sem | No | Standard error of measurement of the form, in raw score points, as returned by `compute_classical_stats`. Enables the conventional cut minus/plus 1 SEM adjustments to be reported. Do not confuse it with the panel standard error also in the result: SEM is uncertainty about measuring a candidate, the panel SE is uncertainty about what a different panel would have recommended. Non-negative: it is used as a distance either side of the cut, so a negative value would swap the two adjustments while leaving both labelled correctly. | |
| method | No | 'angoff' -- ratings are probabilities in [0, 1] that a minimally competent candidate answers each item correctly. 'yes_no_angoff' -- ratings are 0/1 judgements of whether such a candidate answers each item correctly. 'bookmark' -- ratings are page placements in an Ordered Item Booklet, and `ordered_item_thetas` is then required. | angoff |
| item_ids | No | Item identifiers in rating order, so that per-item output names the items a panel has to re-discuss instead of giving positions. Supply these whenever the result will go back to a panel. Must be exactly one per item: a short list is refused, not padded, because the per-item review list is built by walking this and a short one drops the surplus items from the output silently. | |
| ratings_file | No | Path to a CSV of panel ratings: one row per panelist, one column per item, no header row. An alternative to `panel_ratings`, for panels large enough that inlining them is unwieldy. | |
| rp_criterion | No | Bookmark method only: the response probability defining mastery of a page, conventionally 0.67. It is an arbitrary convention that moves the cut, so record the value used with the resulting standard. Note this value is RECORDED, not applied -- the RP criterion is already built into `ordered_item_thetas` -- so it must match the RP those thetas were computed at, or the cut carries a standard it was not set against. Strictly between 0 and 1. | |
| panel_ratings | No | Panel judgements as a panelists-by-items array: one inner list per panelist, one entry per item, in a consistent item order. Inline is acceptable here, unlike candidate responses, because these are aggregate expert judgements rather than individual records. Takes precedence over `ratings_file` if both are given. Angoff ratings are probabilities in [0, 1]; values outside that are refused rather than rescaled, because guessing percentages wrong moves the cut by a factor of 100. | |
| bookmark_pages | No | Bookmark method only, and required for it: each panelist's page placement in the Ordered Item Booklet, one number per panelist. Note this is a FLAT list, unlike `panel_ratings` -- a panelist places one bookmark for the whole form, not one judgement per item. Angoff ratings cannot be used here; if that is what you have, the method is 'angoff' or 'yes_no_angoff'. Pages are 1-based and must fall within the booklet length. | |
| options_per_item | No | Number of answer options for each item, in rating order. Used to flag items whose mean rating falls below the chance level (1/options): a panel saying a minimally competent candidate does worse than guessing is a misunderstanding of the rating task, not a low probability. At least 2 per item. For a multiple-response item count the selectable options, not the correct ones. | |
| empirical_p_values | No | Observed proportion correct for each item, in rating order, from a real administration. SUPPLY THESE WHENEVER THEY EXIST: they enable the reality check that catches the standard failure of Angoff panels, which is systematically overestimating how easy items are because the panel knows the answers. Without them a wholesale miscalibration passes through silently. Proportions, not percentages, and one per item. | |
| score_distribution | No | Observed number-correct scores from a real cohort, used to report the pass rate implied by the cut and by each rounding and SEM adjustment. That set of pass rates is shown together on purpose: the adjustments are policy choices, and choosing among them by which pass rate it produces is the thing this output exists to make visible. Scores only -- no identifiers, and not a response matrix. Raw number-correct, so each value is between 0 and the form length; percentages are refused rather than rescaled. | |
| ordered_item_thetas | No | Bookmark method only, and required for it: the theta at which each Ordered Item Booklet page reaches the response-probability criterion, from an IRT calibration. This package does not fit IRT models, and classical item difficulty is not a substitute -- it is sample-dependent and on a different scale. One per booklet page, in booklet order, and non-decreasing if the booklet is really ordered. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
There are no annotations, so the description carries the full behavioral burden, and it is exceptionally transparent. It states the cut score is not the output, warns that differences within about two standard errors are sampling noise, and discloses refusal rules (fewer than 3 panelists, ratings outside [0, 1]) and the warning below 8 panelists. It also discloses that RP criterion is recorded but not applied and that the package does not fit IRT models.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well organized and front-loaded, with the most important operational warning—'THE CUT SCORE IS NOT THE OUTPUT'—placed early. Some points are repeated from the schema descriptions, such as inline acceptability and the rationale for empirical_p_values, but for an 11-parameter tool with conditional requirements the length is largely justified.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex tool with 11 parameters, an output schema, and method-dependent requirements, the description covers the important combinations: rating input options, bookmark prerequisites, the non-substitutability of classical item difficulty, refusal conditions, and the defensibility-record framing. The presence of an output schema means return fields do not need to be enumerated, and the description goes beyond that requirement anyway.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 an extra point by adding cross-parameter meaning that individual schema descriptions cannot: the relationship between panel_ratings and ratings_file, the conditional requirement of ordered_item_thetas for bookmark, and panel-size rules tied to interpretability of the standard error. It does not need to re-explain every parameter because the schema already does that well.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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: 'Aggregate standard-setting panel judgements into a cut score with its defensibility record.' The method list (angoff, yes_no_angoff, bookmark) further clarifies the tool's domain and clearly distinguishes it from the sibling psychometrics tools such as compute_classical_stats, flag_dif, and compute_local_dependence.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives strong contextual guidance: when ratings can be inline versus CSV, when bookmark requires IRT-based ordered_item_thetas, when empirical p-values should be supplied, and when the tool refuses to run. It does not explicitly name sibling tools as alternatives or state a 'do not use this for X' rule, so it stops just short of the strictest 5 standard.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
TDQS
Each tool maps to a distinct stage or question: dataset familiarization, classical item/form statistics, DIF screening, pairwise local dependence, and standard-setting aggregation. There is no meaningful overlap even between the two compute_* tools, since one is per-item/per-form and the other is item-pair based.
All tool names use a consistent verb_noun or verb_adjective_noun snake_case pattern: describe_dataset, compute_classical_stats, flag_dif, compute_local_dependence, run_standard_setting. The naming clearly signals both the action and the target with no mixing of conventions.
Five tools is a well-scoped size for a psychometrics analysis server. Each tool covers a substantial area of the workflow without bloat or unnecessary fragmentation.
The core classical psychometric workflow is well covered: data description, item analysis, DIF, local dependence, and standard setting. The one notable gap is IRT calibration, which the bookmark method explicitly depends on but the server does not provide; this is a real but workable limitation.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Marketing-measurement MCP: dashboards, cross-channel queries, integrations, and knowledge files.
Governed data discovery, exact queries, decisions, simulations, and runtime utilities over MCP.
Production-grade cryptography toolkit with 31 MCP tools for classical, PQC, and KMS workflows.
AI Visibility and Content Intelligence tools for Claude and MCP-compatible agents.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables analysis of clinical trial protocols using MCP tools for document listing, entity extraction, adverse event clustering, and summarization.41MIT
- AlicenseAqualityBmaintenanceEnables AI clients to browse instruments, items, dimensions, expert assignments, and calculate Content Validity Index (CVI) via a standard MCP interface.11MIT
- AlicenseNot gradedqualityDmaintenanceProvides comprehensive access to CDISC clinical data standards (SDTM, ADaM, CDASH, CT) through 15 MCP tools, resources, and prompts.1MIT
- FlicenseNot gradedqualityCmaintenanceEnables comprehensive statistical analysis including descriptive statistics, hypothesis testing, regression, and more via a FastMCP-based API.3
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/JulieElkinsAWS/psychometrics-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server