mcp-to-cli-kit 0.1.0__py3-none-any.whl
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.
- mcp_to_cli_kit/__init__.py +1 -0
- mcp_to_cli_kit/agent.py +106 -0
- mcp_to_cli_kit/arguments.py +789 -0
- mcp_to_cli_kit/cache.py +115 -0
- mcp_to_cli_kit/cli.py +1011 -0
- mcp_to_cli_kit/client.py +543 -0
- mcp_to_cli_kit/config.py +480 -0
- mcp_to_cli_kit/output.py +125 -0
- mcp_to_cli_kit/py.typed +0 -0
- mcp_to_cli_kit-0.1.0.dist-info/METADATA +238 -0
- mcp_to_cli_kit-0.1.0.dist-info/RECORD +13 -0
- mcp_to_cli_kit-0.1.0.dist-info/WHEEL +4 -0
- mcp_to_cli_kit-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Build a typed command-line interface for one or more remote MCP servers."""
|
mcp_to_cli_kit/agent.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Detection of the AI coding agent this process is running under.
|
|
2
|
+
|
|
3
|
+
The CLI prints a tool's result differently for an agent than for a person: an agent gets
|
|
4
|
+
the server's text verbatim, a person gets it pretty-printed. There is no standard for a
|
|
5
|
+
harness to announce itself, so this recognises the environment variables the known
|
|
6
|
+
harnesses set, plus the two generic ones (``AGENT`` and ``AI_AGENT``) that carry a slug.
|
|
7
|
+
|
|
8
|
+
The table below is the single place to extend. It was researched on 2026-09-05 from
|
|
9
|
+
official documentation, project sources and ``vercel/detect-agent``'s ``agents.json``.
|
|
10
|
+
Only Claude Code is verified first hand; the rest are best effort, and a harness that
|
|
11
|
+
announces nothing is deliberately left out rather than guessed at from something like
|
|
12
|
+
``TERM_PROGRAM``, which a human typing in the same terminal would also set.
|
|
13
|
+
|
|
14
|
+
Known to set nothing detectable: GitHub Copilot (the feature request is unshipped),
|
|
15
|
+
Aider, Windsurf/Cascade, and Warp's agent mode.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
# Harness-specific variables, checked in this order. The presence of the variable is
|
|
24
|
+
# the signal; its value is not read. These win over AGENT and AI_AGENT, which a user
|
|
25
|
+
# may well have set by hand.
|
|
26
|
+
AGENT_ENV_VARS: list[tuple[str, list[str]]] = [
|
|
27
|
+
("Claude Code", ["CLAUDECODE", "CLAUDE_CODE"]),
|
|
28
|
+
("Gemini CLI", ["GEMINI_CLI"]),
|
|
29
|
+
("Antigravity", ["ANTIGRAVITY_AGENT", "ANTIGRAVITY_CLI_ALIAS"]),
|
|
30
|
+
("Codex", ["CODEX_SANDBOX", "CODEX_SANDBOX_NETWORK_DISABLED", "CODEX_CI", "CODEX_THREAD_ID"]),
|
|
31
|
+
# Deliberately not CURSOR_TRACE_ID: the IDE sets it for a human's terminal too
|
|
32
|
+
("Cursor", ["CURSOR_AGENT", "CURSOR_CLI", "CURSOR_EXTENSION_HOST_ROLE"]),
|
|
33
|
+
("Cline", ["CLINE_ACTIVE"]),
|
|
34
|
+
("OpenCode", ["OPENCODE", "OPENCODE_CLIENT"]),
|
|
35
|
+
("Goose", ["GOOSE_TERMINAL"]),
|
|
36
|
+
("Augment", ["AUGMENT_AGENT"]),
|
|
37
|
+
("Junie", ["JUNIE_DATA", "JUNIE_SHIM_PATH"]),
|
|
38
|
+
("Kimi Code", ["KIMI_PLUGIN_ROOT"]),
|
|
39
|
+
("Grok Build", ["GROK_PLUGIN_ROOT", "GROK_PLUGIN_DATA"]),
|
|
40
|
+
("OpenClaw", ["OPENCLAW_SHELL"]),
|
|
41
|
+
("Trae", ["TRAE_AI_SHELL_ID"]),
|
|
42
|
+
("Pi", ["PI_CODING_AGENT"]),
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
# Generic variables carrying a slug, checked after the table above.
|
|
46
|
+
GENERIC_AGENT_ENV_VARS = ["AGENT", "AI_AGENT"]
|
|
47
|
+
|
|
48
|
+
# Slugs whose title-cased form would be wrong or ugly.
|
|
49
|
+
AGENT_SLUG_NAMES = {
|
|
50
|
+
"claude-code": "Claude Code",
|
|
51
|
+
"cursor-cli": "Cursor",
|
|
52
|
+
"gemini-cli": "Gemini CLI",
|
|
53
|
+
"amp": "Amp",
|
|
54
|
+
"goose": "Goose",
|
|
55
|
+
"codex": "Codex",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Devin announces itself with a file rather than a variable.
|
|
59
|
+
DEVIN_MARKER = Path("/opt/.devin")
|
|
60
|
+
|
|
61
|
+
# Name used when something says an agent is running but not which one.
|
|
62
|
+
UNKNOWN_AGENT = "an AI agent"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _name_from_slug(value: str) -> str:
|
|
66
|
+
"""Turn the value of a generic agent variable into a display name.
|
|
67
|
+
|
|
68
|
+
The value is a slug that may carry a version, as in
|
|
69
|
+
``claude-code_2-1-260_agent``, so only the part before the first underscore names
|
|
70
|
+
the agent.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
value: The raw value of AGENT or AI_AGENT.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
The display name, or the fallback when the value names nothing.
|
|
77
|
+
"""
|
|
78
|
+
slug = value.strip().split("_")[0].strip().lower()
|
|
79
|
+
if not slug:
|
|
80
|
+
return UNKNOWN_AGENT
|
|
81
|
+
|
|
82
|
+
if slug in AGENT_SLUG_NAMES:
|
|
83
|
+
return AGENT_SLUG_NAMES[slug]
|
|
84
|
+
|
|
85
|
+
return " ".join(word.capitalize() for word in slug.split("-") if word) or UNKNOWN_AGENT
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def detect_agent() -> str | None:
|
|
89
|
+
"""Detect the AI coding agent this process is running under.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
The agent's display name, or None when no agent is detected.
|
|
93
|
+
"""
|
|
94
|
+
for name, variables in AGENT_ENV_VARS:
|
|
95
|
+
if any(variable in os.environ for variable in variables):
|
|
96
|
+
return name
|
|
97
|
+
|
|
98
|
+
for variable in GENERIC_AGENT_ENV_VARS:
|
|
99
|
+
value = os.environ.get(variable)
|
|
100
|
+
if value is not None:
|
|
101
|
+
return _name_from_slug(value)
|
|
102
|
+
|
|
103
|
+
if DEVIN_MARKER.exists():
|
|
104
|
+
return "Devin"
|
|
105
|
+
|
|
106
|
+
return None
|