Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ fn dreaming_review_queue_report_wires_reviewable_policy_contract() -> color_eyre
)?;
let service_lib = fs::read_to_string(workspace.join("packages/elf-service/src/lib.rs"))?;
let routes = read_rust_module_sources(&workspace.join("apps/elf-api/src"), "routes")?;
let mcp = fs::read_to_string(workspace.join("apps/elf-mcp/src/server.rs"))?;
let mcp = fs::read_to_string(workspace.join("apps/elf-mcp/src/app/server.rs"))?;
let consolidation_spec =
fs::read_to_string(workspace.join("docs/spec/system_consolidation_proposals_v1.md"))?;
let service_spec =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,11 @@ fn recall_debug_panel_report_wires_cross_layer_debug_contract() -> Result<()> {
let service_lib = fs::read_to_string(workspace.join("packages/elf-service/src/lib.rs"))?;
let routes =
rust_module_sources(&workspace, "apps/elf-api/src/routes.rs", "apps/elf-api/src/routes")?;
let mcp =
rust_module_sources(&workspace, "apps/elf-mcp/src/server.rs", "apps/elf-mcp/src/server")?;
let mcp = rust_module_sources(
&workspace,
"apps/elf-mcp/src/app/server.rs",
"apps/elf-mcp/src/app/server",
)?;
let recall_spec =
fs::read_to_string(workspace.join("docs/spec/system_recall_debug_panel_v1.md"))?;
let service_spec =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fn graph_topic_map_report_wires_source_backed_graph_lite_readback() -> Result<()
let api_routes =
fs::read_to_string(support::workspace_root()?.join("apps/elf-api/src/routes.rs"))?;
let mcp_server =
fs::read_to_string(support::workspace_root()?.join("apps/elf-mcp/src/server.rs"))?;
fs::read_to_string(support::workspace_root()?.join("apps/elf-mcp/src/app/server.rs"))?;
let graph_spec = fs::read_to_string(
support::workspace_root()?.join("docs/spec/system_graph_memory_postgres_v1.md"),
)?;
Expand Down
2 changes: 1 addition & 1 deletion apps/elf-mcp/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[path = "server.rs"] mod server;
mod server;

use std::{net::SocketAddr, path::PathBuf};

Expand Down
290 changes: 20 additions & 270 deletions apps/elf-mcp/src/server.rs → apps/elf-mcp/src/app/server.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
#[path = "server/runtime.rs"] mod runtime;
#[path = "server/schemas.rs"] mod schemas;
#[path = "server/state.rs"] mod state;
#[path = "server/support.rs"] mod support;
mod runtime;
mod schemas;
mod state;
mod support;
mod tools;

pub use runtime::serve_mcp;

use color_eyre::Result;
use rmcp::{
ErrorData,
handler::server::router::tool::ToolRouter,
model::{CallToolResult, JsonObject},
};

use schemas::{
admin_ingestion_profile_default_get_schema, admin_ingestion_profile_default_set_schema,
admin_ingestion_profile_get_schema, admin_ingestion_profile_versions_list_schema,
admin_ingestion_profiles_create_schema, admin_ingestion_profiles_list_schema,
admin_memory_history_get_schema, admin_note_provenance_get_schema,
admin_trace_bundle_get_schema, admin_trace_get_schema, admin_trace_item_get_schema,
admin_traces_recent_list_schema, admin_trajectory_get_schema, core_blocks_get_schema,
docs_excerpts_get_schema, docs_get_schema, docs_put_schema, docs_search_l0_schema,
dreaming_review_queue_schema, entity_memory_get_schema, events_ingest_schema,
graph_query_schema, graph_report_schema, notes_get_schema, notes_ingest_schema,
notes_list_schema, notes_patch_schema, notes_publish_schema, notes_unpublish_schema,
recall_debug_panel_schema, searches_create_schema, searches_get_schema, searches_notes_schema,
searches_timeline_schema, space_grant_revoke_schema, space_grant_upsert_schema,
space_grants_list_schema, work_journal_entry_create_schema, work_journal_entry_get_schema,
work_journal_session_readback_schema,
core_blocks_get_schema, dreaming_review_queue_schema, entity_memory_get_schema,
events_ingest_schema, graph_query_schema, graph_report_schema, notes_get_schema,
notes_ingest_schema, notes_list_schema, notes_patch_schema, notes_publish_schema,
notes_unpublish_schema, recall_debug_panel_schema, searches_create_schema, searches_get_schema,
searches_notes_schema, searches_timeline_schema, space_grant_revoke_schema,
space_grant_upsert_schema, space_grants_list_schema, work_journal_entry_create_schema,
work_journal_entry_get_schema, work_journal_session_readback_schema,
};
#[cfg(test)] use schemas::{docs_excerpts_get_schema, docs_put_schema, docs_search_l0_schema};
use state::{ElfContextHeaders, ElfMcp, HttpMethod};
#[cfg(test)] use support::is_authorized;
use support::{
Expand All @@ -40,7 +36,7 @@ const HEADER_READ_PROFILE: &str = "X-ELF-Read-Profile";
const HEADER_REQUEST_ID: &str = "X-ELF-Request-Id";
const HEADER_AUTHORIZATION: &str = "Authorization";

#[rmcp::tool_router]
#[rmcp::tool_router(router = core_tool_router, vis = "pub(in crate::app::server)")]
impl ElfMcp {
#[rmcp::tool(
name = "elf_notes_ingest",
Expand Down Expand Up @@ -78,63 +74,6 @@ impl ElfMcp {
self.forward(HttpMethod::Post, "/v2/events/ingest", params, None).await
}

#[rmcp::tool(
name = "elf_docs_put",
description = "Store a document (evidence source) in ELF Doc Extension v1.",
input_schema = docs_put_schema()
)]
async fn elf_docs_put(&self, params: JsonObject) -> Result<CallToolResult, ErrorData> {
self.forward(HttpMethod::Post, "/v2/docs", params, None).await
}

#[rmcp::tool(
name = "elf_docs_get",
description = "Fetch a single document's metadata by doc_id.",
input_schema = docs_get_schema()
)]
async fn elf_docs_get(&self, mut params: JsonObject) -> Result<CallToolResult, ErrorData> {
let doc_id = support::take_required_string(&mut params, "doc_id")?;
let path = format!("/v2/docs/{doc_id}");

self.forward(HttpMethod::Get, &path, JsonObject::new(), None).await
}

#[rmcp::tool(
name = "elf_docs_delete",
description = "Delete a Source Library document by doc_id and enqueue derived doc-vector removal.",
input_schema = docs_get_schema()
)]
async fn elf_docs_delete(&self, mut params: JsonObject) -> Result<CallToolResult, ErrorData> {
let doc_id = support::take_required_string(&mut params, "doc_id")?;
let path = format!("/v2/docs/{doc_id}");

self.forward(HttpMethod::Delete, &path, JsonObject::new(), None).await
}

#[rmcp::tool(
name = "elf_docs_search_l0",
description = "Run a minimal Doc search (L0): chunk-level results with short snippets.",
input_schema = docs_search_l0_schema()
)]
async fn elf_docs_search_l0(
&self,
mut params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
// read_profile is part of the MCP server configuration and is not client-controlled.
let _ = support::take_optional_string(&mut params, "read_profile")?;

self.forward(HttpMethod::Post, "/v2/docs/search/l0", params, None).await
}

#[rmcp::tool(
name = "elf_docs_excerpts_get",
description = "Hydrate a verifiable excerpt (L1 or L2) from a stored document.",
input_schema = docs_excerpts_get_schema()
)]
async fn elf_docs_excerpts_get(&self, params: JsonObject) -> Result<CallToolResult, ErrorData> {
self.forward(HttpMethod::Post, "/v2/docs/excerpts", params, None).await
}

#[rmcp::tool(
name = "elf_work_journal_entry_create",
description = "Capture one source-adjacent Work Journal entry with source refs, redaction, next-step, rejected-option, and promotion-boundary metadata. Journal content is not authoritative memory.",
Expand Down Expand Up @@ -406,201 +345,12 @@ impl ElfMcp {

self.forward(HttpMethod::Post, &path, params, None).await
}
}

#[rmcp::tool(
name = "elf_admin_traces_recent_list",
description = "List recent traces by tenant/project with optional cursor and filters.",
input_schema = admin_traces_recent_list_schema()
)]
async fn elf_admin_traces_recent_list(
&self,
params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
self.forward(HttpMethod::Get, "/v2/admin/traces/recent", params, None).await
}

#[rmcp::tool(
name = "elf_admin_trace_get",
description = "Fetch trace metadata, items, and optional trajectory summary by trace_id.",
input_schema = admin_trace_get_schema()
)]
async fn elf_admin_trace_get(
&self,
mut params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
let trace_id = support::take_required_string(&mut params, "trace_id")?;
let path = format!("/v2/admin/traces/{trace_id}");

self.forward(HttpMethod::Get, &path, JsonObject::new(), None).await
}

#[rmcp::tool(
name = "elf_admin_trajectory_get",
description = "Fetch trace trajectory and stage payload by trace_id.",
input_schema = admin_trajectory_get_schema()
)]
async fn elf_admin_trajectory_get(
&self,
mut params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
let trace_id = support::take_required_string(&mut params, "trace_id")?;
let path = format!("/v2/admin/trajectories/{trace_id}");

self.forward(HttpMethod::Get, &path, JsonObject::new(), None).await
}

#[rmcp::tool(
name = "elf_admin_trace_item_get",
description = "Fetch a trace item explain payload by item_id.",
input_schema = admin_trace_item_get_schema()
)]
async fn elf_admin_trace_item_get(
&self,
mut params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
let item_id = support::take_required_string(&mut params, "item_id")?;
let path = format!("/v2/admin/trace-items/{item_id}");

self.forward(HttpMethod::Get, &path, JsonObject::new(), None).await
}

#[rmcp::tool(
name = "elf_admin_note_provenance_get",
description = "Fetch provenance bundle and related history for one note.",
input_schema = admin_note_provenance_get_schema()
)]
async fn elf_admin_note_provenance_get(
&self,
mut params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
let note_id = support::take_required_string(&mut params, "note_id")?;
let path = format!("/v2/admin/notes/{note_id}/provenance");

self.forward(HttpMethod::Get, &path, JsonObject::new(), None).await
}

#[rmcp::tool(
name = "elf_admin_memory_history_get",
description = "Fetch chronological memory history for one note.",
input_schema = admin_memory_history_get_schema()
)]
async fn elf_admin_memory_history_get(
&self,
mut params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
let note_id = support::take_required_string(&mut params, "note_id")?;
let path = format!("/v2/admin/notes/{note_id}/history");

self.forward(HttpMethod::Get, &path, JsonObject::new(), None).await
}

#[rmcp::tool(
name = "elf_admin_trace_bundle_get",
description = "Fetch trace bundle for replay and diagnostics by trace_id.",
input_schema = admin_trace_bundle_get_schema()
)]
async fn elf_admin_trace_bundle_get(
&self,
mut params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
let trace_id = support::take_required_string(&mut params, "trace_id")?;
let path = format!("/v2/admin/traces/{trace_id}/bundle");

self.forward(HttpMethod::Get, &path, params, None).await
}

#[rmcp::tool(
name = "elf_admin_events_ingestion_profiles_list",
description = "List latest ingestion profiles for add_event.",
input_schema = admin_ingestion_profiles_list_schema()
)]
async fn elf_admin_events_ingestion_profiles_list(
&self,
_params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
self.forward(
HttpMethod::Get,
"/v2/admin/events/ingestion-profiles",
JsonObject::new(),
None,
)
.await
}

#[rmcp::tool(
name = "elf_admin_events_ingestion_profiles_create",
description = "Create a new ingestion profile version for add_event.",
input_schema = admin_ingestion_profiles_create_schema()
)]
async fn elf_admin_events_ingestion_profiles_create(
&self,
params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
self.forward(HttpMethod::Post, "/v2/admin/events/ingestion-profiles", params, None).await
}

#[rmcp::tool(
name = "elf_admin_events_ingestion_profile_get",
description = "Get a single ingestion profile by id/version for add_event.",
input_schema = admin_ingestion_profile_get_schema()
)]
async fn elf_admin_events_ingestion_profile_get(
&self,
mut params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
let profile_id = support::take_required_string(&mut params, "profile_id")?;
let path = format!("/v2/admin/events/ingestion-profiles/{profile_id}");

self.forward(HttpMethod::Get, &path, params, None).await
}

#[rmcp::tool(
name = "elf_admin_events_ingestion_profile_versions_list",
description = "List all versions of one ingestion profile for add_event.",
input_schema = admin_ingestion_profile_versions_list_schema()
)]
async fn elf_admin_events_ingestion_profile_versions_list(
&self,
mut params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
let profile_id = support::take_required_string(&mut params, "profile_id")?;
let path = format!("/v2/admin/events/ingestion-profiles/{profile_id}/versions");

self.forward(HttpMethod::Get, &path, params, None).await
}

#[rmcp::tool(
name = "elf_admin_events_ingestion_profile_default_get",
description = "Get the active default ingestion profile for add_event.",
input_schema = admin_ingestion_profile_default_get_schema()
)]
async fn elf_admin_events_ingestion_profile_default_get(
&self,
_params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
self.forward(
HttpMethod::Get,
"/v2/admin/events/ingestion-profiles/default",
JsonObject::new(),
None,
)
.await
}

#[rmcp::tool(
name = "elf_admin_events_ingestion_profile_default_set",
description = "Set the default ingestion profile for add_event.",
input_schema = admin_ingestion_profile_default_set_schema()
)]
async fn elf_admin_events_ingestion_profile_default_set(
&self,
params: JsonObject,
) -> Result<CallToolResult, ErrorData> {
self.forward(HttpMethod::Put, "/v2/admin/events/ingestion-profiles/default", params, None)
.await
impl ElfMcp {
pub(in crate::app::server) fn tool_router() -> ToolRouter<Self> {
Self::core_tool_router() + Self::docs_tool_router() + Self::admin_tool_router()
}
}

#[cfg(test)]
#[path = "server/tests.rs"]
mod tests;
#[cfg(test)] mod tests;
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#[path = "schemas/admin.rs"] mod admin;
#[path = "schemas/docs.rs"] mod docs;
#[path = "schemas/events.rs"] mod events;
#[path = "schemas/graph.rs"] mod graph;
#[path = "schemas/memory.rs"] mod memory;
#[path = "schemas/notes.rs"] mod notes;
#[path = "schemas/search.rs"] mod search;
#[path = "schemas/sharing.rs"] mod sharing;
#[path = "schemas/work_journal.rs"] mod work_journal;
mod admin;
mod docs;
mod events;
mod graph;
mod memory;
mod notes;
mod search;
mod sharing;
mod work_journal;

pub(in crate::app::server) use self::{
admin::{
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 2 additions & 0 deletions apps/elf-mcp/src/app/server/tools.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod admin;
mod docs;
Loading