Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 44 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ repository = "https://github.com/aroff/fastskill"

[workspace.dependencies]
# aikit-sdk for agent metadata file resolution
aikit-sdk = { git = "https://github.com/goaikit/aikit", rev = "ec112be4aaedaa0d08a11b4824d69bf082e2f5a5" }
aikit-sdk = { git = "https://github.com/goaikit/aikit", rev = "435a1132" }
# aikit-evals provides the generic eval runner infrastructure
aikit-evals = { git = "https://github.com/goaikit/aikit", rev = "ec112be4aaedaa0d08a11b4824d69bf082e2f5a5" }
aikit-evals = { git = "https://github.com/goaikit/aikit", rev = "435a1132" }
# aikit-skillopt provides the text-gradient skill optimization loop
aikit-skillopt = { git = "https://github.com/goaikit/aikit", rev = "ec112be4aaedaa0d08a11b4824d69bf082e2f5a5" }
aikit-skillopt = { git = "https://github.com/goaikit/aikit", rev = "435a1132" }

# Async runtime
tokio = { version = "1.52", features = ["rt-multi-thread", "net", "fs", "io-util", "macros", "process"] }
Expand Down
2 changes: 1 addition & 1 deletion crates/fastskill-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ path = "src/main.rs"
fastskill-core = { path = "../fastskill-core", features = ["filesystem-storage", "registry-publish", "hot-reload"] }

# CLI framework for AppBuilder, command registry, MCP server, and doctor
cli-framework = { workspace = true, default-features = false, features = ["mcp-server", "doctor", "testkit"] }
cli-framework = { workspace = true, default-features = false, features = ["mcp-server", "mcp-install", "doctor", "testkit"] }
fastskill-evals = { path = "../fastskill-evals" }

# aikit-sdk for agent integration
Expand Down
94 changes: 94 additions & 0 deletions tests/cli/mcp_install_e2e_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//! E2E tests for mcp install, mcp register, and mcp list subcommands.
//!
//! These tests execute the CLI binary and verify actual behavior.

#![allow(clippy::all, clippy::unwrap_used, clippy::expect_used, clippy::panic)]

use super::snapshot_helpers::run_fastskill_command;
use std::fs;
use tempfile::TempDir;

#[test]
fn test_mcp_help_lists_install_register_list_serve() {
let result = run_fastskill_command(&["mcp", "--help"], None);
let combined = format!("{}{}", result.stdout, result.stderr);
assert!(combined.contains("install"), "mcp --help should list 'install'");
assert!(combined.contains("register"), "mcp --help should list 'register'");
assert!(combined.contains("list"), "mcp --help should list 'list'");
assert!(combined.contains("serve"), "mcp --help should list 'serve'");
}

#[test]
fn test_mcp_install_dry_run_exits_zero_and_prints_output() {
let result = run_fastskill_command(
&["mcp", "install", "--agent", "cursor", "--stdio", "--dry-run"],
None,
);
assert!(result.success, "dry-run should exit 0; stderr: {}", result.stderr);
let combined = format!("{}{}", result.stdout, result.stderr);
assert!(!combined.is_empty(), "dry-run should produce non-empty output");
}

#[test]
fn test_mcp_install_cursor_writes_config_file() {
let temp_dir = TempDir::new().unwrap();
let result = run_fastskill_command(
&[
"mcp", "install",
"--agent", "cursor",
"--stdio",
"--scope", "project",
"--overwrite",
],
Some(temp_dir.path()),
);
assert!(result.success, "mcp install should exit 0; stderr: {}", result.stderr);
let config_path = temp_dir.path().join(".cursor").join("mcp.json");
assert!(config_path.exists(), ".cursor/mcp.json should be created");
let contents = fs::read_to_string(&config_path).unwrap();
let json: serde_json::Value = serde_json::from_str(&contents).unwrap();
assert!(json.get("fastskill").is_some(), "mcp.json should contain 'fastskill' key");
let args = &json["fastskill"]["args"];
let args_str = args.to_string();
assert!(
args_str.contains("mcp") && args_str.contains("serve") && args_str.contains("stdio"),
"args should include mcp serve --transport stdio; got: {args_str}"
);
}

#[test]
fn test_mcp_install_duplicate_without_overwrite_exits_nonzero() {
let temp_dir = TempDir::new().unwrap();
// First install
let first = run_fastskill_command(
&[
"mcp", "install",
"--agent", "cursor",
"--stdio",
"--scope", "project",
"--overwrite",
],
Some(temp_dir.path()),
);
assert!(first.success, "first install should succeed; stderr: {}", first.stderr);

// Second install without --overwrite should fail
let second = run_fastskill_command(
&["mcp", "install", "--agent", "cursor", "--stdio", "--scope", "project"],
Some(temp_dir.path()),
);
assert!(!second.success, "second install without --overwrite should exit non-zero");
let combined = format!("{}{}", second.stdout, second.stderr);
assert!(
combined.to_lowercase().contains("already") || combined.to_lowercase().contains("exist"),
"error should mention entry already exists; got: {combined}"
);
}

#[test]
fn test_mcp_list_exits_zero() {
let result = run_fastskill_command(&["mcp", "list"], None);
assert!(result.success, "mcp list should exit 0; stderr: {}", result.stderr);
let combined = format!("{}{}", result.stdout, result.stderr);
assert!(!combined.is_empty(), "mcp list should produce output");
}
1 change: 1 addition & 0 deletions tests/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod example_tests;
pub mod help_tests;
pub mod init_tests;
pub mod install_e2e_tests;
pub mod mcp_install_e2e_tests;
pub mod install_recursive;
pub mod install_tests;
pub mod integration_tests;
Expand Down
19 changes: 19 additions & 0 deletions webdocs/integration/claude-code-integration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,25 @@ directory contains at least one valid `SKILL.md`. Run `fastskill sync --agent cl
**Wrong file detected:** If AGENTS.md exists alongside CLAUDE.md, fastskill picks
AGENTS.md (it is checked first). Use `--agent claude` to force CLAUDE.md.

## MCP Registration

Register `fastskill` as an MCP server inside Claude Code with a single command:

```bash
# Register for the current project (stdio transport, recommended)
fastskill mcp install --agent claude --stdio --scope project --overwrite

# Register globally (writes to ~/.claude.json)
fastskill mcp install --agent claude --stdio --scope global --overwrite

# Preview the config change without writing any files
fastskill mcp install --agent claude --stdio --dry-run
```

After running the command, reload Claude Code. The tools exposed by `fastskill mcp serve --transport stdio` will be callable from the agent.

> **Deprecated:** Manually editing `~/.claude.json` or `.mcp.json` to add a `fastskill` MCP entry is deprecated. Use `fastskill mcp install` instead.

## HTTP API

When using `fastskill serve`, the Claude-compatible skills API surface is available at:
Expand Down
18 changes: 17 additions & 1 deletion webdocs/integration/cursor-integration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,23 @@ For semantic search over skills, index the skills directory:
fastskill reindex --skills-dir .claude/skills/
```

### 3. Cursor integration
### 3. MCP Registration

Register `fastskill` as an MCP server inside Cursor with a single command:

```bash
# Register for the current project (stdio transport, recommended)
fastskill mcp install --agent cursor --stdio --scope project --overwrite

# Preview the config change without writing any files
fastskill mcp install --agent cursor --stdio --dry-run
```

After running the command, reload Cursor. The tools exposed by `fastskill mcp serve --transport stdio` will be callable from the agent.

> **Deprecated:** Manually editing `.cursor/mcp.json` to add a `fastskill` MCP entry is deprecated. Use `fastskill mcp install` instead.

### 4. Cursor integration

Cursor reads AGENTS.md and native skills/agents configuration to discover available skills.

Expand Down
Loading