feat: add read-only gitea_get_profile discovery tool (#13)
Add a read-only MCP tool that reports the active runtime execution profile so an LLM can inspect what the current process is configured to do before deciding whether to attempt an action later. - gitea_get_profile: returns profile_name, allowed/forbidden operation categories, audit_label, token_source_name (a NAME, never a value), base_url, remote, resolved server, and — optionally — the verified authenticated username. Identity resolution fails soft and marks identity_status (verified/unknown/unavailable/not_resolved); the profile config is always returned. Never mutates Gitea. - gitea_auth.get_profile(): extended with forbidden_operations, audit_label, token_source_name from env (non-secret metadata). - .env.example / README: document the new optional metadata vars and the discovery tool. - tests: metadata parsing, verified/unavailable/unknown identity paths, skip-identity, and secret-redaction. Read-only. No token exposure. No multi-token switching. No PR eligibility, review, or merge workflow. No Jenkins/Ops/GlitchTip/ Release/deploy behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -651,6 +651,76 @@ def gitea_whoami(
|
||||
}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def gitea_get_profile(
|
||||
remote: str = "dadeschools",
|
||||
host: str | None = None,
|
||||
resolve_identity: bool = True,
|
||||
) -> dict:
|
||||
"""Describe the active Gitea MCP execution profile for this runtime.
|
||||
|
||||
Read-only. Reports the non-secret configuration of the running MCP
|
||||
process (profile name, allowed/forbidden operation categories, audit
|
||||
label, token *source name*, base URL) plus the resolved server for the
|
||||
given remote. Optionally resolves the authenticated username via
|
||||
``gitea_whoami``'s endpoint so an LLM can see who this runtime acts as.
|
||||
|
||||
This tool never mutates Gitea and never approves, merges, comments, or
|
||||
creates anything. It never returns the token value, Authorization header,
|
||||
password, raw environment, or credential file paths. Identity resolution
|
||||
fails soft: if it cannot be determined, ``authenticated_username`` is null
|
||||
and ``identity_status`` marks it, but the profile config is still returned.
|
||||
|
||||
Args:
|
||||
remote: Known instance — 'dadeschools' or 'prgs'.
|
||||
host: Override the Gitea host.
|
||||
resolve_identity: If True, attempt a read-only identity lookup.
|
||||
|
||||
Returns:
|
||||
dict of safe profile metadata. ``identity_status`` is one of
|
||||
'verified', 'unknown', 'unavailable', or 'not_resolved'.
|
||||
"""
|
||||
profile = get_profile()
|
||||
result = {
|
||||
"profile_name": profile["profile_name"],
|
||||
"allowed_operations": profile["allowed_operations"],
|
||||
"forbidden_operations": profile["forbidden_operations"],
|
||||
"audit_label": profile["audit_label"],
|
||||
"token_source_name": profile["token_source_name"],
|
||||
"base_url": profile["base_url"],
|
||||
"remote": remote if remote in REMOTES else None,
|
||||
"server": None,
|
||||
"authenticated_username": None,
|
||||
"identity_status": "not_resolved",
|
||||
}
|
||||
|
||||
if remote not in REMOTES:
|
||||
# Mark ambiguity rather than raising: the tool stays inspectable.
|
||||
result["identity_status"] = "unknown"
|
||||
result["remote_error"] = f"Unknown remote '{remote}'. Choose from: {list(REMOTES)}"
|
||||
return result
|
||||
|
||||
h = host or REMOTES[remote]["host"]
|
||||
result["server"] = f"https://{h}"
|
||||
|
||||
if resolve_identity:
|
||||
try:
|
||||
auth = _auth(h)
|
||||
data = api_request("GET", f"https://{h}/api/v1/user", auth)
|
||||
login = (data or {}).get("login")
|
||||
if login:
|
||||
result["authenticated_username"] = login
|
||||
result["identity_status"] = "verified"
|
||||
else:
|
||||
result["identity_status"] = "unknown"
|
||||
except Exception:
|
||||
# Fail soft for the identity field only. Never surface the error
|
||||
# detail or any credential material — just mark it unavailable.
|
||||
result["identity_status"] = "unavailable"
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def gitea_mark_issue(
|
||||
issue_number: int,
|
||||
|
||||
Reference in New Issue
Block a user