MCP Server
ArcadeDB includes a built-in Model Context Protocol (MCP) server that allows AI assistants and LLM-based tools to interact with the database. The MCP server exposes database operations as tools that AI clients can discover and invoke using the standard MCP protocol.
The MCP server implements the MCP specification (version 2025-03-26) over HTTP using the JSON-RPC 2.0 protocol.
Endpoint
All MCP requests are sent as POST requests to a single endpoint:
POST /api/v1/mcp
Requests must include a valid Authorization header (Basic Auth or Bearer token) and a JSON-RPC 2.0 body.
An alternative stdio transport carries the same protocol over standard input and output, for clients that launch their MCP servers as a child process rather than connecting to one over the network.
The MCP server ships as the optional mcp module and is installed automatically when its jar is on the classpath,
so no SERVER_PLUGINS entry is needed.
The standard distribution includes it.
A distribution assembled without it, through the arcadedb-builder.sh module selection described in
Custom Package Builder, registers neither endpoint and answers 404 on both.
Configuration
The MCP server is configured via the file config/mcp-config.json in the ArcadeDB server directory.
You can also manage the configuration at runtime through the configuration endpoint.
mcp-config.json{
"enabled": true,
"allowReads": true,
"allowInsert": true,
"allowUpdate": true,
"allowDelete": false,
"allowSchemaChange": false,
"allowAdmin": false,
"profile": "rag",
"allowedUsers": ["root", "analyst"],
"principalProfiles": {
"analyst": "rag"
},
"databases": {
"analytics": {
"allowInsert": false,
"allowedUsers": ["analyst"]
}
}
}
| Property | Default | Description |
|---|---|---|
|
|
Enable or disable the MCP server. When disabled, all MCP requests return an error. |
|
|
Allow read-only queries (SELECT, MATCH, etc.). |
|
|
Allow INSERT operations. |
|
|
Allow UPDATE operations. |
|
|
Allow DELETE operations. |
|
|
Allow schema modifications (CREATE TYPE, CREATE PROPERTY, CREATE INDEX, etc.). |
|
|
Allow administrative operations. |
|
|
Server-global tool profile: |
|
|
Optional per-principal tool profiles, keyed by user name or API token name. Intersected with |
|
|
List of usernames allowed to use the MCP server. Use |
|
|
Extra browser |
|
|
Optional per-database permission and user restrictions. See Per-Database Scoping. |
Tool Profiles
Tool profiles keep the surface presented to an agent focused.
The selected profile filters tools/list and is enforced again by tools/call, so a client cannot invoke a hidden
tool directly.
A profile never grants an operation: read, write, schema, administrative, user, origin, and native database
authorization checks remain independent and mandatory.
| Profile | Tool membership |
|---|---|
|
Every tool registered by the running ArcadeDB version. This is the default. |
|
|
|
|
Only registered tools are advertised.
For example, a version that does not include hybrid_search does not expose it merely because the name belongs to
the rag profile.
The agent-memory upsert tools in rag remain unavailable unless their independent write permissions are enabled;
all write permissions default to false.
The profile setting is server-global and applies to every principal that has no entry in principalProfiles.
Per-database overrides restrict permissions and users only; they cannot select a different profile.
See Per-Database Scoping.
Profiles filter prompts as well, because a prompt’s text names tools: a prompt is offered only when every tool it names survives the profile. See Prompts. Resources are not filtered by profile.
Per-Principal Profiles
Entries under principalProfiles assign a tool profile to an individual authenticated principal.
The effective profile is the intersection of the server-global profile and the principal entry: a tool is
advertised and callable only when both profiles contain it.
An entry can therefore only narrow the surface, and never grants a tool that the global profile hides.
As with the global profile, an intersection never grants a permission: read, write, schema, administrative,
database, user, and origin checks remain independent and mandatory.
A principal is keyed by its user name.
An API token can be keyed either canonically as apitoken:<token-name> or by the bare token name, the same two
spellings that allowedUsers accepts.
When both spellings are present, the canonical apitoken: entry wins.
rag tools on an otherwise unrestricted server{
"enabled": true,
"profile": "all",
"allowedUsers": ["root", "retrieval-agent", "retrieval-token"],
"principalProfiles": {
"retrieval-agent": "rag",
"apitoken:retrieval-token": "rag"
}
}
Profile names are case-insensitive on input and are serialized in lowercase.
A tools/call for a tool excluded by the intersection is rejected with an error naming both profiles, so a client
cannot bypass the filtered tools/list by invoking the tool directly.
The initialize instructions follow the effective profile: they describe the rag surface when the intersection
matches the rag profile, and otherwise describe the general or the restricted surface.
The runtime configuration endpoint merges principalProfiles by principal name.
Set one principal to null to remove that entry, or set the top-level principalProfiles value to null to clear
all per-principal profiles:
{
"principalProfiles": {
"retrieval-agent": null,
"reporting-agent": "rag"
}
}
Per-Database Scoping
Entries under databases further restrict the server-global MCP policy for named databases.
An omitted local field inherits its global value.
A local false can deny an operation, but a local true cannot enable an operation denied globally.
Similarly, a local allowedUsers list is an additional restriction: an authenticated principal must satisfy both
the global and local lists.
ArcadeDB’s native database authorization remains an independent mandatory check.
Each database override accepts allowReads, allowInsert, allowUpdate, allowDelete, allowSchemaChange,
allowAdmin, and allowedUsers.
Unknown fields are rejected rather than silently inherited.
An override naming a database that does not currently exist produces a startup warning but is retained so it can
apply if that database is created later.
{
"enabled": true,
"allowReads": true,
"allowInsert": true,
"allowedUsers": ["root", "tenant-agent"],
"databases": {
"tenant-graph": {
"allowInsert": false,
"allowedUsers": ["tenant-agent"]
}
}
}
The runtime configuration endpoint merges updates by database name.
A non-null entry replaces the complete override for that database, so fields omitted from the replacement inherit
the global policy.
Set one database entry to null to remove that override, or set the top-level databases value to null to clear
all database overrides:
{
"databases": {
"tenant-graph": null,
"reporting": {
"allowReads": true,
"allowInsert": false
}
}
}
Database discovery, server status, and schema resources omit databases whose effective policy denies the authenticated principal or read access.
Configuration Endpoint
The MCP configuration can be managed at runtime via the /api/v1/mcp/config endpoint.
Get current configuration:
curl -u root:arcadedb-password \
http://localhost:2480/api/v1/mcp/config
Update configuration (root user only):
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"enabled": true, "allowReads": true, "allowInsert": true}' \
http://localhost:2480/api/v1/mcp/config
MCP Protocol Methods
The MCP server supports the following JSON-RPC 2.0 methods:
| Method | Description |
|---|---|
|
Handshake that returns the protocol version and server capabilities. |
|
Client notification acknowledging initialization. |
|
Returns the list of available tools with their JSON schemas. |
|
Executes a specific tool by name with the provided arguments. |
|
Returns the readable resources. See Resources. |
|
Returns the contents of one resource, addressed by URI. See Resources. |
|
Returns the available guided prompt templates. See Prompts. |
|
Renders one prompt with its arguments substituted. See Prompts. |
|
Health check that returns a pong response. |
The initialize response advertises all three surfaces under capabilities, as tools, resources, and
prompts.
None of them emits change notifications and resources cannot be subscribed to, so listChanged and subscribe
are all false.
The response also carries an instructions string describing the surface the client actually received.
It follows the effective tool profile, so a rag client is told about the retrieval and upsert tools and the
guided prompts, while a client whose profile hides most of the surface is told to use only what tools/list
returned.
Example: Initialize
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"my-client","version":"1.0"}}}' \
http://localhost:2480/api/v1/mcp
Example: List available tools
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
http://localhost:2480/api/v1/mcp
Available Tools
The MCP server exposes a version-dependent set of tools that AI clients can invoke.
The commonly used tools described below include list_databases, get_schema, query, execute_command,
server_status, sample_records, vector_search, full_text_search, hybrid_search, upsert_entity, and
upsert_relationship.
The operator tools profiler_start, profiler_stop, profiler_status, get_server_settings, and
set_server_setting are described below as well.
Call tools/list for the authoritative list, including the full input schema of every tool.
list_databases
Lists all databases accessible to the authenticated user.
Parameters: None
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_databases","arguments":{}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"databases": ["mydb", "analytics"]
}
get_schema
Retrieves the full schema of a database, including all types (vertex, edge, document), their properties, and indexes.
Parameters:
| Parameter | Required | Description |
|---|---|---|
|
Yes |
The name of the database. |
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_schema","arguments":{"database":"mydb"}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"database": "mydb",
"types": [
{
"name": "Person",
"category": "vertex",
"parentTypes": ["V"],
"properties": [
{
"name": "name",
"type": "STRING",
"mandatory": true,
"readonly": false,
"notNull": true
}
],
"indexes": [
{
"name": "Person[name]",
"type": "LSM_TREE",
"properties": ["name"],
"unique": true
}
]
}
]
}
query
Executes a read-only (idempotent) query against a database. The server performs semantic analysis to ensure the query does not modify data. If a write operation is detected, the request is rejected.
Parameters:
| Parameter | Required | Description |
|---|---|---|
|
Yes |
The name of the database. |
|
Yes |
Query language: |
|
Yes |
The query string to execute. |
|
No |
Maximum number of records to return (default: 1000). |
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query","arguments":{"database":"mydb","language":"sql","query":"SELECT FROM Person LIMIT 10"}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"records": [
{"@rid": "#1:0", "@type": "Person", "name": "Alice"},
{"@rid": "#1:1", "@type": "Person", "name": "Bob"}
],
"count": 2
}
execute_command
Executes a non-idempotent command (write operations, schema changes, administrative commands). The server checks the command against the MCP configuration permissions before execution.
Parameters:
| Parameter | Required | Description |
|---|---|---|
|
Yes |
The name of the database. |
|
Yes |
Command language: |
|
Yes |
The command string to execute. |
|
No |
Maximum number of records to return (default: 1000). |
Permission mapping:
The server analyzes the command semantics and checks against the MCP configuration:
| Operation Type | Required Permission |
|---|---|
INSERT |
|
UPDATE |
|
DELETE |
|
CREATE TYPE, CREATE PROPERTY, CREATE INDEX, etc. |
|
Administrative operations |
|
SELECT, MATCH |
|
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"execute_command","arguments":{"database":"mydb","language":"sql","command":"INSERT INTO Person SET name = '\''Charlie'\''"}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"records": [
{"@rid": "#1:2", "@type": "Person", "name": "Charlie"}
],
"count": 1
}
server_status
Returns server metadata including version, available query languages, and accessible databases.
Parameters: None
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"server_status","arguments":{}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"version": "26.5.1",
"serverName": "ArcadeDB_0",
"languages": ["sql", "sqlscript", "cypher", "gremlin", "graphql", "mongo"],
"databases": ["mydb", "analytics"],
"ha": {
"clusterName": "arcadedb",
"leader": "server1",
"replicas": ["server2", "server3"]
}
}
The ha field is only included when High Availability is enabled and the user has admin permissions.
|
sample_records
Returns the first records in storage order from selected database types so an agent can inspect actual property-value shapes before constructing a query. This is not a random or representative sample.
Parameters:
| Parameter | Required | Description |
|---|---|---|
|
Yes |
The name of the database. |
|
No |
Up to 20 type names. Explicit names may include edge types. When omitted, ArcadeDB selects the first 20 vertex and document type names in alphabetical order and excludes edge types. |
|
No |
Maximum records per type, from 1 through 20 (default: 3). |
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"sample_records","arguments":{"database":"mydb","types":["Person","Company"],"limit":3}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"samples": {
"Person": [
{"@rid": "#1:0", "@type": "Person", "name": "Alice"}
],
"Company": [
{"@rid": "#2:0", "@type": "Company", "name": "Acme"}
]
},
"sampledTypes": 2,
"availableTypes": 2,
"recordsReturned": 2,
"truncated": false
}
availableTypes always reports the number of eligible vertex and document types in the database, even when types
is supplied. When types is omitted, truncated indicates that more than 20 eligible types exist.
The generated queries are checked as read-only, and serialized records do not include vertex edge collections.
vector_search
Searches a dense LSM_VECTOR or sparse LSM_SPARSE_VECTOR index with a pre-computed query vector.
ArcadeDB does not generate embeddings; the caller must supply the vector produced by its embedding model.
Parameters:
| Parameter | Required | Description |
|---|---|---|
|
Yes |
The name of the database. |
|
Yes |
The name of an |
|
Yes |
Dense vector values, or sparse weights corresponding to |
|
Sparse only |
Sparse dimension identifiers corresponding to the weights in |
|
Yes |
Maximum results, from 1 through 1,000. |
|
No |
Dense-index search beam width. Higher values can improve recall at additional cost. This option is rejected for sparse indexes. |
|
No |
Read-only SQL |
|
No |
Use |
The optional filter is evaluated against each expanded neighbor row.
Record properties are flattened into that row, and @rid, @type, record, plus distance for dense results or
score for sparse results are also available.
Dense-vector example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"vector_search","arguments":{"database":"research","indexName":"Paper[embedding]","queryVector":[0.12,-0.08,0.31],"k":20,"efSearch":100,"filter":"publishedYear >= 2024"}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"indexName": "Paper[embedding]",
"sparse": false,
"scoring": "distance_lower_is_better:COSINE",
"candidateLimit": 160,
"truncated": true,
"count": 20,
"results": [
{
"rid": "#12:34",
"type": "Paper",
"distance": 0.083,
"properties": {
"title": "Example paper",
"publishedYear": 2025
}
}
]
}
Dense results expose only distance, where lower is better.
Sparse results expose only score, where higher is better.
The response-level scoring value also records the active dense similarity function or sparse scoring modifier.
Without a filter, candidateLimit equals k.
With a filter, ArcadeDB examines up to min(k * 8, 8000) vector candidates before applying the predicate.
truncated is true when the returned result window is full (count equals k), so more matches may exist.
When it is false, fewer than k matches survived the current candidate window.
For a filtered search, that does not prove that no matching vectors exist beyond candidateLimit; increasing k
also expands the candidate window until the 8,000-candidate ceiling is reached.
full_text_search
Searches a full-text index and returns the matching records ranked by relevance score. Requires read permission.
Address the index either by indexName, or by typeName with optional properties.
When both forms are supplied, indexName wins.
Parameters:
| Parameter | Required | Description |
|---|---|---|
|
Yes |
The name of the database. |
|
Yes |
The full-text query. A blank query is rejected. |
|
Either |
The name of the full-text index, for example |
|
Either |
The type carrying the index, for example |
|
No |
Indexed properties, used with |
|
No |
Maximum results (default: 10). |
Query syntax:
| Form | Meaning |
|---|---|
|
Both terms required. |
|
Excludes |
|
Matches either term. |
|
All terms must appear in the same record. Term order is not enforced. |
|
Prefix match. |
|
Fuzzy match. |
|
Restricts to one property of a multi-property index. |
|
Boosts a term. |
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"full_text_search","arguments":{"database":"research","indexName":"Paper[abstract]","queryText":"+graph +retrieval -sql","limit":5}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"indexName": "Paper[abstract]",
"similarity": "BM25",
"count": 2,
"results": [
{
"rid": "#12:34",
"score": 4.71,
"properties": {
"title": "Graph retrieval over citation networks"
}
}
]
}
The similarity field reports whether scores are BM25 or legacy CLASSIC coordination counts.
The limit is pushed down per bucket, so the search merges at most bucket count * limit entries rather than every
match in the index.
Because of that push-down, a record deleted concurrently between the index scan and the record read is skipped
without being back-filled, so a search can legitimately return fewer than limit results.
hybrid_search
Retrieves records by fusing up to three retrieval legs into a single ranked list: a vector search, an optional
full-text search, and an optional graph expansion.
Requires read permission.
As with vector_search, ArcadeDB does not generate embeddings.
The graph expansion is seeded from the records the other legs found and walks outward from them, so a neighbor of a strong match can itself rank. Expanded records carry the number of hops from their seed and the path back to it.
Parameters:
| Parameter | Required | Description |
|---|---|---|
|
Yes |
The name of the database. |
|
Yes |
The name of an |
|
Yes |
Dense vector values, or sparse weights corresponding to |
|
Yes |
Maximum results after fusion, from 1 through 1,000. |
|
Sparse only |
Sparse dimension identifiers corresponding to the weights in |
|
No |
Use |
|
No |
Dense-index search beam width. Rejected for sparse indexes. |
|
No |
Read-only SQL |
|
With |
The full-text index for the second leg. |
|
With |
The full-text query for the second leg. |
|
No |
Graph expansion leg. See below. |
|
No |
|
|
No |
Per-leg fusion weights, keyed by leg name. |
Supplying only one of fulltextIndexName and fulltextQuery is rejected: half a leg is always a mistake, and
ignoring it silently would return a plausible result set that quietly dropped what the caller asked for.
The expand object:
| Field | Required | Description |
|---|---|---|
|
No |
Edge types to traverse. Omit to traverse every edge type. |
|
No |
|
|
No |
Hops to walk, from 1 through 3 (default: 1). |
An unknown edge type is rejected, listing the available ones.
Traversing a name that matches nothing would otherwise return an empty neighborhood without an error, silently
degrading the search to plain two-way fusion.
maxDepth above 3 is rejected rather than clamped, and the cap is enforced server-side regardless of the value sent.
Graph expansion requires a vertex type; requesting it against a document type is rejected.
Fusion strategies:
RRF (Reciprocal Rank Fusion) is rank-only and ignores per-row scores.
DBSF and LINEAR normalize per-source scores and therefore require a numeric score on every row of every source.
The graph expansion leg ranks by traversal order and carries no score, so DBSF and LINEAR cannot be combined
with expand and that combination is rejected.
Both remain available for vector plus full-text fusion.
Weights:
weights accepts vector, fulltext, and expand.
The first two default to 1.0 and expand defaults to 0.5, because under RRF a rank-one expanded record would
otherwise score exactly what the nearest neighbor scores, letting an arbitrary one-hop neighbor tie the best
semantic match.
A weight naming a leg the request does not ask for is rejected, as is an unknown key, a negative value, and a
non-numeric value.
A weight of 0.0 is accepted: it keeps a leg’s records in the candidate set and in each result’s sources while
contributing nothing to the ranking.
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"hybrid_search","arguments":{"database":"research","vectorIndexName":"Paper[embedding]","queryVector":[0.12,-0.08,0.31],"fulltextIndexName":"Paper[abstract]","fulltextQuery":"graph retrieval","expand":{"edgeTypes":["CITES"],"direction":"out","maxDepth":2},"fusionStrategy":"RRF","k":10}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"vectorIndexName": "Paper[embedding]",
"sparse": false,
"scoring": "distance_lower_is_better:COSINE",
"fused": true,
"fusionStrategy": "RRF",
"fulltextIndexName": "Paper[abstract]",
"legs": {
"vector": { "count": 40 },
"fulltext": { "indexName": "Paper[abstract]", "similarity": "BM25", "count": 27 },
"expand": {
"direction": "out",
"edgeTypes": ["CITES"],
"maxDepth": 2,
"truncated": false,
"seedCount": 61,
"seedsTruncated": false,
"count": 133
}
},
"truncated": true,
"count": 10,
"results": [
{
"rid": "#12:34",
"fusedScore": 0.0325,
"sources": ["vector", "fulltext"],
"properties": { "title": "Graph retrieval over citation networks" }
},
{
"rid": "#12:99",
"fusedScore": 0.0161,
"sources": ["expand"],
"depth": 1,
"path": ["#12:34", "#12:99"],
"properties": { "title": "Citation graphs as retrieval context" }
}
]
}
sources names the legs that contributed to each result, so a fused score can be interpreted rather than taken on
faith: it distinguishes a top semantic match from a record that arrived two hops from something relevant.
depth and path appear only on results the expansion leg contributed, and path starts at the seed and ends at
the result itself.
fused reports whether fusion actually ran.
Fusion requires at least two sources, so a request naming neither fulltextQuery nor expand cannot be fused: that
response sets fused to false and its results carry the vector leg’s native distance or score instead of
fusedScore.
The full-text index name and similarity are reported under legs whenever that leg ran, including when it matched
nothing.
The response carries several independent budgets.
Top-level truncated means the result window filled (count equals k), so raising k may surface more.
legs.expand.truncated means the traversal hit its fan-out cap, so the neighborhood was only partly explored;
narrowing edgeTypes or maxDepth gives a more complete picture of the part that matters.
legs.expand.seedsTruncated means the retrieval legs produced more candidate seeds than the traversal budget
accepts, so expansion started from a subset.
For pure two-way fusion without graph expansion, vector.fuse remains available through the query tool.
upsert_entity
Creates or updates a single vertex addressed by a match key, without duplicating it. Requires both insert and update permission, because a merge resolves to a create or an update depending on what already exists.
The vertex is matched, or created, by the matchKeys property/value pairs, and then any setProperties are
written.
Repeated calls with identical matchKeys resolve to the same vertex.
Values are bound as parameters, so they are safe to pass verbatim; identifiers are quoted.
Parameters:
| Parameter | Required | Description |
|---|---|---|
|
Yes |
The name of the database. |
|
Yes |
The vertex type. Created automatically if it does not exist. |
|
Yes |
property/value pairs used as the match key. Must be non-empty; values should be scalars. |
|
No |
property/value pairs to write on the matched or created vertex. |
|
Create a |
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"upsert_entity","arguments":{"database":"memory","typeName":"Person","matchKeys":{"email":"[email protected]"},"setProperties":{"name":"Ada Lovelace","role":"analyst"}}}}' \
http://localhost:2480/api/v1/mcp
upsert_relationship
Creates or updates a single directed edge between two vertices, without duplicating it. Requires both insert and update permission.
Each endpoint is matched, or created, by its own match keys; then the edge of type relType between them is matched
or created, and any relProperties are written.
Repeated calls between the same resolved endpoints with the same relType resolve to the same edge.
Parameters:
| Parameter | Required | Description |
|---|---|---|
|
Yes |
The name of the database. |
|
Yes |
The source vertex type. Created automatically if it does not exist. |
|
Yes |
property/value pairs identifying the source vertex. Must be non-empty. |
|
Yes |
The destination vertex type. Created automatically if it does not exist. |
|
Yes |
property/value pairs identifying the destination vertex. Must be non-empty. |
|
Yes |
The edge type. Created automatically if it does not exist. |
|
No |
property/value pairs to write on the matched or created edge. |
|
As with |
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"upsert_relationship","arguments":{"database":"memory","fromType":"Person","fromMatchKeys":{"email":"[email protected]"},"toType":"Project","toMatchKeys":{"code":"ANALYTICAL-ENGINE"},"relType":"WORKS_ON","relProperties":{"since":1843}}}}' \
http://localhost:2480/api/v1/mcp
The edge properties are written in a trailing assignment, so the edge is keyed by its endpoints and type alone and is never duplicated by a change to its properties.
profiler_start
Starts the query profiler, which records every query with its execution time and plan. Requires admin permission.
The profiler auto-stops after timeoutSeconds, so a session left running does not profile indefinitely.
Use profiler_stop to end it early and collect the results, or profiler_status to check progress while it runs.
Parameters:
| Parameter | Required | Description |
|---|---|---|
|
No |
Recording duration, from 1 through 3,600 seconds (default: 60). The profiler stops itself when it elapses. |
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"profiler_start","arguments":{"timeoutSeconds":120}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"status": "started",
"timeoutSeconds": 120,
"message": "Query profiler started. It will auto-stop after 120 seconds. Use profiler_stop to stop early and retrieve results."
}
Only one profiling session runs at a time.
Starting the profiler while it is already recording does not restart it, extend it, or raise an error: the call
returns status: "already_recording" and leaves the session untouched, so an agent retrying a start cannot
silently discard the data collected so far.
profiler_stop
Stops the query profiler and returns the data captured during the session. Requires admin permission. Takes no parameters.
Results are aggregated per query and include execution counts, timing (minimum, maximum, average, and p99), and execution-plan step costs. They are also written to disk, so a session can be retrieved after the fact.
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"profiler_stop","arguments":{}}}' \
http://localhost:2480/api/v1/mcp
If the profiler was not recording, the call returns status: "not_recording" rather than an error, so stopping an
already-stopped profiler is safe to retry.
profiler_status
Reports whether the profiler is recording and returns the current or most recent results. Requires admin permission. Takes no parameters.
Where profiler_stop ends the session, this reads it without disturbing it, so it is the safe call for an agent
that wants to check progress mid-session.
Alongside the captured queries and their timing statistics, the response carries server metric snapshots taken
during the session.
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"profiler_status","arguments":{}}}' \
http://localhost:2480/api/v1/mcp
get_server_settings
Returns the server-level configuration settings with their current values, defaults, and descriptions. Takes no parameters.
|
This tool requires read permission, not admin permission, even though it belongs to the |
Only settings whose scope is the server are returned; database-scoped settings are excluded.
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_server_settings","arguments":{}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"settings": [
{
"key": "arcadedb.asyncWorkerThreads",
"value": 7,
"description": "Number of asynchronous worker threads. 0 (default) = available cores minus 1",
"overridden": true,
"default": 0
},
{
"key": "arcadedb.ha.clusterToken",
"value": "*****",
"description": "Token to authenticate a server joining the cluster",
"overridden": true,
"default": "*****"
}
]
}
overridden is true when the setting has been given an explicit value rather than inheriting its default.
|
Secret settings are masked as |
set_server_setting
Updates a server configuration setting at runtime. Requires admin permission.
Changes take effect immediately.
Whether they survive a restart depends on the individual setting, so treat this as a runtime adjustment rather than
a way to persist configuration.
Call get_server_settings first to see the available keys and their current values.
Parameters:
| Parameter | Required | Description |
|---|---|---|
|
Yes |
The configuration key, for example |
|
Yes |
The new value, as a string. |
An unknown key is rejected by name rather than silently accepted, so a typo cannot masquerade as a successful update.
Example:
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"set_server_setting","arguments":{"key":"arcadedb.asyncWorkerThreads","value":"8"}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"key": "arcadedb.asyncWorkerThreads",
"previousValue": "7",
"newValue": "8",
"message": "Setting 'arcadedb.asyncWorkerThreads' updated successfully."
}
The response reports the replaced value so a change can be reverted without a separate read.
|
For a secret setting, |
Resources
Besides tools, the MCP server exposes a read-only Resources surface. Every database publishes its schema at:
arcadedb://{database}/schema
The resource is served with the MIME type application/json and its text payload is the same document that the
get_schema tool returns.
A client that reads the resource at session start therefore obtains the schema without spending a tool call.
Example: list resources
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"resources/list"}' \
http://localhost:2480/api/v1/mcp
Response:
{
"resources": [
{
"uri": "arcadedb://mydb/schema",
"name": "mydb schema",
"description": "Schema of the ArcadeDB database 'mydb': types (vertex, edge, document), properties, indexes, inheritance.",
"mimeType": "application/json"
}
]
}
Example: read a resource
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"resources/read","params":{"uri":"arcadedb://mydb/schema"}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"contents": [
{
"uri": "arcadedb://mydb/schema",
"mimeType": "application/json",
"text": "{\"database\":\"mydb\",\"types\":[...]}"
}
]
}
Resources are gated by the same policy as reads.
allowReads must be enabled globally, the effective per-database policy must allow the authenticated principal to
read that database, and ArcadeDB’s native database authorization applies as usual.
Tool profiles do not filter resources: a rag or admin profile changes the tool list only.
resources/list is a discovery call that most clients issue unprompted at session start, so a denial is reported as
an empty resources array rather than as an error, and databases the principal cannot read are omitted.
resources/read rejects an unknown database, an unauthorized database, and a malformed URI identically with JSON-RPC
error -32002 (Resource not found), so the surface cannot be used to probe which databases exist.
When allowReads is disabled, resources/read returns error -32600.
Prompts
The MCP server also exposes a Prompts surface: guided templates that steer a model through a task using the
retrieval and agent-memory tools, instead of leaving it to hand-write vector or MERGE syntax.
A client typically offers them to the user as named commands.
| Prompt | Purpose |
|---|---|
|
Answer a question over a database with its retrieval tools, citing the records the answer came from. |
|
Extract entities and relationships from text and write them into a database without creating duplicates. |
Both prompts take two required arguments.
| Prompt | Argument | Description |
|---|---|---|
|
|
The name of the database to search. |
|
The question to answer from the contents of the database. |
|
|
|
The name of the database to write into. |
|
The text to extract entities and relationships from. |
The rendered messages are static text with the arguments substituted verbatim, so prompts/get opens no database
and reads no data.
graphrag_query directs the model to load the schema first, choose between vector_search, full_text_search,
hybrid_search, and query according to the shape of the question, supply its own embedding vector because
ArcadeDB does not generate one, and cite every claim with the @rid it came from.
build_knowledge_graph centers on match-key selection, which is what keeps repeated extraction from duplicating
vertices.
It directs the model to read the existing schema and reuse its types, choose the smallest stable match key per
entity, prefer one backed by a UNIQUE index and report a missing one rather than creating it, then call
upsert_entity once per entity and upsert_relationship once per relationship, reusing exactly the same match
keys for the endpoints.
Availability
A prompt is a script that names tools by name, so it is offered only when the caller can actually run it. Each prompt is listed, and rendered, only when both conditions hold:
-
every tool its text names is allowed by the caller’s effective tool profile, and
-
the permissions those tools require are granted server-globally.
| Prompt | Tools named | Permissions required |
|---|---|---|
|
|
|
|
|
|
Neither prompt is therefore available under the admin profile, which contains none of the retrieval or upsert
tools.
Under rag and all, graphrag_query is available whenever reads are permitted, and build_knowledge_graph
additionally requires the two write permissions, which are false by default.
These checks are evaluated against the server-global permission flags, because no database is resolved while a prompt is rendered. Per-database overrides still apply to every tool call the model subsequently makes.
Like resources/list, prompts/list is a discovery call most clients issue unprompted at session start, so a
caller entitled to no prompts receives an empty prompts array rather than an error.
Availability is then re-checked by prompts/get, for the same reason tools/call re-checks the profile after
tools/list has already filtered: an unavailable prompt is refused with error -32600 even when it is requested
by name.
An unknown prompt name, or a missing argument, returns -32602.
Example: list prompts
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"prompts/list"}' \
http://localhost:2480/api/v1/mcp
Response:
{
"prompts": [
{
"name": "graphrag_query",
"description": "Answer a question over an ArcadeDB database using its retrieval tools, citing the records the answer came from.",
"arguments": [
{"name": "database", "description": "The name of the database to search", "required": true},
{"name": "question", "description": "The question to answer from the contents of the database", "required": true}
]
}
]
}
Example: render a prompt
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"prompts/get","params":{"name":"graphrag_query","arguments":{"database":"research","question":"Which papers cite the original RRF paper?"}}}' \
http://localhost:2480/api/v1/mcp
Response:
{
"description": "Answer a question over an ArcadeDB database using its retrieval tools, citing the records the answer came from.",
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Answer the following question using the ArcadeDB database 'research'.\n\n<question>\n..."
}
}
]
}
Each argument is substituted inside a delimiter block that marks it as data rather than procedure. Because the value is substituted verbatim, a value that itself carries the closing tag would end that block early and have the remaining text read as instructions. The server therefore suffixes both delimiters with an unpredictable token whenever the value contains such a tag, which moves the boundary instead of altering the value. An ordinary argument carries no such tag and renders against the constant delimiters shown above.
Transport
The same protocol surface is served over two transports: HTTP, and standard input and output. Tools, resources, prompts, and every permission and profile rule behave identically on both; only the envelope differs.
HTTP
The HTTP endpoint follows the Streamable HTTP transport rules of MCP 2025-03-26.
HTTP methods. JSON-RPC is carried over POST only.
The server does not offer a Server-Sent Events stream, so any other method (including GET) is answered with
405 Method Not Allowed and an Allow: POST header.
Requests and notifications. A request must carry an id that is a string or an integer; null, fractional
and structured ids are rejected with JSON-RPC error -32600.
A notification is a message with a method and no id: the server never answers one, and a POST that
carried only notifications returns 202 Accepted with an empty body.
Before version 26.8.1 a notification-only POST returned 204 No Content, and notifications other than
notifications/initialized were answered with a Method not found error carrying a null id.
|
Batches. A top-level JSON array is accepted on both the HTTP and the stdio transport.
Every element is dispatched independently and only the elements that are requests contribute a response, so a
batch of notifications alone yields 202 Accepted with no body and a mixed batch yields an array holding just
the request responses.
curl -u root:arcadedb-password \
-X POST \
-H "Content-Type: application/json" \
-d '[{"jsonrpc":"2.0","id":1,"method":"ping"},{"jsonrpc":"2.0","id":2,"method":"tools/list"}]' \
http://localhost:2480/api/v1/mcp
Origin validation. To mitigate DNS rebinding, a request carrying an Origin header is accepted only when the
origin is a loopback address, matches the host the request was addressed to, or is listed in allowedOrigins.
Anything else is rejected with 403.
A request with no Origin is accepted, because non-browser MCP clients do not send one and the header is
attached by the browser precisely in the case the check is meant to catch.
Setting allowedOrigins to ["*"] accepts any origin and disables this protection: use it only when the
endpoint is fronted by a proxy that performs its own origin control.
Stdio
Clients that launch their MCP servers as a child process, rather than connecting to one over the network, speak
JSON-RPC 2.0 over standard input and output with one message per line.
The distribution ships that transport as bin/mcp-stdio.sh, which starts an ArcadeDB server and serves MCP on its
stdin and stdout.
export ARCADEDB_SETTINGS="-Darcadedb.server.rootPassword=arcadedb-password"
bin/mcp-stdio.sh
The root password is mandatory: the process authenticates as root and exits with an error if
arcadedb.server.rootPassword is unset.
Because the client owns stdout, the server redirects its own logging to stderr so that log lines cannot corrupt the
JSON-RPC stream.
Stdio mode enables the MCP server for the lifetime of the process regardless of the enabled flag, since the
client started it precisely to speak MCP.
Everything else in config/mcp-config.json still applies unchanged: permissions, tool profile, per-database
overrides, and the allowedUsers list, which must admit root.
Origin validation does not apply, because there is no HTTP request to carry an Origin header.
A line holding a top-level JSON array is dispatched as a batch, exactly as over HTTP. A message that produces no response, such as a notification or a batch of them, is answered with no output line at all.
{
"mcpServers": {
"arcadedb": {
"command": "/opt/arcadedb/bin/mcp-stdio.sh",
"env": {
"ARCADEDB_SETTINGS": "-Darcadedb.server.rootPassword=arcadedb-password"
}
}
}
}
Security
The MCP server enforces multiple layers of security:
-
Authentication — All requests require valid ArcadeDB credentials (Basic Auth or Bearer token).
-
User authorization — Users can only access databases they are authorized for in the ArcadeDB security configuration.
-
MCP permissions — The MCP configuration controls which operations are allowed (reads, inserts, updates, deletes, schema changes, admin).
-
User allow-list — The
allowedUserssetting restricts which users can access the MCP server. -
Per-database restrictions — Optional database entries can further restrict operations and users without granting authority above the global policy.
-
Tool profiles — Hidden tools are removed from discovery and rejected if invoked directly; profiles do not grant permissions.
-
Per-principal profiles — An individual principal can be narrowed further; the effective profile is the intersection with the global one. See Per-Principal Profiles.
-
Prompt gating — A prompt is offered, and rendered, only when the tools its text names are visible to the caller and the permissions those tools need are granted. See Prompts.
-
Origin validation — Cross-origin browser requests are rejected to mitigate DNS rebinding. See [mcp-origin].
-
Semantic analysis — Queries and commands are analyzed to detect their operation type and enforce the appropriate permission.
Using with AI Clients
The ArcadeDB MCP server is compatible with any MCP-compliant client.
To connect an AI assistant to ArcadeDB, configure the client with the MCP endpoint URL and authentication
credentials, or launch bin/mcp-stdio.sh as a child process for clients that prefer the
stdio transport.
Example configuration for an MCP client:
{
"mcpServers": {
"arcadedb": {
"url": "http://localhost:2480/api/v1/mcp",
"headers": {
"Authorization": "Basic cm9vdDphcmNhZGVkYi1wYXNzd29yZA=="
}
}
}
}
Once connected, the AI client automatically discovers the available tools via tools/list and can use them to
explore databases, query data, and execute commands within the configured permissions.
A client that supports the other two surfaces should also read arcadedb://{database}/schema from
Resources at session start, which supplies the schema without spending a tool call, and offer the
Prompts its profile exposes.