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
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
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.
search
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
readsees 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.