1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::net::SocketAddr;
use std::path::PathBuf;
use clap::{Args, Subcommand, ValueEnum};
#[derive(Debug, Args)]
pub(crate) struct ServeArgs {
#[command(subcommand)]
pub command: ServeCommand,
}
#[derive(Debug, Subcommand)]
pub(crate) enum ServeCommand {
/// Serve a .harn agent over stdio using ACP.
Acp(ServeAcpArgs),
/// Serve a .harn agent over HTTP using A2A.
A2a(A2aServeArgs),
/// Serve a `.harn` file as an MCP server. Exposes either exported
/// `pub fn` entrypoints (recommended) or, when the script registers
/// tools/resources/prompts via `mcp_tools(...)` / `mcp_resource(...)`
/// / `mcp_prompt(...)`, that script-driven surface.
Mcp(ServeMcpArgs),
}
#[derive(Debug, Args)]
pub(crate) struct ServeAcpArgs {
/// Path to the .harn file to serve.
pub file: String,
}
#[derive(Debug, Args)]
pub(crate) struct A2aServeArgs {
/// Port to bind the A2A server to.
#[arg(long, default_value_t = 8080)]
pub port: u16,
/// Public URL advertised in the A2A agent card.
#[arg(long = "public-url", env = "HARN_SERVE_A2A_PUBLIC_URL")]
pub public_url: Option<String>,
/// Static API keys accepted via `Authorization: Bearer` or `X-API-Key`.
#[arg(long = "api-key", env = "HARN_SERVE_API_KEY", value_delimiter = ',')]
pub api_key: Vec<String>,
/// Shared secret for HMAC request signing.
#[arg(long = "hmac-secret", env = "HARN_SERVE_HMAC_SECRET")]
pub hmac_secret: Option<String>,
/// Shared secret used to attach an HS256 signature to the agent card.
#[arg(long = "card-signing-secret", env = "HARN_SERVE_A2A_CARD_SECRET")]
pub card_signing_secret: Option<String>,
/// TLS listener mode. Supplying both `--cert` and `--key` implies `pem`.
#[arg(long = "tls", value_enum, default_value_t = ServeTlsMode::Plain)]
pub tls: ServeTlsMode,
/// PEM-encoded certificate chain for in-process HTTPS termination.
#[arg(long, env = "HARN_SERVE_CERT", value_name = "PATH")]
pub cert: Option<PathBuf>,
/// PEM-encoded private key for in-process HTTPS termination.
#[arg(long, env = "HARN_SERVE_KEY", value_name = "PATH")]
pub key: Option<PathBuf>,
/// Path to the .harn file to serve.
pub file: String,
}
#[derive(Debug, Args)]
pub(crate) struct ServeMcpArgs {
/// Transport to expose for MCP clients.
#[arg(long, value_enum, default_value_t = McpServeTransport::Stdio)]
pub transport: McpServeTransport,
/// Socket address to bind when serving over HTTP.
#[arg(
long,
env = "HARN_SERVE_MCP_BIND",
default_value = "127.0.0.1:8765",
value_name = "ADDR"
)]
pub bind: SocketAddr,
/// Streamable HTTP endpoint path.
#[arg(long, default_value = "/mcp", value_name = "PATH")]
pub path: String,
/// Legacy SSE endpoint path for older MCP clients.
#[arg(long = "sse-path", default_value = "/sse", value_name = "PATH")]
pub sse_path: String,
/// Legacy SSE POST endpoint path for older MCP clients.
#[arg(
long = "messages-path",
default_value = "/messages",
value_name = "PATH"
)]
pub messages_path: String,
/// Static API keys accepted over HTTP via `Authorization: Bearer` or `X-API-Key`.
#[arg(long = "api-key", env = "HARN_SERVE_API_KEY", value_delimiter = ',')]
pub api_key: Vec<String>,
/// Shared secret for HMAC request signing on HTTP transports.
#[arg(long = "hmac-secret", env = "HARN_SERVE_HMAC_SECRET")]
pub hmac_secret: Option<String>,
/// TLS listener mode. Supplying both `--cert` and `--key` implies `pem`.
#[arg(long = "tls", value_enum, default_value_t = ServeTlsMode::Plain)]
pub tls: ServeTlsMode,
/// PEM-encoded certificate chain for in-process HTTPS termination.
#[arg(long, env = "HARN_SERVE_CERT", value_name = "PATH")]
pub cert: Option<PathBuf>,
/// PEM-encoded private key for in-process HTTPS termination.
#[arg(long, env = "HARN_SERVE_KEY", value_name = "PATH")]
pub key: Option<PathBuf>,
/// Optional Server Card JSON to advertise (MCP v2.1). Path to a
/// `.json` file OR an inline JSON string. The card is embedded in
/// the `initialize` response's `serverInfo.card` field AND exposed
/// as a static resource at `well-known://mcp-card`.
#[arg(long = "card", value_name = "PATH_OR_JSON")]
pub card: Option<String>,
/// Path to the `.harn` file whose exported `pub fn` entrypoints are
/// served. Scripts that instead call `mcp_tools(registry)` /
/// `mcp_resource(...)` / `mcp_prompt(...)` are detected and served
/// via the script-driven surface.
pub file: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub(crate) enum McpServeTransport {
Stdio,
Http,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub(crate) enum ServeTlsMode {
Plain,
Edge,
SelfSignedDev,
Pem,
}