Hard fork of xai-org/grok-build (Apache-2.0) re-targeted as Kigi, an
unofficial Kimi Code CLI community build.
Rename & identity
- 72 xai-*/xai-grok-* crates -> kigi-* (explicit: xai-grok-pager-bin ->
kigi-bin [binary `kigi`], xai-grok-pager -> kigi-tui; rest mechanical);
ptyctl, ptyctl-cli, third_party/ unchanged; proto package
xai.grok.tools.v1 -> kigi.tools.v1
- Config home ~/.kigi (KIGI_SHARE_DIR override), env prefix GROK_* ->
KIGI_*, `kigi --version` carries the unofficial-community-build notice
- clap identity, help text, startup banner, prompt templates rebranded
(templates re-encrypted)
Deletions (PRD removal list #5/#6/#7/#9/#10)
- voice input (xai-grok-voice) and all TUI wiring
- telemetry: Mixpanel client, external OTel stream, Sentry, OTLP layers,
trace/GCS/S3 upload queues (kigi-file-utils halved), workspace upload
module & dc_log, heap-profile uploader, auth-diagnostics uploader,
session-analytics halves of feedback; local zero-egress observability
preserved in new kigi-log crate (unified log, --debug firehose,
subsystem file logs, opt-in instrumentation)
- announcements (crate, remote-settings fields, TUI surfaces)
- plugin marketplace (crate, sources/browse/CTA/extensions-modal tab);
direct plugin install/uninstall/update via kigi-agent git_install kept
- relay/gateway/assets endpoints and features (agent relay, headless
relay transport, gateway bridge, LeaderEnvUrls); leader IPC socket now
~/.kigi/leader.sock + KIGI_LEADER_SOCKET, no ws-url derivation
- functional types rehomed instead of deleted: PermissionMode ->
kigi-config-types, McpInitStrategy -> kigi-mcp, PrCreationSource ->
session signals, TerminalDiagnostics -> kigi-pager-render, agent_id ->
shell util
Endpoints
- kigi-env rewritten: single production KigiEndpoints {coding_api_base_url
https://api.kimi.com/coding/v1 (KIGI_CODE_BASE_URL), oauth_host
https://auth.kimi.com (KIGI_OAUTH_HOST), update_base_url (GitHub
Releases API), upgrade_page_url}; GrokBuildEnvironment enum deleted
Toolchain & workspace hygiene
- Rust 1.97.0 pinned; edition 2024; full cargo update; git2 hoisted to
workspace at 0.21 (Option->Result API migration), quick-xml 0.41
- Root Cargo.toml hand-maintained (PRD §8.1): version 0.1.0 inherited by
all members, members sorted, unused deps pruned
- cargo-deny advisories gate (deny.toml with documented transitive
exceptions); CI workflow (check/clippy/fmt/deny/test, macOS+Linux)
- cross-crate test seams re-gated behind `test-support` cargo feature;
insta snapshot baselines renamed to the kigi_tui prefix
- clippy --workspace --all-targets: zero warnings; fmt clean
Fixes surfaced by the port
- updater probe/installer divergence (bin/kigi vs bin/grok symlink set)
- idle model-metadata refresh dead under KIGI_CODE_BASE_URL override
(new is_effective_coding_endpoint_url, loopback+override aware)
- macOS symlinked-TMPDIR fixture canonicalization (foreign_sessions,
fast-worktree); RSS measurement tests serialized via serial_test
Docs & legal (Apache §4)
- NOTICE added (upstream attribution + change statement); THIRD-PARTY
notices sustained; kigi-tools ported-code notices extended; README,
CONTRIBUTING, SECURITY, AGENTS.md rewritten
Out of scope for M0 (tracked): Kimi auth/inference (M1), search/fetch,
command parity, config import (M2), Computer Hub excision & final
brand-token sweep (M2), distribution & self-update rewrite (M3).
159 lines
5.8 KiB
Rust
159 lines
5.8 KiB
Rust
//! MCP server re-exports + shell-side wrappers for timeout override resolution.
|
|
|
|
pub use kigi_mcp::servers::{
|
|
AcpServerEntry, HttpConfig, MCP_TOOL_NAME_DELIMITER, McpClient, McpClientTimeoutOverrides,
|
|
McpConfigDiff, McpError, McpInitStrategy, McpMetaConfigMap, McpServerMetaConfig, McpServerName,
|
|
McpService, McpState, McpTool, McpToolRegistration, OauthInteractivity, SharedMcpPool,
|
|
mcp_server_name, mcp_target_str, mcp_transport_str, parse_mcp_meta_config, parse_mcp_tool_name,
|
|
sanitize_descriptor_segment, validate_tool_name,
|
|
};
|
|
|
|
use std::collections::HashMap;
|
|
use std::path::Path;
|
|
|
|
use agent_client_protocol as acp;
|
|
use kigi_mcp::oauth_config::{McpOAuthConfig, McpOAuthConfigMap};
|
|
use kigi_mcp::servers as inner;
|
|
|
|
fn resolve_overrides(
|
|
server_name: &str,
|
|
cwd: Option<&Path>,
|
|
) -> Option<inner::McpClientTimeoutOverrides> {
|
|
let config = match cwd {
|
|
Some(cwd) => crate::util::config::get_mcp_server_config_with_project(server_name, cwd),
|
|
None => crate::util::config::get_mcp_server_config(server_name),
|
|
};
|
|
// Fall back to the globally-resolved startup timeout so servers without a
|
|
// per-server `startup_timeout_sec` (e.g. `~/.claude.json` imports) still get it.
|
|
let global_startup = crate::util::config::resolved_mcp_startup_timeout_secs();
|
|
Some(inner::McpClientTimeoutOverrides {
|
|
startup_timeout_sec: config
|
|
.as_ref()
|
|
.and_then(|c| c.startup_timeout_sec)
|
|
.or(Some(global_startup)),
|
|
tool_timeout_sec: config.as_ref().and_then(|c| c.tool_timeout_sec),
|
|
tool_timeouts: config.as_ref().and_then(|c| c.tool_timeouts.clone()),
|
|
expose_image_base64: config.as_ref().and_then(|c| c.expose_image_base64),
|
|
})
|
|
}
|
|
|
|
/// Build the config-resolved event data from a list of MCP server configs.
|
|
pub fn build_config_resolved_event(
|
|
configs: &[acp::McpServer],
|
|
cwd: &Path,
|
|
) -> kigi_file_utils::events::Event {
|
|
let disabled: Vec<String> = crate::util::config::disabled_mcp_server_names(cwd)
|
|
.into_iter()
|
|
.collect();
|
|
let servers = configs
|
|
.iter()
|
|
.map(|c| kigi_file_utils::events::McpConfigServer {
|
|
name: inner::mcp_server_name(c).to_string(),
|
|
transport: inner::mcp_transport_str(c).to_string(),
|
|
source: if inner::mcp_server_name(c)
|
|
.starts_with(crate::session::managed_mcp::MANAGED_MCP_PREFIX)
|
|
{
|
|
"managed"
|
|
} else {
|
|
"local"
|
|
}
|
|
.to_string(),
|
|
})
|
|
.collect();
|
|
kigi_file_utils::events::Event::McpConfigResolved { servers, disabled }
|
|
}
|
|
|
|
pub async fn start_mcp_server(
|
|
mcp_server: acp::McpServer,
|
|
session_id: Option<&str>,
|
|
cwd: Option<&Path>,
|
|
meta_config: Option<&inner::McpServerMetaConfig>,
|
|
byo_config: Option<&McpOAuthConfig>,
|
|
event_writer: &kigi_file_utils::events::EventWriter,
|
|
mode: OauthInteractivity,
|
|
) -> Result<inner::McpClient, inner::McpError> {
|
|
let overrides = resolve_overrides(inner::mcp_server_name(&mcp_server), cwd);
|
|
inner::start_mcp_server(
|
|
mcp_server,
|
|
session_id,
|
|
overrides.as_ref(),
|
|
meta_config,
|
|
byo_config,
|
|
event_writer,
|
|
mode,
|
|
)
|
|
.await
|
|
}
|
|
|
|
/// Build all pending MCP clients for one init pass as a single merged list: config-declared
|
|
/// servers (HTTP/stdio, spawned lock-free via [`start_mcp_servers`]) and SDK in-process
|
|
/// servers (built under a brief lock via `McpState::build_pending_acp_clients`). SDK clients
|
|
/// never fail to build, so they enter as `Ok`. One entry point so the init batch doesn't
|
|
/// invoke two builders.
|
|
pub async fn build_pending_clients(
|
|
mcp_state: &tokio::sync::Mutex<inner::McpState>,
|
|
configs_to_start: Vec<acp::McpServer>,
|
|
session_id: Option<&str>,
|
|
cwd: Option<&Path>,
|
|
meta_config_map: &inner::McpMetaConfigMap,
|
|
oauth_config_map: &McpOAuthConfigMap,
|
|
event_writer: &kigi_file_utils::events::EventWriter,
|
|
mode: OauthInteractivity,
|
|
) -> Vec<Result<inner::McpClient, inner::McpError>> {
|
|
let mut results = start_mcp_servers(
|
|
configs_to_start,
|
|
session_id,
|
|
cwd,
|
|
meta_config_map,
|
|
oauth_config_map,
|
|
event_writer,
|
|
mode,
|
|
)
|
|
.await;
|
|
// Re-resolve SDK (ACP) config.toml overrides for THIS init, matching HTTP/stdio, so a
|
|
// mid-session config change applies on the next init (resolved outside the lock — it
|
|
// reads config.toml — then handed to the pure, under-lock builder).
|
|
let acp_overrides: HashMap<String, inner::McpClientTimeoutOverrides> = {
|
|
let names = mcp_state.lock().await.pending_acp_server_names();
|
|
names
|
|
.iter()
|
|
.filter_map(|name| resolve_overrides(name, cwd).map(|o| (name.clone(), o)))
|
|
.collect()
|
|
};
|
|
// Brief lock, no `.await` held: the SDK clients are built synchronously (pure).
|
|
let acp_clients = mcp_state
|
|
.lock()
|
|
.await
|
|
.build_pending_acp_clients(&acp_overrides);
|
|
results.extend(acp_clients.into_iter().map(Ok));
|
|
results
|
|
}
|
|
|
|
pub async fn start_mcp_servers(
|
|
mcp_servers: Vec<acp::McpServer>,
|
|
session_id: Option<&str>,
|
|
cwd: Option<&Path>,
|
|
meta_config_map: &inner::McpMetaConfigMap,
|
|
oauth_config_map: &McpOAuthConfigMap,
|
|
event_writer: &kigi_file_utils::events::EventWriter,
|
|
mode: OauthInteractivity,
|
|
) -> Vec<Result<inner::McpClient, inner::McpError>> {
|
|
let overrides_map: HashMap<String, inner::McpClientTimeoutOverrides> = mcp_servers
|
|
.iter()
|
|
.filter_map(|s| {
|
|
let name = inner::mcp_server_name(s);
|
|
resolve_overrides(name, cwd).map(|o| (name.to_string(), o))
|
|
})
|
|
.collect();
|
|
inner::start_mcp_servers(
|
|
mcp_servers,
|
|
session_id,
|
|
&overrides_map,
|
|
meta_config_map,
|
|
oauth_config_map,
|
|
event_writer,
|
|
mode,
|
|
)
|
|
.await
|
|
}
|