SemaBLOCK
A Lisp with first-class LLM primitives, implemented in Rust
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
A Lisp where LLM agents are language primitives, not an SDK — compiled to a fast bytecode VM, shipped as a single binary.
[](https://sema.run) [](https://sema-lang.com/docs/) [](https://github.com/sema-lisp/sema/releases/latest) [](https://codecov.io/gh/sema-lisp/sema) [](LICENSE)
**Docs** · **Playground** · **For Agents** · **Examples** · **Issues**
Stop rewriting the agent loop. Every LLM script grows the same scaffolding — retries, caching, cost caps, rate limits, tool dispatch, conversation state. Sema makes that scaffolding the runtime: your script stays the size of its idea, ships as a single binary, and your coding agent already speaks the language.
Sema is a Scheme-like Lisp where prompts are s-expressions, conversations are persistent data structures, and LLM calls are just another form of evaluation — with Clojure-style keywords (:foo), map literals ({:key val}), and vector literals ([1 2 3]).
What It Looks Like
A coding agent with file tools, safety checks, and budget tracking — in ~40 lines:
;; Define tools the LLM can call
(deftool read-file
"Read a file's contents"
{:path {:type :string :description "File path"}}
(lambda (path)
(if (file/exists? path) (file/read path) "File not found")))
(deftool edit-file
"Replace text in 9f8c1a173cd8OBSERVED · 2026-10-08Connect
Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.
claude mcp add sema-website --env ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} --env OPENAI_API_KEY=${OPENAI_API_KEY} -- npx -y sema-website{
"mcpServers": {
"sema-website": {
"command": "npx",
"args": [
"-y",
"sema-website"
],
"env": {
"ANTHROPIC_API_KEY": "${ANTHROPIC_API_KEY}",
"OPENAI_API_KEY": "${OPENAI_API_KEY}"
}
}
}
}Exposed tools (17)
9 read · 7 write · 1 destructive. Blast radius: 1 tool can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.
| Tool | Risk | Description |
|---|---|---|
continue_debugging | read | Continue the paused Sema debugger and wait for the next breakpoint, finish, or error. |
find_examples | read | Find bundled Sema examples by filename, category, or identifier. Returns identifiers accepted by load_example. |
format_editor | write | Format and replace the current Sema source in the editor. This does not run the code. |
get_debug_state | read | Read debugger status, active line, breakpoints, locals, and stack frames. |
list_files | read | List the immediate children of one virtual filesystem directory. The listing is non-recursive. |
load_example | read | Replace the editor with one bundled Sema example selected by identifier or filename. This does not run the code. |
read_editor | read | Read Sema source from the playground editor. Use this before editing or running unfamiliar code. |
read_file | read | Read a character range from a UTF-8 text file in the playground virtual filesystem. |
read_output | read | Read the current playground output, including values, printed lines, errors, and timing. |
run_editor | write | Run the current Sema source, using the worker runtime when available, and wait for evaluation to finish. |
set_breakpoints | write | Replace all debugger breakpoints with one-based lines, snapping requests to executable source lines. |
start_debugging | write | Start debugging the current Sema source and wait for a pause, finish, or error. |
step_debugger | read | Step the paused Sema debugger into, over, or out, then wait for its next stable state. |
stop_debugging | write | Stop the active Sema debugger and return the playground to its idle state. |
stop_run | write | Cancel the active worker-backed Sema evaluation when one is running. |
write_editor | write | Replace the entire playground editor with Sema source. This does not run the code. |
write_file | destructive | Create or overwrite a UTF-8 virtual file, creating parent directories as needed. Content is limited to 1 MiB. |
Trust audit
BLOCKgrade F · trust 32/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | FAIL |
| L2 | Instruction surface (what it tells the agent) | WARN |
| L3 | Class-specific surface | WARN |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- declared (8 observation(s))
- Network
- declared (14 observation(s))
- Shell
- declared (5 observation(s))
- Dependencies
- not all pinned
- Secrets in source
- found
Findings (25)
let gh = "ghp_0000000000000000000000000000000000000000";
let pk = "-----BEGIN RSA PRIVATE KEY-----";
self.map_err(|e| SemaError::eval(format!("{what}: {e}")))Err(SemaError::eval(format!(
return Err(SemaError::eval(
.map_err(|error| SemaError::eval(format!("module load scope: {error}")))?;Err(SemaError::eval(
url_host("http://169.254.169.254/latest").as_deref(),Some("169.254.169.254")"169.254.169.254", // cloud metadata
"0xA9FEA9FE", // 169.254.169.254 cloud metadata
url_host("http://169.254.169.254/latest").as_deref(),Value::string("http://169.254.169.254/"),"http://127.0.0.1:1/callback".to_string()
"http://127.0.0.1:1/callback".to_string()
assert!(driver.redirect_uri().starts_with("http://127.0.0.1"));zwnj: "",
zwj: "",
`).map((t) => _`<span class="cl">${an(t === "" ? "" : t)}</span>`);(r, o) => _`<div class="ln ${o + 1 === t ? "cur" : ""}" part="line">${an(r === "" ? "" : r)}</div>`let hit = "api_key = 'a8Fk3Lm9Zq2Wx7Bv1Nc4Pd6'";
<p style="font-size:0.78rem; color:var(--text);">
<a href="/auth/github?mode=connect&return_to=/account" class="btn btn-secondary" style="font-size:0.7rem; padding:0.4rem 0.8rem;">Reconnect</a>
<p style="font-size:0.78rem; color:var(--text-dim); margin-bottom:0.75rem;">
<a href="/auth/github?mode=connect&return_to=/account" class="btn btn-primary" style="font-size:0.7rem; padding:0.45rem 0.8rem;">Connect GitHub</a>
Gates applied: critical_finding, no_behavioural_pass.
9f8c1a173cd8full audit observations/trust-audit/mcp-server/sema-lisp__sema-1.json · Report an issue / request a re-scanAudit history
Every audit this server has had. A grade with a past is a grade somebody is still checking.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-08 | 9f8c1a173cd8 | BLOCK | F | 32 | first audit |
Questions
What is the Sema MCP server?
A Lisp with first-class LLM primitives, implemented in Rust
What tools does Sema expose?
17 in total: 9 read-only, 7 that write, and 1 that can delete or overwrite (write_file). Every one is listed on this page with its risk.
Is Sema safe to connect to an agent?
No — not without reading the findings first. The audit graded it F (32/100) and found 11 critical or high issues in the source. Each one is listed on this page with the file and line it is on. Separately from the audit: 1 of its tools can destroy data, so scope the token you give it to what you actually need.
What credentials does Sema need?
It reads ANTHROPIC_API_KEY and OPENAI_API_KEY from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.
How does Sema run?
It speaks stdio and streamable-http, so it runs as a local process your client starts. It is published on npm as sema-website.
How current is this page?
The grade is for one exact copy of the source (9f8c1a173cd8), read on 2026-10-08. The repository is watched and re-audited when it changes.