@matterailab/orbcode 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +351 -8
- package/dist/commands/mcp.js +266 -0
- package/dist/config/settings.js +27 -0
- package/dist/core/agent.js +28 -7
- package/dist/headless.js +18 -0
- package/dist/index.js +9 -0
- package/dist/mcp/auth.js +289 -0
- package/dist/mcp/client.js +132 -0
- package/dist/mcp/config.js +270 -0
- package/dist/mcp/manager.js +277 -0
- package/dist/mcp/types.js +8 -0
- package/dist/memory/loader.js +167 -0
- package/dist/memory/types.js +1 -0
- package/dist/prompts/system.js +26 -7
- package/dist/skills/loader.js +132 -0
- package/dist/skills/types.js +1 -0
- package/dist/tools/executors/skills.js +28 -0
- package/dist/tools/index.js +22 -3
- package/dist/tools/schemas/index.js +6 -3
- package/dist/tools/schemas/use_skill.js +1 -1
- package/dist/ui/App.js +205 -50
- package/dist/ui/components/McpApprovalPrompt.js +61 -0
- package/dist/ui/components/McpAuthScreen.js +55 -0
- package/dist/ui/components/McpPicker.js +262 -0
- package/dist/ui/components/StatusBar.js +28 -9
- package/dist/utils/clipboard.js +45 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -36,6 +36,9 @@ activity rows, edit/command approvals, and todo tracking.
|
|
|
36
36
|
- [Headless mode](#headless-mode)
|
|
37
37
|
- [Configuration](#configuration)
|
|
38
38
|
- [Hooks](#hooks)
|
|
39
|
+
- [MCP servers](#mcp-servers)
|
|
40
|
+
- [Skills](#skills)
|
|
41
|
+
- [AGENTS.md memory](#agentsmd-memory)
|
|
39
42
|
- [Architecture](#architecture)
|
|
40
43
|
- [Tools](#tools)
|
|
41
44
|
- [Agent loop](#agent-loop)
|
|
@@ -293,8 +296,9 @@ MatterAI gateway untouched.
|
|
|
293
296
|
| `/compact` | summarize the conversation and replace history with the summary |
|
|
294
297
|
| `/tasks` | print the current task list |
|
|
295
298
|
| `/status` | version, model, account, gateway, context usage, cost, approval modes |
|
|
296
|
-
| `/
|
|
299
|
+
| `/usage` | fetch plan usage |
|
|
297
300
|
| `/init` | analyze the codebase and create/improve `AGENTS.md` |
|
|
301
|
+
| `/mcp` | manage MCP servers — enable, disable, reconnect, view status & tool counts |
|
|
298
302
|
| `/login` | start the browser sign-in flow |
|
|
299
303
|
| `/logout` | remove the saved token |
|
|
300
304
|
| `/version` | print the CLI version |
|
|
@@ -486,6 +490,316 @@ untrusted.
|
|
|
486
490
|
**→ Full reference with worked recipes for every event:
|
|
487
491
|
[docs/HOOKS.md](https://github.com/MatterAIOrg/OrbCode/blob/main/docs/HOOKS.md).**
|
|
488
492
|
|
|
493
|
+
## MCP servers
|
|
494
|
+
|
|
495
|
+
OrbCode connects to external tools via the **Model Context Protocol** (MCP). MCP
|
|
496
|
+
servers expose tools that appear alongside the native tools as
|
|
497
|
+
`mcp__<server>__<tool>` and can be called by the model like any other tool.
|
|
498
|
+
|
|
499
|
+
### Configuration
|
|
500
|
+
|
|
501
|
+
MCP servers are configured in three scopes (highest precedence last):
|
|
502
|
+
|
|
503
|
+
1. **User scope** — `mcpServers` in `~/.orbcode/settings.json`. Applies to every
|
|
504
|
+
project on this machine.
|
|
505
|
+
2. **Project scope** — `.mcp.json` in the project root (and parent directories,
|
|
506
|
+
closer-to-cwd wins). This is the check-into-git, shared format, compatible
|
|
507
|
+
with Claude Code's `.mcp.json`.
|
|
508
|
+
3. **Local scope** — `mcpServers` in `.orbcode/settings.json`. Per-project,
|
|
509
|
+
per-machine overrides (not checked in).
|
|
510
|
+
|
|
511
|
+
### Adding servers from the command line
|
|
512
|
+
|
|
513
|
+
The `orbcode mcp` subcommand manages servers without editing JSON by hand —
|
|
514
|
+
like Claude Code's `claude mcp add/remove/list`:
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
# stdio (default transport): name + command + args
|
|
518
|
+
orbcode mcp add filesystem npx -y @modelcontextprotocol/server-filesystem /Users/me/projects
|
|
519
|
+
|
|
520
|
+
# http transport
|
|
521
|
+
orbcode mcp add --transport http linear-server https://mcp.linear.app/mcp
|
|
522
|
+
|
|
523
|
+
# sse transport with a header
|
|
524
|
+
orbcode mcp add -t sse --header "Authorization=Bearer ${TOKEN}" my-sse https://example.com/sse
|
|
525
|
+
|
|
526
|
+
# http with OAuth (auth from /mcp after adding)
|
|
527
|
+
orbcode mcp add --transport http --oauth notion https://mcp.notion.com/mcp
|
|
528
|
+
orbcode mcp add -t http --oauth --oauth-scope "read:issues" linear https://mcp.linear.app/mcp
|
|
529
|
+
|
|
530
|
+
# stdio with env vars, written to user scope (~/.orbcode/settings.json)
|
|
531
|
+
orbcode mcp add -s user -e API_KEY=secret -e DEBUG=true my-server node server.js
|
|
532
|
+
|
|
533
|
+
# list all configured servers (name, scope, transport detail)
|
|
534
|
+
orbcode mcp list
|
|
535
|
+
|
|
536
|
+
# remove a server (finds it in whichever scope it lives)
|
|
537
|
+
orbcode mcp remove filesystem
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
Flags go before the server name; everything after the name is the command +
|
|
541
|
+
args (so stdio servers can take their own flags like `-y`). Use `--` to force
|
|
542
|
+
the split if needed. `-s`/`--scope` selects `project` (default, writes
|
|
543
|
+
`.mcp.json`), `user` (writes `~/.orbcode/settings.json`), or `local` (writes
|
|
544
|
+
`.orbcode/settings.json`). Run `orbcode mcp help` for the full reference.
|
|
545
|
+
|
|
546
|
+
`add` and `remove` print the file they modified:
|
|
547
|
+
|
|
548
|
+
```
|
|
549
|
+
$ orbcode mcp add --transport http linear-server https://mcp.linear.app/mcp
|
|
550
|
+
Added HTTP MCP server linear-server
|
|
551
|
+
URL: https://mcp.linear.app/mcp
|
|
552
|
+
Scope: project
|
|
553
|
+
File modified: /Users/me/my-project/.mcp.json
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
OAuth servers show `needs-auth` after adding — they don't auto-open a browser.
|
|
557
|
+
Open `/mcp` in the TUI, select the server, and press `a` (or `1`) to
|
|
558
|
+
authenticate. OrbCode shows an auth screen:
|
|
559
|
+
|
|
560
|
+
```
|
|
561
|
+
Authenticating with linear-server…
|
|
562
|
+
|
|
563
|
+
✽ A browser window will open for authentication
|
|
564
|
+
|
|
565
|
+
If your browser doesn't open automatically, copy this URL manually (c to copy)
|
|
566
|
+
https://mcp.linear.app/authorize?response_type=code&client_id=…
|
|
567
|
+
|
|
568
|
+
If the redirect page shows a connection error, paste the URL from your browser's address bar:
|
|
569
|
+
URL> █
|
|
570
|
+
|
|
571
|
+
Return here after authenticating in your browser. Press Esc to go back.
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
The browser opens automatically to the server's OAuth authorize page (RFC 9728
|
|
575
|
+
discovery → PKCE → redirect to a loopback callback → token exchange). Press `c`
|
|
576
|
+
to copy the URL to the clipboard if the browser doesn't open. If the redirect
|
|
577
|
+
fails (e.g. wrong port), press enter and paste the redirect URL from the
|
|
578
|
+
browser's address bar — OrbCode extracts the code from it. Either path
|
|
579
|
+
(callback or paste) completes the flow. Tokens persist under
|
|
580
|
+
`~/.orbcode/mcp-auth/<server>.json` (mode 0600) for future sessions; refresh
|
|
581
|
+
happens automatically when they expire. For M2M grants (`client_credentials`,
|
|
582
|
+
`private_key_jwt`), there's no browser — the token exchange is direct.
|
|
583
|
+
|
|
584
|
+
`.mcp.json` format (project scope):
|
|
585
|
+
|
|
586
|
+
```json
|
|
587
|
+
{
|
|
588
|
+
"mcpServers": {
|
|
589
|
+
"filesystem": {
|
|
590
|
+
"command": "npx",
|
|
591
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
|
|
592
|
+
},
|
|
593
|
+
"github": {
|
|
594
|
+
"type": "http",
|
|
595
|
+
"url": "https://api.githubcopilot.com/mcp/",
|
|
596
|
+
"headers": { "Authorization": "Bearer ${GITHUB_TOKEN}" }
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
`settings.json` `mcpServers` (user/local scope) uses the same per-server shape.
|
|
603
|
+
Environment variables (`${VAR}`) are expanded from `process.env`.
|
|
604
|
+
|
|
605
|
+
Three server types are supported:
|
|
606
|
+
|
|
607
|
+
- **stdio** (default, omit `type`): OrbCode spawns `command` with `args` and
|
|
608
|
+
talks over stdin/stdout. Optional `env` and `cwd`.
|
|
609
|
+
- **http**: Streamable HTTP transport. `url` + optional `headers` + optional
|
|
610
|
+
`oauth` (see [Authentication](#mcp-authentication)).
|
|
611
|
+
- **sse**: Server-Sent Events (legacy remote). `url` + optional `headers` +
|
|
612
|
+
optional `oauth`.
|
|
613
|
+
|
|
614
|
+
### MCP authentication
|
|
615
|
+
|
|
616
|
+
Remote servers (http/sse) often require auth. OrbCode supports three ways:
|
|
617
|
+
|
|
618
|
+
**1. Static headers** (API keys, personal access tokens). Use `headers` with
|
|
619
|
+
`${ENV_VAR}` expansion — the token is read from the environment, never written
|
|
620
|
+
to disk:
|
|
621
|
+
|
|
622
|
+
```json
|
|
623
|
+
{
|
|
624
|
+
"github": {
|
|
625
|
+
"type": "http",
|
|
626
|
+
"url": "https://api.githubcopilot.com/mcp/",
|
|
627
|
+
"headers": { "Authorization": "Bearer ${GITHUB_TOKEN}" }
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
**2. OAuth 2.0 flows** (for servers that require user login: GitHub, Google
|
|
633
|
+
Drive, Slack, Notion, …). Set `oauth: true` (or `{ "scope": "..." }`) on an
|
|
634
|
+
http/sse server. OrbCode runs the full authorization-code flow via the SDK:
|
|
635
|
+
RFC 9728 protected-resource discovery, RFC 8414 authorization-server metadata,
|
|
636
|
+
PKCE, dynamic client registration, and token refresh. A one-shot loopback HTTP
|
|
637
|
+
server receives the browser redirect. Tokens, client info, code verifiers, and
|
|
638
|
+
discovery state are persisted per-server under `~/.orbcode/mcp-auth/<server>.json`
|
|
639
|
+
(mode 0600) so re-auth is only needed when a token expires or is revoked.
|
|
640
|
+
|
|
641
|
+
```json
|
|
642
|
+
{
|
|
643
|
+
"notion": {
|
|
644
|
+
"type": "http",
|
|
645
|
+
"url": "https://mcp.notion.com/mcp",
|
|
646
|
+
"oauth": true
|
|
647
|
+
},
|
|
648
|
+
"google-drive": {
|
|
649
|
+
"type": "http",
|
|
650
|
+
"url": "https://mcp.google.com/drive",
|
|
651
|
+
"oauth": { "scope": "https://www.googleapis.com/auth/drive.readonly" }
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
**3. Machine-to-machine OAuth** (no browser). For service-to-service auth,
|
|
657
|
+
use `client_credentials` or `private_key_jwt` grants — these use the SDK's
|
|
658
|
+
built-in `ClientCredentialsProvider` / `PrivateKeyJwtProvider`:
|
|
659
|
+
|
|
660
|
+
```json
|
|
661
|
+
{
|
|
662
|
+
"internal-api": {
|
|
663
|
+
"type": "http",
|
|
664
|
+
"url": "https://internal.example.com/mcp",
|
|
665
|
+
"oauth": {
|
|
666
|
+
"grantType": "client_credentials",
|
|
667
|
+
"clientId": "orbcode-client",
|
|
668
|
+
"clientSecret": "${INTERNAL_CLIENT_SECRET}",
|
|
669
|
+
"scope": "mcp:tools"
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
"jwt-api": {
|
|
673
|
+
"type": "http",
|
|
674
|
+
"url": "https://jwt.example.com/mcp",
|
|
675
|
+
"oauth": {
|
|
676
|
+
"grantType": "private_key_jwt",
|
|
677
|
+
"clientId": "orbcode-client",
|
|
678
|
+
"privateKey": "${JWT_PRIVATE_KEY_PEM}",
|
|
679
|
+
"algorithm": "RS256"
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
When a server needs auth, its status shows `needs-auth` in `/mcp`; press **a**
|
|
686
|
+
to re-authenticate (clears stored tokens and re-runs the flow). Secrets in
|
|
687
|
+
`oauth` blocks support `${ENV_VAR}` expansion like `headers`, so client
|
|
688
|
+
secrets and private keys can be sourced from the environment rather than
|
|
689
|
+
committed to `.mcp.json`.
|
|
690
|
+
|
|
691
|
+
### Enabling & disabling servers
|
|
692
|
+
|
|
693
|
+
- **User/local-scope servers** connect automatically on startup (you wrote them,
|
|
694
|
+
so they're trusted).
|
|
695
|
+
- **Project-scope servers** (from `.mcp.json`) require a one-time approval: on
|
|
696
|
+
first launch in a project, OrbCode shows a checklist of detected servers.
|
|
697
|
+
Select the ones you trust; the decision is persisted to
|
|
698
|
+
`.orbcode/settings.json` (`enabledMcpServers` / `disabledMcpServers`) so they
|
|
699
|
+
auto-connect on future sessions.
|
|
700
|
+
|
|
701
|
+
The `/mcp` command opens an interactive manager at any time:
|
|
702
|
+
|
|
703
|
+
- ↑/↓ to select a server. The selected server shows a detail panel with
|
|
704
|
+
**Status**, **Auth** (authenticated / not authenticated / static headers),
|
|
705
|
+
**URL** (or stdio command), **Config location** (the file path), and a
|
|
706
|
+
numbered action list.
|
|
707
|
+
- **enter** or **2** toggles enable/disable. **r** or **3** reconnects. **a**
|
|
708
|
+
or **1** authenticates a `needs-auth` OAuth server (opens a browser for the
|
|
709
|
+
authorization-code flow; M2M grants exchange directly). **esc** closes.
|
|
710
|
+
- Each server shows a live status icon: `✓ connected`, `△ needs-auth`,
|
|
711
|
+
`✗ failed`, `○ disabled`, `⋯ connecting`, plus a tool count when connected.
|
|
712
|
+
- Enable/disable choices are persisted per-project. OAuth tokens are persisted
|
|
713
|
+
per-server under `~/.orbcode/mcp-auth/`.
|
|
714
|
+
|
|
715
|
+
### Headless mode
|
|
716
|
+
|
|
717
|
+
In `orbcode -p`, there's no interactive approval, so project-scope servers are
|
|
718
|
+
only connected if they were previously approved (via an interactive `/mcp`
|
|
719
|
+
session). Unapproved project servers are skipped with a stderr note. User/local
|
|
720
|
+
servers connect as usual.
|
|
721
|
+
|
|
722
|
+
## Skills
|
|
723
|
+
|
|
724
|
+
Skills are reusable instruction sets the model can load on demand. A skill is a
|
|
725
|
+
directory containing a `SKILL.md` file with optional YAML frontmatter and a
|
|
726
|
+
markdown body of specialized instructions.
|
|
727
|
+
|
|
728
|
+
### Creating a skill
|
|
729
|
+
|
|
730
|
+
Place a directory under `~/.orbcode/skills/` (user, applies everywhere) or
|
|
731
|
+
`.orbcode/skills/` (project, checked into the repo):
|
|
732
|
+
|
|
733
|
+
```
|
|
734
|
+
~/.orbcode/skills/
|
|
735
|
+
my-skill/
|
|
736
|
+
SKILL.md
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
`SKILL.md` format:
|
|
740
|
+
|
|
741
|
+
```markdown
|
|
742
|
+
---
|
|
743
|
+
description: Write concise, idiomatic Go code following project conventions
|
|
744
|
+
when_to_use: the task involves writing or reviewing Go code
|
|
745
|
+
---
|
|
746
|
+
|
|
747
|
+
# Go style skill
|
|
748
|
+
|
|
749
|
+
When writing Go in this project:
|
|
750
|
+
- Use `errors.Join` for multi-error aggregation
|
|
751
|
+
- Prefer table-driven tests
|
|
752
|
+
- ...
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
- `description` (frontmatter or first paragraph): shown in the skill catalog.
|
|
756
|
+
- `when_to_use` (frontmatter): a hint telling the model when to invoke this skill.
|
|
757
|
+
- `${SKILL_DIR}` in the body is replaced with the skill's absolute directory
|
|
758
|
+
path, so you can reference bundled scripts.
|
|
759
|
+
|
|
760
|
+
### How skills are used
|
|
761
|
+
|
|
762
|
+
The skill catalog (names + descriptions + when-to-use) is injected into the
|
|
763
|
+
system prompt. When a task matches a skill's `when_to_use` condition, the model
|
|
764
|
+
calls the `use_skill` tool with the skill's name, which loads the full
|
|
765
|
+
instructions into context. Project skills override user skills on name
|
|
766
|
+
collisions (closer-to-cwd wins).
|
|
767
|
+
|
|
768
|
+
## AGENTS.md memory
|
|
769
|
+
|
|
770
|
+
AGENTS.md files provide project- and user-level instructions that are injected
|
|
771
|
+
into every system prompt — build commands, code style, architecture notes,
|
|
772
|
+
conventions. This is OrbCode's equivalent of Claude Code's `CLAUDE.md`, using
|
|
773
|
+
the open `AGENTS.md` filename so it works across tools.
|
|
774
|
+
|
|
775
|
+
### Discovery
|
|
776
|
+
|
|
777
|
+
Files are loaded in this order (lowest precedence first; higher-precedence files
|
|
778
|
+
appear later and get more weight):
|
|
779
|
+
|
|
780
|
+
1. **User memory**: `~/.orbcode/AGENTS.md` — personal global instructions.
|
|
781
|
+
2. **Project memory**: `AGENTS.md` and `.orbcode/AGENTS.md` in the cwd and every
|
|
782
|
+
parent directory (closer-to-cwd wins). Checked into the repo, shared with the
|
|
783
|
+
team.
|
|
784
|
+
3. **Local memory**: `AGENTS.local.md` in the cwd and parents — private
|
|
785
|
+
per-machine overrides (gitignore this).
|
|
786
|
+
|
|
787
|
+
### @include directives
|
|
788
|
+
|
|
789
|
+
An AGENTS.md file can include other files with `@path` references:
|
|
790
|
+
|
|
791
|
+
```markdown
|
|
792
|
+
# Project guide
|
|
793
|
+
|
|
794
|
+
See the detailed style guide: @./docs/style.md
|
|
795
|
+
And the global one: @~/orbcode-global.md
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
`@path` (relative to the including file), `@./path`, `@~/path`, and `@/abs/path`
|
|
799
|
+
are all supported. Includes are resolved recursively (up to 5 levels, with cycle
|
|
800
|
+
detection). Use `/init` to have OrbCode generate a starter `AGENTS.md` by
|
|
801
|
+
analyzing the codebase.
|
|
802
|
+
|
|
489
803
|
## Architecture
|
|
490
804
|
|
|
491
805
|
```
|
|
@@ -502,23 +816,50 @@ src/
|
|
|
502
816
|
stream.ts chunk model: text / reasoning / native_tool_calls / usage
|
|
503
817
|
headers.ts X-AxonCode-Version, X-AxonCode-TaskId, X-AXON-REPO, …
|
|
504
818
|
prompts/system.ts system prompt: agent roleDefinition + tool guide (ported
|
|
505
|
-
verbatim from the extension) + CLI system-info section
|
|
819
|
+
verbatim from the extension) + CLI system-info section +
|
|
820
|
+
AGENTS.md memory + skills catalog injection
|
|
506
821
|
tools/
|
|
507
822
|
schemas/ native-tools JSON schemas, copied verbatim from the extension
|
|
508
|
-
executors/ CLI implementations (fs, child_process, search, web)
|
|
509
|
-
index.ts dispatch, approval classification, call summaries
|
|
823
|
+
executors/ CLI implementations (fs, child_process, search, web, skills)
|
|
824
|
+
index.ts dispatch, approval classification, call summaries, MCP routing
|
|
510
825
|
core/
|
|
511
|
-
agent.ts the agent loop (see below)
|
|
826
|
+
agent.ts the agent loop (see below); owns the McpManager
|
|
512
827
|
events.ts AgentEvent model consumed by the UI
|
|
513
828
|
hooks.ts lifecycle hooks engine, Claude-Code compatible (see Hooks)
|
|
829
|
+
mcp/
|
|
830
|
+
types.ts MCP server config + connection state types
|
|
831
|
+
config.ts .mcp.json + settings.json mcpServers loader (3-scope merge)
|
|
832
|
+
client.ts SDK transport (stdio/http/sse) + tool enum + tool call
|
|
833
|
+
manager.ts McpManager: connection lifecycle, enable/disable, tool routing
|
|
834
|
+
skills/
|
|
835
|
+
types.ts Skill + frontmatter types
|
|
836
|
+
loader.ts ~/.orbcode/skills + .orbcode/skills discovery (SKILL.md format)
|
|
837
|
+
memory/
|
|
838
|
+
types.ts MemoryFile type
|
|
839
|
+
loader.ts AGENTS.md discovery (user/project/local) + @include resolution
|
|
514
840
|
ui/
|
|
515
841
|
App.tsx main Ink app: static finalized rows + dynamic streaming area
|
|
516
842
|
LoginView.tsx device-flow login screen with paste fallback
|
|
517
843
|
components/ Header, InputBox, rows, ApprovalPrompt, FollowupPrompt,
|
|
518
|
-
|
|
844
|
+
HookTrustPrompt, McpApprovalPrompt, McpPicker, ModelPicker,
|
|
845
|
+
SessionPicker, Spinner, StatusBar
|
|
519
846
|
markdown.ts markdown → ANSI renderer
|
|
520
847
|
```
|
|
521
848
|
|
|
849
|
+
**MCP integration**: the `McpManager` (owned by the agent) loads the merged
|
|
850
|
+
config on `start()`, connects to approved servers in parallel, and exposes
|
|
851
|
+
their tools as `mcp__<server>__<tool>` definitions alongside the native tools.
|
|
852
|
+
The agent routes any `mcp__*` tool call to the manager, which dispatches it to
|
|
853
|
+
the right SDK client. Project-scope servers (from `.mcp.json`) require a
|
|
854
|
+
one-time approval; the TUI shows a checklist at startup and `/mcp` manages them
|
|
855
|
+
at runtime. Connections are torn down on session end.
|
|
856
|
+
|
|
857
|
+
**Skills & memory**: on agent construction, `loadMemoryFiles()` walks the
|
|
858
|
+
AGENTS.md discovery chain (user → project → local, with `@include` resolution)
|
|
859
|
+
and `loadSkills()` walks the skills directories. Both are injected into the
|
|
860
|
+
system prompt so the model sees project instructions and the skill catalog on
|
|
861
|
+
every turn. The `use_skill` tool loads a skill's full body on demand.
|
|
862
|
+
|
|
522
863
|
**Streaming** faithfully ports the extension's handler quirks: cumulative
|
|
523
864
|
content dedup (some backends re-send full content), `<think>` blocks routed to
|
|
524
865
|
reasoning, both `reasoning` and `reasoning_content` delta fields, tool-call
|
|
@@ -535,7 +876,7 @@ extension). It shows in the status bar, is written into the session file (so
|
|
|
535
876
|
`/resume` lists real titles), and becomes the terminal window title:
|
|
536
877
|
`<title> (orbcode)`.
|
|
537
878
|
|
|
538
|
-
**Usage data**: `/status` and `/
|
|
879
|
+
**Usage data**: `/status` and `/usage` fetch `/axoncode/profile` and show the
|
|
539
880
|
plan, usage percentage (used/remaining), remaining reviews, and the credits
|
|
540
881
|
reset date — the same data as the extension's profile view.
|
|
541
882
|
|
|
@@ -554,11 +895,13 @@ Active in the CLI (schemas byte-identical to the extension's `native-tools`):
|
|
|
554
895
|
| `execute_command` | user's shell, 120s timeout, 30k output cap, optional cwd |
|
|
555
896
|
| `web_search` / `web_fetch` | proxied through the MatterAI backend with your token |
|
|
556
897
|
| `update_todo_list` | drives the TUI todo panel |
|
|
898
|
+
| `use_skill` | loads a skill's full instructions from ~/.orbcode/skills or .orbcode/skills |
|
|
557
899
|
| `ask_followup_question` | interactive menu in the TUI |
|
|
558
900
|
| `attempt_completion` | ends the turn with a completion card |
|
|
901
|
+
| `mcp__<server>__<tool>` | any tool exposed by a connected MCP server (see [MCP servers](#mcp-servers)) |
|
|
559
902
|
|
|
560
903
|
Present in `tools/schemas/` but **inactive** (need IDE services): `codebase_search`,
|
|
561
|
-
`lsp`, `list_code_definition_names`, `
|
|
904
|
+
`lsp`, `list_code_definition_names`, `check_past_chat_memories`,
|
|
562
905
|
`browser_action`, `generate_image`, `new_task`, `switch_mode`,
|
|
563
906
|
`fetch_instructions`, `run_slash_command`.
|
|
564
907
|
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { addMcpServer, configPathForScope, loadMcpConfig, removeMcpServerAnyScope, } from "../mcp/config.js";
|
|
2
|
+
/**
|
|
3
|
+
* `orbcode mcp` subcommand — manage MCP servers from the command line, like
|
|
4
|
+
* Claude Code's `claude mcp add/remove/list`.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* orbcode mcp add <name> <command> [args...] stdio (default)
|
|
8
|
+
* orbcode mcp add --transport http <name> <url> http
|
|
9
|
+
* orbcode mcp add --transport sse <name> <url> sse
|
|
10
|
+
* orbcode mcp add -s <scope> -e KEY=value ... <name> ... with scope/env
|
|
11
|
+
* orbcode mcp add --header KEY=value ... <name> <url> http/sse headers
|
|
12
|
+
* orbcode mcp remove <name> remove from any scope
|
|
13
|
+
* orbcode mcp list list all servers
|
|
14
|
+
*
|
|
15
|
+
* Flags:
|
|
16
|
+
* -s, --scope <user|project|local> config scope (default: project)
|
|
17
|
+
* -t, --transport <stdio|http|sse> transport type (default: stdio)
|
|
18
|
+
* -e, --env KEY=value environment variable (repeatable, stdio)
|
|
19
|
+
* --header KEY=value HTTP header (repeatable, http/sse)
|
|
20
|
+
* --oauth enable OAuth for http/sse
|
|
21
|
+
* --oauth-scope <scope> OAuth scope (implies --oauth)
|
|
22
|
+
*/
|
|
23
|
+
const VALID_SCOPES = ["user", "project", "local"];
|
|
24
|
+
const VALID_TRANSPORTS = ["stdio", "http", "sse"];
|
|
25
|
+
/** Parse a KEY=value pair from a flag argument. */
|
|
26
|
+
function parseKeyValue(arg) {
|
|
27
|
+
const idx = arg.indexOf("=");
|
|
28
|
+
if (idx === -1)
|
|
29
|
+
throw new Error(`Expected KEY=value, got "${arg}"`);
|
|
30
|
+
return [arg.slice(0, idx), arg.slice(idx + 1)];
|
|
31
|
+
}
|
|
32
|
+
/** Print usage and exit. */
|
|
33
|
+
function printMcpHelp() {
|
|
34
|
+
console.log(`orbcode mcp — manage MCP servers
|
|
35
|
+
|
|
36
|
+
Usage:
|
|
37
|
+
orbcode mcp add [options] <name> <command> [args...] add a stdio server
|
|
38
|
+
orbcode mcp add [options] --transport http <name> <url> add an http server
|
|
39
|
+
orbcode mcp add [options] --transport sse <name> <url> add an sse server
|
|
40
|
+
orbcode mcp remove <name> remove a server
|
|
41
|
+
orbcode mcp list list all servers
|
|
42
|
+
|
|
43
|
+
Options:
|
|
44
|
+
-s, --scope <user|project|local> config scope (default: project)
|
|
45
|
+
-t, --transport <stdio|http|sse> transport type (default: stdio)
|
|
46
|
+
-e, --env KEY=value environment variable (repeatable, stdio)
|
|
47
|
+
--header KEY=value HTTP header (repeatable, http/sse)
|
|
48
|
+
--oauth enable OAuth for http/sse
|
|
49
|
+
--oauth-scope <scope> OAuth scope (implies --oauth)
|
|
50
|
+
|
|
51
|
+
Scopes:
|
|
52
|
+
project .mcp.json in the current directory (shared, checked into git)
|
|
53
|
+
user ~/.orbcode/settings.json (applies to all projects on this machine)
|
|
54
|
+
local .orbcode/settings.json (per-project, per-machine, not checked in)`);
|
|
55
|
+
}
|
|
56
|
+
/** Handle `orbcode mcp ...`. Returns the process exit code. */
|
|
57
|
+
export async function runMcpCommand(args) {
|
|
58
|
+
const [subcommand, ...rest] = args;
|
|
59
|
+
switch (subcommand) {
|
|
60
|
+
case "add":
|
|
61
|
+
return runAdd(rest);
|
|
62
|
+
case "remove":
|
|
63
|
+
case "rm":
|
|
64
|
+
return runRemove(rest);
|
|
65
|
+
case "list":
|
|
66
|
+
case "ls":
|
|
67
|
+
return runList(rest);
|
|
68
|
+
case "help":
|
|
69
|
+
case "--help":
|
|
70
|
+
case "-h":
|
|
71
|
+
printMcpHelp();
|
|
72
|
+
return 0;
|
|
73
|
+
default:
|
|
74
|
+
console.error(`Unknown mcp subcommand: "${subcommand ?? ""}". Try: orbcode mcp help`);
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Parse the flags shared by add (scope, transport, env, headers, oauth). */
|
|
79
|
+
function parseAddFlags(args) {
|
|
80
|
+
let scope = "project";
|
|
81
|
+
let transport = "stdio";
|
|
82
|
+
const env = {};
|
|
83
|
+
const headers = {};
|
|
84
|
+
let oauth = false;
|
|
85
|
+
const positional = [];
|
|
86
|
+
// Flags must come before the server name. Once we hit the first positional
|
|
87
|
+
// (the server name), everything after it — including args that look like
|
|
88
|
+
// flags (-y, -f, --foo) — becomes the server's command/args. This matches
|
|
89
|
+
// Claude Code and lets stdio servers take their own flags.
|
|
90
|
+
let sawPositional = false;
|
|
91
|
+
for (let i = 0; i < args.length; i++) {
|
|
92
|
+
const arg = args[i];
|
|
93
|
+
if (sawPositional) {
|
|
94
|
+
// A "--" immediately after the server name is the separator between
|
|
95
|
+
// orbcode's flags and the server's command (e.g. `add name -- npx`),
|
|
96
|
+
// matching Claude Code's `claude mcp add <name> -- <command>`. Consume
|
|
97
|
+
// it so it isn't mistaken for the command. A later "--" is kept as a
|
|
98
|
+
// literal arg for the server command.
|
|
99
|
+
if (arg === "--" && positional.length === 1)
|
|
100
|
+
continue;
|
|
101
|
+
positional.push(arg);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (arg === "-s" || arg === "--scope") {
|
|
105
|
+
const value = args[++i];
|
|
106
|
+
if (!value || !VALID_SCOPES.includes(value)) {
|
|
107
|
+
throw new Error(`Invalid scope "${value ?? ""}". Valid: ${VALID_SCOPES.join(", ")}`);
|
|
108
|
+
}
|
|
109
|
+
scope = value;
|
|
110
|
+
}
|
|
111
|
+
else if (arg === "-t" || arg === "--transport") {
|
|
112
|
+
const value = args[++i];
|
|
113
|
+
if (!value || !VALID_TRANSPORTS.includes(value)) {
|
|
114
|
+
throw new Error(`Invalid transport "${value ?? ""}". Valid: ${VALID_TRANSPORTS.join(", ")}`);
|
|
115
|
+
}
|
|
116
|
+
transport = value;
|
|
117
|
+
}
|
|
118
|
+
else if (arg === "-e" || arg === "--env") {
|
|
119
|
+
const value = args[++i];
|
|
120
|
+
if (!value)
|
|
121
|
+
throw new Error("Missing value for --env");
|
|
122
|
+
const [k, v] = parseKeyValue(value);
|
|
123
|
+
env[k] = v;
|
|
124
|
+
}
|
|
125
|
+
else if (arg === "--header") {
|
|
126
|
+
const value = args[++i];
|
|
127
|
+
if (!value)
|
|
128
|
+
throw new Error("Missing value for --header");
|
|
129
|
+
const [k, v] = parseKeyValue(value);
|
|
130
|
+
headers[k] = v;
|
|
131
|
+
}
|
|
132
|
+
else if (arg === "--oauth") {
|
|
133
|
+
oauth = true;
|
|
134
|
+
}
|
|
135
|
+
else if (arg === "--oauth-scope") {
|
|
136
|
+
const value = args[++i];
|
|
137
|
+
if (!value)
|
|
138
|
+
throw new Error("Missing value for --oauth-scope");
|
|
139
|
+
oauth = { scope: value };
|
|
140
|
+
}
|
|
141
|
+
else if (arg === "--") {
|
|
142
|
+
// Explicit separator: everything after is positional.
|
|
143
|
+
sawPositional = true;
|
|
144
|
+
}
|
|
145
|
+
else if (arg.startsWith("-") && arg.length > 1) {
|
|
146
|
+
throw new Error(`Unknown flag: ${arg}. Put flags before the server name, or use -- to separate.`);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
positional.push(arg);
|
|
150
|
+
sawPositional = true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { scope, transport, env, headers, oauth, positional };
|
|
154
|
+
}
|
|
155
|
+
/** `orbcode mcp add` — build a server config from flags and write it. */
|
|
156
|
+
function runAdd(args) {
|
|
157
|
+
let parsed;
|
|
158
|
+
try {
|
|
159
|
+
parsed = parseAddFlags(args);
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
console.error(error.message);
|
|
163
|
+
return 1;
|
|
164
|
+
}
|
|
165
|
+
const { scope, transport, env, headers, oauth, positional } = parsed;
|
|
166
|
+
if (positional.length === 0) {
|
|
167
|
+
console.error("Missing server name. Usage: orbcode mcp add <name> <command> [args...]");
|
|
168
|
+
return 1;
|
|
169
|
+
}
|
|
170
|
+
const [name, ...rest] = positional;
|
|
171
|
+
let config;
|
|
172
|
+
if (transport === "http" || transport === "sse") {
|
|
173
|
+
if (rest.length === 0) {
|
|
174
|
+
console.error(`Missing URL for ${transport} transport. Usage: orbcode mcp add -t ${transport} <name> <url>`);
|
|
175
|
+
return 1;
|
|
176
|
+
}
|
|
177
|
+
const url = rest[0];
|
|
178
|
+
const cfg = { type: transport, url };
|
|
179
|
+
if (Object.keys(headers).length > 0)
|
|
180
|
+
cfg.headers = headers;
|
|
181
|
+
if (oauth !== false)
|
|
182
|
+
cfg.oauth = oauth;
|
|
183
|
+
config = cfg;
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
// stdio
|
|
187
|
+
if (rest.length === 0) {
|
|
188
|
+
console.error("Missing command for stdio transport. Usage: orbcode mcp add <name> <command> [args...]");
|
|
189
|
+
return 1;
|
|
190
|
+
}
|
|
191
|
+
const [command, ...cmdArgs] = rest;
|
|
192
|
+
const cfg = { type: "stdio", command: command };
|
|
193
|
+
if (cmdArgs.length > 0)
|
|
194
|
+
cfg.args = cmdArgs;
|
|
195
|
+
if (Object.keys(env).length > 0)
|
|
196
|
+
cfg.env = env;
|
|
197
|
+
config = cfg;
|
|
198
|
+
}
|
|
199
|
+
let detail;
|
|
200
|
+
if (transport === "http" || transport === "sse") {
|
|
201
|
+
const cfg = config;
|
|
202
|
+
detail = `${transport} ${cfg.url}`;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const cfg = config;
|
|
206
|
+
detail = `stdio ${cfg.command}${cfg.args?.length ? " " + cfg.args.join(" ") : ""}`;
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
addMcpServer(process.cwd(), name, config, scope);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
console.error(error.message);
|
|
213
|
+
return 1;
|
|
214
|
+
}
|
|
215
|
+
const filePath = configPathForScope(process.cwd(), scope);
|
|
216
|
+
console.log(`Added ${transport === "http" || transport === "sse" ? transport.toUpperCase() : "stdio"} MCP server ${name}`);
|
|
217
|
+
if (transport === "http" || transport === "sse") {
|
|
218
|
+
console.log(` URL: ${config.url}`);
|
|
219
|
+
}
|
|
220
|
+
console.log(` Scope: ${scope}`);
|
|
221
|
+
console.log(` File modified: ${filePath}`);
|
|
222
|
+
return 0;
|
|
223
|
+
}
|
|
224
|
+
/** `orbcode mcp remove <name>` — remove from whichever scope it's in. */
|
|
225
|
+
function runRemove(args) {
|
|
226
|
+
if (args.length === 0) {
|
|
227
|
+
console.error("Missing server name. Usage: orbcode mcp remove <name>");
|
|
228
|
+
return 1;
|
|
229
|
+
}
|
|
230
|
+
const name = args[0];
|
|
231
|
+
const scope = removeMcpServerAnyScope(process.cwd(), name);
|
|
232
|
+
if (!scope) {
|
|
233
|
+
console.error(`MCP server "${name}" not found in any scope.`);
|
|
234
|
+
return 1;
|
|
235
|
+
}
|
|
236
|
+
const filePath = configPathForScope(process.cwd(), scope);
|
|
237
|
+
console.log(`Removed MCP server "${name}" from ${scope} scope.`);
|
|
238
|
+
console.log(` File modified: ${filePath}`);
|
|
239
|
+
return 0;
|
|
240
|
+
}
|
|
241
|
+
/** `orbcode mcp list` — print all configured servers with their scope + status. */
|
|
242
|
+
function runList(_args) {
|
|
243
|
+
const { servers } = loadMcpConfig(process.cwd());
|
|
244
|
+
const names = Object.keys(servers).sort();
|
|
245
|
+
if (names.length === 0) {
|
|
246
|
+
console.log("No MCP servers configured.");
|
|
247
|
+
console.log("Add one with: orbcode mcp add <name> <command> [args...]");
|
|
248
|
+
return 0;
|
|
249
|
+
}
|
|
250
|
+
for (const name of names) {
|
|
251
|
+
const cfg = servers[name];
|
|
252
|
+
const scope = cfg.scope;
|
|
253
|
+
let detail;
|
|
254
|
+
if (cfg.type === "http" || cfg.type === "sse") {
|
|
255
|
+
detail = `${cfg.type} ${cfg.url}`;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// stdio (type undefined or "stdio")
|
|
259
|
+
const cmd = cfg.command;
|
|
260
|
+
const args = cfg.args ?? [];
|
|
261
|
+
detail = `stdio ${cmd}${args.length ? " " + args.join(" ") : ""}`;
|
|
262
|
+
}
|
|
263
|
+
console.log(` ${name.padEnd(20)} ${scope.padEnd(8)} ${detail}`);
|
|
264
|
+
}
|
|
265
|
+
return 0;
|
|
266
|
+
}
|