MCP¶
The gateway serves a Model Context Protocol endpoint, so any MCP client can use Myelinate's learning tools: Claude Code, Claude Desktop, Cursor, Hermes, or an agent framework's MCP client.
- URL:
https://<gateway>/mcp - Transport: streamable HTTP (stateless, JSON responses). There is no stdio or SSE transport.
- Auth: the same bearer credential as the REST API:
Authorization: Bearer myl_.... Every request to/mcp, includinginitializeandtools/list, needs a valid credential; without one the gateway answers401withWWW-Authenticate: Bearer. The tools need thelearnscope, which is checked per call. Requests are rate limited like the REST API. A dev gateway with no credentials configured needs no header.
Connect a client¶
Claude Code¶
claude mcp add --transport http myelinate https://learner.internal/mcp \
--header "Authorization: Bearer $MYELINATE_API_KEY"
Or commit a project-level .mcp.json that reads the key from the environment:
{
"mcpServers": {
"myelinate": {
"type": "http",
"url": "${MYELINATE_URL:-http://localhost:8080}/mcp",
"headers": {
"Authorization": "Bearer ${MYELINATE_API_KEY}"
}
}
}
}
Cursor¶
.cursor/mcp.json in the project, or ~/.cursor/mcp.json for all projects:
{
"mcpServers": {
"myelinate": {
"url": "https://learner.internal/mcp",
"headers": {
"Authorization": "Bearer ${env:MYELINATE_API_KEY}"
}
}
}
}
Claude Desktop¶
claude_desktop_config.json launches local (stdio) servers. Bridge to the gateway with
mcp-remote:
{
"mcpServers": {
"myelinate": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://learner.internal/mcp", "--header", "Authorization:${AUTH_HEADER}"],
"env": { "AUTH_HEADER": "Bearer myl_..." }
}
}
}
The header value is passed through env without a space after the colon because some clients mangle spaces inside
args.
Any other client¶
Point a streamable-HTTP MCP client at <gateway>/mcp and send the Authorization header on every request. The server
is stateless, so no session affinity is needed behind a load balancer. To check connectivity by hand:
curl -s -X POST "${MYELINATE_URL:-http://localhost:8080}/mcp" \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-H "Authorization: Bearer $MYELINATE_API_KEY" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Tools¶
All tools take an optional agent_id (default "default"). Set it, either in the client's instructions ("use
agent_id acme-coding") or by relying on the default, so all of a team's sessions share one knowledge namespace.
| Tool | Arguments | Returns |
|---|---|---|
recall |
query (required), agent_id, user_id, session_id |
{recall_id, instructions}. instructions is "(nothing learned yet for this task)" when there is nothing relevant. |
skill_view |
name (required), agent_id |
The skill as SKILL.md text, or No skill named '<name>'. |
remember |
content (required), kind (fact | preference | lesson, default fact), agent_id, user_id |
Saved., or Already known. for a duplicate. |
record_episode |
task, outcome, summary (required); recall_id, error, tests_passed, test_details, skills_used, agent_id, user_id, session_id |
{episode_id, reflection_scheduled} |
feedback |
episode_id, score (0.0–1.0) (required); comment, correction |
Feedback recorded. |
ask |
question (required), agent_id, user_id |
{answer, evidence}: an answer drawn only from learned skills, memories and the user profile. |
Details:
record_episode.outcomeissuccess,failure,partialorunknown; anything else is stored asunknown.summarybecomes the assistant message of the episode, so describe what was asked, what was done and what happened.tests_passed(boolean) withtest_detailsbecomes the episode's verification, the strongest learning signal. Encourage the model to pass real test or check output there.skills_usedcredits skills the model loaded withskill_view.askneeds the learning model: if nothing relevant was learned it answers "Nothing has been learned about that yet."; otherwise, with no model configured or reachable, the call fails.
The server also sends instructions on initialize telling the model to call recall at the start of a non-trivial
task, load listed skills with skill_view, save durable facts with remember, and call record_episode when the task
ends.
Allowed hosts¶
The MCP endpoint has DNS-rebinding protection: it only accepts requests whose Host header is in
MYELINATE_MCP_ALLOWED_HOSTS. The default is ["localhost:*", "127.0.0.1:*"], so a gateway reached through any
other hostname answers 421 Invalid Host header on /mcp until you add that hostname. The value is a JSON list;
host:* matches any port, and a bare host matches requests without a port (the usual case behind TLS on 443):
Behind a reverse proxy, list the host name the proxy forwards in Host. Requests that carry an Origin header (from
browsers) must also match MYELINATE_CORS_ORIGINS. See Configuration.
Limitations¶
- The model decides. With MCP, recall and recording happen only if the model chooses to call the tools. Coverage is
typically partial: short tasks often skip
recall, and sessions that end abruptly skiprecord_episode. Where you control the agent code, an adapter or explicit call is deterministic. You can raise coverage by telling the model in its system prompt or project instructions (e.g.CLAUDE.md) to callrecallfirst andrecord_episodelast. - Self-reported outcomes. The model grades its own run in
record_episode. Ask it to reportunknownunless tests or the user confirmed the result, and to pass real test output viatests_passed/test_details. Use thefeedbacktool orPOST /v1/feedbackfor user verdicts. - No per-user identity. The MCP credential identifies the tenant, not the end user. Pass
user_idexplicitly if you need per-user memories. - Recording only what the model summarizes. An MCP episode holds the task and the model's summary, not the full transcript or tool calls, so reflection has less to work with than with an adapter.
Troubleshooting¶
| Symptom | Fix |
|---|---|
421 Invalid Host header |
Add the gateway's hostname to MYELINATE_MCP_ALLOWED_HOSTS. |
403 Invalid Origin header |
The client sends an Origin header that is not in MYELINATE_CORS_ORIGINS. |
401 Unauthorized on connect |
Missing or invalid token. Check the Authorization header your client sends. |
Tool calls return isError: true ("Error executing tool ...") |
The key lacks the learn scope, or an argument is invalid (e.g. an unknown kind). The error text is generic, so check both. |
remember returns an error |
kind must be fact, preference or lesson. |
| The model never calls the tools | Add explicit instructions to call recall at the start and record_episode at the end. |