Incident Management Event Log
Click on "Deploy 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., "@Incident Management Event Logwhat's the mean processing duration for High priority tickets?"
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.
Incident Management Event Log
An MCP server over a realistic IT incident-management event log (242,900 events, 31,588 tickets). It exposes deterministic aggregation tools, a BM25 keyword search tool, a report prompt, and a report tool that borrows the client's model through MCP sampling — plus the small Python SDK underneath and an optional browser console.
The design rule throughout: the server does the arithmetic, the model does the writing. Every number a model sees comes from a tested, deterministic aggregation, never from the model re-deriving it.
Setup
git clone <this repo> && cd <repo>
python3 -m venv .venv
.venv/bin/python -m pip install -e .The dataset ships in the repo as data/incident_event_log.csv.zip (2.2 MB) and is read straight from the zip, so there is no download step.
Validate the deployment:
.venv/bin/python scripts/validate_dataset.pyRelated MCP server: TicketAI
Tests
.venv/bin/python -m pip install pytest
.venv/bin/python -m pytest # whole suite, ~10s
.venv/bin/python -m pytest -m "not slow" # skip the checks that load the full CSVFour layers, in tests/:
File | Covers |
| Schema contract, blank-activity rows, day-first dates, dtypes, derived reassignment/reopen counters, event ordering |
| Aggregation semantics — final-event attribution, filter/group agreement, counter rule, windows |
| BM25 tokenizing, indexing and ranking |
| Handshake, tool schemas, dispatch and error messages, the report prompt, the sampling round-trip, over the real protocol |
Most tests run against the six-case fixture in tests/conftest.py, where every expected number can be checked by hand; it deliberately includes an escalated case, a downgraded case, a case with a missing final issue_type, and a case outside the March window. Tests marked slow assert totals against the real 242,900-row file.
Two things worth knowing before extending the suite:
MCP errors must be asserted through the protocol.
server.call_tool()raises; it is the dispatch layer that converts aToolErrorinto theis_errorresult a client sees.tests/test_mcp_server.pytherefore drives a realClientSessionover in-memory streams rather than calling the tool functions directly — no subprocess, same exchange.The suite is checked against regressions it should catch. Reverting final-event attribution and moving filtering back to the event layer both fail multiple tests, and swapping a
ToolErrorfor aValueErrorfails others.
Use the SDK
from incident_management import load_event_log, to_pm4py
events = load_event_log()
pm4py_log = to_pm4py(events)Deterministic query contract
Tool 1 is exposed as processing_duration(DurationQuery). Its input is fixed:
group_by: one or more ofpriority,issue_type,report_channelpriority: optional values from theprioritycolumn (High,Medium,Low)issue_type: optional values from theissue_typecolumn (Bug,Incident,Feature Request,Maintenance,Performance Issue,Service Request,Technical Issue)report_channel: optional values from thereport_channelcolumn (App,Email,Phone,Website)timestamp_from/timestamp_to: optional inclusive ISO-8601 bounds on case start time
A case is attributed by its final event: if a case's priority, issue_type or report_channel changes mid-life, it is reported under the value it ended with (in this dataset these three are in fact constant per case, but the rule is still enforced generically). Filters and group_by share this one definition, so filtering to High selects exactly the cases that group_by=("priority",) reports as high priority (6,466 of them). Filters apply to case-level attributes after aggregation, so they never truncate a case's duration.
The fixed output is DurationResult(metric, duration_unit, timestamp_from, timestamp_to, rows). Each row has group, count, mean_duration_seconds, and median_duration_seconds. Duration is the difference between the first and last event of each case; a time window selects cases by their first event and does not truncate their duration.
Example:
from incident_management import DurationQuery, processing_duration
result = processing_duration(
DurationQuery(group_by=("priority", "issue_type"), priority=("High",))
)Counter distribution contract
Tool 2 is exposed as counter_distribution(DistributionQuery). Its input is fixed:
metric: eitherreassignment_countorreopen_countpriority/issue_type/report_channel: optional case-level value filters, same as Tool 1 (final-event attribution)timestamp_from/timestamp_to: optional inclusive ISO-8601 bounds on case start time
The fixed output is DistributionResult(metric, case_count, timestamp_from, timestamp_to, rows). Each row has value (the counter value), count (cases with that value), and share (count / case_count, rounded to 6 decimals). Rows are sorted ascending by value, and counter values with no cases are omitted rather than emitted as zero rows.
Counting rule. This event log carries no raw reassignment/reopen counter columns at all — only an 18-value Event activity per row (Ticket created, ... escalates to level 2/3 support, ... rejected by level 1 support, Ticket reopened by customer, Ticket closed, etc.). dataset.py derives both counters at load time from that activity sequence: reassignment_count increments on any handoff to a different level/agent after the initial assignment (escalations, follow-up assignments, rejections back down); reopen_count increments on Ticket reopened by customer. Both are running counters carried on every event, so each case contributes exactly one observation: its maximum counter value, which is also its final value (verified non-decreasing within every case).
from incident_management import DistributionQuery, counter_distribution
result = counter_distribution(DistributionQuery(metric="reassignment_count"))Across all 31,588 cases, 34.3% are never reassigned (by the derived rule above) and 96.2% are never reopened; reassignment reaches a maximum of 5 and reopen a maximum of 1.
Keyword search
keyword_search ranks cases against a free-text query using Okapi BM25 (src/incident_management/search.py, no external search dependency). The sentence is split into words and scored against one document per case, built by concatenating short_description, issue_type, priority, report_channel, reporter and resolver (final-event attribution again). short_description only takes eight distinct values (Application crash, Server outage, Unable to login, Password reset needed, Printer not working, UI glitch, Data loss issue, New feature request) rather than free-form prose per ticket, but they're real English words, so a query like "password reset" or "server down" returns meaningfully relevant cases instead of matching anonymized codes.
Unlike the two query tools, there's no top-level SDK function for it — call it as the MCP tool keyword_search(sentence, top_k=25), which returns the matched terms, total case count, and up to top_k hits with their BM25 score and the field values they matched on. The underlying pieces (tokenize, BM25Index, build_index, search_incidents) live in src/incident_management/search.py if you want to call them directly.
MCP server
The query contracts and search are exposed over MCP (stdio transport) in src/incident_management/mcp_server.py, together with a report prompt and a report tool that samples the client's model:
.venv/bin/python -m incident_management.mcp_serverRegistered tools:
Tool | Purpose |
| Exact values for |
| Tool 1 — duration aggregates by dimension |
| Tool 2 — |
| BM25 keyword search over incidents |
| A finished report, written by the client's own model via sampling |
Registered prompts:
Prompt | Purpose |
| A report brief with the aggregated data already attached |
Register it with an MCP client (e.g. claude_desktop_config.json). Replace /path/to/repo with
your checkout. If Claude Desktop and this project are both on the same Linux (or macOS) machine:
{
"mcpServers": {
"incident-management": {
"command": "/path/to/repo/.venv/bin/python",
"args": ["-m", "incident_management.mcp_server"],
"cwd": "/path/to/repo"
}
}
}If Claude Desktop runs on Windows but the project lives in WSL, the command
must be a Windows-executable — wsl.exe — that shells into the distro, since Windows can't run a
Linux binary directly:
{
"mcpServers": {
"incident-management": {
"command": "wsl.exe",
"args": [
"-d", "Ubuntu",
"--cd", "/path/to/repo",
"--",
"/path/to/repo/.venv/bin/python",
"-m", "incident_management.mcp_server"
]
}
}
}The config file itself is not always at the classic %APPDATA%\Claude\claude_desktop_config.json
either — some builds (e.g. Microsoft Store installs) virtualize it under
%LOCALAPPDATA%\Packages\<package name>\LocalCache\Roaming\Claude\claude_desktop_config.json.
Fully quit and relaunch Claude Desktop after editing either file for the change to take effect.
Two constraints matter when editing this server:
stdout belongs to the JSON-RPC stream. Diagnostics go to stderr (
loggingis configured that way); a strayprint()corrupts the protocol.Raise
ToolErrorfor bad input, notValueError. The SDK preserves aToolErrormessage so the model can correct itself, but replaces any other exception's text with a generic one to avoid leaking internals.
The 242,900-row dataset loads lazily on the first tool call or prompt render, so the initialize handshake returns immediately. Filter values are validated against the dataset — an unknown value such as "high" returns an error naming the valid forms rather than a silently empty result.
Report prompt
incident_report turns the query contracts into a written report. It is a prompt, not a tool: the client shows it as a slash command or menu entry the user picks, and the server answers with the messages that seed the conversation.
Argument | Default | Meaning |
|
| Breakdown dimension — |
| none | Inclusive ISO-8601 bounds on case start time |
|
|
|
All four are optional, so the bare prompt reports priority over the whole log.
The server does the arithmetic, the model does the writing. The handler runs processing_duration and both counter_distribution metrics itself over one shared scope, then returns two messages: the report brief, and the results as an embedded application/json resource. The model formats numbers it was handed rather than calling the tools and re-deriving them, and durations arrive in seconds and hours so no conversion is left to it. The brief fixes the four sections (headline, duration table, churn, what stands out), forbids numbers that are not in the payload, and carries the final-event attribution rule so the report cannot misstate it.
prompts/get incident_report {"dimension": "report_channel", "timestamp_from": "2023-06-01"}
-> user message: the brief (sections, formatting rules, audience)
-> user message: data://incident-management/report/report_channel (application/json)Prompt errors need MCPError, not ToolError. The prompt layer replaces any ordinary exception's text with Error rendering prompt <name>, so a bad dimension or an unparseable date is raised as MCPError(INVALID_PARAMS, ...) to keep the message that names the valid alternatives.
Sampling: the server borrows the client's model
write_incident_report returns the report already written. The server has no API key and no model of its own — it asks the client for a completion over MCP sampling (sampling/createMessage), which is the mirror image of the prompt: the prompt hands work to the caller's model and stops, sampling calls the caller's model and keeps going.
It takes the same four arguments as the prompt and returns:
Field | Meaning |
| The markdown the client's model wrote |
| Which model the client actually used |
|
|
| Dimension, window, case count and audience the report was written from |
Two resolvers, one aggregation. The work is wired as a resolver DAG rather than done in the tool body:
_report_data ──> the aggregated payload (validates arguments, hits pandas once)
│
└──> _sampled_report ──> Sample(...) ──> sampling/createMessage ──> the client's model
│
┌──────────────┴──────────────┐
v v
write_incident_report(payload=..., draft=...)payload and draft are Annotated[..., Resolve(...)] parameters, so the framework fills them before the body runs and keeps them out of the input schema — the model calling the tool sees only the four real arguments. Resolvers are memoised per call, so _report_data runs once even though both the sampling request and the tool result depend on it.
Three consequences worth knowing:
The brief is shared with the prompt. Both paths build it with
_report_brief, so the two report surfaces cannot drift apart; a test asserts the sampled request and the prompt render the same text.Validation happens in the resolver, before the model is asked. A bad
dimensionfails the call without spending a token.Sampling is a client capability, not a given. A client that declares no
samplingcapability gets a protocol error naming the missing capability, which is why the tool description tells the model to fall back to the two query tools and write the report itself.
Dataset schema
The loader reads data/incident_event_log.csv.zip (one ;-separated CSV inside, 242,900 events after one malformed row is dropped, 31,588 cases, 17 activities) and normalizes its headers to snake_case, which already gives the three process-mining names the loader relies on:
column | meaning |
| the ticket number, e.g. |
| one of 18 activities: |
| per-event, |
|
|
|
|
|
|
| one of 8 fixed phrases (see Keyword search) — constant per case |
| who opened the ticket / who is handling this event ( |
| which of the 13 process variants the case follows |
| 1–5, on the case's closing event |
| derived, not raw columns — see Counter distribution contract |
A single row with a blank Event is dropped at load time (one such row exists in the source file; it cannot be placed in a case's process flow). Sorting is by case_id then timestamp, stable, so any tied timestamps keep the source file's order.
The zip is the unmodified Kaggle download. The dataset is Process Mining Event Log - Incident Management by Alberto P on Kaggle, redistributed here under its MIT license: https://www.kaggle.com/datasets/albertopmd/process-mining-event-log-incident-management. It was chosen over the better-known UCI incident log because the UCI data is fully anonymized (Category 42, Symptom 491, ...) and has no free text to back a keyword search tool.
Browser GUI
Two independent modules give the MCP server a browser front end:
src/incident_management/gui.py— a small JSON API. It is a plain MCP client: it spawnsincident-management-mcpas a subprocess and speaks the same protocolscripts/call_tool.pydoes, then exposes the query/report tools and theincident_reportprompt as HTTP routes (/api/...).web/— a standalone React app (Vite) that only ever talks to that HTTP API. It never imports Python, so it could be pointed at any server implementing the same routes.
Development (two processes, hot reload on the frontend):
.venv/bin/incident-management-gui # API on :8000
cd web && npm install && npm run dev # UI on :5173, proxies /api to :8000Production (one process):
cd web && npm install && npm run build # writes web/dist
cd .. && .venv/bin/incident-management-gui # serves web/dist and the API together on :8000The console covers list_dimension_values, case_processing_duration, counter_value_distribution, and the incident_report prompt (brief + attached data, no model needed). It also exposes write_incident_report, but since the browser has no model of its own to satisfy MCP sampling, that call is expected to report itself unavailable — the UI surfaces the server's own error message and points back at the brief. keyword_search is not yet wired into the GUI (no /api/search route or search panel); use scripts/call_tool.py keyword_search '{"sentence": "..."}', Claude Desktop, or another MCP client to exercise it today.
License
Code is MIT-licensed (see LICENSE). The bundled dataset is MIT-licensed by its original author; see Dataset schema for the source.
Available Tools
5 toolscase_processing_durationCase processing durationB
Aggregate case processing duration (first event to last event) grouped by priority, issue_type and/or report_channel. Returns count, mean and median seconds per group.
| Name | Required | Description | Default |
|---|---|---|---|
| group_by | Yes | ||
| priority | No | ||
| issue_type | No | ||
| timestamp_to | No | ||
| report_channel | No | ||
| timestamp_from | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden; it does clarify that results are aggregated (one row per group) and lists the returned metrics. It does not mention read-only nature, default time window, permission needs, or ordering/pagination of groups.
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?
Two sentences, front-loaded with the metric definition and followed by the output shape; nothing is wasted.
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?
The output schema covers return values, and the description correctly explains the metric. But with six parameters at 0% documentation, an agent still cannot tell how to format or scope the timestamp filters or what group_by accepts.
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 0%, so the description must compensate for 6 undocumented parameters. It only hints at three group_by dimensions (priority, issue_type, report_channel) and ignores timestamp_from, timestamp_to entirely, giving no format or filtering semantics.
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?
States a specific verb+resource ('Aggregate case processing duration') plus the exact metric definition (first event to last event) and the grouping dimensions. An agent can tell what it computes, though the siblings listed are unrelated operations so no differentiation is offered.
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?
There is no indication of when to use this versus alternatives, no prerequisites, and no note about whether filters are combinable or whether group_by is mandatory. Usage is only implied by the tool name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
counter_value_distributionCounter distributionA
Distribution of reassignment_count or reopen_count across cases. Each case contributes its final counter value; rows give value, case count and share. Both counters are derived from the activity sequence, not raw columns.
| Name | Required | Description | Default |
|---|---|---|---|
| metric | Yes | ||
| priority | No | ||
| issue_type | No | ||
| timestamp_to | No | ||
| report_channel | No | ||
| timestamp_from | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations to lean on, the description does real work: it discloses the aggregation rule (each case contributes only its final counter value, so results are not summed events) and warns that both counters are derived from the activity sequence rather than raw columns. It omits that the operation is read-only and says nothing about cost or limits, which is the remaining gap.
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 sentences, each carrying distinct information: what is distributed, how rows are composed, and the data-provenance caveat. The key constraint is front-loaded 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?
Purpose, aggregation semantics and output shape are adequately covered, and an output schema exists so return values need not be detailed. However, for a six-parameter tool with zero schema description coverage and no annotations, leaving five filter parameters entirely undescribed is a material gap.
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 0% across six parameters, so the description must compensate and mostly does not. It usefully enumerates the two legal values for the required 'metric' parameter, but priority, issue_type, report_channel, timestamp_from and timestamp_to are never mentioned, leaving their semantics and combination behavior to guesses from their names.
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?
States a specific analysis (value distribution) over specific named metrics (reassignment_count, reopen_count) scoped to cases. It is distinguishable from the analytics sibling case_processing_duration by the resource it operates on, though it never names that sibling explicitly.
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?
Usage is only implied: an agent can infer this is the tool for counter histograms rather than duration analysis, but there is no explicit when-to-use, no when-not, and no named alternative. Nothing misleads, but routing between this and case_processing_duration is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
keyword_searchKeyword search incidentsA
BM25 keyword search over incidents. Splits sentence into words and ranks cases by relevance across their descriptive fields (short_description, issue_type, priority, report_channel, reporter, resolver). Returns the top 25 matches by default, each with its score and the field values it matched on.
| Name | Required | Description | Default |
|---|---|---|---|
| top_k | No | ||
| sentence | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses BM25 ranking, sentence tokenization, the fields searched, the default top 25 limit, and that results include scores and matched field values. It does not cover edge cases like empty results or any permission/rate-limit behavior.
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 tight sentences, front-loaded with the core operation and then the ranking and return behavior. 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?
The tool is simple with only two parameters and an output schema, so the description need not explain return shape. It covers method, searched fields, and default behavior, but leaves `top_k` semantics incomplete.
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 0%, so the description must compensate. It explains that `sentence` is split into words and implies a default result count, but it never names `top_k` or explains its bounds or how it overrides the default.
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: BM25 keyword search over incidents. It also names the ranking fields and distinguishes this search operation from siblings such as write_incident_report and list_dimension_values.
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 search context is clear, but the description does not explicitly say when to use this tool versus alternatives, nor does it state exclusions. Usage is implied by 'keyword search over incidents' rather than deliberately routed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_dimension_valuesList dimension valuesA
List the exact values available for a filter dimension, with the number of cases carrying each. Call this before filtering: e.g. priority is 'High', not '1 - High'.
| Name | Required | Description | Default |
|---|---|---|---|
| dimension | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden. It discloses that results include exact values and counts, and implies a read-only listing operation, but does not state permissions, side effects, rate limits, or error behavior. The output schema likely covers return structure.
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?
Two tightly written sentences with no waste. The core action and the pre-filtering usage rule are both front-loaded.
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?
The tool is simple, has one required parameter, and has an output schema, which reduces what the description must cover. However, it leaves out the set of valid dimensions and any behavioral safety context, which an agent may need before invoking it correctly.
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 0%, so the description must explain the dimension parameter. It identifies it as a filter dimension and gives a 'priority' example, but does not enumerate valid dimensions or describe the accepted format beyond the example.
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?
States a specific verb and resource: list the exact values available for a filter dimension, including case counts. The purpose is clear, but it does not explicitly differentiate itself from sibling tools such as counter_value_distribution.
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?
Gives clear usage context: call this before filtering, plus a concrete example showing that the exact label must be used rather than a formatted numeric label. It does not name alternatives or when-not-to-use conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
write_incident_reportWrite the incident reportA
Return a finished markdown incident report for one dimension (priority, issue_type or report_channel) over an optional time window. The server aggregates the data and asks your model to write it up (MCP sampling), so this needs the client's sampling capability; without it, call case_processing_duration and counter_value_distribution and write the report yourself.
| Name | Required | Description | Default |
|---|---|---|---|
| audience | No | operations | |
| dimension | No | priority | |
| timestamp_to | No | ||
| timestamp_from | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
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 does disclose the key trait: the server aggregates data and asks the client model to write it via MCP sampling, requiring sampling capability. It also gives a fallback path. It does not discuss permissions, rate limits, or whether repeated calls produce identical reports, so it falls just short of a 5.
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?
Two tight sentences, front-loaded with the core action and followed by the critical operational caveat. Every sentence earns its place; 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?
The output schema exists, so return values need not be explained. The description covers the sampling dependency and fallback well, but it omits the audience parameter entirely and gives no guidance on timestamp formats, leaving a small but meaningful gap for a tool with defaulted, undocumented inputs.
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 0%, so the description must compensate. It explains the dimension parameter's allowed values and that the time window is optional, but it never mentions the audience parameter or timestamp format/syntax, leaving half the parameters semantically undocumented.
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: 'Return a finished markdown incident report for one dimension.' It names the supported dimensions (priority, issue_type, report_channel) and the optional time window, which clearly distinguishes it from data-querying siblings like case_processing_duration and counter_value_distribution.
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 explicitly says when to use this tool and what to do when the client lacks MCP sampling capability: 'without it, call case_processing_duration and counter_value_distribution and write the report yourself.' That names both the prerequisite and concrete alternatives, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
case_processing_duration - First observed
counter_value_distribution - First observed
keyword_search - First observed
list_dimension_values - First observed
write_incident_report
TDQS
Scored across 5 tools
Each tool has a fairly distinct role: search, list filter values, two different aggregations, and report generation. The only overlap is that write_incident_report internally composes the same kind of aggregations exposed by case_processing_duration and counter_value_distribution, which could make an agent wonder when to use the report tool versus the raw aggregators.
All names use snake_case, but the grammatical pattern is mixed: some are verb-led (write_incident_report, list_dimension_values), one is noun-led (keyword_search), and two are noun phrases without a verb (case_processing_duration, counter_value_distribution). It remains readable, but the set does not follow a predictable verb_noun convention throughout.
Five tools is well within the appropriate range for an incident analytics server. Each tool covers a distinct analysis capability, and none feels like filler.
The set covers search, filter-value discovery, aggregation, and report generation, but it lacks direct incident retrieval (e.g., get incident by ID) and explicit structured filtering by dimension for incident lists. The aggregation tools also do not advertise time-window filtering except through the report tool, leaving notable gaps for incident investigation workflows.
Maintenance
Related MCP Connectors
Make your knowledge agent-ready. One MCP endpoint, 5 connectors, 3 search modes.
MCP-Native LLM Orchestration Agent
Salesforce-grounded retrieval, diagnoses, and a vetted-Force marketplace for MCP clients.
- WauldoOAuthcom.wauldo
Stateless agentic tools over MCP: concept extraction, long-context, knowledge graph, planning.
Related MCP Servers
- FlicenseCqualityDmaintenanceEnables semantic search over knowledge-base articles and listing of sample support tickets using MCP tools.4-
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to analyze IT support tickets, categorize urgency, suggest responses, and retrieve statistics via MCP tools.-
- FlicenseNot gradedqualityDmaintenanceMCP server for an AI IT helpdesk that exposes 29 tools, 10 resources, and 6 prompts for ticket management, knowledge base search, employee lookup, reporting, and Slack notifications, with a multi-agent conversation pipeline.-
- FlicenseNot gradedqualityDmaintenanceEnables IT helpdesk automation by classifying support tickets, estimating priority, generating troubleshooting steps, and predicting resolution times through MCP tools.1-