mod auth;
mod serve;
use std::fs;
use std::sync::Arc;
use axum::body::Body;
use axum::http::Request;
use tempfile::TempDir;
use tokio_util::sync::CancellationToken;
use super::{MCP_PATH, build_router};
use crate::catalog::{Catalog, CatalogHandle, OnBroken};
use crate::config::{Config, Secret};
use crate::server::{PreparedTools, PromptForgeServer};
const TOKEN: &str = "shared-bearer";
const ECHO: &str = "---\nname: echo\ndescription: Returns its argument\npromptforge: 1\n---\n\n\
# Test prompt\n\n## Main\n\n```lua\nreturn args\n```\n";
fn router() -> (TempDir, axum::Router) {
router_with(Secret::try_from(TOKEN.to_string()).expect("the fixture token is non-blank"))
}
fn router_with(token: Secret) -> (TempDir, axum::Router) {
let (dir, config) = fixture(&format!("token = \"{TOKEN}\"\n"));
let catalog =
Catalog::resolve(&config, OnBroken::Reject).expect("the fixture catalog resolves");
let tools = Arc::new(
PreparedTools::new(
&config.gateway,
promptforge_core::model::ModelCatalog::empty(),
)
.expect("prepare fixture live tools"),
);
let server = PromptForgeServer::new(
Arc::new(config),
Arc::new(CatalogHandle::new(catalog)),
tools,
);
(
dir,
build_router(
server,
Arc::new(token),
CancellationToken::new(),
vec!["127.0.0.1".to_string()],
),
)
}
fn server_fixture(server_lines: &str) -> (TempDir, Arc<Config>, PromptForgeServer) {
let (dir, config) = fixture(server_lines);
let catalog =
Catalog::resolve(&config, OnBroken::Reject).expect("the fixture catalog resolves");
let tools = Arc::new(
PreparedTools::new(
&config.gateway,
promptforge_core::model::ModelCatalog::empty(),
)
.expect("prepare fixture live tools"),
);
let config = Arc::new(config);
let server = PromptForgeServer::new(
Arc::clone(&config),
Arc::new(CatalogHandle::new(catalog)),
tools,
);
(dir, config, server)
}
fn fixture(server_lines: &str) -> (TempDir, Config) {
let dir = tempfile::tempdir().expect("create a temporary prompts directory");
fs::write(dir.path().join("echo.md"), ECHO).expect("write the fixture prompt");
let config = Config::from_toml_str(&format!(
"[server]\n{server_lines}\n\
[gateway]\nurl = \"http://127.0.0.1:8081/v1\"\nkey = \"gw\"\n\n\
[paths]\nprompts = '{}'\n\n\
[catalog]\ninclude = [\"*.md\"]\n",
dir.path().display()
))
.expect("the fixture configuration parses");
(dir, config)
}
fn initialize(authorization: Option<&str>) -> Request<Body> {
let mut builder = Request::builder()
.method("POST")
.uri(MCP_PATH)
.header("host", "127.0.0.1")
.header("accept", "application/json, text/event-stream")
.header("content-type", "application/json");
if let Some(value) = authorization {
builder = builder.header("authorization", value);
}
builder
.body(Body::from(
serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "test", "version": "0" },
},
})
.to_string(),
))
.expect("build the initialize request")
}
fn router_hosts(allowed_hosts: Vec<String>) -> (TempDir, axum::Router) {
let (dir, config) = fixture(&format!("token = \"{TOKEN}\"\n"));
let catalog =
Catalog::resolve(&config, OnBroken::Reject).expect("the fixture catalog resolves");
let tools = Arc::new(
PreparedTools::new(
&config.gateway,
promptforge_core::model::ModelCatalog::empty(),
)
.expect("prepare fixture live tools"),
);
let server = PromptForgeServer::new(
Arc::new(config),
Arc::new(CatalogHandle::new(catalog)),
tools,
);
(
dir,
build_router(
server,
Arc::new(Secret::try_from(TOKEN.to_string()).expect("the fixture token is non-blank")),
CancellationToken::new(),
allowed_hosts,
),
)
}
fn initialize_host(host: &str) -> Request<Body> {
Request::builder()
.method("POST")
.uri(MCP_PATH)
.header("host", host)
.header("authorization", format!("Bearer {TOKEN}"))
.header("accept", "application/json, text/event-stream")
.header("content-type", "application/json")
.body(Body::from(
serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "test", "version": "0" },
},
})
.to_string(),
))
.expect("build the initialize request")
}