-
Notifications
You must be signed in to change notification settings - Fork 0
MCP server: expose Rivet tools, resources, and prompts via Model Context Protocol #98
Description
Context
The AI agent ecosystem is converging on Model Context Protocol (MCP) as the standard for tool integration. Claude Code, Cursor, Goose, Kiro, and OpenCode all support MCP natively. Currently agents interact with Rivet via CLI (rivet validate --format json, rivet list --format json, etc.) which requires shell-out + JSON parsing. An MCP server would give agents typed, validated, direct access to the artifact graph.
This pairs with issue #93 (spec-driven development) — the Guide API from Phase 4 is the ideal surface to expose over MCP. It also pairs with #22 (rowan + salsa) — incremental validation via salsa makes real-time MCP resource subscriptions efficient.
Strategic value: Rivet becomes a first-class tool in any MCP-capable agent's toolbox, not just a CLI to shell out to. Agents can query traceability, validate changes, create artifacts, and check coverage without parsing text output.
Prior art
- rmcp — Official Rust SDK for MCP (v0.16+). Provides
#[tool]macro,ServerHandlertrait,ToolRouter, stdio/SSE/streamable-HTTP transports. - codeprism-mcp-server — Rust MCP server for code analysis (reference for tool design patterns).
- Rivet already has axum for the dashboard (
rivet serve) — streamable HTTP transport can share the same server.
Design
New command
# Standalone MCP server over stdio (for Claude Code, Cursor, etc.)
rivet mcp
# MCP over streamable HTTP (for web-based agents, can co-host with dashboard)
rivet mcp --transport http --port 3001
# Combined: dashboard + MCP on same server
rivet serve --mcpCrate structure
Add MCP support to rivet-cli (where serve already lives), with a new module rivet-cli/src/mcp/. Depends on:
[dependencies]
rmcp = { version = "0.16", features = ["server", "transport-io", "transport-streamable-http-server", "macros"] }
schemars = "0.8" # Already used for JSON schema generation in --format jsonTools (agent-callable functions)
These map to existing CLI commands, exposing them as MCP tools with typed parameters:
| Tool | Parameters | Returns | Maps to |
|---|---|---|---|
rivet_validate |
baseline?: string, format?: "summary"|"full" |
Diagnostics array (level, artifact_id, message, rule) | rivet validate --format json |
rivet_list |
type?: string, status?: string, search?: string, limit?: number |
Artifact array (id, title, type, status, links) | rivet list --format json |
rivet_get |
id: string |
Full artifact (all fields, links, backlinks) | rivet get --format json (from #93 Phase 3) |
rivet_add |
type: string, title: string, fields?: object, links?: [{type, target}] |
Created artifact ID | rivet add |
rivet_link |
source: string, link_type: string, target: string |
Success/failure | rivet link |
rivet_modify |
id: string, fields: object |
Updated artifact | rivet modify |
rivet_coverage |
type?: string |
Coverage percentages per type + gap list | rivet coverage --format json |
rivet_impact |
since?: string |
Changed artifacts + affected downstream | rivet impact --format json |
rivet_stats |
— | Artifact counts by type | rivet stats --format json |
rivet_schema_info |
schema?: string |
Types, link types, rules for a schema | rivet schema show --format json |
rivet_guide |
scope?: string |
Guide report (schema guidance + project health) | rivet guide (from #93 Phase 4) |
rivet_spec_status |
spec_id: string |
Spec completion: requirements, design, tasks, tests coverage | New — builds on SDD schema |
Resources (agent-readable data)
MCP resources let agents read artifact data directly without calling tools:
| Resource URI | Description | MIME type |
|---|---|---|
rivet://artifacts/{id} |
Single artifact as YAML | text/yaml |
rivet://schemas/{name} |
Schema definition | text/yaml |
rivet://coverage |
Full coverage matrix | application/json |
rivet://diagnostics |
Current validation diagnostics | application/json |
rivet://agents-md |
Generated AGENTS.md content | text/markdown |
rivet://graph/{id} |
Subgraph reachable from artifact | application/json |
Resource templates (dynamic):
rivet://artifacts?type={type}— all artifacts of a typerivet://artifacts?status={status}— all artifacts with status
Prompts (stored prompts for agents)
| Prompt | Arguments | Purpose |
|---|---|---|
rivet_create_spec |
title, description |
Guides agent through creating a spec with requirements, design, tasks |
rivet_fill_gaps |
type? |
Generates instructions to fill traceability coverage gaps |
rivet_review_artifact |
id |
Reviews an artifact for completeness, link quality, field coverage |
Implementation sketch
use rmcp::{ServerHandler, tool_handler, tool_router, tool};
use rmcp::model::{ServerInfo, ServerCapabilities, Implementation};
use schemars;
#[derive(Clone)]
pub struct RivetMcpServer {
tool_router: ToolRouter<Self>,
// Shared state: AppState from rivet-cli (store, schema_set, config)
state: Arc<AppState>,
}
#[tool_router]
impl RivetMcpServer {
pub fn new(state: Arc<AppState>) -> Self {
Self { tool_router: Self::tool_router(), state }
}
}
#[tool_handler]
impl ServerHandler for RivetMcpServer {
fn get_info(&self) -> ServerInfo {
ServerInfo {
protocol_version: ProtocolVersion::V_2025_06_18,
capabilities: ServerCapabilities::builder()
.enable_tools()
.enable_resources()
.enable_prompts()
.build(),
server_info: Implementation {
name: "rivet".into(),
version: env!("CARGO_PKG_VERSION").into(),
},
instructions: Some(
"Rivet is an SDLC traceability tool. Use tools to query, \
validate, and mutate lifecycle artifacts.".into()
),
}
}
}
// Example tool definition
#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct ValidateParams {
#[schemars(description = "Baseline version for scoped validation")]
baseline: Option<String>,
#[schemars(description = "Output detail level")]
format: Option<String>,
}
#[tool(description = "Validate artifact traceability, link integrity, and schema rules")]
async fn rivet_validate(
&self,
Parameters(params): Parameters<ValidateParams>,
) -> Result<CallToolResult, McpError> {
// Reuse existing validate logic from rivet-core
let diagnostics = self.state.store.validate(&self.state.schema_set, ...)?;
let json = serde_json::to_string_pretty(&diagnostics)?;
Ok(CallToolResult::success(vec![Content::text(json)]))
}Transport configuration for Claude Code
Users add to .claude/settings.json:
{
"mcpServers": {
"rivet": {
"command": "rivet",
"args": ["mcp"],
"cwd": "/path/to/project"
}
}
}Or for HTTP transport (shared with dashboard):
{
"mcpServers": {
"rivet": {
"url": "http://localhost:3000/mcp"
}
}
}Phases
Phase 1: Core tools over stdio
rivet mcpcommand with stdio transport- Tools:
rivet_validate,rivet_list,rivet_get,rivet_stats,rivet_coverage - Basic error handling (McpError mapping from anyhow)
- Test with Claude Code MCP integration
Phase 2: Mutation tools + resources
- Tools:
rivet_add,rivet_link,rivet_modify,rivet_impact - Resources:
rivet://artifacts/{id},rivet://schemas/{name},rivet://diagnostics - Resource change notifications (when validation state changes)
Phase 3: HTTP transport + dashboard integration
- Streamable HTTP transport on
rivet serve --mcp - Share
AppStatebetween axum dashboard routes and MCP handler - SSE support for backwards compatibility
Phase 4: Prompts + guide integration
- Prompts:
rivet_create_spec,rivet_fill_gaps,rivet_review_artifact rivet_guidetool (depends on Spec-driven development: schema packages, bridges, guide API, CRUD CLI #93 Phase 4)rivet_spec_statustool (depends on SDD schema)
Dependencies
- Spec-driven development: schema packages, bridges, guide API, CRUD CLI #93 Phase 3 (
rivet getcommand) forrivet_gettool - Spec-driven development: schema packages, bridges, guide API, CRUD CLI #93 Phase 4 (Guide API) for
rivet_guidetool andrivet_fill_gapsprompt - SDD schema for
rivet_spec_statustool
References
- MCP specification
- rmcp crate (official Rust SDK)
- rmcp server guide
- Streamable HTTP transport
- MCP reference servers
- Claude Code MCP integration:
.claude/settings.json→mcpServers - Goose MCP: native MCP client across 6 transport types
- Cursor MCP: supports stdio and HTTP MCP servers