MCP Server — Signet Docs

Docs / Reference

MCP Server

Model Context Protocol integration for native tool access.

MCP Server

The Signet Daemon exposes an MCP (Model Context Protocol) server that gives AI Harnesses native tool access to Memory operations. Instead of relying on shell commands or skill invocations, harnesses call Signet tools directly through MCP’s standardized interface.

Overview

MCP complements Signet’s existing hook-based integration:

  • Hooks handle lifecycle events (session start/end, prompt submission, compaction). They run automatically.
  • MCP tools provide on-demand operations (search, store, modify, forget). The agent invokes them when needed.

Both systems can be active simultaneously — they serve different purposes and don’t conflict.

Remote MCP endpoints expose tools only. If a harness also needs automatic identity, session-start context, prompt-time recall, or session-end extraction, install that harness’s Signet hooks as well. For Codex, run the connector with SIGNET_DAEMON_URL set to the remote daemon URL so the generated hooks and [mcp_servers.signet] block target the same instance:

SIGNET_DAEMON_URL=http://192.168.0.60:3850 signet setup --harness codex

SIGNET_DAEMON_URL must point at the daemon origin only. Signet rejects paths, query strings, fragments, credentials, and non-HTTP protocols so a remote MCP registration cannot silently fall back to localhost or bake unsafe shell syntax into generated lifecycle hooks.

When to Use MCP vs Hooks

ScenarioUse
Session start/end lifecycleHooks
Automatic memory extraction after each promptHooks
Agent wants to search memories mid-conversationMCP (memory_search)
Sub-agent needs to inspect its parent sessionMCP (session_search)
Agent wants to store a specific factMCP (memory_store)
Agent needs to run a command with secretsMCP (secret_exec)
Compaction boundary handlingHooks
Agent-initiated memory edits or deletionsMCP (memory_modify, memory_forget)

Rule of thumb: hooks are for automatic, lifecycle-driven events. MCP is for agent-initiated, on-demand operations.

Tool Reference

All tools are defined in platform/daemon/src/mcp/tools.ts. Tool handlers call the daemon’s HTTP API internally and use the shared recall/remember surface helpers from @signet/core so MCP, CLI, and harness integrations do not drift into separate request shapes or result formatting.

Hybrid vector + keyword search over stored memories. Returns results ranked by combined BM25 + vector similarity score with optional graph boost and reranking.

Parameters:

Primary controls:

NameTypeRequiredDescription
querystringyesSearch query text
limitnumbernoMax results to return (default 10)
projectstringnoOptional project path filter
expandbooleannoInclude expanded transcript/context sources

Common refinements:

NameTypeRequiredDescription
typestringnoFilter by memory type (e.g. "preference", "fact")
tagsstringnoFilter by tags (comma-separated)
whostringnoFilter by author
sincestringnoOnly include memories created after this date
untilstringnoOnly include memories created before this date

Advanced controls:

NameTypeRequiredDescription
keyword_querystringnoOverride the keyword/FTS query used for recall
pinnedbooleannoOnly return pinned memories
importance_minnumbernoMinimum memory importance threshold
min_scorenumbernoDeprecated compatibility alias for importance_min
score_minnumbernoMinimum recall score threshold, applied client-side to returned rows

Returns: A formatted recall brief with primary matches, supporting context, and no-hit handling. The tool still reads from POST /api/memory/recall under the hood.

Example:

{
  "query": "user prefers dark mode",
  "project": "/home/user/myapp",
  "limit": 5,
  "type": "preference",
  "score_min": 0.8
}

Daemon endpoint: POST /api/memory/recall

memory_store

Save a new memory to the database. Tags, structured graph payloads, hints, and transcripts are forwarded as request metadata instead of being folded into memory text. This keeps MCP aligned with CLI and harness remember behavior.

Parameters:

NameTypeRequiredDescription
contentstringyesMemory content to save
typestringnoMemory type (fact, preference, decision, etc.)
importancenumbernoImportance score 0–1
tagsstringnoComma-separated tags for categorization
pinnedbooleannoPin this memory so it bypasses decay
hintsstring[]noProspective recall hints and alternate phrasings
transcriptstringnoRaw source text to preserve alongside the extracted memory
structuredobjectnoPre-extracted entity/aspect/attribute graph data for structured remembering

Returns: The created memory object with its assigned ID.

Example:

{
  "content": "User prefers Bun over npm for package management",
  "importance": 0.8,
  "tags": "preference,tooling"
}

Daemon endpoint: POST /api/memory/remember

memory_get

Retrieve a single memory by its ID.

Parameters:

NameTypeRequiredDescription
idstringyesMemory ID to retrieve

Returns: Full memory object including content, type, importance, tags, created/updated timestamps, and version history.

Example:

{
  "id": "a1b2c3d4-..."
}

Daemon endpoint: GET /api/memory/:id

memory_list

List memories with optional pagination and type filtering.

Parameters:

NameTypeRequiredDescription
limitnumbernoMax results (default 100)
offsetnumbernoPagination offset
typestringnoFilter by memory type

Returns: Array of memory objects.

Example:

{
  "limit": 20,
  "offset": 0,
  "type": "decision"
}

Daemon endpoint: GET /api/memories

memory_modify

Edit an existing memory. Requires a reason for the edit (used for version history tracking).

Parameters:

NameTypeRequiredDescription
idstringyesMemory ID to modify
reasonstringyesWhy this edit is being made
contentstringnoNew content
typestringnoNew type
importancenumbernoNew importance
tagsstringnoNew tags (comma-separated)

Returns: Updated memory object.

Example:

{
  "id": "a1b2c3d4-...",
  "content": "User prefers Bun for all JS projects",
  "reason": "Updated to reflect broader preference"
}

Daemon endpoint: PATCH /api/memory/:id

memory_forget

Soft-delete a memory. The memory is not physically removed — it’s marked as forgotten with a reason for auditability.

Parameters:

NameTypeRequiredDescription
idstringyesMemory ID to forget
reasonstringyesWhy this memory should be forgotten

Returns: Confirmation of deletion.

Example:

{
  "id": "a1b2c3d4-...",
  "reason": "User corrected this preference"
}

Daemon endpoint: DELETE /api/memory/:id

memory_feedback

Rate how relevant injected memories were to the current conversation. Scores update session_memories.relevance_score immediately, feeding the predictor scorer’s training pipeline and the aspect feedback loop.

Parameters:

NameTypeRequiredDescription
session_keystringyesCurrent session key
ratingsobjectyesMap of memory ID to relevance score (−1 to 1)

Score interpretation: 1 = directly helpful, 0 = unused/neutral, −1 = harmful or misleading.

Returns: Object with ok: true, recorded, accepted, rejected, and propagated counts.

recorded is the number of ratings submitted. accepted is the subset whose memory IDs were actually recorded for the given session and agent. propagated is the subset that also produced path-based graph updates.

Example:

{
  "session_key": "abc123",
  "ratings": {
    "a1b2c3d4-e5f6-...": 0.9,
    "b2c3d4e5-f6a7-...": 0.0,
    "c3d4e5f6-a7b8-...": -0.5
  }
}

Daemon endpoint: POST /api/memory/feedback

Note: Prefer this tool over embedding feedback as raw JSON text. Both are supported for backward compatibility, but the MCP tool is recorded immediately on the current turn.

Search active or completed session transcripts. This is the pull side of sub-agent context continuity: session-start injects a compact parent context block when Signet can infer the parent, while session_search lets the child query the parent transcript on demand.

Parameters:

NameTypeRequiredDescription
querystringyesTranscript search query
session_keystringnoSpecific transcript session key to search
current_session_keystringnoCurrent session key; OpenClaw lineage can resolve this to the parent
agent_idstringnoAgent scope (default default)
projectstringnoOptional project path filter
limitnumbernoMax hits to return (default 10, max 20)

Returns: Object with query, hits, and count. Each hit includes sessionKey, project, updatedAt, excerpt, and rank.

Example:

{
  "query": "trunk ports",
  "current_session_key": "agent:nicholai:subagent:abc123",
  "agent_id": "nicholai",
  "limit": 5
}

Daemon endpoint: POST /api/sessions/search

agent_peers

List currently active peer sessions for cross-agent coordination.

Parameters:

NameTypeRequiredDescription
agent_idstringnoCurrent agent id (default default)
session_keystringnoCurrent session key (used to exclude self session)
include_selfbooleannoInclude this agent’s sessions (default false)
projectstringnoOptional project filter
limitnumbernoMax sessions to return

Returns: Object with sessions array and count.

Daemon endpoint: GET /api/cross-agent/presence

agent_message_send

Send a message to another agent/session or broadcast to all active peers. Supports local daemon delivery and optional ACP relay.

Parameters:

NameTypeRequiredDescription
contentstringyesMessage body
from_agent_idstringnoSender agent id
from_session_keystringnoSender session key
to_agent_idstringnoTarget agent id
to_session_keystringnoTarget session key
broadcastbooleannoBroadcast to all sessions
typeenumnoassist_request, decision_update, info, question
viaenumnolocal (default) or acp
acp_base_urlstringnoACP server URL (required if via=acp)
acp_target_agent_namestringnoACP target agent name (required if via=acp)
acp_timeout_msnumbernoACP relay timeout in milliseconds (used when via=acp)

Returns: Stored message object including delivery status.

Daemon endpoint: POST /api/cross-agent/messages

agent_message_inbox

Read recent inbound cross-agent messages for an agent/session.

Parameters:

NameTypeRequiredDescription
agent_idstringnoRecipient agent id (default default)
session_keystringnoRecipient session key
sincestringnoISO timestamp lower bound
limitnumbernoMax messages to return
include_sentbooleannoInclude messages sent by this agent
include_broadcastbooleannoInclude broadcast messages

Returns: Object with items array and count.

Daemon endpoint: GET /api/cross-agent/messages

session_bypass

Toggle per-session hook bypass. When enabled, all hook endpoints for the target session return empty no-op responses with bypassed: true. MCP tools (memory_search, memory_store, etc.) continue to work normally — only automatic hooks are silenced.

Parameters:

NameTypeRequiredDescription
session_keystringyesSession key to toggle bypass for, raw or session:<uuid>
enabledbooleanyestrue to enable bypass, false to disable

Returns: Object with key and bypassed fields confirming the new state.

Example:

{
  "session_key": "session-uuid",
  "enabled": true
}

Daemon endpoint: POST /api/sessions/:key/bypass

secret_list

List available secret names. This value-safe tool is owned by the signet.secrets core plugin. It returns names only, raw secret values are never exposed to agents.

Parameters: None.

Returns: Object with a secrets array of string names.

Example:

{}

Daemon endpoint: GET /api/secrets

secret_exec

Queue a shell command with secrets injected as environment variables. Output is automatically redacted, secret values never appear in results. Bare secret names resolve through the active Signet secrets provider: local by default, or Bitwarden first when Bitwarden is active. Use local://NAME to force the local encrypted store, bw://... to reference a Bitwarden item explicitly, or op://... for 1Password compatibility references.

Parameters:

NameTypeRequiredDescription
commandstringyesShell command to queue
secretsobjectyesMap of env var name to secret reference (Signet name, bw://..., or op://...)
timeoutSecondsnumbernoMax subprocess runtime for the queued job; defaults to 300 seconds, max 1800

Returns: A queued secret exec job object with id, status, and timeoutMs. Poll with secret_exec_status for redacted stdout, stderr, and code after completion. Secret values in output are replaced with [REDACTED].

Example:

{
  "command": "curl -H \"Authorization: Bearer $OPENAI_API_KEY\" https://api.openai.com/v1/models",
  "secrets": {
    "OPENAI_API_KEY": "OPENAI_API_KEY"
  }
}

Daemon endpoint: POST /api/secrets/exec (always queued; 5 minute default job timeout)

secret_exec_status

Poll a queued secret_exec job and retrieve redacted output when it finishes.

Parameters:

NameTypeRequiredDescription
jobIdstringyesJob id returned by secret_exec

Returns: The secret exec job status. Completed jobs include redacted stdout, stderr, code, and timedOut when applicable.

Daemon endpoint: GET /api/secrets/exec/:jobId

Optional GraphIQ Code Tools

The MCP server registers generic GraphIQ code retrieval tools as stable tool names. Each call is runtime-gated by the optional signet.graphiq plugin state and the active indexed project. Run signet index <path> to index a project and make it active. Re-running the command for another path moves the active GraphIQ context to that project.

GraphIQ stores its index at <project>/.graphiq/. Signet stores only plugin state and the active project pointer, so GraphIQ code indexes remain outside the main Signet memory architecture.

ToolPurpose
signet_code_searchSearch the active indexed project for symbols and implementation context
signet_code_contextRead source and structural neighborhood for a symbol
signet_code_blastAnalyze forward/backward impact radius for a symbol
signet_code_statusShow GraphIQ status for the active project
signet_code_doctorDiagnose GraphIQ artifact health
signet_code_constantsFind shared numeric and string constants

signet_code_search parameters:

NameTypeRequiredDescription
querystringyesCode search query
topnumbernoMax results to return (default 10)
filestringnoOptional file path filter
debugbooleannoInclude GraphIQ score/debug details

signet_code_context and signet_code_blast both take a symbol string. signet_code_blast also accepts optional depth and direction (forward, backward, or both).

Discovery Protocol

AI harnesses discover Signet’s MCP server in one of two ways:

Automatic (via signet install)

The connector for each harness registers the MCP server in the harness’s configuration file during installation. No manual steps needed.

Manual discovery

  1. The daemon must be running (signet daemon start)
  2. The MCP server is available at:
    • Streamable HTTP: http://localhost:3850/mcp
    • stdio: spawn the signet-mcp binary as a subprocess
  3. The daemon port can be overridden via SIGNET_PORT (default: 3850)
  4. The daemon host can be overridden via SIGNET_HOST (default: localhost)

Clients can verify the server is reachable with the MCP initialize handshake:

echo '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"test","version":"1.0"},"capabilities":{}},"id":1}' | signet-mcp

Transports

The MCP server supports two transports:

Streamable HTTP

Embedded in the daemon’s Hono server at /mcp. Uses the web-standard Streamable HTTP transport (MCP spec 2025-03-26). Runs stateless — each request gets a fresh server instance.

POST http://localhost:3850/mcp     # Send MCP messages
GET  http://localhost:3850/mcp     # SSE stream (server notifications)
DELETE http://localhost:3850/mcp   # Session termination (no-op, stateless)

stdio

The signet-mcp binary runs as a subprocess, reading JSON-RPC from stdin and writing to stdout. The daemon must be running — tool handlers call the daemon’s HTTP API internally.

signet-mcp

Environment variables:

SIGNET_DAEMON_URL   # Override daemon URL (default: http://localhost:3850)
SIGNET_HOST         # Override daemon host (default: localhost)
SIGNET_PORT         # Override daemon port (default: 3850)

Configuration per Harness

Claude Code

The Claude Code connector registers the MCP server in ~/.claude/settings.json during signet install:

{
  "mcpServers": {
    "signet": {
      "type": "stdio",
      "command": "signet-mcp",
      "args": []
    }
  }
}

OpenCode

The OpenCode connector registers the MCP server in ~/.config/opencode/opencode.json during signet install:

{
  "mcp": {
    "signet": {
      "type": "local",
      "command": ["signet-mcp"],
      "enabled": true
    }
  }
}

This coexists with the plugin (plugins/signet.mjs) — the plugin handles lifecycle hooks, MCP handles on-demand tool calls.

OpenClaw

OpenClaw uses the @signetai/adapter-openclaw runtime plugin, which already provides the same tool surface. MCP registration will be added when OpenClaw supports native mcpServers configuration.

Manual Setup

If you don’t use signet install, you can configure MCP manually:

  1. Ensure the daemon is running: signet daemon start
  2. Add the MCP server to your harness config (see examples above)
  3. Verify connectivity: echo '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"test","version":"1.0"},"capabilities":{}},"id":1}' | signet-mcp

Authentication

MCP connections inherit the daemon’s auth model:

  • local (default): No authentication required.
  • team: Streamable HTTP requests require a Bearer token. The stdio bridge runs locally and connects to the daemon with the same auth context.
  • hybrid: Localhost requests (including MCP) are trusted; remote requests require a token.

Internals

The MCP tool handlers use a shared daemonFetch helper that sends HTTP requests to the daemon API with these headers:

  • x-signet-runtime-path: plugin — identifies this as a plugin-path request
  • x-signet-actor: mcp-server — identifies the calling actor
  • x-signet-actor-type: harness — actor type classification

The default request timeout is 10 seconds. secret_exec returns quickly because it only queues daemon-owned work; poll with secret_exec_status to retrieve redacted output after completion.

Errors are returned as MCP error results with isError: true and a human-readable message.

Roadmap

Phase 2 tool candidates (not yet implemented):

  • secret_get — retrieve a secret value
  • skill_list — list installed skills
  • diagnostics — health score summary
  • config_read — read agent config
  • document_ingest — ingest a document