Skip to content

feat: AAIS/Jarvis governed workspace tools MCP - #17

Open
warheart1984-ctrl wants to merge 3 commits into
mainfrom
feat/aais-tools-mcp
Open

feat: AAIS/Jarvis governed workspace tools MCP#17
warheart1984-ctrl wants to merge 3 commits into
mainfrom
feat/aais-tools-mcp

Conversation

@warheart1984-ctrl

@warheart1984-ctrl warheart1984-ctrl commented Aug 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add governed AAIS/Jarvis workspace tools MCP (services/aais-tools-mcp/) with sandboxed read/write/search, allowlisted tests, and git status/diff.
  • Wire Jarvis to prefer stdio MCP when AAIS_JARVIS_TOOLS_MCP=1, via src/aais_tools_mcp_client.py, with fail-open to the in-process adapter (src/aais_tools_mcp_adapter.py) so chat never dies on spawn/protocol failure.
  • Include the workspace-root pin from PR fix: stop Jarvis reply failures on /proc map_files EPERM #16 (src/workspace_root.py) so MCP children and Jarvis walks never treat / as the workspace (avoids /proc/*/map_files EPERM).

Env flags

Flag Role
AAIS_JARVIS_TOOLS_MCP=1 Prefer stdio MCP for operator tools
AAIS_TOOLS_MCP_CMD Optional spawn override (default python3 -m aais_tools_mcp)
AAIS_TOOLS_MCP_TIMEOUT_SEC Stdio timeout (default 30)
AAIS_WORKSPACE_ROOT Sandbox root (passed to MCP child)
AAIS_TOOLS_MCP_ALLOW_WRITES Write gate (still needs allow_write=true)

Selection

JarvisOperator.handle_tool_request routes read_file / write_file / apply_patch / list_dir / search_code / run_tests / git_status / git_diffinvoke_aais_operator_tool (MCP then adapter). Natural-language workspace browse still uses WorkspaceTools.

Test plan

  • python3 -m pytest tests/test_aais_tools_mcp_client.py tests/test_aais_tools_mcp_adapter.py tests/test_workspace_root.py -q
  • cd services/aais-tools-mcp && PYTHONPATH=. python3 -m pytest tests -q
  • Live smoke: AAIS_JARVIS_TOOLS_MCP=1transport=mcp_stdio for list_dir
  • Cursor MCP config with writes off by default

Related

Greptile Summary

The PR adds a sandboxed AAIS/Jarvis workspace-tools MCP service and routes structured Jarvis tool requests through stdio MCP with fallback to the in-process adapter.

  • Adds governed file, search, test, and Git tools with mutation evidence logging.
  • Adds stdio client/server transport and Jarvis operator routing.
  • Pins workspace roots in application and deployment configuration.

Confidence Score: 0/5

The PR is not safe to merge while test selectors can escape the workspace, hostile regex searches can block the MCP service, and malformed arguments can break the structured tool contract.

Test selector arguments still reach pytest without sandbox resolution, regex evaluation remains unbounded in the serial request loop, and wrong-typed numeric fields still raise outside the catalog's documented structured-error handling.

Files Needing Attention: services/aais-tools-mcp/aais_tools_mcp/tools.py, services/aais-tools-mcp/aais_tools_mcp/server.py, services/aais-tools-mcp/aais_tools_mcp/capability_adapter.py

Important Files Changed

Filename Overview
services/aais-tools-mcp/aais_tools_mcp/tools.py Implements the workspace tools, but test selectors remain outside path-sandbox enforcement, regex evaluation can block the service, and malformed numeric arguments violate the structured error contract.
services/aais-tools-mcp/aais_tools_mcp/server.py Implements the serial stdio MCP protocol and exposes the tool catalog; its synchronous dispatch amplifies blocking regex evaluation.
src/aais_tools_mcp_client.py Adds stdio process management and protocol invocation with fallback support.
src/aais_tools_mcp_adapter.py Selects MCP or local transport, but local catalog exceptions from malformed arguments can still propagate into Jarvis.
src/jarvis_operator.py Routes structured operator tool requests through the new unified invocation path.
src/workspace_root.py Centralizes workspace-root resolution and rejects filesystem-root selection for current callers and deployment configurations.

Sequence Diagram

sequenceDiagram
    participant J as JarvisOperator
    participant I as invoke_aais_operator_tool
    participant C as Stdio MCP Client
    participant S as AAIS Tools MCP
    participant A as Local Adapter
    participant W as Workspace
    J->>I: structured tool request
    alt MCP enabled
        I->>C: invoke tool
        C->>S: tools/call over stdio
        S->>W: sandboxed operation
        S-->>C: structured result
        C-->>I: result
    else MCP unavailable or disabled
        I->>A: fail-open invocation
        A->>W: sandboxed operation
        A-->>I: structured result
    end
    I-->>J: tool response
Loading

Reviews (2): Last reviewed commit: "docs: note local adapter is stdio fallba..." | Re-trigger Greptile

Expose sandboxed read/write/search/git and allowlisted tests over stdio
MCP for Cursor, plus a local capability adapter so Jarvis can call the
same tools before an MCP client exists.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment on lines +386 to +392
elif not re.fullmatch(r"[A-Za-z0-9_./\\-]+", text):
return {
"ok": False,
"reason_code": "COMMAND_ARG_DENIED",
"error": f"Unsafe extra arg refused: {text}",
}
argv.append(text)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 security Pytest selectors bypass sandbox

When extra_args contains an absolute or traversal pytest selector, the character check accepts it and appends it without calling WorkspacePathSandbox.resolve_path, allowing pytest to execute Python outside AAIS_WORKSPACE_ROOT with the server's privileges.

How this was verified: The request-controlled selector was traced through the permissive argument check directly into the pytest subprocess without the sandbox's absolute-path or traversal guards.

Prompt To Fix With AI
This is a comment left during a code review.
Path: services/aais-tools-mcp/aais_tools_mcp/tools.py
Line: 386-392

Comment:
**Pytest selectors bypass sandbox**

When `extra_args` contains an absolute or traversal pytest selector, the character check accepts it and appends it without calling `WorkspacePathSandbox.resolve_path`, allowing pytest to execute Python outside `AAIS_WORKSPACE_ROOT` with the server's privileges.

**How this was verified:** The request-controlled selector was traced through the permissive argument check directly into the pytest subprocess without the sandbox's absolute-path or traversal guards.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Cursor

Comment on lines +283 to +304
regex = re.compile(cleaned, flags)
except re.error as exc:
return {"ok": False, "reason_code": "PATTERN_INVALID", "error": str(exc)}

start = self.sandbox.resolve_path(path or ".")
root = self.sandbox.resolve_root()
if start.is_file():
files = [start]
elif start.is_dir():
files = list(self._iter_text_files(start))
else:
return {"ok": False, "reason_code": "PATH_NOT_FOUND", "error": f"Not found: {path}"}

matches: list[dict[str, Any]] = []
limit = max(1, min(int(max_matches or MAX_SEARCH_MATCHES), MAX_SEARCH_MATCHES))
for file_path in files:
try:
text = file_path.read_text(encoding="utf-8", errors="replace")
except OSError:
continue
for line_no, line in enumerate(text.splitlines(), start=1):
if regex.search(line):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 security Regex search blocks server

When a caller applies a catastrophic-backtracking expression to a long source line, regex.search runs without a timeout, blocking the synchronous stdio loop and making every subsequent MCP request unresponsive.

How this was verified: Caller-controlled patterns reach unbounded regex evaluation on lines up to the file-size limit inside the server's serial request path.

Prompt To Fix With AI
This is a comment left during a code review.
Path: services/aais-tools-mcp/aais_tools_mcp/tools.py
Line: 283-304

Comment:
**Regex search blocks server**

When a caller applies a catastrophic-backtracking expression to a long source line, `regex.search` runs without a timeout, blocking the synchronous stdio loop and making every subsequent MCP request unresponsive.

**How this was verified:** Caller-controlled patterns reach unbounded regex evaluation on lines up to the file-size limit inside the server's serial request path.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Cursor

Comment on lines +1 to +9
"""Governed workspace tool implementations for AAIS / Jarvis.

Mythic: Operator Workshop Toolkit
Engineering: AaisOperatorToolCatalog (+ per-tool classes)

Inputs: tool name + JSON-compatible args
Outputs: result dict with ok/error and reason_code on failure
Constraints: sandboxed paths; writes gated; commands allowlisted only
Failure modes: sandbox deny, policy deny, OS errors → structured error dict

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Subsystem boundaries remain undocumented

This new module lacks the required Mythic/Engineering/Responsibilities/Non-responsibilities/Invariants header and combines seven tool implementations with the catalog dispatcher. Split the independent subsystems and add the mandatory header blocks so ownership and boundaries remain explicit; the same missing-header pattern occurs across the other new Python subsystem files.

Context Used: One subsystem per file; mandatory file header bloc... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: services/aais-tools-mcp/aais_tools_mcp/tools.py
Line: 1-9

Comment:
**Subsystem boundaries remain undocumented**

This new module lacks the required Mythic/Engineering/Responsibilities/Non-responsibilities/Invariants header and combines seven tool implementations with the catalog dispatcher. Split the independent subsystems and add the mandatory header blocks so ownership and boundaries remain explicit; the same missing-header pattern occurs across the other new Python subsystem files.

**Context Used:** One subsystem per file; mandatory file header bloc... ([source](https://github.com/warheart1984-ctrl/project-infinity/blob/main/.cursor/rules/jon-file-structure.mdc))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Cursor

Comment on lines +47 to +49
"sys",
"dev",
"map_files",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Dual ontology comments absent

The new public classes and functions use ordinary docstrings rather than the required adjacent # Mythic: and # Engineering: comment pair, leaving their operator-facing and implementation-facing roles inconsistent with the repository convention. This pattern also affects the public declarations in the capability, evidence, sandbox, and server modules.

Context Used: Jon mythic vs engineering ontology — identifiers a... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: services/aais-tools-mcp/aais_tools_mcp/tools.py
Line: 47-49

Comment:
**Dual ontology comments absent**

The new public classes and functions use ordinary docstrings rather than the required adjacent `# Mythic:` and `# Engineering:` comment pair, leaving their operator-facing and implementation-facing roles inconsistent with the repository convention. This pattern also affects the public declarations in the capability, evidence, sandbox, and server modules.

**Context Used:** Jon mythic vs engineering ontology — identifiers a... ([source](https://github.com/warheart1984-ctrl/project-infinity/blob/main/.cursor/rules/jon-ontology.mdc))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Cursor

Comment on lines +531 to +537
try:
return handler(dict(arguments or {}))
except WorkspaceSandboxError as exc:
return {"ok": False, "reason_code": exc.reason_code, "error": exc.message}

def _call_read_file(self, args: dict[str, Any]) -> dict[str, Any]:
return self.read_file.run(str(args.get("path") or ""), max_chars=int(args.get("max_chars") or MAX_READ_CHARS))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Malformed arguments escape contract

When a non-conforming MCP or local capability caller provides a wrong-typed JSON value such as max_chars="abc", unchecked conversions raise ValueError or TypeError while catalog.call catches only WorkspaceSandboxError. Local invocation therefore raises into Jarvis, and MCP invocation returns a generic text error instead of the documented structured ok=false result with a reason code; the same issue affects max_matches and patch string operands.

Context Used: Jon Safety Net — engineering-first prompts with IO... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: services/aais-tools-mcp/aais_tools_mcp/tools.py
Line: 531-537

Comment:
**Malformed arguments escape contract**

When a non-conforming MCP or local capability caller provides a wrong-typed JSON value such as `max_chars="abc"`, unchecked conversions raise `ValueError` or `TypeError` while `catalog.call` catches only `WorkspaceSandboxError`. Local invocation therefore raises into Jarvis, and MCP invocation returns a generic text error instead of the documented structured `ok=false` result with a reason code; the same issue affects `max_matches` and patch string operands.

**Context Used:** Jon Safety Net — engineering-first prompts with IO... ([source](https://github.com/warheart1984-ctrl/project-infinity/blob/main/.cursor/rules/jon-prompting.mdc))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Cursor

warheart1984-ctrl and others added 2 commits August 27, 2026 02:05
Prefer stdio MCP when AAIS_JARVIS_TOOLS_MCP=1 with fail-open to the
in-process adapter, and pin workspace roots via workspace_root so
container walks never hit /proc map_files EPERM.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
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