moeru-ai/airi · error · Error

MCP tools are not available in this runtime.

Error message

MCP tools are not available in this runtime.

What it means

stage-ui ships default MCP tool schemas via mcp(), but the default runtime is createUnavailableMcpToolRuntime(), whose listTools throws this. A concrete runtime (e.g. Electron main over IPC) is expected to build real tools with createMcpTools(runtime) and register them through useLlmToolsStore to override the defaults. Inside createMcpTools the throw is caught and '' is returned to the model, so the raw error mainly reaches direct runtime callers and tests.

Source

Thrown at packages/stage-ui/src/tools/mcp.ts:137

            isError: true,
            content: [{ type: 'text', text: errorMessageFromValue(error) }],
          }
        }
      },
      // NOTICE: `arguments` is z.string() (JSON) because z.unknown() produces `{}` (no `type` key)
      // and z.record() emits `propertyNames`, both rejected by OpenAI.
      parameters: z.object({
        name: z.string().describe('Tool name in "<serverName>::<toolName>" format'),
        arguments: z.string().describe('JSON object of tool arguments, e.g. {"query":"hello","limit":10}'),
      }).strict(),
    }),
  ]
}

function createUnavailableMcpToolRuntime(): McpToolRuntime {
  return {
    async listTools() {
      throw new Error('MCP tools are not available in this runtime.')
    },
    async callTool() {
      throw new Error('MCP tools are not available in this runtime.')
    },
  }
}

/**
 * Builds the default stage-ui MCP tool set without depending on runtime singletons.
 *
 * Use when:
 * - Shared code needs the MCP tool schema before a concrete runtime registers live implementations
 *
 * Expects:
 * - Runtime-specific callers override these tools through `useLlmToolsStore`
 *
 * Returns:
 * - MCP tool definitions with an unavailable-runtime fallback

View on GitHub (pinned to 677329427f)

Solutions

  1. In runtime bootstrap, implement McpToolRuntime (listTools/callTool) and register createMcpTools(runtime) via useLlmToolsStore so it overrides the defaults.
  2. If the runtime cannot support MCP, exclude the default mcp() tools from the tool list instead of shipping them.
  3. In tests, pass a stub runtime to createMcpTools rather than calling mcp().

Example fix

// before
const tools = await mcp() // default unavailable runtime

// after
const runtime: McpToolRuntime = electronMcpRuntime // real bridge
const tools = await Promise.all(createMcpTools(runtime))
await useLlmToolsStore().setTools('mcp', tools)
Defensive patterns

Strategy: validation

Validate before calling

const mcpSupported = isStageTamagotchi() // or your runtime capability flag
const tools = mcpSupported
  ? await Promise.all(createMcpTools(runtime))
  : [] // do not register the unavailable defaults

Try / catch

try {
  return await runtime.listTools()
}
catch (e) {
  if (errorMessageFrom(e)?.includes('not available in this runtime'))
    return [] // treat as zero MCP tools
  throw e
}

Prevention

When it happens

Trigger: The LLM invokes builtIn_mcpListTools while only the default mcp() tool set is registered — stage-web or a test environment where no runtime wired an McpToolRuntime bridge.

Common situations: Running the shared toolset in a new runtime integration before implementing the bridge; unit tests importing the default tools; MCP enabled in web where it is Electron-only.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/5585331b20322344. Report an issue: GitHub.