Skip to main content
Glama
absgrafx
by absgrafx

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-mcp

  • Version: 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

morpheus_chat

POST {base}/chat/completions (chat and tool calling)

morpheus_list_models

GET {base}/models

morpheus_embed (optional)

POST {base}/embeddings

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

use-morpheus-model

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.

morpheus-setup

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 example https://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.py

The last command starts the MCP server from your clone and prints its tools:

tools: morpheus_chat, morpheus_embed, morpheus_list_models

To 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

MORPHEUS_API_KEY

yes

Your key from app.mor.org or your Uplink

Mode

MORPHEUS_MODE

yes, default api.mor.org

api.mor.org or uplink

Base URL

MORPHEUS_BASE_URL

Uplink mode only

For example https://uplink.example.com/v1

Model hierarchy

MORPHEUS_MODELS

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://. Plain http:// is accepted only for localhost, 127.0.0.1 and ::1.

  • A user name or password in the URL (https://user:pass@host) is rejected. Put the key in MORPHEUS_API_KEY instead.

  • The plugin makes the URL end in /v1: it removes a pasted /chat/completions, /embeddings or /models, collapses /v1/v1, and adds /v1 if 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_seconds on 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": true and 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_models makes a single call. If it fails, it also returns host_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

  1. 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).

  2. Set MORPHEUS_MODELS. Without it, a chat call that names no model goes straight to host_fallback.

  3. 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"

morpheus_list_models returns "status": "ok" and the model list.

"Use morpheus_chat: reply with the word pong"

"status": "ok", model_used and requested_model are deepseek-v4-flash, and the content is pong.

"Use morpheus_chat with a get_weather tool (parameters: city, a string): What's the weather in Boston?"

"status": "ok" and tool_calls with one call to get_weather with arguments {"city": "Boston"}.

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 for BGE-EN-ICL (an embedding model can't be used for chat), then pong from deepseek-v4-flash. On api.mor.org the endpoint may instead choose a different model, which shows as an informational model_substituted note.

  • MORPHEUS_MODELS=BGE-EN-ICL,Aion 3.5 Mini: chat returns an alert for BGE-EN-ICL. If Aion 3.5 Mini is 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

model

The model that failed

endpoint

The full URL that was called

http_status

The HTTP status code, or null if there was no HTTP response

error

A short description: a parsed error message (the status code is in http_status), or for example timed out after 60s

time

Local time with UTC offset

action

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:

  1. In the tool result: alerts (structured) and user_alert (one line per failure), with agent_instructions telling the agent to show user_alert to you.

  2. As an MCP log message (warning level) to Cursor.

  3. 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's model field. If the response has no model field, 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 --verbose adds 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.2 tag. 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 read uv.lock, so pyproject.toml pins 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.

  1. If you don't have uv yet, install it with brew install uv (or the uv installer).

  2. Run which uvx in Terminal; it shows the real path.

  3. Put that full path as the "command" in mcp.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 tools
morpheus_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.

ParametersJSON Schema
NameRequiredDescriptionDefault
modelNo
toolsNo
messagesYes
max_tokensNo
temperatureNo
tool_choiceNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
inputYes
modelYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

C2.9/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.3/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

  1. 3 tool updatesv0.1.0
    • First observedmorpheus_chat
    • First observedmorpheus_embed
    • First observedmorpheus_list_models

TDQS

A4/5.0

Scored across 3 tools

Disambiguation5/5

Each tool targets a distinct operation: chat completions, embeddings, and model listing. There is no overlap or ambiguity between them.

Naming Consistency5/5

All tools share the 'morpheus_' prefix and use a consistent verb-based naming pattern (chat, list_models, embed). The convention is uniform and predictable.

Tool Count5/5

Three tools are well-scoped for an inference server providing chat and embedding endpoints plus model discovery. Each tool earns its place without bloat.

Completeness5/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • -
    license
    C
    quality
    Not graded
    maintenance
    Enables 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
    -
  • A
    license
    Not graded
    quality
    A
    maintenance
    Aggregates 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 npm
    MIT