# Edt MCP Tool Conventions

> Cross-tool consistency contract for EDT-MCP tools — parameter naming, error shape, output format, pagination, not-found semantics, schema/README alignment. Use when editing any class under tools/impl, adding a parameter, changing a tool's output, or reviewing a tool for consistency.

- Skill: `ditrixnew/edt-mcp-tool-conventions` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ditrixnew/edt-mcp-tool-conventions`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ditrixnew/edt-mcp-tool-conventions/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: ditrixnew (https://skillmd.com/u/ditrixnew)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ditrixnew/edt-mcp-tool-conventions

---


# EDT-MCP — tool contract (consistency)

The same concept must behave the same way across every tool. This skill is the canon to hold to.

## Parameter naming

| Concept | Canonical | Anti-example |
|---|---|---|
| Project | `projectName` | `project` |
| Module | `modulePath` (from `src/`; the resolver also accepts an absolute path) | `module`, or a decomposed `objectName+moduleType+formName+commandName` |
| Fully-qualified name | `fqn` | — |
| Object name | `objectName` | — |
| Synonym / language | `synonym` / `language` | — |
| Limit / offset | `limit` / `offset` | `maxResults`, `maxDepth` |

Parameter names are **lowerCamelCase** — `ToolContractConsistencyTest` fails snake_case. When renaming a parameter, accept the old name as a documented alias for one release, then deprecate.

## Read parameters typed

Read arguments via `JsonUtils.extractStringArgument/extractIntArgument/extractBooleanArgument/extractObjectArray`; check required ones via `JsonUtils.requireArguments(params, "a", "b")` (returns a ready error-JSON or null). Don't parse a raw `Map<String,String>` by hand. (There is no typed `ToolParams` class — `JsonUtils` is the canon.)

**Every parameter read in `execute()` must be declared in `getInputSchema()`** (otherwise it is invisible to schema-driven clients) and vice versa.

## Error contract

- Errors only via `ToolResult.error(...)` (machine-readable: success=false + message/code). Never return a bare `"Error: …"` / `"Project not found"` string, never let an exception escape the tool.
- "project not found" — one text/shape through the shared `ProjectContext` resolver.

## Output format

- Similar tools use the same format (don't make one JSON and another markdown without a reason). The plugin default is markdown (see `IMcpTool.ResponseType`).
- JSON fields use one style (don't mix camelCase/snake_case in similar responses).
- not-found — one semantics: an empty list for "list", an error for "get a specific one". Don't mix null/empty/error.

### Response format policy (Markdown vs JSON)

`structuredContent`/JSON is for the client to consume (UI rendering, verbatim round-trip of identifiers, a future `outputSchema`), NOT for the agent to read. **MARKDOWN is the default** (token-efficient, readable).

A tool returns JSON only when its result carries:

- **(a)** round-trip IDs another tool consumes;
- **(b)** machine-structured positions (e.g. error line/column);
- **(c)** a declared `outputSchema`;
- **(d)** UI-rendered data.

Action/confirmation/status results with none of these return MARKDOWN. `write_module_source` is the reference MARKDOWN action tool; `AbstractMetadataWriteTool` subclasses stay JSON because they return the created object's round-trip FQN.

Tool families that stay JSON, and why:

- metadata-writes (`create_metadata`, `modify_metadata`, `delete_metadata`) → addressed by **FQN** *(a)*;
- debug / profiling tools → launch / application / breakpoint **IDs** + live session state *(a)*;
- `validate_query` → error **line/col** *(b)*;
- `list_configurations` → config **identities** *(a)*;
- `clean_project`, `update_database` → destructive status whose JSON shape is consumed by e2e *(d-like)*.

Markdown action tools (`revalidate_objects`, `export_configuration_to_xml`, `import_configuration_from_xml`, `write_module_source`) emit status + paths/counts only — no round-trip data. Build the body with `FrontMatter` + a markdown summary; any table goes through `MarkdownUtils` (escapes every cell); errors still via `ToolResult.error(...)`.

## Pagination

Use the shared `Pagination` (`utils/Pagination.java`: one `limit`/`offset`, one truncation-notice format). Don't invent a per-tool scheme.

## Result / markdown assembly

Through `ToolResult`/`JsonUtils` and the shared markdown-table builder, not a hand-rolled StringBuilder.

## Schema ↔ code ↔ README

- `BuiltInToolRegistrar` is the source of truth for the tool set; the README catalog + the e2e golden (`tests/e2e/tools_list.golden.json`) must match it. A new tool changes the golden → regenerate (`EDT_MCP_UPDATE_GOLDEN=1`).
- Every tool: a non-empty meaningful description and a description for every parameter.

## Class placement

`tools/impl/` holds `IMcpTool` classes only. Utilities/abstract bases go in `utils/`/`tools/base/`. Project/model resolution goes through the shared helpers (skill `edt-mcp-architecture`). ru/en correctness — skill `edt-mcp-bilingual`.

