Morpheus Inference
This MCP server lets an agent run OpenAI-compatible chat/tool calls, list models, and optionally create embeddings on Morpheus, with fallback to the host model.
morpheus_chat: send OpenAI-shapedmessages; optionally setmodel,tools,tool_choice,max_tokens, andtemperature; receive assistant content andtool_calls.morpheus_list_models: fetch the model catalog fromGET {base}/models.morpheus_embed(optional): embed a string or string array viaPOST {base}/embeddingsusing an explicit embeddingmodel.Model fallback: tries the requested/configured Morpheus models, reports alerts, and returns
host_fallbackwhen no Morpheus model can complete the request.Works with hosted
api.mor.orgor a self-hosted Morpheus Uplink.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Morpheus InferenceList the available models."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Morpheus Inference for Cursor
Run chat and tool calling on Morpheus, a decentralized AI inference network, through api.mor.org or your own Uplink. Embeddings are optional.
Plugin name:
morpheus-inference(Cursor Plugin, manifest in.cursor-plugin/plugin.json)Repository:
absgrafx/morpheus-grok-mcpVersion: 0.2.2 (see CHANGELOG.md)
Author: ABSGrafx, devs@absgrafx.com
License: MIT
What it does
The plugin adds an MCP (Model Context Protocol) server, which gives your agent three tools, plus two skills. The main tool is morpheus_chat: chat completions with tool calling (pass OpenAI-format tools, get tool_calls back).
Tool | What it calls |
|
|
|
|
|
|
Embeddings are optional: morpheus_embed takes an explicit embedding model, for example BGE-EN-ICL through an Uplink. Hosted api.mor.org embedding models may be cold.
Skill | Purpose |
| Tells the agent to use Morpheus only when you ask for it or have said you want it, and how to handle fallbacks and alerts. |
| Walks through configuration and a first test. |
Using Morpheus is opt-in. The plugin does not replace your agent's own model.
Related MCP server: openai-mcp-server
Get a key
You need one API key. Pick where it comes from:
api.mor.org: sign in at app.mor.org and create an API key. The plugin then uses
https://api.mor.org/api/v1.Your own Uplink: if you run a Morpheus Uplink, use a key from the Uplink web interface. The Uplink documentation (API keys) lists three kinds: Master (inference and admin), Prompt (inference only) and ephemeral (named keys for apps and agents). Use a Prompt key or an ephemeral key, not the Master key. The base URL (web address) is your Uplink host followed by
/v1, for examplehttps://uplink.example.com/v1.
Keep the key out of chat, prompts, and files in your repo.
Install
From the Cursor Marketplace
The listing is not live yet. Cursor's plugin documentation describes the install steps: open Customize in the sidebar, find the plugin, and select Install. Then set the values described in Configuration.
mcp.json starts the server with uvx, pinned to the v0.2.2 tag of this repository:
"command": "uvx",
"args": ["--from", "git+https://github.com/absgrafx/morpheus-grok-mcp@v0.2.2", "morpheus-mcp"]You need uv installed for uvx. See Supply chain for what the pin covers.
From a clone (local testing)
git clone https://github.com/absgrafx/morpheus-grok-mcp.git
cd morpheus-grok-mcp
uv sync --extra dev
uv run python scripts/validate_plugin.py
uv run pytest -q
uv run python scripts/smoke_mcp.pyThe last command starts the MCP server from your clone and prints its tools:
tools: morpheus_chat, morpheus_embed, morpheus_list_modelsTo load the plugin in Cursor from your machine, copy it into the local plugins folder:
mkdir -p ~/.cursor/plugins/local/morpheus-inference
cp -r .cursor-plugin assets mcp.json skills README.md LICENSE ~/.cursor/plugins/local/morpheus-inference/Copy the files rather than linking them: Cursor's local testing notes say a symlink in this folder loads only if its target is inside the folder. Then restart Cursor (or run Developer: Reload Window) and check Customize for the MCP server and skills.
Configuration
Three settings, plus an optional model list. Set them in the Cursor dashboard under Plugins, at install time or later with Configure. The plugin declares only the names; it never stores the values.
Setting | Variable | Required | Value |
API key |
| yes | Your key from app.mor.org or your Uplink |
Mode |
| yes, default |
|
Base URL |
| Uplink mode only | For example |
Model hierarchy |
| no | Chat models to try, comma-separated, most preferred first |
In api.mor.org mode the base URL is always https://api.mor.org/api/v1, and MORPHEUS_BASE_URL is ignored.
In uplink mode:
The base URL must start with
https://. Plainhttp://is accepted only forlocalhost,127.0.0.1and::1.A user name or password in the URL (
https://user:pass@host) is rejected. Put the key inMORPHEUS_API_KEYinstead.The plugin makes the URL end in
/v1: it removes a pasted/chat/completions,/embeddingsor/models, collapses/v1/v1, and adds/v1if it is missing. It logs a warning for each change. It never adds/api.
Model ids must match the endpoint's catalog exactly. Call morpheus_list_models to see them.
Per-agent keys and the old policy flags from 0.1.0 were removed. See docs/DESIGN.md for how to get the same results.
Model hierarchy and fallback
For morpheus_chat, the plugin tries models in this order: the model argument (if the agent passed one), then each id in MORPHEUS_MODELS. MORPHEUS_MODELS applies to chat only, because embedding models are different: for morpheus_embed, the plugin tries the model argument, then each id in the optional fallback_models argument.
If the agent passes no model and MORPHEUS_MODELS is empty, nothing is sent to Morpheus. The tool returns host_fallback at once, with an alert saying no model was set.
Each model is called once. There are no silent retries.
Each call waits up to 60 seconds for a reply. The agent can set
timeout_secondson a call; values below 1 are raised to 1 and values above 300 are lowered to 300.An HTTP (web request) error, a timeout, a network error, or an empty response moves on to the next model, and each failure is reported.
HTTP 401 or 403 means the key was rejected. Every model uses the same key, so the plugin stops there.
When no Morpheus model is left, the tool returns
"status": "host_fallback"with"use_host_model": trueand this instruction to the agent: "Morpheus could not complete this request. Use your own model to handle it now. First show the user the text in user_alert, then continue with your own model."morpheus_list_modelsmakes a single call. If it fails, it also returnshost_fallback, with an alert, and tells the agent that Morpheus is not reachable right now.
Example: with MORPHEUS_MODELS set to model-a,model-b, suppose model-a returns HTTP 502. The plugin records an alert for model-a, calls model-b, and returns the answer from model-b together with that alert. If model-b also fails, a second alert is added and the tool returns host_fallback, so your agent's own model takes over.
Testing
Get a key. Mint your own at app.mor.org, which includes $1 of free inference, or use an Uplink Prompt key (not the Master key).
Set
MORPHEUS_MODELS. Without it, a chat call that names no model goes straight tohost_fallback.Use a warm chat model:
deepseek-v4-flash. It is a reasoning model, so a reply can take up to about 60 seconds.
Use warm models for testing. Cheaper hosted models (for example Aion 3.5 Mini) may return HTTP 503 model_unavailable when they are not warm. Try another warm model or use an Uplink.
With MORPHEUS_MODELS=deepseek-v4-flash, try these prompts:
Prompt | Expected result |
"List the Morpheus models" |
|
"Use morpheus_chat: reply with the word pong" |
|
"Use morpheus_chat with a |
|
The tool-calling test as a morpheus_chat call:
{
"messages": [{"role": "user", "content": "What's the weather in Boston?"}],
"tools": [{"type": "function", "function": {
"name": "get_weather",
"description": "Get the current weather for a city",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
}}]
}Fallback checks:
Through an Uplink,
MORPHEUS_MODELS=BGE-EN-ICL,deepseek-v4-flash: chat returns an alert forBGE-EN-ICL(an embedding model can't be used for chat), thenpongfromdeepseek-v4-flash. On api.mor.org the endpoint may instead choose a different model, which shows as an informationalmodel_substitutednote.MORPHEUS_MODELS=BGE-EN-ICL,Aion 3.5 Mini: chat returns an alert forBGE-EN-ICL. IfAion 3.5 Miniis not warm, a second alert follows, then"status": "host_fallback", and your agent's own model answers. If it is warm, it answers with"status": "ok".
Alerts
Every failure produces an alert. Each alert has:
Field | Meaning |
| The model that failed |
| The full URL that was called |
| The HTTP status code, or |
| A short description: a parsed error message (the status code is in |
| Local time with UTC offset |
| What was done instead |
If the endpoint returns an HTTP error, the alert shows the status code and a short parsed error message (the error type up to 60 characters and the message up to 200 characters). The raw response body is never shown. If the body is not JSON or has no message, the alert says so instead. The configured key is always masked, and the request text is never included: a message that quotes the request is replaced by "(upstream message withheld: it quoted the request)".
The alert reaches you and the agent in three ways:
In the tool result:
alerts(structured) anduser_alert(one line per failure), withagent_instructionstelling the agent to showuser_alertto you.As an MCP log message (warning level) to Cursor.
On the server's standard error output, at the moment the failure happens.
Example user_alert:
Morpheus alert at 2026-10-14T16:43:57-04:00: model no-such-model-xyz on https://uplink.example.com/v1/chat/completions failed (HTTP 404; invalid_request_error: model "no-such-model-xyz" not found in catalog). Action: Tried the next Morpheus model, deepseek-v4-flash.Model substitution (informational)
An endpoint can choose a different model than the one requested. For example, api.mor.org corrects close names and serves names it doesn't know with its default model. This is expected behaviour, not a failure, so the plugin reports it as information. Every successful chat or embedding result has two fields:
model_used: the model named in the response'smodelfield. If the response has nomodelfield, this is the requested model.requested_model: the model the plugin asked for.
The plugin treats two names as the same model if they differ only in letter case or in spaces, - or _, if the request has no provider and the response adds a known vendor's prefix (deepseek/deepseek-v4-flash for DeepSeek V4 Flash; the vendors are deepseek, qwen, meta-llama, mistralai, google, openai, moonshotai and tencent), or if the pair is in the plugin's alias list (an Uplink serves BGE-EN-ICL as text-embedding-bge-en-icl). Any other added provider (for example evilorg/llama-3-70b, meta/llama-3-70b or llama/llama-3-70b for llama-3-70b, o/o3, or deep/deepseek-v4-flash) can mean different weights, so it is noted. Otherwise the result keeps "status": "ok" and adds an informational entry to alerts with type model_substituted, severity info, and the fields requested, served, endpoint and time. Its text is added to user_alert, and agent_instructions tells the agent to mention it to you, not to treat it as a failure. It goes to Cursor as an info-level MCP log message, and to standard error only with --verbose. Log lines never contain newlines or other control characters.
A model name from the endpoint is shown only if it uses letters, digits and . _ : / @ + -, is at most 100 characters, and does not contain the key or repeat the request text. Otherwise every output (model_used, alerts, user_alert, MCP log messages and standard error) shows (invalid model name) instead. A name from the endpoint may contain spaces only if it is the requested model or an alias of it (for example Aion 3.5 Mini for aion-3.5-mini). A different served name must also be at most 64 characters with at most 8 parts (split on . _ : / @ + -), and no part may be a word such as system, ignore, instructions, prompt, api, key or password (parts are also split at camelCase and letter-digit boundaries for this check, so SystemIgnoreApiKey is rejected). In user_alert, both the served and the requested name are shown in quotes. Requested names are checked the same way when they are shown, and may contain single spaces. Example:
Morpheus note at 2026-10-15T10:25:16-04:00: served by "google-gemma-4-31b-it" instead of "nonexistent-model-xyz" on https://api.mor.org/api/v1/chat/completions (the endpoint picked the model).Privacy
Your messages and embedding inputs go to the endpoint you choose: api.mor.org, or your own Uplink. That endpoint passes them to a Morpheus compute provider to run the model. Check the terms of the endpoint you use for how it handles data.
When the plugin hands back to your agent's own model, the request is handled by that model in the usual way. The plugin does not send it anywhere else.
The plugin has no telemetry and stores nothing on disk.
Logs go to standard error only. By default only warnings (alerts and URL fixes) are logged. Starting the server with
morpheus-mcp --verboseadds one line per request: method, URL, model id and the last four characters of the key. Message text is never logged.Every log line, alert and error passes through redaction: the configured key is masked whatever its format, as are
sk-keys, Bearer tokens and credentials in URLs.Masking matches the key's exact text. A key that is split, base64-encoded, or URL-encoded with reserved characters is not masked. A key shorter than 4 characters is not masked.
The key is passed to the server as an environment variable by Cursor. It is not written to this repository, and CI uses no keys.
Supply chain
The install is pinned to the
v0.2.2tag. A Git tag can be moved, so a commit-hash pin would be stronger. It can't be used here because the release commit only exists after the merge, so the tag is the pin for 0.2.2.uvx --from git+...does not readuv.lock, sopyproject.tomlpins the two direct dependencies exactly (mcp==1.30.0,httpx==0.28.1). Their own dependencies are resolved at install time.The CI workflow pins its GitHub Actions to commit hashes and has read-only repository access.
Troubleshooting
spawn uvx ENOENT on macOS. Apps opened from the Dock or Finder don't get your shell's PATH, so Cursor can't find uvx.
If you don't have uv yet, install it with
brew install uv(or the uv installer).Run
which uvxin Terminal; it shows the real path.Put that full path as the
"command"inmcp.json, for example"command": "<full path from which uvx>", then restart Cursor.
With a Marketplace install, the plugin manages mcp.json, so edit a local copy instead: see From a clone.
Support
Email devs@absgrafx.com or open an issue on this repository.
Development
Continuous integration (CI) on GitHub Actions (Linux) runs uv sync --extra dev --frozen, uv run python scripts/validate_plugin.py and uv run pytest -q on every pull request and on pushes to main. The validator checks the manifest and mcp.json with jsonschema, checks that every ${VAR} in mcp.json is declared, checks that the install is pinned to v<version>, and checks that the contact email matches the manifest everywhere. Tests use mocked HTTP and need no key.
To change the contact email, edit author.email in .cursor-plugin/plugin.json and replace the old address in pyproject.toml, this README and docs/listing/cursor-directory.md. The validator fails until they match.
License
MIT. Copyright (c) 2026 ABSGrafx LLC. See LICENSE.
Available Tools
3 toolsmorpheus_chatA
Call Morpheus OpenAI-compatible chat/completions.
Pass messages (OpenAI shape). Model defaults to MORPHEUS_MODEL_DEFAULT when omitted. Optional tools / tool_choice for models that support tool_calls. Returns JSON with assistant content and tool_calls if any.
| Name | Required | Description | Default |
|---|---|---|---|
| model | No | ||
| tools | No | ||
| messages | Yes | ||
| max_tokens | No | ||
| temperature | No | ||
| tool_choice | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It states key behaviors: OpenAI-compatible request shape, default model fallback, optional tools/tool_choice, and JSON response containing assistant content and tool_calls. It does not mention auth, rate limits, or error behavior, but the core behavior is clearly disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded with the core purpose. Every sentence adds useful information: call type, message shape, model default, optional tool parameters, and return format. No wasted wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that an output schema exists, the return value details are covered elsewhere. The description addresses the main invocation requirements: messages are required, model has a default, and optional tools/tool_choice are supported. It lacks explicit usage guidance for max_tokens and temperature, but overall an agent can correctly invoke the tool with this definition.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It adds useful meaning for messages ('OpenAI shape'), model ('defaults to MORPHEUS_MODEL_DEFAULT when omitted'), and tools/tool_choice ('for models that support tool_calls'). However, max_tokens and temperature are not explicitly described, though their names are self-explanatory.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description uses a specific verb and resource ('Call Morpheus OpenAI-compatible chat/completions') that immediately clarifies the tool's function. This clearly differentiates it from siblings morpheus_list_models and morpheus_embed, which are distinct operations.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly establishes context: use this for OpenAI-compatible chat completions with messages, model selection, and optional tool calling. It does not explicitly name alternatives or state when not to use it, but the sibling names make the intended use obvious.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
morpheus_embedC
Call Morpheus OpenAI-compatible embeddings (POST {base}/embeddings).
| Name | Required | Description | Default |
|---|---|---|---|
| input | Yes | ||
| model | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the full burden of behavioral disclosure. It reveals the HTTP method and OpenAI compatibility, but omits side effects, authorization requirements, rate limits, or whether this is a read-only inference operation. For a tool with no annotations, this is too thin.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence with no filler, front-loading the core operation and endpoint. Every word contributes information, making it easy to parse quickly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema covers return shape, and the two required parameters are simple. However, the absence of annotations and lack of usage guidance leave gaps in safety and tool-selection context. It is minimally adequate for an agent already familiar with embeddings APIs, but not complete on its own.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description does not explain the parameters. The names 'Input' and 'Model' are somewhat intuitive, and 'OpenAI-compatible' hints at the expected shape, but the description adds almost no explicit meaning beyond what the bare schema already shows.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific operation—calling Morpheus embeddings—and identifies the exact endpoint (POST {base}/embeddings). This distinguishes it from the sibling tools morpheus_chat and morpheus_list_models by resource. It does not elaborate on what an embedding is or returns, but the resource name is clear enough in the ML context.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no guidance on when to use this tool versus alternatives. It never mentions morpheus_chat or morpheus_list_models, nor does it describe conditions that would favor this tool. The only usage signal is implicit: 'embeddings' implies tasks needing vector representations.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
morpheus_list_modelsA
List models from GET {base}/models (install smoke + model picker).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral disclosure burden. It indicates an HTTP GET, which implies a read-only operation with no side effects, and the list use case suggests non-destructive behavior. However, it does not explicitly state the read-only nature, nor does it address auth, rate limits, or other behavioral traits that could affect an agent's invocation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence with no filler; it states the operation, the endpoint, and two concrete use cases. The parenthetical adds useful context without bloating the description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has no parameters, an output schema exists to define return values, and the implementation is a simple list operation, the description is complete. It gives the endpoint and use cases, so an agent can invoke it correctly. It does not need to explain return structure since the output schema is available.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, and the schema confirms an empty object, so the baseline for parameter semantics is 4. The description adds no parameter-level detail, but none is needed since there are no parameters to document.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') and resource ('models') and names the exact endpoint GET {base}/models. It clearly distinguishes this from the sibling tools morpheus_chat and morpheus_embed, which serve different purposes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The parenthetical '(install smoke + model picker)' gives concrete contexts where this tool should be used. It does not explicitly mention alternatives or when not to use it, but with only chat and embed as siblings, the usage boundary is clear enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
morpheus_chat - First observed
morpheus_embed - First observed
morpheus_list_models
TDQS
Scored across 3 tools
Each tool targets a distinct operation: chat completions, embeddings, and model listing. There is no overlap or ambiguity between them.
All tools share the 'morpheus_' prefix and use a consistent verb-based naming pattern (chat, list_models, embed). The convention is uniform and predictable.
Three tools are well-scoped for an inference server providing chat and embedding endpoints plus model discovery. Each tool earns its place without bloat.
The set covers the core OpenAI-compatible inference surface: chat completions, embeddings, and model listing. No obvious dead ends or missing essential operations for this domain.
Maintenance
Related MCP Connectors
MCP server for OpenAI API (chat completions, image generation, embeddings) via AceDataCloud
Open AI models through one OpenAI-compatible API: chat, live model list, prices and cost estimates.
Chat, image, video, and audio models from many vendors behind one remote MCP server and one key.
- QuallaaOAuthcom.quallaa
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Related MCP Servers
- -licenseCqualityNot gradedmaintenanceEnables interaction with OpenAI-compatible APIs (like Ollama) through MCP tools. Provides access to chat completions, model listings, and embeddings generation from local or remote OpenAI-style endpoints.3-
- AlicenseAqualityBmaintenanceIntegrates OpenAI APIs into MCP-compatible clients, providing tools for text generation, chat completions, model discovery, image creation/editing, audio transcription, speech synthesis, embeddings, and content moderation.987 npmMIT
- AlicenseNot gradedqualityAmaintenanceAggregates dozens of pre-built connectors into one OpenAI-compatible endpoint for AI clients, with a web UI for installing, configuring, monitoring, and managing them.303 npmMIT
- AlicenseAqualityCmaintenanceEnables interaction with OpenAI-compatible model proxy APIs through MCP, supporting text generation, vision understanding, and image generation with automatic model discovery and routing.3MIT