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.json and 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.eu.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 the end_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 as profile: 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 same idempotency_key within 24 hours returns the original event_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 any custom.<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. There is no relevance cut-off: the closest profiles come back even when none is close, so check relevance_score before acting on one. 0.5 means no similarity, and unrelated text usually scores only a little above it.

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: define custom.<name> dimensions and journey.<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 tells you how many events and subjects it covers before anything runs, counts its own extraction calls (one per non-blank historical event, retries included), 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 queues it for the worker’s next pass. 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 counts: events processed, subjects processed, extraction calls.

Security & data

Intended purpose. Ediccio builds a deep profile of a business's own customers, prospects and product users from the conversations that business already has with them, so that the AI agents in its product (sales assistant, support copilot, customer-onboarding bot, customer-success agent, marketing agent that messages customers who can reply to it) can personalise those conversations. It is not intended for use in, or as an input to or part of, systems that evaluate people for credit, insurance or public benefits, for emergency triage, for hiring or at work, or for education or training, nor in law enforcement, migration, justice or elections. It is not for profiling a business's own workforce, job candidates, students, trainees or patients. A dimension, field or journey definition that names a special category of personal data or a criminal record (GDPR Art. 9 and 10) is refused before it takes effect.

  • 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 & limits

Pricing counts one thing: signals, the natural-language inputs you feed the extractor. Reads never count against it, and neither does the number of profiles you hold. These are the prices that take effect when billing starts. During early access nothing is metered and no plan limit is enforced; the only ceiling is a platform-wide concurrency guard against runaway traffic, not a per-plan cap. Prices exclude VAT/IVA and other applicable taxes.

  • Free. $0 forever. 5,000 signals / mo.
  • Team. $349 / month. Includes 250,000 signals / mo · then $0.0015 each.
  • Scale. $1,499 / month. Includes 1.5M signals / mo · then $0.0011 each.
  • Enterprise. Coming soon. Custom volume · annual · talk to us.

Every plan carries the same product: the same tools, the same scope-partitioned credentials, the same isolation, the same residency. Nothing on this page is gated by plan.

Errors

Tool errors follow JSON-RPC: a code, a message, and a named identifier in data.identifier you can match on. Two things that are not 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.