Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

release: v0.2.3#7

Merged
code-crusher merged 2 commits into
mainMatterAIOrg/OrbCode:mainfrom
release/v0.2.3MatterAIOrg/OrbCode:release/v0.2.3Copy head branch name to clipboard
Jun 19, 2026
Merged

release: v0.2.3#7
code-crusher merged 2 commits into
mainMatterAIOrg/OrbCode:mainfrom
release/v0.2.3MatterAIOrg/OrbCode:release/v0.2.3Copy head branch name to clipboard

Conversation

@code-crusher

Copy link
Copy Markdown
Member

release: v0.2.3

Fixed

  • orbcode mcp add no longer swallows a -- separator immediately after the server name. orbcode mcp add --scope user context7 -- npx -y @upstash/context7-mcp … previously wrote command: "--", so the server failed to spawn. A -- directly after the server name is now consumed as the flag/command separator, matching Claude Code's claude mcp add <name> -- <command>. A later -- is still passed through as a literal argument to the server command.

Changed

  • /cost slash command renamed to /usage. It now fetches and prints your plan usage from /axoncode/profile — the plan name (uppercased) and, for tiered accounts, the 5-hour / weekly / monthly windows with percentage used and reset time (or the credits reset date for non-tiered accounts) — instead of showing the session cost and fetching the account balance. Session cost remains in the status bar and /status. The usage block no longer prints the legacy usagePercentage and remainingReviews lines.

Chore

  • Remove stale .orbital/AGENTS.md reference; ignore per-developer .orbcode/AGENTS.md so local project memory isn't accidentally committed.

Post-merge

After this PR is merged into main, push the tag to publish:

```bash
git checkout main && git pull
git tag v0.2.3
git push origin v0.2.3
```

matterai-app Bot and others added 2 commits June 19, 2026 10:59
…and clipboard utility

Introduce three major feature systems that extend OrbCode beyond its
native tool set, along with supporting utilities and UI.

### MCP (Model Context Protocol) Server Support

- New `src/mcp/` module: manager, client, config, auth, and types
  for connecting to external tools via the Model Context Protocol.
- New `src/commands/mcp.ts`: CLI subcommand (`orbcode mcp add/remove/list`)
  for managing MCP servers from the command line.
- Three configuration scopes: user (~/.orbcode/settings.json), project
  (.mcp.json), and local (.orbcode/settings.json) with layered precedence.
- Supports stdio, HTTP Streamable, and SSE transports.
- OAuth 2.0 authentication flow (PKCE, RFC 9728, RFC 8414) with token
  persistence and automatic refresh. Also supports M2M grants
  (client_credentials, private_key_jwt).
- Interactive MCP management in the TUI: `/mcp` command opens a picker
  for enabling/disabling/reconnecting/authenticating servers.
- Startup approval prompt for project-scope servers (persisted to
  .orbcode/settings.json).
- MCP tools are surfaced as `mcp__<server>__<tool>` in the model tool
  list and routed through the existing executor pipeline.

### Skills System

- New `src/skills/` module: loader and types for reusable instruction
  sets stored in ~/.orbcode/skills/ and .orbcode/skills/.
- New `src/tools/executors/skills.ts`: executor for the use_skill tool.
- Skills catalog is injected into the system prompt so the model knows
  which skills are available and when to use them.
- use_skill tool schema updated with richer description linking to the
  system prompt catalog.

### AGENTS.md Memory

- New `src/memory/` module: loader and types for reading AGENTS.md
  files from user home, project root, and parent directories.
- Memory content (project and user instructions) is injected into the
  system prompt as a "Project & User Instructions (AGENTS.md)" section
  that the model treats as authoritative.

### Supporting Changes

- New `src/utils/clipboard.ts`: clipboard utility for headless modes.
- MCP manager lifecycle integrated into Agent (teardown on SessionEnd),
  headless mode, and the TUI App component.
- getActiveTools() now accepts an optional McpManager to include MCP
  tools alongside native tools.
- executeTool() routes mcp__-prefixed tool calls through the McpManager.
- describeToolCall() handles MCP tool names for UI logging.
- Settings schema extended with mcpServers, enabledMcpServers,
  disabledMcpServers, and saveMcpApproval() helper.
- System prompt refactored to accept optional memory and skills data
  for section injection.
…AGENTS.md

- /cost slash command renamed to /usage; fetches plan usage from
  /axoncode/profile (plan name + 5hr/weekly/monthly windows with %
  used and reset time) instead of session cost + account balance.
  Session cost remains in the status bar and /status.
- orbcode mcp add no longer swallows a -- separator immediately
  after the server name, matching claude mcp add <name> -- <command>.
  A later -- is still passed through as a literal arg.
- Drop the stale .orbital/AGENTS.md reference.
- Ignore .orbcode/AGENTS.md so per-developer project memory doesn't
  get committed.
@matterai-app

matterai-app Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Summary By MatterAI MatterAI logo

🔄 What Changed

This release introduces Model Context Protocol (MCP) support, enabling the agent to connect to external tools via stdio, HTTP, and SSE transports. It adds a Skills system for reusable markdown-based instructions and a Memory system (AGENTS.md) for project-specific context. The TUI has been significantly updated with an MCP manager, OAuth authentication flows, and a new /mcp command suite.

🔍 Impact of the Change

The agent's capabilities are exponentially expanded through external MCP tool integration. Users can now define persistent project rules and specialized skills that the agent dynamically invokes. Security is maintained through a mandatory approval flow for project-scope MCP servers and restricted file permissions for stored OAuth tokens.

📁 Total Files Changed

Click to Expand
File ChangeLog
MCP Command src/commands/mcp.ts Implements CLI subcommands for adding, removing, and listing MCP servers.
OAuth Logic src/mcp/auth.ts Handles complex OAuth flows including loopback servers, PKCE, and M2M providers.
MCP Manager src/mcp/manager.ts Orchestrates server lifecycles, tool discovery, and routing for the agent.
Memory Loader src/memory/loader.ts Discovers and parses AGENTS.md files with recursive @include support.
Skills Loader src/skills/loader.ts Loads specialized markdown instructions from user and project directories.
TUI Update src/ui/App.tsx Major refactor to support MCP management, approval prompts, and skill usage.
System Prompt src/prompts/system.ts Injects memory, skills, and MCP tool definitions into the LLM context.
MCP Client src/mcp/client.ts Manages low-level SDK connections for various MCP transport types.

🧪 Test Added/Recommended

Recommended

  • Integration Tests: Verify the full MCP connection lifecycle (connect -> tool call -> disconnect) using a mock stdio server.
  • Unit Tests: Validate the recursive @include logic in memory/loader.ts for cycle detection and depth limits.
  • Security Tests: Ensure OAuth tokens in mcp-auth/ are always written with 0o600 permissions across platforms.

🔒 Security Vulnerabilities

No critical vulnerabilities detected. The implementation uses an explicit approval model for project-provided MCP servers to prevent unauthorized code execution from untrusted repositories. 🛡️

@code-crusher code-crusher merged commit 9a0b4a8 into main Jun 19, 2026
1 check was pending

@matterai-app matterai-app Bot left a comment

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.

🧪 PR Review is completed: Substantial feature PR adding MCP support, skills, and AGENTS.md memory. Found a critical regex bug breaking MCP tool routing for servers with underscores, a race condition in OAuth port allocation, missing error handling on the callback server, and an unsafe type assertion when parsing MCP tool results.

Reviewed src/commands/mcp.ts: no issues found.
Reviewed src/config/settings.ts: no issues found.
Reviewed src/core/agent.ts: no issues found.
Reviewed src/headless.ts: no issues found.
Reviewed src/index.tsx: no issues found.
Reviewed src/mcp/config.ts: no issues found.
Reviewed src/mcp/types.ts: no issues found.
Reviewed src/memory/loader.ts: no issues found.
Reviewed src/prompts/system.ts: no issues found.
Reviewed src/skills/loader.ts: no issues found.
Reviewed src/ui/App.tsx: no issues found.
Reviewed src/ui/components/McpApprovalPrompt.tsx: no issues found.
Reviewed src/ui/components/McpAuthScreen.tsx: no issues found.
Reviewed src/ui/components/McpPicker.tsx: no issues found.
Reviewed src/ui/components/StatusBar.tsx: no issues found.
Reviewed src/utils/clipboard.ts: no issues found.

Skipped files
  • .orbital/AGENTS.md: Skipped file pattern
  • CHANGELOG.md: Skipped file pattern
  • README.md: Skipped file pattern
  • package-lock.json: Skipped file pattern
⬇️ Low Priority Suggestions (2)
src/mcp/auth.ts (1 suggestion)

Location: src/mcp/auth.ts (Lines 217-220)

🟠 Error Handling

Issue: startCallbackServer creates an HTTP server and calls listen() without attaching an 'error' event handler. If the callback port is already in use (likely due to the ephemeralPort fallback always returning 8765), the unhandled 'error' event will crash the Node.js process.

Fix: Attach a no-op error handler to prevent the process from crashing on EADDRINUSE or other listen errors.

Impact: Prevents fatal crashes during OAuth authentication

-  	})
-  	server.listen(port, "127.0.0.1")
-  	return server
-  }
+  	})
+  	server.on("error", () => {})
+  	server.listen(port, "127.0.0.1")
+  	return server
src/mcp/client.ts (1 suggestion)

Location: src/mcp/client.ts (Lines 123-139)

🟡 Type Safety

Issue: callMcpTool uses (result.content ?? []) as Array<Record<string, unknown>> to cast the MCP tool result content. If a misbehaving server returns a non-array value, the subsequent for...of loop will throw or behave unpredictably.

Fix: Validate that result.content is an array before iterating, and guard each block with an object type check.

Impact: Hardens the client against malformed MCP server responses

-  	const content = (result.content ?? []) as Array<Record<string, unknown>>
-  	const parts: string[] = []
-  	for (const block of content) {
-  		const type = block.type as string
-  		if (type === "text" && typeof block.text === "string") {
-  			parts.push(block.text)
-  		} else if (type === "resource") {
-  			const resource = block.resource as Record<string, unknown> | undefined
-  			if (resource && typeof resource.text === "string") parts.push(resource.text)
-  		} else if (type === "image") {
-  			parts.push(`[image: ${block.mimeType}]`)
-  		} else if (type === "audio") {
-  			parts.push(`[audio: ${block.mimeType}]`)
-  		} else if (type === "resource_link" && typeof block.uri === "string") {
-  			parts.push(`[resource: ${block.uri}]`)
-  		}
-  	}
+  	const content = Array.isArray(result.content) ? result.content : []
+  	const parts: string[] = []
+  	for (const block of content) {
+  		if (!block || typeof block !== "object") continue
+  		const b = block as Record<string, unknown>
+  		const type = b.type as string
+  		if (type === "text" && typeof b.text === "string") {
+  			parts.push(b.text)
+  		} else if (type === "resource") {
+  			const resource = b.resource as Record<string, unknown> | undefined
+  			if (resource && typeof resource.text === "string") parts.push(resource.text)
+  		} else if (type === "image") {
+  			parts.push(`[image: ${b.mimeType}]`)
+  		} else if (type === "audio") {
+  			parts.push(`[audio: ${b.mimeType}]`)
+  		} else if (type === "resource_link" && typeof b.uri === "string") {
+  			parts.push(`[resource: ${b.uri}]`)
+  		}
+  	}

Comment thread src/mcp/manager.ts
Comment on lines +232 to +234
hasTool(toolName: string): boolean {
return /^mcp__[^_]+__/.test(toolName)
}

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.

🔴 Logic Error

Issue: The hasTool regex /^mcp__[^_]+__/ fails to match valid MCP tool names when the server name contains an underscore. Server names are validated with /^[A-Za-z0-9_-]+$/, so underscores are allowed. For a server named my_server, the tool name mcp__my_server__tool will not be recognized as an MCP tool, causing the tool router to fall through to native tools and return "not available".

Fix: Update the character class to explicitly match the allowed server name characters.

Impact: Restores MCP tool routing for all valid server names

Suggested change
hasTool(toolName: string): boolean {
return /^mcp__[^_]+__/.test(toolName)
}
hasTool(toolName: string): boolean {
return /^mcp__([A-Za-z0-9_-]+)__/.test(toolName)
}

Comment thread src/mcp/manager.ts

/** Route an `mcp__<server>__<tool>` call to the right connection. */
async callTool(toolName: string, args: Record<string, unknown>): Promise<{ text: string; isError?: boolean }> {
const match = /^mcp__([^_]+)__(.+)$/.exec(toolName)

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.

🔴 Logic Error

Issue: The callTool regex /^mcp__([^_]+)__(.+)$/ fails for server names containing underscores (which are valid per /^[A-Za-z0-9_-]+$/). For mcp__my_server__tool, the regex match produces my as the server name and server__tool as the original tool name, causing the lookup this.connections.get(server) to miss the connection and return an error.

Fix: Use a character class that matches the full server name, identical to the hasTool fix.

Impact: Fixes broken MCP tool execution for servers with underscores in their names

Suggested change
const match = /^mcp__([^_]+)__(.+)$/.exec(toolName)
const match = /^mcp__([A-Za-z0-9_-]+)__(.+)$/.exec(toolName)

Comment thread src/tools/index.ts
default:
// MCP tools (mcp__<server>__<tool>) — show the server + tool name.
if (/^mcp__/.test(toolName)) {
const match = /^mcp__([^_]+)__(.+)$/.exec(toolName)

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.

🟡 Code Quality

Issue: The summarizeToolCall regex /^mcp__([^_]+)__(.+)$/ has the same underscore-matching bug as the manager's callTool and hasTool methods. While this only affects display text (falling back to the raw tool name), it should be consistent with the rest of the codebase.

Fix: Align the regex with the corrected server name character class.

Impact: Consistent MCP tool name formatting in the TUI

Suggested change
const match = /^mcp__([^_]+)__(.+)$/.exec(toolName)
const match = /^mcp__([A-Za-z0-9_-]+)__(.+)$/.exec(toolName)

Comment thread src/mcp/auth.ts
Comment on lines +223 to +229
function ephemeralPort(): number {
// Bind to port 0 and read back the assigned port, then close immediately.
const probe = http.createServer().listen(0, "127.0.0.1")
const addr = probe.address()
probe.close()
return typeof addr === "object" && addr ? addr.port : 8765
}

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.

🟠 Performance / Reliability

Issue: ephemeralPort() calls probe.address() synchronously after listen(0). Because TCP port binding is asynchronous, address() returns null in most executions, forcing a fallback to the hardcoded port 8765. Multiple concurrent OAuth flows (or a stale process) will collide on this fixed port and throw EADDRINUSE.

Fix: Replace the broken synchronous probe with a random port selection in the IANA ephemeral range.

Impact: Eliminates port collisions and makes OAuth callback allocation reliable

Suggested change
function ephemeralPort(): number {
// Bind to port 0 and read back the assigned port, then close immediately.
const probe = http.createServer().listen(0, "127.0.0.1")
const addr = probe.address()
probe.close()
return typeof addr === "object" && addr ? addr.port : 8765
}
function ephemeralPort(): number {
// Pick a random port in the IANA ephemeral range to avoid collisions.
return 49152 + Math.floor(Math.random() * 16384)
}

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

Morty Proxy This is a proxified and sanitized view of the page, visit original site.