Skip to main content

CodingMcp

Struct CodingMcp 

Source
pub struct CodingMcp { /* private fields */ }
Expand description

The primary MCP tool server for coding workflows. Provides file I/O, shell execution, LSP code intelligence, and web fetch/search – all exposed as MCP tools that an agent can invoke.

§Construction

Use the builder-style methods to configure the server:

ⓘ
use mcp_servers::CodingMcp;

// Default: local filesystem tools rooted at the project, no LSP
let server = CodingMcp::new("/my/project");

// With LSP code intelligence for that root and explicit read-rule directories
let server = CodingMcp::new("/my/project")
    .with_rules_dirs(&["/my/project/.aether/skills".into(), "/my/project/.claude/rules".into()])
    .with_lsp()
    .with_permission_mode(mcp_servers::PermissionMode::Auto);

§Tools provided

File operations:

  • read_file – Read file contents with optional offset/limit
  • write_file – Write content to a file (creates parent dirs)
  • edit_file – String replacement in an existing file

Shell:

  • bash – Execute shell commands (with background process support)

Web:

  • web_fetch – Fetch a URL and extract text content
  • web_search – Web search via Brave Search API

LSP (requires with_lsp):

  • lsp_symbol – Go to definition, find references, hover for types and docs
  • lsp_check_errors – Get diagnostics for a file or workspace
  • lsp_rename – Rename a symbol across the project

§Safety

The server enforces several safety checks:

  • Read-before-edit – write_file and edit_file require the file to have been read first in the same session, preventing blind overwrites.
  • Dangerous command detection – Bash commands containing destructive patterns (rm, git push --force, redirect operators, etc.) are flagged.
  • Permission modes – PermissionMode controls whether flagged operations require user approval via MCP elicitation.

§See also

Implementations§

Source§

impl CodingMcp

Source

pub fn new(root_dir: impl Into<PathBuf>) -> Self

Source

pub fn from_args(args: CodingMcpArgs, base_dir: &Path) -> Self

Source

pub fn with_bash_environment(self, environment: BashEnvironment) -> Self

Source

pub fn with_lsp(self) -> Self

Enable LSP code intelligence for the server’s root directory.

LSP servers for detected project languages are spawned immediately in the background, allowing indexing to start right away.

Source

pub fn with_web_searcher(self, searcher: WebSearcher) -> Self

Enable the web_search tool, backed by searcher.

Source

pub fn with_rules_dirs(self, rules_dirs: &[PathBuf]) -> Self

Set prompt directories used for read-triggered rule activation.

Source

pub fn with_permission_mode(self, mode: PermissionMode) -> Self

Set the permission mode controlling user approval for tool calls.

Source

pub fn read_file_tool_attr() -> Tool

Generated tool metadata function for read_file

Source

pub fn read_file( &self, Parameters: Parameters<ReadFileArgs>, context: RequestContext<RoleServer>, ) -> Pin<Box<dyn Future<Output = Result<Displayed<ReadFileResult>, CodingError>> + Send + '_>>

Reads a file from the local filesystem with line numbers.

§Usage
{"file_path": "/absolute/path/to/file.rs"}
{"file_path": "/path/to/file.rs", "offset": 150, "limit": 50}
  • file_path — required, must be absolute path
  • offset — 1-indexed starting line (default: 1)
  • limit — max lines to read

Output format: 1\tline content (line number, tab, content)

§Tips
  • Omit offset/limit to read from the top, then follow nextOffset to keep reading a long file
  • Read multiple files in parallel when you need several
  • Use shell commands through bash for directory listings, not this tool
§Safety

You MUST read a file before editing (edit_file) or overwriting (write_file). This prevents accidental data loss.

Source

pub fn write_file_tool_attr() -> Tool

Generated tool metadata function for write_file

Source

pub fn write_file( &self, Parameters: Parameters<WriteFileArgs>, context: RequestContext<RoleServer>, ) -> Pin<Box<dyn Future<Output = Result<Displayed<WriteFileResponse>, CodingError>> + Send + '_>>

Writes a file to the filesystem, replacing entire contents. Creates parent directories if needed.

§Usage
{"filePath": "/path/to/new_file.rs", "content": "fn main() {}"}
  • filePath — required, absolute path
  • content — required, full file contents
§Tips
  • Prefer edit_file for existing files — only use write_file for new files or complete rewrites
  • Don’t proactively create documentation (*.md, README) unless explicitly requested
§Safety

If the file exists, you MUST read it with read_file first. This prevents accidental data loss. New files don’t require reading.

Source

pub fn edit_file_tool_attr() -> Tool

Generated tool metadata function for edit_file

Source

pub fn edit_file( &self, Parameters: Parameters<EditFileArgs>, context: RequestContext<RoleServer>, ) -> Pin<Box<dyn Future<Output = Result<Displayed<EditFileResponse>, CodingError>> + Send + '_>>

Use this tool to edit a file. This tool accepts an array of batched exact-string replacements. If 1 edit fails, no edits in the batch are applied.

§Usage
{"filePath": "/path/to/file.rs", "edits": [
  {"oldString": "foo", "newString": "bar"},
  {"oldString": "old_name", "newString": "new_name", "replaceAll": true}
]}
  • filePath — required, absolute path
  • edits — required, a non-empty array. Each edit replaces oldString with newString.
    • oldString — exact string to find (must be unique unless replaceAll).
    • newString — replacement text.
    • replaceAll — replace all occurrences (default: false).
§Tips
  • Batch every edit to a file into one call; edits must target non-overlapping regions.
  • Preserve exact indentation from read_file output — match text AFTER the tab character, not the line-number prefix.
  • To rename symbols across the codebase, prefer lsp_rename instead (if available).
  • Unless your edits require overwriting an entire file, prefer this tool over create_file.
§Safety

You MUST read a file with read_file before editing it. This prevents accidental data loss.

Source

pub fn bash_tool_attr() -> Tool

Generated tool metadata function for bash

Source

pub fn bash( &self, Parameters: Parameters<BashInput>, context: RequestContext<RoleServer>, ) -> Pin<Box<dyn Future<Output = Result<Displayed<BashOutput>, CodingError>> + Send + '_>>

Executes a bash command. Each call runs in a fresh shell — no state persists between calls.

§Usage
{"command": "cargo test", "description": "Run tests"}
{"command": "git status && git diff", "description": "Check git status and diff"}
{"command": "npm run build", "timeout": 300000, "description": "Build with 5min timeout"}
  • command — required, the bash command
  • description — concise description (5-10 words)
  • timeout — max runtime in ms (max: 600000)
  • run_in_background — Run a task in the background. You don’t need to poll, results are delivered automatically into context when it completes.
§Tips
  • Run independent commands in parallel with multiple bash calls
  • Chain dependent commands with && in a single call
  • Use ; only if you don’t care if earlier commands fail
Source

pub fn web_fetch_tool_attr() -> Tool

Generated tool metadata function for web_fetch

Source

pub fn web_fetch( &self, Parameters: Parameters<WebFetchInput>, context: RequestContext<RoleServer>, ) -> Pin<Box<dyn Future<Output = Result<Displayed<WebFetchOutput>, CodingError>> + Send + '_>>

Fetches content from a URL, extracts the main article content, and converts it to Markdown.

§Usage
{"url": "https://docs.rs/serde"}
{"url": "https://example.com", "prompt": "Find API documentation", "timeout": 60000}
  • url — required, HTTP/HTTPS URL (HTTP auto-upgraded)
  • prompt — describe what you’re looking for (optional)
  • timeout — max wait in ms (default: 30000, max: 60000)
§Tips
  • Use specific URLs rather than relying on search
  • Handle 4xx/5xx status codes gracefully
  • Falls back to full-page conversion when readability extraction fails
Source

pub fn web_search_tool_attr() -> Tool

Generated tool metadata function for web_search

Searches the web via Brave Search API for up-to-date information.

§Usage
{"query": "rust async best practices 2024"}
{"query": "tokio documentation", "count": 5, "allowed_domains": ["docs.rs"]}
  • query — required, search query (be specific)
  • count — max results (default: 10, max: 20)
  • allowed_domains — only include these domains
  • blocked_domains — exclude these domains
§Tips
  • Use snippets to assess relevance before fetching full pages
  • Always cite sources using the returned URLs
  • Use web_fetch to get full content from promising results
Source

pub fn lsp_symbol_tool_attr() -> Tool

Generated tool metadata function for lsp_symbol

Source

pub fn lsp_symbol( &self, Parameters: Parameters<LspSymbolInput>, context: RequestContext<RoleServer>, ) -> Pin<Box<dyn Future<Output = Result<Displayed<LspSymbolOutput>, CodingError>> + Send + '_>>

Tools powered by LSP a server.

§Operations
QueryOperation
“Where is X defined?” (including in dependencies)definition
“Where is X used?”references
“What type is X?”hover
§Usage

Required: file_path (absolute or relative to the workspace root), symbol (exact name as it appears) Optional: line (1-indexed optimization hint; stale hints fall back to automatic resolution)

{"operation": "definition", "file_path": "src/main.rs", "symbol": "HashMap"}
{"operation": "references", "file_path": "src/main.rs", "symbol": "process_request"}
§Output Control
  • limit — cap locations (default: 50). totalCount always reports the full count.
  • context_lines — include N lines around definition and reference locations. Eliminates the need for a separate read_file call.
  • include_declaration — for references only (default: true)
§Tips
  • Cross-crate navigation: Use definition on an import to jump directly into dependency source — no need to manually navigate ~/.cargo/registry/....
Source

pub fn lsp_check_errors_tool_attr() -> Tool

Generated tool metadata function for lsp_check_errors

Source

pub fn lsp_check_errors( &self, Parameters: Parameters<LspDiagnosticsRequest>, context: RequestContext<RoleServer>, ) -> Pin<Box<dyn Future<Output = Result<Displayed<LspDiagnosticsOutput>, CodingError>> + Send + '_>>

Gets instant compiler errors and warnings without running a build.

Prefer this over cargo check, npm run build, tsc, go build.

§Usage

The tool infers scope from filePath:

Workspace-wide diagnostics:

{}

Single-file diagnostics:

{"filePath":"src/main.rs"}
§Parameters
  • filePath — optional path to an existing file, absolute or relative to the workspace root. When omitted, checks the workspace.
§Output

diagnostics lists errors and warnings keyed by file path (relative to the workspace root), errors first:

diagnostics:
  src/main.rs:
  - '2:18 error[E0308]: mismatched types'

If the required language server cannot start, the tool returns the startup error. For a missing TypeScript server, the error includes local and global npm installation commands.

Source

pub fn lsp_rename_tool_attr() -> Tool

Generated tool metadata function for lsp_rename

Source

pub fn lsp_rename( &self, Parameters: Parameters<LspRenameInput>, context: RequestContext<RoleServer>, ) -> Pin<Box<dyn Future<Output = Result<Displayed<LspRenameOutput>, CodingError>> + Send + '_>>

Renames a symbol across the entire workspace using LSP-powered refactoring.

A single rename updates all references — no manual file-by-file editing needed.

§Usage
{"file_path": "/project/src/lib.rs", "symbol": "old_name", "new_name": "better_name"}
  • file_path — required, file containing the symbol
  • symbol — required, current symbol name
  • new_name — required, new symbol name
  • line — optional 1-indexed optimization hint; stale hints fall back to automatic resolution

Returns: files affected, total edit count, and the edited lines grouped by file (relative to the workspace root): src/lib.rs: 1, 14.

Source§

impl CodingMcp

Source

pub fn with_web_fetcher(self, web_fetcher: WebFetcher) -> Self

Trait Implementations§

Source§

impl Debug for CodingMcp

Source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl ServerHandler for CodingMcp

Source§

fn get_info(&self) -> ServerConfig

Source§

async fn call_tool( &self, request: CallToolRequestParams, context: RequestContext<RoleServer>, ) -> Result<CallToolResponse, ErrorData>

Handle a tools/call request from a client. Read more
Source§

fn get_task( &self, request: GetTaskParams, _context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<GetTaskResult, ErrorData>> + Send + '_

SEP-2663 tasks/get: return the current DetailedTask state.
Source§

fn update_task( &self, request: UpdateTaskParams, _context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<(), ErrorData>> + Send + '_

SEP-2663 tasks/update: accept responses to outstanding in-task input requests. Returns an empty acknowledgement on success.
Source§

fn cancel_task( &self, request: CancelTaskParams, _context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<(), ErrorData>> + Send + '_

SEP-2663 tasks/cancel: cooperative cancellation. Returns an empty acknowledgement; the task’s observable status may lag.
Source§

async fn list_tools( &self, _request: Option<PaginatedRequestParams>, context: RequestContext<RoleServer>, ) -> Result<ListToolsResult, ErrorData>

Source§

fn get_tool(&self, name: &str) -> Option<Tool>

Get a tool definition by name. Read more
Source§

fn ping( &self, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<(), ErrorData>> + MaybeSendFuture

Source§

fn initialize( &self, request: InitializeRequestParams, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<InitializeResult, ErrorData>> + MaybeSendFuture

Source§

fn negotiate_initialize( &self, request: &InitializeRequestParams, ) -> Result<InitializeResult, ErrorData>

Build the initialize response for request, negotiating the protocol version against Self::supported_protocol_versions. Read more
Source§

fn supported_protocol_versions(&self) -> Cow<'static, [ProtocolVersion]>

Return the protocol versions supported by this server. Read more
Source§

fn discover( &self, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<DiscoverResult, ErrorData>> + MaybeSendFuture

Return this server’s discovery information.
Source§

fn complete( &self, request: CompleteRequestParams, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<CompleteResult, ErrorData>> + MaybeSendFuture

Source§

fn set_level( &self, request: SetLevelRequestParams, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<(), ErrorData>> + MaybeSendFuture

Source§

fn get_prompt( &self, request: GetPromptRequestParams, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<GetPromptResponse, ErrorData>> + MaybeSendFuture

Source§

fn list_prompts( &self, request: Option<PaginatedRequestParams>, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<ListPromptsResult, ErrorData>> + MaybeSendFuture

Source§

fn list_resources( &self, request: Option<PaginatedRequestParams>, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<ListResourcesResult, ErrorData>> + MaybeSendFuture

Source§

fn list_resource_templates( &self, request: Option<PaginatedRequestParams>, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<ListResourceTemplatesResult, ErrorData>> + MaybeSendFuture

Source§

fn read_resource( &self, request: ReadResourceRequestParams, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<ReadResourceResponse, ErrorData>> + MaybeSendFuture

Source§

fn accepted_subscription_filter( &self, requested: &SubscriptionFilter, ) -> Option<SubscriptionFilter>

Return the subset of a requested notification filter this server accepts. Read more
Source§

fn listen( &self, context: SubscriptionContext, ) -> impl Future<Output = Result<(), ErrorData>> + MaybeSendFuture

Run one established subscription until it is cancelled or closed gracefully. Read more
Source§

fn subscribe( &self, request: SubscribeRequestParams, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<(), ErrorData>> + MaybeSendFuture

👎Deprecated:

resources/subscribe is legacy-only; implement accepted_subscription_filter and listen for protocol version 2026-07-28

Source§

fn unsubscribe( &self, request: UnsubscribeRequestParams, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<(), ErrorData>> + MaybeSendFuture

👎Deprecated:

resources/unsubscribe is legacy-only; subscriptions/listen is cancelled through its request lifecycle

Source§

fn on_custom_request( &self, request: CustomRequest, context: RequestContext<RoleServer>, ) -> impl Future<Output = Result<CustomResult, ErrorData>> + MaybeSendFuture

Source§

fn on_cancelled( &self, notification: CancelledNotificationParam, context: NotificationContext<RoleServer>, ) -> impl Future<Output = ()> + MaybeSendFuture

Source§

fn on_progress( &self, notification: ProgressNotificationParam, context: NotificationContext<RoleServer>, ) -> impl Future<Output = ()> + MaybeSendFuture

Source§

fn on_initialized( &self, context: NotificationContext<RoleServer>, ) -> impl Future<Output = ()> + MaybeSendFuture

Source§

fn on_roots_list_changed( &self, context: NotificationContext<RoleServer>, ) -> impl Future<Output = ()> + MaybeSendFuture

Source§

fn on_custom_notification( &self, notification: CustomNotification, context: NotificationContext<RoleServer>, ) -> impl Future<Output = ()> + MaybeSendFuture

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<R, S> DynService<R> for S
where R: ServiceRole, S: Service<R>,

Source§

fn handle_request( &self, request: <R as ServiceRole>::PeerReq, context: RequestContext<R>, ) -> Pin<Box<dyn Future<Output = Result<<R as ServiceRole>::Resp, ErrorData>> + Send + '_>>

Source§

fn handle_notification( &self, notification: <R as ServiceRole>::PeerNot, context: NotificationContext<R>, ) -> Pin<Box<dyn Future<Output = Result<(), ErrorData>> + Send + '_>>

Source§

fn get_info(&self) -> <R as ServiceRole>::Info

Source§

fn supported_protocol_versions(&self) -> Cow<'static, [ProtocolVersion]>

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<Unshared, Shared> IntoShared<Shared> for Unshared
where Shared: FromUnshared<Unshared>,

Source§

fn into_shared(self) -> Shared

Creates a shared type from an unshared type.
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<H> Service<RoleServer> for H
where H: ServerHandler,

Source§

async fn handle_request( &self, request: <RoleServer as ServiceRole>::PeerReq, context: RequestContext<RoleServer>, ) -> Result<<RoleServer as ServiceRole>::Resp, ErrorData>

Source§

async fn handle_notification( &self, notification: <RoleServer as ServiceRole>::PeerNot, context: NotificationContext<RoleServer>, ) -> Result<(), ErrorData>

Source§

fn get_info(&self) -> <RoleServer as ServiceRole>::Info

Source§

fn supported_protocol_versions(&self) -> Cow<'static, [ProtocolVersion]>

The protocol versions this service can speak, bounding what initialize negotiation may agree to. Read more
Source§

impl<S> ServiceExt<RoleServer> for S
where S: Service<RoleServer>,

Source§

fn serve_with_ct<T, E, A>( self, transport: T, ct: CancellationToken, ) -> impl Future<Output = Result<RunningService<RoleServer, S>, ServerInitializeError>> + MaybeSendFuture
where T: IntoTransport<RoleServer, E, A>, E: Error + Send + Sync + 'static, S: Sized,

Source§

fn into_dyn(self) -> Box<dyn DynService<R>>

Convert this service to a dynamic boxed service Read more
Source§

fn serve<T, E, A>( self, transport: T, ) -> impl Future<Output = Result<RunningService<R, Self>, <R as ServiceRole>::InitializeError>> + MaybeSendFuture
where T: IntoTransport<R, E, A>, E: Error + Send + Sync + 'static, Self: Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more