# MCP Server

Tediware hosts a Model Context Protocol server at `POST /mcp`. Its tools cover the same ground as the REST API and the `tedi` CLI, with the same API key, the same rate limits, and the same service terms as every other endpoint: interchange inspection and reading and driving your own EDI traffic.

The server implements MCP revision `2026-07-28`, which is stateless and has no `initialize` handshake or session id. Every call is a single HTTP POST.

## Connecting

Configure your MCP client with the endpoint URL and your API key as a header:

```
URL:     https://tediware.com/mcp
Header:  Authorization: Key your-api-key-here
```

Every request also needs the transport's own headers, which conforming clients send for you:

```
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: transaction_list
```

The server checks that those headers match the request body and rejects the call with JSON-RPC error `-32020` if they do not. The headers are what the rate limiter meters on, so a request that disagrees with itself cannot be metered correctly and is not accepted.

Standard and read-only API keys work here. A sandbox key is rejected with `403`.

## Exercise caution when choosing a key

An agent connected via MCP has all the permissions that its API key allows, and it decides for itself which tools to call. With a standard key, that includes sending documents to your trading partners, receiving documents into your flows, and resending past transactions.

This is exceptionally useful when you are building and testing new configurations, but should be treated with caution when dealing with production flows that are sending and receiving live data. A purchase order an agent sends is an order your partner may ship against. Documents your partners send are input to the agent too, and text inside one can steer an agent that reads it.

A read-only key is shown only the tools that change nothing: `tools/list` leaves out `partner_send`, `partner_receive`, `transaction_resend`, `transaction_redeliver` and `suggestion_submit`, and calling one of them answers as an unknown tool would. Everything else on this page is available to it.

- Give an agent a read-only key unless its job includes sending. An agent that answers questions about traces, errors and partner setup needs nothing more.
- If an agent does need a standard key, have your MCP client ask before it runs a write tool. The [write tools](#write-tools) carry the annotations clients use for that.
- Create a separate key for each agent, named for it, so you can delete one without disturbing your other integrations.
- With the `tedi` CLI, keep a read-only key and a standard key in separate profiles and turn on the standard one only when a task needs it. [Switching between read-only and standard](#switching-between-read-only-and-standard) walks through the setup.

A read-only key still reads your transactions in full, including the business data in them. Whatever the key can read, the agent can pass to the model it runs on. Scope is fixed when a key is created; see [Authentication](/resources/api-docs/authentication) for the full list.

## Discovering the surface

`server/discover` reports the supported protocol versions and capabilities. `tools/list` returns the tools. Both are cacheable by the client (`ttlMs`) and never by a shared cache (`cacheScope: private`).

```bash
curl -X POST https://tediware.com/mcp \
  -H "Authorization: Key your-api-key-here" \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/list" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {
      "_meta": { "io.modelcontextprotocol/protocolVersion": "2026-07-28" }
    }
  }'
```

## The tools

Every tool maps onto an endpoint documented elsewhere in this reference, so the scoping, filters and error meanings are the ones described there.

```
X12 releases and inspection
  x12_releases           The release codes this server carries
  edi_inspect            Decode and validate a document

Your EDI traffic (structured)
  whoami                 Which organization this key belongs to
  transaction_list       List EDI transactions
  transaction_get        One transaction in full
  transaction_logs       The processing log for a trace
  transaction_resend     Re-deliver an outbound transaction
  transaction_redeliver  Re-deliver an inbound transaction
  trace_get              One processing run, assembled
  result_list            List processing results
  result_get             One result
  result_payload         The data one result's step produced
  feed_list              The forward feed of documents and errors
  artifact_get           One artifact's document

Your configuration (structured, read-only)
  partner_list           List partners and what each exchanges
  partner_get            One partner: connection, envelopes, webhooks, sets, flows
  connection_list        List connections
  connection_get         One connection and the partners on it
  envelope_list          List envelopes
  envelope_get           One envelope and the partners using it, with role
  webhook_list           List webhooks
  webhook_get            One webhook and the partners delivering to it, with role
  flow_list              List flows, filtered by partner, direction or status
  flow_get               One flow with its nodes and edges
  mapping_list           List mappings and the partners using each
  mapping_get            One mapping with its current version and source
  mapping_versions       The versions of one mapping
  implementation_list    List your own implementations
  implementation_get     One implementation and what points at it
  implementation_schema  The JSON schema a mapping targets
  implementation_guide   The rendered guide (text only)
  source_list            List sources
  source_get             One source with its sample

Submissions (standard keys only)
  partner_send           Submit an outbound document
  partner_receive        Submit an inbound EDI document
  suggestion_submit      Report a confirmed gap to the Tediware team
```

### Releases and inspection

`x12_releases` returns the release codes as structured data. `edi_inspect` returns its report as text and, as structured fields, the finding counts (`errors`, `notices`, `complete`) matching the headers the HTTP inspector sets, plus `findings`: each one's severity, code, title, details, and the line and segment it cites, in document order.

`implementation_guide` returns the rendered guide as one text block, Markdown by default, and no `structuredContent`. The structure as data is `implementation_schema`, the JSON schema a mapping targets.

### Data tools answer with structured content

The data tools return `structuredContent` in the same shape the REST endpoints return, including the `pagination` envelope with `hasMore` and `nextCursor`. It is your own data, so structured access is the point. The text block beside it is a one-line summary (the kind of record, its id, its status) rather than the same JSON again. Fields mean what the matching API page says: on a transaction, `partnerKey` is the partner recorded when the document was processed, and `deliveredAt` is when it left its delivery step (see [EDI Transactions](/resources/api-docs/edi-transactions)).

A result's payload is the data its step produced, kept on the result; `result_payload` reads it as `{format, contents}`, and `trace_get` shows each result's payload `format` and `bytes` so you can choose one before reading it. An artifact is a stored file a result points to in `detail.artifacts`; `artifact_get` downloads it. Most steps keep a payload, but only some store artifacts. The mapped document an implementation refused is a payload: read it from the failed result's `detail.incomingResultId`. `result_payload` takes `jsonPath` (a dot-path into `contents`) and `keysOnly` (the shape only), and refuses payloads over 256 KB with a pointer to `GET /platform/results/:id/payload`.

The configuration tools (the `partner_*`, `connection_*`, `envelope_*`, `webhook_*`, `flow_*`, `mapping_*`, `implementation_*` and `source_*` tools) are in preview, as are the [Partners](/resources/api-docs/partners) and [Configuration](/resources/api-docs/configuration) endpoints. Their output shapes may change without notice. `partner_send` and `partner_receive` are submissions, not configuration reads, and are not in preview.

`edi_inspect` and `partner_receive` send your document to the server. Scrub identifiers or personal data out of a document before an agent submits it: there is no local scrubbing step inside a hosted tool. `tedi edi obfuscate` is that step, and it runs entirely on the caller's machine.

## Arguments

Tool argument names are camelCase, matching the REST query parameters and the keys the tools return: `transactionSetIdentifier`, `ackStatus`, `transactionId`, `partnerKey`.

Arguments are validated against each tool's schema before the tool runs, and every schema declares `additionalProperties: false`, so an unknown or misspelled argument is a refusal rather than a filter that silently did nothing. Three rules follow from the same principle:

- An empty string for an identifying argument (`trace`, `partner`, `reference`, `transactionId`, `node`) is an error, not "no filter". A blank shell variable should not return your whole organization.
- `direction` and `status` declare their allowed values and refuse anything else with the list, as `ackStatus` and `level` do. `warnings` on `transaction_list` is a boolean: `true` for transactions with at least one warning.
- A `limit` outside 1 to 100 is an error rather than a silent clamp.

### Compact rows for scanning

A full page of `transaction_list` runs to tens of kilobytes, more than many agents can take in one tool result. For questions that scan or count ("errors by partner this month"), pass `compact: true` to `transaction_list`, `result_list` or `feed_list`. Each row is then a fixed set of fields:

```
transaction_list  id, createdAt, direction, usageIndicator, partnerKey, transactionSetIdentifier,
                  reference, status, acknowledgmentStatus, warningCount, deliveredAt,
                  traceGuid
result_list       id, traceGuid, createdAt, nodeName, status, partnerKey, errorMessage
feed_list         id, createdAt, direction, status, partnerKey, traceGuid, resultId, errorMessage
```

`errorMessage` is cut to 200 characters. Filters and pagination work as they do without `compact`. The REST endpoints take the same option as `compact=true` and return the same rows. To see everything about one row, get it by id.

## Write tools

`transaction_resend`, `transaction_redeliver`, `partner_send`, `partner_receive` and `suggestion_submit` are annotated `readOnlyHint: false` so an agent framework can require confirmation before running them. `transaction_resend` and `transaction_redeliver` also carry `destructiveHint: true`: an accepted resend puts a duplicate document on your trading partner's wire, and an accepted redelivery reaches your own endpoint again.

`transaction_resend`, `transaction_redeliver`, `partner_send` and `partner_receive` queue work rather than completing it inline. A successful call means processing started. Follow the returned `traceGuid` with `trace_get`, which carries a `processing` boolean so a polling agent has a defined end.

`suggestion_submit` completes inline. It is for a confirmed gap in Tediware, with evidence: `body`, `tried` and `expected` are required. It shares the limits of the [suggestions endpoint](/resources/api-docs/suggestions): five a minute per key across both surfaces, 20 per organization in 24 hours, and a repeat of the same title or body within 24 hours returns the first suggestion with `duplicate: true`. Sandbox keys cannot reach MCP, so a sandbox agent uses `POST /platform/suggestions`.

## Errors

Two kinds, and the difference is who can act on them.

**Tool errors** come back as an ordinary result with `isError: true` and a message in the text block: a record that does not exist, a document that will not parse, a filter value out of range. An agent can read these and try again.

**Protocol errors** come back as JSON-RPC errors with an HTTP status:

```
401  -32000  Missing or invalid API key
403  -32000  Organization disabled, sandbox key, or service terms not accepted
400  -32020  Headers disagree with the request body
400  -32022  Unsupported protocol version (the error lists the supported ones)
400  -32602  Unknown tool
404  -32601  Unknown method
400  -32700  The body is not valid JSON
500  -32603  An unhandled fault on the server, carrying your request id
```

The `403` cases carry a `data.reason` naming the specific condition: `organization_disabled`, `sandbox_key_not_supported`, `service_terms_required`.

## Connecting a stdio client

Most MCP hosts launch a server as a subprocess over stdio rather than calling an HTTP endpoint, and most of them still open with the older `initialize` handshake this revision removed. A host that sends that handshake straight to `/mcp` gets a `-32020` error naming the bridge below; it is not a credential problem. `tedi mcp serve`, from the [tedi CLI](/tools/cli), bridges both gaps: it answers the handshake locally, stamps this revision onto everything it forwards, and uses the key from `tedi auth login` so one never has to be written into an agent's configuration file.

```bash
claude mcp add tediware -- tedi mcp serve
```

The bridge holds no tool logic. It forwards every request, so the tool list and descriptions you see through it are the server's own.

### Switching between read-only and standard

The `tedi` CLI can hold more than one key. Each profile is a directory under `~/.tedi-profiles/` with its own key and settings, and every command takes `--profile <name>` to choose one. With two profiles, your agent works read-only by default, and you turn on standard access only when a task needs to send.

1. On the **API Keys** page, create two keys: one read-only and one standard. Name them for the agent, for example "Claude Code (read-only)" and "Claude Code (standard)".

2. Store each key in its own profile. Each command asks you to paste the key:

   ```bash
   tedi auth login --profile read-only
   tedi auth login --profile standard
   ```

3. Confirm each profile holds the scope you expect:

   ```bash
   tedi whoami --profile read-only
   tedi whoami --profile standard
   ```

4. Register each profile as its own MCP server:

   ```bash
   claude mcp add tediware -- tedi mcp serve --profile read-only
   claude mcp add tediware-send -- tedi mcp serve --profile standard
   ```

   A client configured by file takes the same arguments: `{"command": "tedi", "args": ["mcp", "serve", "--profile", "read-only"]}`.

5. Keep `tediware` on and `tediware-send` off. In Claude Code, `/mcp` lists both servers and turns each one on or off; other hosts have an equivalent setting. When a task needs to send, receive or resend, turn `tediware-send` on, and turn it off again when the task is done.

Keep only one of the two on at a time. With both on, the agent has the write tools anyway, and every read tool appears twice.

The in-app help assistant uses the same tools: signed in, it runs the read-only ones above (the configuration and traffic reads, never the submissions) as the signed-in user, so what it tells you about a partner or a trace is what these tools return.

## Rate limits

MCP calls count against the same ceilings as the equivalent HTTP endpoints, not separate ones. `x12_releases` spends the same reference allowance an `/api/x12` read spends; `edi_inspect` spends the inspection allowance; the data tools spend the platform allowance; `transaction_resend` and `transaction_redeliver` share the resend ceiling. See [Platform Limits](/resources/docs/platform-limits).
