Docs

Quickstart. Connect an agent, send a signal.

No portal gymnastics. A key, three tools, and the smallest API surface that lets an agent remember.

Quickstart

Ediccio is a remote MCP server. Nothing to install: your agent host points at your endpoint with a bearer token and calls three data-plane tools. Signals go in as plain text; profiles build in the background.

1. Get your credential

Your dashboard at app.ediccio.ai holds your connection details and your data-plane credential (a client id and a one-time-shown secret). Exchange it for a bearer token:

# endpoint + credential: dashboard → connection details
TOKEN=$(curl -s "$EDICCIO_TOKEN_URL" \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d "grant_type=client_credentials" | jq -r .access_token)

2. Connect your agent

Drop this into any MCP-compatible host’s mcp.jsonand restart. Your agent can now call the three data-plane tools:ediccio___write, ediccio___read,ediccio___search. (The admin tools appear on the same listing but reject an agent key.) Tokens are short-lived: re-run the exchange when a 401 comes back.

{
  "mcpServers": {
    "ediccio": {
      "type": "http",
      "url": "https://mcp.ediccio.ai/mcp",
      "headers": { "Authorization": "Bearer <your token>" }
    }
  }
}

3. Send a signal

Ask your agent to record what a customer said, or call the tool yourself. One natural-language input, keyed by theend_user_id you already use for that customer:

# via your agent, or one MCP tools/call by hand
write(
  end_user_id = "pat@acme.com",
  source      = "email",
  text        = "we keep reinventing memory for every agent",
)

# → { "event_id": "0197f3a8-…", "accepted_at": "…" }
# the accept is immediate; extraction runs in the background

4. Read it back

Then ask your agent about the customer:

> What does Pat at Acme care about?

Calling tool ediccio___read(end_user_id="pat@acme.com")
✓ profile

Pat is evaluating managed customer-profile layers.
Top pain: re-implementing memory per agent...

Profiles build asynchronously: a brand-new subject reads back asprofile: null until the first projection lands. That is a normal state, not an error.

API reference

Three tools on the data plane. Every value a profile holds carries a confidence score and points back at the event it came from.

write

TOOLediccio___write · scope: ediccio/mcp.write

Land one signal under an end_user_id. Extraction and projection run asynchronously.

write(
  end_user_id     = "pat@acme.com",   # required · your key, never resolved by us
  text            = "…",              # required · the conversation text
  source          = "zendesk",        # optional · defaults to "mcp"
  metadata        = {…},              # optional
  idempotency_key = "…",              # optional · see below
)
# → { "event_id": "0197f3a8-…", "accepted_at": "…" }

Agents retry; the profile counts it once. The sameidempotency_key within 24 hours returns the originalevent_id and writes nothing new.

read

TOOLediccio___read · scope: ediccio/mcp.read

Fetch the profile. dimensions subsets the response (intent, pain, sentiment,preferences, traditional, plus anycustom.<name> or journey.<name>your semantic layer defines).

read(
  end_user_id      = "pat@acme.com",
  dimensions       = ["intent", "pain"],  # optional
  include_evidence = true,                # optional
)
# → { end_user_id, profile: { intent: [{value, confidence,
#      observed_at, provenance, evidence_event_id}], pain: […], … },
#      schema_version, updated_at, last_event_id }

A journey bucket’s first entry is the customer’s current stage. last_event_id is the freshness watermark.

TOOLediccio___search · scope: ediccio/mcp.read

Natural-language search across every profile in your tenant. Recency is a filter, not query text.

search(
  query               = "anyone close to cancelling",
  top_k               = 10,     # max 50
  dimensions          = ["pain", "sentiment"],  # optional
  min_confidence      = 0.5,    # optional
  recency_window_days = 30,     # optional
)
# → { matches: [{ end_user_id, relevance_score,
#      matched_observations: [{dimension, value, confidence, provenance}],
#      updated_at }] }

Semantic layer

Everything above is the data plane. This section and the next are the admin surface: a second credential (ediccio/semantics.admin scope) that can change what profiles mean but cannot read them.

The base dimensions are five. The rest are yours: definecustom.<name> dimensions andjourney.<name> stages in plain English, and the extractor fills them on every profile. Four tools:

  • get_semantic_layer returns the active version and open proposals.
  • propose_semantic_layer takes your refinements, custom dimensions, and journeys, and returns a diff plus a confirmation token. Nothing changes yet.
  • test_semantic_layer dry-runs one real extraction against your text so you see exactly what a proposed version would store, before it stores anything.
  • confirm_semantic_layer activates the version, forward-only. Schema changes ship like code: diff, dry-run, confirm.

Replay

Redefine a dimension today and your whole history answers to it: replay re-projects every stored event under the active semantic layer. It is metered (one extraction per non-blank historical event; the meter counts attempts, retries included), shows you the cost before anything runs, and never starts on its own.

  • request_replay returns the estimate:events_total, subjects_total, and a confirmation token pinned to the active layer version.
  • confirm_replay starts it. The worker rebuilds one subject per transaction, so a concurrent read sees the old projection or the new one, never half of each.
  • get_replay reports progress and meters: events processed, subjects processed, extraction calls.

Security & data

  • Tenant isolation is not a row filter. Each tenant’s profiles live in their own database. No tool takes a tenant parameter: your tenant is derived from your verified token, never from anything you send.
  • Scope-partitioned credentials. The key your agents hold can write signals and read profiles; it cannot touch the schema. The admin key can change the schema; it cannot read profiles. A prompt-injected agent stays a data-entry clerk, never an administrator.
  • Provenance on every fact. Each observation carries its confidence, its timestamp, and the event it came from. If it cannot show its evidence, it never gets stored.
  • Deletion and export on request. Erasure hard-deletes the subject from the live database in one cascade, while the request is still open; export arrives as a complete bundle the same way.
  • Zero-downtime key rotation. Rotate from the dashboard; old and new credentials overlap for seven days. The new secret is shown once and is never retrievable from the dashboard again.
  • EU data residency by default. Storage, processing, and TLS termination for the API endpoints all stay in the EU; the models and providers behind the service are disclosed on the trust page.

Plans & quotas

One meter: signals, the natural-language inputs you feed the extractor. Reads are never metered.

  • Free. 5,000 signals/mo · 500 active profiles · reads are never cut off.
  • Team. 250,000 signals/mo · then $0.0015 per signal.
  • Scale. 1.5M signals/mo · then $0.0011 per signal.
  • Enterprise. Negotiated.

Errors

Tool errors follow JSON-RPC: a code, amessage, and a named identifier indata.identifier you can match on. Two things that arenot errors: a subject with no profile yet (read returns profile: null), and a retried write (the same idempotency_key returns the original event_id).

{
  "code": -32602,
  "message": "text must be non-empty",
  "data": { "identifier": "validation_error" }
}

# a token without the tool's scope:
#   -32001 · data.identifier = mcp_write_scope_required

For support, quote the event_id a write returned.

That’s the whole surface. Start with one signal.