Skip to content

feat(persona): wire relaycast MCP into broker-launched personas#170

Open
khaliqgant wants to merge 1 commit into
mainfrom
feat/relaycast-persona-mcp
Open

feat(persona): wire relaycast MCP into broker-launched personas#170
khaliqgant wants to merge 1 commit into
mainfrom
feat/relaycast-persona-mcp

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

Why

A persona launched under an Agent Relay broker (e.g. from Pear) couldn't message the team, while a regular broker-spawned agent can. Root cause:

  • The broker auto-wires the relaycast MCP for harnesses it spawns directly, by recognizing the CLI.
  • A persona's PTY command is the agentworkforce launcher, not the harness — the real claude is a grandchild the broker never sees, so it skips MCP wiring.
  • We can't fix it with a project .mcp.json either: the claude branch emits --strict-mcp-config, so claude only sees the --mcp-config payload and ignores project/user config.

The only injection point that survives strict mode is the --mcp-config payload itself — i.e. the persona's mcpServers map that buildInteractiveSpec compiles. So that's where relaycast has to go.

What changed

packages/persona-kitbuildInteractiveSpec:

  • New optional relayMcp input ({ apiKey, agentName, baseUrl?, defaultWorkspace? }) + exported RelayMcpConfig type.
  • When set, a relaycast stdio server (npx -y @relaycast/mcp + the RELAY_* env block, mirroring what the broker injects for a recognized harness) is merged into the persona's declared mcpServers before the harness switch. A persona-declared relaycast still wins, so authors can override.
  • Wired for claude (rides inside the strict --mcp-config payload) and codex (--config mcp_servers.relaycast.*). opencode keeps its existing "MCP injection unsupported" warning — unchanged.
  • Kept pure: the function reads no env; callers pass relayMcp explicitly.

packages/cli:

  • resolveRelayMcpFromEnv(process.env) builds the config from the RELAY_* env the broker sets on the launcher: RELAY_API_KEY + the broker-assigned RELAY_AGENT_NAME (both required), plus optional base-url / default-workspace. Passed through at the live-launch and dry-run call sites.
  • No relay env ⇒ undefined ⇒ no behavior change for a plain agentworkforce agent run.

Depends on

AgentWorkforce/relay#1018 — the broker must expose RELAY_AGENT_NAME to the launcher even under skip_relay_prompt (persona spawns set it). Without that the agent name is absent and resolveRelayMcpFromEnv returns undefined (safe no-op), so this PR is harmless to merge independently but only takes effect once the broker change ships.

Testing

  • persona-kit: tsc --noEmit clean; full suite 235 passed (6 new tests covering claude injection, env-omission, persona-override precedence, no-relay baseline, codex --config, opencode warning).
  • cli: tsc --noEmit clean; full suite 210 passed.

Not covered (follow-ups)

  • opencode MCP injection (relaycast or persona-declared) is still unsupported by harness-kit; relaycast is merged into the map but dropped with the existing warning.
  • Multi-workspace (RELAY_WORKSPACES_JSON) is not threaded through — single-workspace (RELAY_DEFAULT_WORKSPACE) only, which is the common case.

🤖 Generated with Claude Code

A persona launched under an Agent Relay broker couldn't message the team:
the broker auto-wires the relaycast MCP for harnesses it spawns directly,
but a persona's PTY command is the `agentworkforce` launcher (not the
harness), and the claude branch emits `--strict-mcp-config`, so any
project `.mcp.json` is ignored. The only injection point that survives
strict mode is the `--mcp-config` payload itself.

- persona-kit/interactive-spec: add an optional `relayMcp` input to
  `buildInteractiveSpec`. When set, a `relaycast` stdio MCP server
  (`npx -y @relaycast/mcp` + `RELAY_*` env) is merged into the persona's
  declared `mcpServers` before the harness switch. A persona-declared
  `relaycast` still wins. Wired for claude (inside the strict payload)
  and codex (`--config`); opencode keeps its existing "MCP unsupported"
  warning. Kept pure — callers pass `relayMcp` explicitly.
- cli: resolve `relayMcp` from the `RELAY_*` env the broker sets on the
  launcher (`RELAY_API_KEY` + the broker-assigned `RELAY_AGENT_NAME`,
  plus optional base-url / default-workspace) and pass it through at the
  live-launch and dry-run call sites. No relay env ⇒ undefined ⇒ a plain
  `agentworkforce agent` run is unaffected.

Pairs with a broker change that exposes RELAY_AGENT_NAME to the launcher
even when skip_relay_prompt is set (AgentWorkforce/relay).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 31, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds Agent Relay MCP broker support to interactive harness sessions. A new RelayMcpConfig type models relay credentials and routing parameters. The CLI detects relay environment variables, and conditionally injects a relaycast MCP server into harness specs during both dry-run and interactive launches. Tests verify relay injection behavior across claude, codex, and opencode harnesses.

Changes

Agent Relay MCP Integration

Layer / File(s) Summary
Relay MCP type and spec builder
packages/persona-kit/src/interactive-spec.ts, packages/persona-kit/src/index.ts
RelayMcpConfig interface models relay broker credentials and routing. BuildInteractiveSpecInput extends to accept optional relayMcp field. New buildRelaycastMcpServer() helper constructs a stdio MCP spec with @relaycast/mcp and relay environment variables. Integration into buildInteractiveSpec merges injected relaycast server with persona-declared mcpServers, allowing persona-defined relaycast to take precedence. Type is re-exported from persona-kit public API.
Relay MCP harness integration tests
packages/persona-kit/src/interactive-spec.test.ts
Tests validate that relayMcp injects a relaycast MCP server into claude --mcp-config payload and codex --config arguments. Optional relay fields (baseUrl, defaultWorkspace) are omitted when not provided. Persona-declared relaycast overrides injected config. Baseline behavior without relayMcp is unchanged. Opencode harness logs a warning indicating MCP injection is unsupported.
CLI relay environment detection and spec wiring
packages/cli/src/cli.ts
CLI imports RelayMcpConfig and implements resolveRelayMcpFromEnv() to read RELAY_API_KEY, RELAY_AGENT_NAME, and optional RELAY_BASE_URL and RELAY_DEFAULT_WORKSPACE from process environment. Resolved relay config is passed to buildInteractiveSpec during dry-run (runDryRun) and interactive (runInteractive) harness spec construction. Relay injection is conditional; absent or empty-trimmed relay env vars skip injection.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • AgentWorkforce/workforce#85: Both PRs modify packages/persona-kit/src/interactive-spec.ts to translate mcpServers into harness launch arguments—PR #85 adds the mcpServers→Codex --config translation, and this PR injects a Relaycast-derived entry into mcpServers so it flows through the same wiring.
  • AgentWorkforce/workforce#78: Both PRs modify the spec-construction pathway in persona-kit—PR #78 builds buildNonInteractiveSpec on top of buildInteractiveSpec, while this PR changes buildInteractiveSpec input and merges relay MCP config, directly affecting the code path PR #78 depends on.

Poem

🐰 A relay whispers through the MCP stream,
Credentials bundled in a harness dream,
Claude and Codex now speak relay's name,
While opencode watches and logs the flame!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: wiring relaycast MCP into personas launched by broker, which is the core purpose of the changeset.
Description check ✅ Passed The description comprehensively explains the problem, solution, files changed, and testing, all directly related to the relaycast MCP injection feature in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/relaycast-persona-mcp

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for injecting a relaycast MCP server configuration into the harness's MCP config when a persona is launched under an Agent Relay broker. It resolves the broker's environment variables and merges the relaycast server into the persona's declared servers. Feedback on the changes suggests simplifying the code by replacing conditional object spreading with direct property assignments or passing optional properties directly where applicable.

Comment thread packages/cli/src/cli.ts
Comment on lines +515 to +520
return {
apiKey,
agentName,
...(baseUrl ? { baseUrl } : {}),
...(defaultWorkspace ? { defaultWorkspace } : {})
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Instead of using conditional object spreading for optional properties like baseUrl and defaultWorkspace, you can simplify the return object by directly assigning them with a fallback to undefined. Since these properties are optional in RelayMcpConfig, undefined values are perfectly valid and cleaner to read.

Suggested change
return {
apiKey,
agentName,
...(baseUrl ? { baseUrl } : {}),
...(defaultWorkspace ? { defaultWorkspace } : {})
};
return {
apiKey,
agentName,
baseUrl: baseUrl || undefined,
defaultWorkspace: defaultWorkspace || undefined
};

Comment thread packages/cli/src/cli.ts
systemPrompt,
harnessSettings,
mcpServers: mcpResolution.servers,
...(relayMcp ? { relayMcp } : {}),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since relayMcp is an optional property on BuildInteractiveSpecInput, you can pass it directly instead of using conditional object spreading. If relayMcp is undefined, it will be safely ignored or handled by the receiver.

Suggested change
...(relayMcp ? { relayMcp } : {}),
relayMcp,

Comment thread packages/cli/src/cli.ts
systemPrompt,
harnessSettings,
mcpServers: resolvedMcp,
...(relayMcp ? { relayMcp } : {}),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since relayMcp is an optional property on BuildInteractiveSpecInput, you can pass it directly instead of using conditional object spreading. If relayMcp is undefined, it will be safely ignored or handled by the receiver.

Suggested change
...(relayMcp ? { relayMcp } : {}),
relayMcp,

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/src/cli.ts`:
- Around line 1751-1759: The spawn summary uses resolvedMcp rather than the
final spec (which may include relayMcp), causing relaycast to be omitted from
the sanitized mcp output; update the summary generation to derive the displayed
mcp/relay info from the built spec returned by buildInteractiveSpec (inspect
spec.mcpServers and spec.relayMcp) instead of using resolvedMcp so that injected
relayMcp is shown in the "mcp-strict=" log entry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15388f36-f792-4ac8-a36f-d197ecc336f3

📥 Commits

Reviewing files that changed from the base of the PR and between e155b84 and 2993b15.

📒 Files selected for processing (4)
  • packages/cli/src/cli.ts
  • packages/persona-kit/src/index.ts
  • packages/persona-kit/src/interactive-spec.test.ts
  • packages/persona-kit/src/interactive-spec.ts

Comment thread packages/cli/src/cli.ts
Comment on lines +1751 to +1759
const relayMcp = resolveRelayMcpFromEnv(process.env);
const spec = buildInteractiveSpec({
harness,
personaId,
model,
systemPrompt,
harnessSettings,
mcpServers: resolvedMcp,
...(relayMcp ? { relayMcp } : {}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Include injected relaycast in the sanitized spawn summary.

The broker path now injects relaycast into spec, but the later mcp-strict= log still derives from resolvedMcp, so broker launches can print (none) even when relaycast was added successfully. That makes this feature harder to verify and debug.

🩹 Proposed fix
   const relayMcp = resolveRelayMcpFromEnv(process.env);
+  const summaryMcpServerNames = Object.keys(
+    relayMcp ? { relaycast: true, ...(resolvedMcp ?? {}) } : (resolvedMcp ?? {})
+  );
   const spec = buildInteractiveSpec({
     harness,
     personaId,
     model,
     systemPrompt,
@@
   const summary: string[] = [`model=${model}`];
   if (harness === 'claude') {
-    const servers = Object.keys(resolvedMcp ?? {});
-    summary.push(`mcp-strict=${servers.length ? servers.join(',') : '(none)'}`);
+    summary.push(
+      `mcp-strict=${summaryMcpServerNames.length ? summaryMcpServerNames.join(',') : '(none)'}`
+    );
     if (effectiveSelection.permissions?.allow?.length) {
       summary.push(`allow=${effectiveSelection.permissions.allow.length} rule(s)`);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/cli.ts` around lines 1751 - 1759, The spawn summary uses
resolvedMcp rather than the final spec (which may include relayMcp), causing
relaycast to be omitted from the sanitized mcp output; update the summary
generation to derive the displayed mcp/relay info from the built spec returned
by buildInteractiveSpec (inspect spec.mcpServers and spec.relayMcp) instead of
using resolvedMcp so that injected relayMcp is shown in the "mcp-strict=" log
entry.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files

You’re at about 92% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/persona-kit/src/interactive-spec.ts">

<violation number="1" location="packages/persona-kit/src/interactive-spec.ts:259">
P3: Relay-injected MCP servers now trigger an opencode warning that incorrectly says the persona declared `mcpServers`, which is misleading for relay-only runs.</violation>
</file>

<file name="packages/cli/src/cli.ts">

<violation number="1" location="packages/cli/src/cli.ts:1382">
P3: The launch summary can become misleading here: relaycast may be injected via `relayMcp`, but `mcp-strict=` still reflects only `resolvedMcp`. Include injected relay MCP servers in the summary calculation so broker-launched runs don't report `(none)` when MCP wiring is actually present.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

// under a broker. A persona-declared `relaycast` wins, so authors can still
// override it. Kept pure: callers pass relayMcp explicitly (resolved from
// env), so this function reads no environment itself.
const mcpServers = input.relayMcp
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Relay-injected MCP servers now trigger an opencode warning that incorrectly says the persona declared mcpServers, which is misleading for relay-only runs.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/persona-kit/src/interactive-spec.ts, line 259:

<comment>Relay-injected MCP servers now trigger an opencode warning that incorrectly says the persona declared `mcpServers`, which is misleading for relay-only runs.</comment>

<file context>
@@ -186,17 +224,41 @@ function appendCodexMcpServerArgs(
+  // under a broker. A persona-declared `relaycast` wins, so authors can still
+  // override it. Kept pure: callers pass relayMcp explicitly (resolved from
+  // env), so this function reads no environment itself.
+  const mcpServers = input.relayMcp
+    ? { relaycast: buildRelaycastMcpServer(input.relayMcp), ...(input.mcpServers ?? {}) }
+    : input.mcpServers;
</file context>

Comment thread packages/cli/src/cli.ts
systemPrompt,
harnessSettings,
mcpServers: mcpResolution.servers,
...(relayMcp ? { relayMcp } : {}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The launch summary can become misleading here: relaycast may be injected via relayMcp, but mcp-strict= still reflects only resolvedMcp. Include injected relay MCP servers in the summary calculation so broker-launched runs don't report (none) when MCP wiring is actually present.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/cli/src/cli.ts, line 1382:

<comment>The launch summary can become misleading here: relaycast may be injected via `relayMcp`, but `mcp-strict=` still reflects only `resolvedMcp`. Include injected relay MCP servers in the summary calculation so broker-launched runs don't report `(none)` when MCP wiring is actually present.</comment>

<file context>
@@ -1343,13 +1371,15 @@ function runDryRun(selection: PersonaSelection): number {
       systemPrompt,
       harnessSettings,
       mcpServers: mcpResolution.servers,
+      ...(relayMcp ? { relayMcp } : {}),
       permissions: effectiveSelection.permissions
     });
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant