mirror of
https://github.com/samvallad33/vestige.git
synced 2026-04-25 00:36:22 +02:00
feat(mcp): opt-in VESTIGE_SYSTEM_PROMPT_MODE=full composition mandate
The MCP `instructions` field is injected into every connecting client's
system prompt on Initialize — Claude Code, Cursor, Zed, Windsurf, and
anyone else running Vestige. That reach demands a default that serves
every user, not the project maintainer's personal workflow.
Default ("minimal", 3 sentences) tells the client how to use Vestige
and how to respond to explicit feedback signals. It is safe for every
audience: competitive coders, hobbyists saving recipes, Rails devs
saving bug fixes, enterprise deployments under system-prompt review.
The full composition mandate — Composing / Never-composed /
Recommendation shape + FSRS-trust blocking phrase + origin case study
— is load-bearing for decision-adjacent work but misfires on trivial
retrievals ("what's my favorite color"). Opt in on your own machine
with `VESTIGE_SYSTEM_PROMPT_MODE=full`; four hundred strangers do not
inherit one maintainer's trauma scar on every session.
Extracted into build_instructions() so the branch is a single env-var
check on Initialize, not a compile-time switch. main.rs --help output
advertises the new variable alongside VESTIGE_DASHBOARD_ENABLED.
This commit is contained in:
parent
cc0e70acc8
commit
5772cdcb19
2 changed files with 48 additions and 8 deletions
|
|
@ -91,6 +91,9 @@ fn parse_args() -> Config {
|
|||
println!(" VESTIGE_HTTP_PORT HTTP transport port (default: 3928)");
|
||||
println!(" VESTIGE_DASHBOARD_ENABLED Enable dashboard (default: disabled)");
|
||||
println!(" VESTIGE_DASHBOARD_PORT Dashboard port (default: 3927)");
|
||||
println!(
|
||||
" VESTIGE_SYSTEM_PROMPT_MODE Inject the full composition mandate into every MCP session (minimal|full, default: minimal)"
|
||||
);
|
||||
println!();
|
||||
println!("EXAMPLES:");
|
||||
println!(" vestige-mcp");
|
||||
|
|
|
|||
|
|
@ -22,6 +22,50 @@ use crate::resources;
|
|||
use crate::tools;
|
||||
use vestige_core::Storage;
|
||||
|
||||
/// Build the MCP `instructions` string injected into every connecting client's
|
||||
/// system prompt.
|
||||
///
|
||||
/// Default ("minimal", 3 sentences) is safe for any user: competitive coders,
|
||||
/// hobbyists saving recipes, Rails devs saving bug fixes, enterprise deployments.
|
||||
/// It earns its per-session token cost by telling the client *how* to use
|
||||
/// Vestige without imposing one maintainer's workflow on strangers.
|
||||
///
|
||||
/// The "full" variant is the composition mandate that enforces the
|
||||
/// Composing / Never-composed / Recommendation response shape, names the
|
||||
/// AIMO3 36/50 case study as the origin, and includes the "Vestige is
|
||||
/// blocking this:" refusal phrase. It is load-bearing for Sam's own
|
||||
/// decision-adjacent work but would misfire on trivial retrievals for a
|
||||
/// general audience, so it is opt-in via `VESTIGE_SYSTEM_PROMPT_MODE=full`.
|
||||
///
|
||||
/// Anything other than `full` falls back to minimal.
|
||||
fn build_instructions() -> String {
|
||||
let mode = std::env::var("VESTIGE_SYSTEM_PROMPT_MODE").unwrap_or_default();
|
||||
if mode.eq_ignore_ascii_case("full") {
|
||||
"Vestige is your long-term cognitive memory AND reasoning engine, not a RAG database. \
|
||||
Every retrieval MUST be composed into a recommendation, never summarized.\
|
||||
\n\nCOMPOSITION MANDATE: When you receive memories from search, deep_reference, \
|
||||
cross_reference, or explore_connections, your response MUST follow this shape. \
|
||||
(a) Composing: [memory IDs], followed by your composition logic (your chain-of-thought \
|
||||
about how the memories relate, NOT a restatement of their contents). \
|
||||
(b) Never-composed detected: list combinations of retrieved memories that share \
|
||||
tags/topics but have never been referenced together, or write 'None.' \
|
||||
(c) Recommendation: what the user should DO, as a concrete executable action. \
|
||||
If your draft begins 'Memory A says X. Memory B says Y.' STOP and rewrite.\
|
||||
\n\nBLOCKING PHRASE: If retrieved high-trust memories (retention > 0.7, reps > 0) \
|
||||
contradict what you were about to say, start your response with 'Vestige is blocking this:' \
|
||||
and surface the contradiction verbatim before proceeding. FSRS trust overrides fresh guesses.\
|
||||
\n\nFEEDBACK: If the user confirms a memory was helpful, call memory(action='promote'). \
|
||||
If they correct it, call memory(action='demote'). Do not ask permission, just act."
|
||||
.to_string()
|
||||
} else {
|
||||
"Vestige is your long-term memory system. Compose retrievals into recommendations \
|
||||
rather than listing their contents when the user is making a decision. \
|
||||
On user feedback, call memory(action='promote') for helpful retrievals and \
|
||||
memory(action='demote') for wrong ones — do not ask permission, just act."
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// MCP Server implementation
|
||||
pub struct McpServer {
|
||||
storage: Arc<Storage>,
|
||||
|
|
@ -157,14 +201,7 @@ impl McpServer {
|
|||
}),
|
||||
prompts: None,
|
||||
},
|
||||
instructions: Some(
|
||||
"Vestige is your long-term memory system. Use it to remember important information, \
|
||||
recall past knowledge, and maintain context across sessions. The system uses \
|
||||
FSRS-6 spaced repetition to naturally decay memories over time. \
|
||||
\n\nFeedback Protocol: If the user explicitly confirms a memory was helpful, use \
|
||||
memory(action='promote'). If they correct a hallucination or say a memory was wrong, use \
|
||||
memory(action='demote'). Do not ask for permission - just act on their feedback.".to_string()
|
||||
),
|
||||
instructions: Some(build_instructions()),
|
||||
};
|
||||
|
||||
serde_json::to_value(result).map_err(|e| JsonRpcError::internal_error(&e.to_string()))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue