different-ai/openwork · error · PluginArchRouteFailure

mcp_server_not_remote

mcp_server_not_remote

Error message

Only declared remote MCP servers with a URL can be configured.

What it means

Thrown as PluginArchRouteFailure(400, 'mcp_server_not_remote') after the server declaration was found but its config record has no `url` field. The store only supports configuring declared remote MCP servers (HTTP/SSE URLs), so local/stdio command-based servers or declarations missing `url` are rejected.

Source

Thrown at ee/apps/den-api/src/routes/org/plugin-system/store.ts:4892

  })
  await deletePluginMcpRequirementBindingsByIds({ bindingIds: staleBindingIds })
}

function mcpRequirementServerFromVersion(input: {
  configObject: ConfigObjectRow
  serverName: string
  version: ConfigObjectVersionRow
}): PluginMcpRequirementServer {
  const spec = parseConfigObjectVersionSpec(input.version)
  const serverName = input.serverName.trim()
  const entry = marketplaceMcpServerEntries(spec, input.configObject.title).find((candidate) => candidate.name === serverName)
  if (!entry) {
    throw new PluginArchRouteFailure(404, "mcp_server_not_found", "MCP server declaration not found on this config object.")
  }

  const url = readRecordString(entry.config, "url")
  if (!url) {
    throw new PluginArchRouteFailure(400, "mcp_server_not_remote", "Only declared remote MCP servers with a URL can be configured.")
  }

  return { config: entry.config, name: entry.name, url }
}

function configVersionOwnsImportedExternalMcpConnection(version: ConfigObjectVersionRow, connectionId: string) {
  const spec = parseConfigObjectVersionSpec(version)
  const metadata = isRecord(spec.metadata) ? spec.metadata : null
  const recordedConnectionId = readRecordString(spec, "externalMcpConnectionId")
    || (metadata ? readRecordString(metadata, "externalMcpConnectionId") : null)
  const owned = spec.externalMcpConnectionOwnedByPlugin === true
    || metadata?.externalMcpConnectionOwnedByPlugin === true
  return owned && recordedConnectionId === connectionId
}

async function assertRemotePluginMcpUrl(url: string) {
  let parsed: URL
  try {

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Add a valid `url` field to the MCP server declaration in the config object spec, converting it to a remote server if it was local/stdio.
  2. Confirm the key is spelled `url` (not uri/endpoint/address) and is a non-empty string.
  3. If a local server is genuinely needed, host it as a remote HTTP MCP endpoint and declare that URL instead.
  4. Check the config object version you are using — a newer version may already declare the remote form.

Example fix

// before (mcpServers in config spec)
"github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"] }
// after
"github": { "url": "https://mcp.github.com/sse" }
Defensive patterns

Strategy: validation

Validate before calling

const entry = marketplaceMcpServerEntries(spec, title).find((s) => s.name === serverName)
if (entry && typeof readRecordString(entry.config, "url") !== "string") {
  throw new Error(`MCP server '${serverName}' is not remote; add a 'url' field.`)
}

Type guard

function isRemoteMcpEntry(entry: { config: Record<string, unknown> }): boolean {
  const url = entry.config["url"]
  return typeof url === "string" && url.length > 0
}

Try / catch

try {
  await configureMcpServer({ version, configObject, serverName })
} catch (e) {
  if (isPluginArchRouteFailure(e) && e.code === "mcp_server_not_remote") {
    // convert declaration to remote or host it via HTTP
  }
}

Prevention

When it happens

Trigger: Resolving an MCP requirement server whose config entry declares a local server (command/args based, no url key) or whose url is absent/empty — readRecordString(entry.config, 'url') returns undefined.

Common situations: Marketplace config declares a stdio-based MCP server (e.g. { command: 'npx', args: [...] }) which the hosted pipeline cannot configure; a declaration typo like 'uri' or 'endpoint' instead of 'url'; url defined but empty string.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/9d02ab54e534c138. Report an issue: GitHub.