github-mcp 0.1.3

GitHub v3 REST API MCP server, generated by mcpify.
Documentation
// GitHub v3 REST API MCP server — generated by mcpify. Do not hand-edit.
//
// Populates a store db's semantic_endpoints vec0 table with real
// embedding vectors. mcpify's Rust generator only creates this table's
// schema (see the plan's embeddings decision) — this binary is the single
// place vectors get computed, reusing the exact same embedding function the
// `search` tool calls at runtime (`services::embedding_service::embed`), so
// indexing-time and live-query embeddings are guaranteed to share the same
// model and vector space. Vectors are bound as a raw little-endian `f32`
// blob (sqlite-vec's native `FLOAT[N]` wire format) rather than a JSON
// array string — `services::embedding_service`'s `search`-side query code
// (Story R6) must encode/decode with this exact same byte layout.

use std::path::{Path, PathBuf};

use github_mcp::data::store::{VERSION_STORE_FILES, open_store_read_write};
use github_mcp::services::embedding_service::embed;

struct EndpointRow {
    operation_id: String,
    path: String,
    method: String,
    summary: Option<String>,
    description: Option<String>,
}

fn vector_to_le_bytes(vector: &[f32]) -> Vec<u8> {
    vector
        .iter()
        .flat_map(|value| value.to_le_bytes())
        .collect()
}

/// Populates one store file's `semantic_endpoints` table in place,
/// returning how many operations were (re)indexed.
fn populate_one(path: &Path) -> anyhow::Result<usize> {
    let conn = open_store_read_write(path)?;

    let mut select =
        conn.prepare("SELECT operation_id, path, method, summary, description FROM endpoints")?;
    let rows: Vec<EndpointRow> = select
        .query_map([], |row| {
            Ok(EndpointRow {
                operation_id: row.get(0)?,
                path: row.get(1)?,
                method: row.get(2)?,
                summary: row.get(3)?,
                description: row.get(4)?,
            })
        })?
        .collect::<Result<_, _>>()?;

    // sqlite-vec's vec0 virtual tables don't implement conflict resolution
    // (no ON CONFLICT / INSERT OR REPLACE support — a duplicate primary key
    // always raises a UNIQUE constraint error, regardless of the conflict
    // clause used), so re-running this script against a db that already has
    // rows requires an explicit delete before each insert to stay idempotent.
    let mut delete = conn.prepare("DELETE FROM semantic_endpoints WHERE operation_id = ?1")?;
    let mut insert =
        conn.prepare("INSERT INTO semantic_endpoints (operation_id, embedding) VALUES (?1, ?2)")?;

    // Sequential, not parallelized: keeps peak memory bounded regardless of
    // how many operations a spec declares, at the cost of total wall time —
    // an acceptable trade for a one-time setup script.
    let count = rows.len();
    for row in rows {
        let text = [
            Some(row.method),
            Some(row.path),
            row.summary,
            row.description,
        ]
        .into_iter()
        .flatten()
        .collect::<Vec<_>>()
        .join(" ");
        let vector = embed(&text)?;
        delete.execute(rusqlite::params![row.operation_id])?;
        insert.execute(rusqlite::params![
            row.operation_id,
            vector_to_le_bytes(&vector)
        ])?;
    }

    Ok(count)
}

/// Which store file(s) to populate: bare invocation targets just
/// `mcp_store.db` (the default version), matching every deployment that
/// only ever ships that one file (this project's own Dockerfile
/// included); an explicit path targets exactly that file; `--all` walks
/// every version this project's ledger knows about
/// (`VERSION_STORE_FILES`) — the one-time local backfill needed after
/// `mcpify add-version` adds a new version file, since each version's
/// `.db` gets its own independent `semantic_endpoints` table.
fn targets() -> Vec<PathBuf> {
    let mut args = std::env::args().skip(1);
    match args.next().as_deref() {
        Some("--all") => VERSION_STORE_FILES
            .iter()
            .map(|(_, file)| PathBuf::from(file))
            .collect(),
        Some(path) => vec![PathBuf::from(path)],
        None => vec![PathBuf::from("mcp_store.db")],
    }
}

fn main() -> anyhow::Result<()> {
    for path in targets() {
        let count = populate_one(&path)?;
        println!(
            "populated embeddings for {count} operation(s) in '{}'",
            path.display()
        );
    }
    Ok(())
}