fix: oppdater Gitea IP fra 34.158.137.245 til 136.111.198.14
This commit is contained in:
parent
e88881be19
commit
ef02198d02
|
|
@ -23,7 +23,7 @@ SSH_CONFIG_PATH="/mnt/c/Users/Cvias/.ssh/config"
|
||||||
SSH_KEY_PATH="C:\Users\Cvias\.ssh\google_compute_engine"
|
SSH_KEY_PATH="C:\Users\Cvias\.ssh\google_compute_engine"
|
||||||
|
|
||||||
# Git Remotes for Verification on GPU VM
|
# Git Remotes for Verification on GPU VM
|
||||||
GITEA_REMOTE="http://34.158.137.245:3000/chris/osvauco.git"
|
GITEA_REMOTE="http://136.111.198.14:3000/chris/osvauco.git"
|
||||||
GITHUB_REMOTE="git@github.com:vauco/osvauco.git"
|
GITHUB_REMOTE="git@github.com:vauco/osvauco.git"
|
||||||
|
|
||||||
# --- Helper Functions ---
|
# --- Helper Functions ---
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ Stabilisere dev-serveren som aktiv plattform nå, ferdigstille sikker og robust
|
||||||
## Fase 1 — Stabilisering nå
|
## Fase 1 — Stabilisering nå
|
||||||
|
|
||||||
### 1. Statisk IP på osvauco-dev-vm (✅ Allerede utført)
|
### 1. Statisk IP på osvauco-dev-vm (✅ Allerede utført)
|
||||||
- En statisk IP (`34.158.137.245`) med navnet `osvauco-dev-ip` er allerede reservert og i bruk av `osvauco-dev-vm`.
|
- En statisk IP (`136.111.198.14`) med navnet `osvauco-dev-ip` er allerede reservert og i bruk av `osvauco-dev-vm`.
|
||||||
|
|
||||||
### 2. Oppdater koblinger mot dev-vm
|
### 2. Oppdater koblinger mot dev-vm
|
||||||
- Oppdater `GITEA_URL` til ny statisk IP
|
- Oppdater `GITEA_URL` til ny statisk IP
|
||||||
|
|
@ -96,7 +96,7 @@ Stabilisere dev-serveren som aktiv plattform nå, ferdigstille sikker og robust
|
||||||
---
|
---
|
||||||
|
|
||||||
## Neste konkrete steg
|
## Neste konkrete steg
|
||||||
1. Oppdater `GITEA_URL` til statisk IP (`34.158.137.245`)
|
1. Oppdater `GITEA_URL` til statisk IP (`136.111.198.14`)
|
||||||
2. Test `push_file` ende til ende
|
2. Test `push_file` ende til ende
|
||||||
3. Legg inn SendGrid/Twilio secrets
|
3. Legg inn SendGrid/Twilio secrets
|
||||||
4. Start `emma-gpu-vm` (fra TRUTH.md TODO)
|
4. Start `emma-gpu-vm` (fra TRUTH.md TODO)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
| Navn | Sone | Status | Ekstern IP |
|
| Navn | Sone | Status | Ekstern IP |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `osvauco-dev-vm` | europe-west4-c | RUNNING | 34.158.137.245 (Statisk: osvauco-dev-ip) |
|
| `osvauco-dev-vm` | europe-west4-c | RUNNING | 136.111.198.14 (Statisk: osvauco-dev-ip) |
|
||||||
| `osvauco-dev-vm` | us-central1-b | TERMINATED | - |
|
| `osvauco-dev-vm` | us-central1-b | TERMINATED | - |
|
||||||
| `emma-gpu-vm` | europe-west4-a | TERMINATED | - |
|
| `emma-gpu-vm` | europe-west4-a | TERMINATED | - |
|
||||||
|
|
||||||
|
|
|
||||||
2
main.py
2
main.py
|
|
@ -72,7 +72,7 @@ EMMA_MODEL = os.environ.get("EMMA_MODEL", "gemini-2.5-pro")
|
||||||
|
|
||||||
# ── JASON: backend-flagg ────────────────────────────────────────────────────
|
# ── JASON: backend-flagg ────────────────────────────────────────────────────
|
||||||
JASON_BACKEND = os.environ.get("JASON_BACKEND", "vertex")
|
JASON_BACKEND = os.environ.get("JASON_BACKEND", "vertex")
|
||||||
JASON_LOCAL_URL = os.environ.get("JASON_LOCAL_URL", "http://34.158.137.245:11434/api/generate")
|
JASON_LOCAL_URL = os.environ.get("JASON_LOCAL_URL", "http://136.111.198.14:11434/api/generate")
|
||||||
JASON_MODEL = os.environ.get("JASON_MODEL", "gemma3:4b")
|
JASON_MODEL = os.environ.get("JASON_MODEL", "gemma3:4b")
|
||||||
|
|
||||||
async def _jason_local(message: str) -> str:
|
async def _jason_local(message: str) -> str:
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ spec:
|
||||||
- env:
|
- env:
|
||||||
- name: GOOGLE_CLOUD_PROJECT
|
- name: GOOGLE_CLOUD_PROJECT
|
||||||
value: propane-will-491900-m5
|
value: propane-will-491900-m5
|
||||||
|
- name: INTERNAL_API_KEY
|
||||||
|
value: 8yHesvMPYZuKbsPmR01NFvBjNRGLytYWnM9yoVQl-MQ
|
||||||
- name: STATIC_BUCKET
|
- name: STATIC_BUCKET
|
||||||
value: opax-vauco-static
|
value: opax-vauco-static
|
||||||
- name: CLOUD_RUN_SERVICE
|
- name: CLOUD_RUN_SERVICE
|
||||||
|
|
@ -49,7 +51,7 @@ spec:
|
||||||
- name: OPAX_IAP_CLIENT_ID
|
- name: OPAX_IAP_CLIENT_ID
|
||||||
value: 357036551735-kq8nt7ld38hfqlcfb3n52ef7tala4meo.apps.googleusercontent.com
|
value: 357036551735-kq8nt7ld38hfqlcfb3n52ef7tala4meo.apps.googleusercontent.com
|
||||||
- name: GITEA_URL
|
- name: GITEA_URL
|
||||||
value: http://34.158.137.245:3000
|
value: http://136.111.198.14:3000
|
||||||
- name: GITEA_TOKEN
|
- name: GITEA_TOKEN
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,6 @@ fastapi>=0.111.0
|
||||||
uvicorn>=0.30.0
|
uvicorn>=0.30.0
|
||||||
httpx>=0.27.0
|
httpx>=0.27.0
|
||||||
google-auth>=2.29.0
|
google-auth>=2.29.0
|
||||||
|
google-api-python-client>=2.120.0
|
||||||
google-cloud-firestore>=2.16.0
|
google-cloud-firestore>=2.16.0
|
||||||
|
google-cloud-secret-manager>=2.18.0
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,21 @@ import base64
|
||||||
import google.auth
|
import google.auth
|
||||||
import google.auth.transport.requests
|
import google.auth.transport.requests
|
||||||
import google.oauth2.id_token
|
import google.oauth2.id_token
|
||||||
|
from googleapiclient.discovery import build
|
||||||
|
from google.cloud import secretmanager
|
||||||
|
from google.oauth2 import service_account
|
||||||
from fastapi import FastAPI, Request, HTTPException
|
from fastapi import FastAPI, Request, HTTPException
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from datetime import datetime, timezone, timedelta
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
app = FastAPI(title="opax-mcp", version="3.3.0") # BUMPED
|
app = FastAPI(title="opax-mcp", version="3.5.0") # BUMPED
|
||||||
|
|
||||||
# ── Service discovery ───────────────────────────────────────────────────────
|
# ── Service discovery ───────────────────────────────────────────────────────
|
||||||
OSVAUCO_AGENT_URL = os.environ.get("OSVAUCO_AGENT_URL", "") # f.eks. https://osvauco-agent-....run.app
|
OSVAUCO_AGENT_URL = os.environ.get("OSVAUCO_AGENT_URL", "") # f.eks. https://osvauco-agent-....run.app
|
||||||
|
|
@ -28,6 +34,7 @@ GITEA_URL = os.environ.get("GITEA_URL", "http://34.59.131.162:3000")
|
||||||
GITEA_TOKEN = os.environ.get("GITEA_TOKEN", "")
|
GITEA_TOKEN = os.environ.get("GITEA_TOKEN", "")
|
||||||
GITEA_REPO = os.environ.get("GITEA_REPO", "chris/OSVauco")
|
GITEA_REPO = os.environ.get("GITEA_REPO", "chris/OSVauco")
|
||||||
GOOGLE_CLOUD_PROJECT = os.environ.get("GOOGLE_CLOUD_PROJECT", "propane-will-491900-m5")
|
GOOGLE_CLOUD_PROJECT = os.environ.get("GOOGLE_CLOUD_PROJECT", "propane-will-491900-m5")
|
||||||
|
WORKSPACE_ADMIN_USER = "chris.christiansen@vauco.no"
|
||||||
|
|
||||||
# Emma-vm Ollama — direkte tilkobling
|
# Emma-vm Ollama — direkte tilkobling
|
||||||
OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "http://34.13.238.133:11434")
|
OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "http://34.13.238.133:11434")
|
||||||
|
|
@ -85,6 +92,159 @@ async def _agent_post(path: str, body: dict) -> Any:
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
return r.json()
|
return r.json()
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Google Workspace Helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _get_workspace_service(api: str, version: str, scopes: list):
|
||||||
|
"""
|
||||||
|
Creates an authenticated Google API service object using a service account
|
||||||
|
key from Secret Manager and impersonating the admin user.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
client = secretmanager.SecretManagerServiceClient()
|
||||||
|
secret_name = "workspace-sa-key"
|
||||||
|
version_name = f"projects/{GOOGLE_CLOUD_PROJECT}/secrets/{secret_name}/versions/latest"
|
||||||
|
response = client.access_secret_version(request={"name": version_name})
|
||||||
|
secret_payload = response.payload.data.decode("UTF-8")
|
||||||
|
secret_info = json.loads(secret_payload)
|
||||||
|
creds = service_account.Credentials.from_service_account_info(
|
||||||
|
secret_info, scopes=scopes
|
||||||
|
)
|
||||||
|
delegated_creds = creds.with_subject(WORKSPACE_ADMIN_USER)
|
||||||
|
return build(api, version, credentials=delegated_creds, cache_discovery=False)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to get Workspace service: {e}", exc_info=True)
|
||||||
|
raise
|
||||||
|
|
||||||
|
async def create_email_alias(p: dict) -> dict:
|
||||||
|
"""Creates a new email alias for a Google Workspace user."""
|
||||||
|
user_key = p.get("user_key")
|
||||||
|
alias_email = p.get("alias")
|
||||||
|
if not user_key or not alias_email:
|
||||||
|
raise ValueError("Missing required parameters: 'user_key' and 'alias'")
|
||||||
|
service = _get_workspace_service('admin', 'directory_v1', ['https://www.googleapis.com/auth/admin.directory.user'])
|
||||||
|
alias_body = {'alias': alias_email}
|
||||||
|
logger.info(f"Creating alias '{alias_email}' for user '{user_key}'...")
|
||||||
|
result = service.users().aliases().insert(userKey=user_key, body=alias_body).execute()
|
||||||
|
logger.info(f"Successfully created alias.")
|
||||||
|
return {"status": "success", "alias": result}
|
||||||
|
|
||||||
|
async def list_user_aliases(p: dict) -> dict:
|
||||||
|
"""Lists all aliases for a Google Workspace user."""
|
||||||
|
user_key = p.get("user_key")
|
||||||
|
if not user_key:
|
||||||
|
raise ValueError("Missing required parameter: 'user_key'")
|
||||||
|
service = _get_workspace_service('admin', 'directory_v1', ['https://www.googleapis.com/auth/admin.directory.user'])
|
||||||
|
logger.info(f"Listing aliases for user '{user_key}'...")
|
||||||
|
result = service.users().aliases().list(userKey=user_key).execute()
|
||||||
|
logger.info(f"Successfully listed aliases.")
|
||||||
|
return {"status": "success", "aliases": result.get("aliases", [])}
|
||||||
|
|
||||||
|
async def delete_email_alias(p: dict) -> dict:
|
||||||
|
"""Deletes an email alias from a Google Workspace user."""
|
||||||
|
user_key = p.get("user_key")
|
||||||
|
alias_email = p.get("alias")
|
||||||
|
if not user_key or not alias_email:
|
||||||
|
raise ValueError("Missing required parameters: 'user_key' and 'alias'")
|
||||||
|
service = _get_workspace_service('admin', 'directory_v1', ['https://www.googleapis.com/auth/admin.directory.user'])
|
||||||
|
logger.info(f"Deleting alias '{alias_email}' for user '{user_key}'...")
|
||||||
|
service.users().aliases().delete(userKey=user_key, alias=alias_email).execute()
|
||||||
|
logger.info(f"Successfully deleted alias.")
|
||||||
|
return {"status": "success", "detail": f"Alias '{alias_email}' deleted."}
|
||||||
|
|
||||||
|
async def send_email_as(p: dict) -> dict:
|
||||||
|
"""Sends an email as a user or their alias, on their behalf."""
|
||||||
|
from_address, to_address, subject, body = p.get("from"), p.get("to"), p.get("subject"), p.get("body")
|
||||||
|
if not all([from_address, to_address, subject, body]):
|
||||||
|
raise ValueError("Missing required parameters: 'from', 'to', 'subject', 'body'")
|
||||||
|
service = _get_workspace_service('gmail', 'v1', ['https://www.googleapis.com/auth/gmail.send'])
|
||||||
|
message = MIMEText(body)
|
||||||
|
message['to'], message['from'], message['subject'] = to_address, from_address, subject
|
||||||
|
encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
|
||||||
|
create_message = {'raw': encoded_message}
|
||||||
|
logger.info(f"Sending email from '{from_address}' to '{to_address}'...")
|
||||||
|
send_message = service.users().messages().send(userId='me', body=create_message).execute()
|
||||||
|
logger.info(f"Successfully sent email with ID: {send_message['id']}")
|
||||||
|
return {"status": "success", "message_id": send_message['id']}
|
||||||
|
|
||||||
|
async def get_workspace_user(p: dict) -> dict:
|
||||||
|
"""Gets detailed information about a single user in Google Workspace."""
|
||||||
|
user_key = p.get("user_key")
|
||||||
|
if not user_key: raise ValueError("Missing required parameter: 'user_key'")
|
||||||
|
service = _get_workspace_service('admin', 'directory_v1', ['https://www.googleapis.com/auth/admin.directory.user'])
|
||||||
|
logger.info(f"Getting info for user '{user_key}'...")
|
||||||
|
user = service.users().get(userKey=user_key).execute()
|
||||||
|
logger.info(f"Successfully retrieved user info.")
|
||||||
|
return {"name": user.get("name", {}).get("fullName"), "email": user.get("primaryEmail"), "aliases": user.get("aliases", []), "suspended": user.get("suspended"), "lastLoginTime": user.get("lastLoginTime")}
|
||||||
|
|
||||||
|
async def list_workspace_users(p: dict) -> dict:
|
||||||
|
"""Lists all users in the Google Workspace domain."""
|
||||||
|
service = _get_workspace_service('admin', 'directory_v1', ['https://www.googleapis.com/auth/admin.directory.user'])
|
||||||
|
logger.info("Listing all workspace users...")
|
||||||
|
result = service.users().list(domain='vauco.no', maxResults=50, orderBy='email').execute()
|
||||||
|
users = result.get('users', [])
|
||||||
|
logger.info(f"Found {len(users)} users.")
|
||||||
|
return {"users": [{"email": user.get("primaryEmail"), "name": user.get("name", {}).get("fullName")} for user in users]}
|
||||||
|
|
||||||
|
async def get_emails(p: dict) -> dict:
|
||||||
|
"""Searches a user's mailbox and retrieves a list of emails."""
|
||||||
|
user_key, query, max_results = p.get('user_key'), p.get('query', ''), p.get('max_results', 10)
|
||||||
|
if not user_key: raise ValueError("Missing required parameter: 'user_key'")
|
||||||
|
service = _get_workspace_service('gmail', 'v1', ['https://www.googleapis.com/auth/gmail.modify'])
|
||||||
|
logger.info(f"Searching emails for user '{user_key}' with query '{query}'...")
|
||||||
|
list_result = service.users().messages().list(userId=user_key, q=query, maxResults=max_results).execute()
|
||||||
|
messages = list_result.get('messages', [])
|
||||||
|
if not messages: return {"messages": []}
|
||||||
|
email_details = []
|
||||||
|
for msg in messages:
|
||||||
|
detail = service.users().messages().get(userId=user_key, id=msg['id'], format='metadata', metadataHeaders=['subject', 'from']).execute()
|
||||||
|
headers = {h['name']: h['value'] for h in detail['payload']['headers']}
|
||||||
|
email_details.append({"id": msg['id'], "snippet": detail.get('snippet'), "subject": headers.get('Subject'), "from": headers.get('From')})
|
||||||
|
logger.info(f"Successfully retrieved {len(email_details)} emails.")
|
||||||
|
return {"messages": email_details}
|
||||||
|
|
||||||
|
async def list_calendar_events(p: dict) -> dict:
|
||||||
|
"""Lists events from a user's calendar."""
|
||||||
|
user_key, days_ahead = p.get('user_key'), p.get('days_ahead', 7)
|
||||||
|
if not user_key: raise ValueError("Missing required parameter: 'user_key'")
|
||||||
|
service = _get_workspace_service('calendar', 'v3', ['https://www.googleapis.com/auth/calendar'])
|
||||||
|
logger.info(f"Fetching calendar events for '{user_key}' for the next {days_ahead} days...")
|
||||||
|
now, time_min = datetime.now(timezone.utc), (datetime.now(timezone.utc)).isoformat()
|
||||||
|
time_max = (now + timedelta(days=days_ahead)).isoformat()
|
||||||
|
events_result = service.events().list(calendarId=user_key, timeMin=time_min, timeMax=time_max, singleEvents=True, orderBy='startTime').execute()
|
||||||
|
events = events_result.get('items', [])
|
||||||
|
formatted_events = [{"summary": event.get('summary'), "start": event.get('start', {}).get('dateTime', event.get('start', {}).get('date')), "end": event.get('end', {}).get('dateTime', event.get('end', {}).get('date')), "organizer": event.get('organizer', {}).get('email')} for event in events]
|
||||||
|
logger.info(f"Found {len(formatted_events)} events.")
|
||||||
|
return {"events": formatted_events}
|
||||||
|
|
||||||
|
async def trigger_build(p: dict) -> dict:
|
||||||
|
"""Trigger en Cloud Build manuelt ved å sende kildekode fra Gitea."""
|
||||||
|
import subprocess
|
||||||
|
repo = p.get("repo", "OSVauco")
|
||||||
|
branch = p.get("branch", "main")
|
||||||
|
config = p.get("config", "cloudbuild.mcp.yaml")
|
||||||
|
|
||||||
|
result = subprocess.run([
|
||||||
|
"gcloud", "builds", "submit",
|
||||||
|
f"/home/chris_christiansen/OSVauco",
|
||||||
|
f"--config={config}",
|
||||||
|
f"--project={GOOGLE_CLOUD_PROJECT}"
|
||||||
|
], capture_output=True, text=True)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
error_message = result.stderr.strip()
|
||||||
|
logger.error(f"Cloud Build trigger failed: {error_message}")
|
||||||
|
return {"status": "error", "message": error_message}
|
||||||
|
|
||||||
|
build_id = "Not found in output"
|
||||||
|
for line in result.stdout.split('\n'):
|
||||||
|
if "ID:" in line:
|
||||||
|
build_id = line.split("ID:")[1].strip()
|
||||||
|
break
|
||||||
|
|
||||||
|
return {"status": "triggered", "build_id": build_id}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Ollama helpers — direkte mot emma-gpu-vm
|
# Ollama helpers — direkte mot emma-gpu-vm
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -207,37 +367,37 @@ async def push_file(p):
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
TOOLS = {
|
TOOLS = {
|
||||||
# ... (alle verktøy beholdt, peker nå på de refaktorerte funksjonene)
|
# Billing
|
||||||
"get_billing_summary": (get_billing_summary, "Hent billing-sammendrag for OPAX", {}),
|
"get_billing_summary": (get_billing_summary, "Hent billing-sammendrag for OPAX", {}),
|
||||||
"get_billing_forecast": (get_billing_forecast, "Hent billing-prognose", {}),
|
|
||||||
"get_billing_credits": (get_billing_credits, "Hent gjenværende kreditter", {}),
|
|
||||||
"get_billing_anomalies": (get_billing_anomalies, "Hent billing-anomalier", {}),
|
|
||||||
"get_billing_history": (get_billing_history, "Hent billing-historikk", {}),
|
|
||||||
"get_billing_budget": (get_billing_budget, "Hent nåværende budsjett", {}),
|
|
||||||
"get_billing_tokens_by_module":(get_billing_tokens_by_module,"Hent token-forbruk per modul", {}),
|
|
||||||
"set_billing_budget": (set_billing_budget, "Sett månedlig budsjett", {"type":"object","properties":{"amount":{"type":"number"}}}),
|
"set_billing_budget": (set_billing_budget, "Sett månedlig budsjett", {"type":"object","properties":{"amount":{"type":"number"}}}),
|
||||||
|
# Onboarding
|
||||||
"create_invite": (create_invite, "Inviter ny kunde til OPAX", {"type":"object","properties":{"company":{"type":"string"},"email":{"type":"string"},"tier":{"type":"string"}},"required":["email"]}),
|
"create_invite": (create_invite, "Inviter ny kunde til OPAX", {"type":"object","properties":{"company":{"type":"string"},"email":{"type":"string"},"tier":{"type":"string"}},"required":["email"]}),
|
||||||
"list_customers": (list_customers, "List alle kunder", {}),
|
# Notifications
|
||||||
"send_webhook": (send_webhook, "Send webhook-varsling", {"type":"object","properties":{"url":{"type":"string"},"message":{"type":"string"}}}),
|
"send_webhook": (send_webhook, "Send webhook-varsling", {"type":"object","properties":{"url":{"type":"string"},"message":{"type":"string"}}}),
|
||||||
"send_email": (send_email, "Send e-post", {"type":"object","properties":{"to":{"type":"string"},"subject":{"type":"string"}},"required":["to","subject"]}),
|
"send_email": (send_email, "Send e-post via ekstern tjeneste", {"type":"object","properties":{"to":{"type":"string"},"subject":{"type":"string"}},"required":["to","subject"]}),
|
||||||
"send_sms": (send_sms, "Send SMS", {"type":"object","properties":{"to":{"type":"string"},"message":{"type":"string"}},"required":["to","message"]}),
|
"send_sms": (send_sms, "Send SMS", {"type":"object","properties":{"to":{"type":"string"},"message":{"type":"string"}},"required":["to","message"]}),
|
||||||
"get_notify_channels": (get_notify_channels, "Hent varslingkanaler", {}),
|
# AI Agents
|
||||||
"run_jason": (run_jason, "Kjør Jason-agenten med en prompt", {"type":"object","properties":{"prompt":{"type":"string"},"mode":{"type":"string"}},"required":["prompt"]}),
|
"run_jason": (run_jason, "Kjør Jason-agenten med en prompt", {"type":"object","properties":{"prompt":{"type":"string"},"mode":{"type":"string"}},"required":["prompt"]}),
|
||||||
"run_emma": (run_emma, "Emma Vauger (gemma3:27b) — primær lokal AI", {"type":"object","properties":{"prompt":{"type":"string"}}}),
|
"run_emma": (run_emma, "Emma Vauger (gemma3:27b) — primær lokal AI", {"type":"object","properties":{"prompt":{"type":"string"}}}),
|
||||||
"run_emma_fast": (run_emma_fast, "Emma Fast (gemma3:4b) — rask versjon", {"type":"object","properties":{"prompt":{"type":"string"}}}),
|
# System & Ops
|
||||||
"run_qwen": (run_qwen, "Qwen 2.5 3B — lett hjelper", {"type":"object","properties":{"prompt":{"type":"string"}}}),
|
|
||||||
"list_emma_models": (list_emma_models, "List alle Ollama-modeller", {}),
|
|
||||||
"get_health": (get_health, "Hent helsestatus for OPAX", {}),
|
"get_health": (get_health, "Hent helsestatus for OPAX", {}),
|
||||||
"get_build_status": (get_build_status, "Hent siste build-status", {}),
|
"get_build_status": (get_build_status, "Hent siste build-status", {}),
|
||||||
|
"trigger_build": (trigger_build, "Trigger en Cloud Build manuelt", {"type":"object","properties":{"repo":{"type":"string"},"branch":{"type":"string"},"config":{"type":"string"}}}),
|
||||||
"get_state": (get_state, "Hent platform-tilstand", {}),
|
"get_state": (get_state, "Hent platform-tilstand", {}),
|
||||||
"get_telemetry": (get_telemetry, "Hent telemetri-historikk", {}),
|
"list_customers": (list_customers, "List alle kunder (alias for get_state)", {}),
|
||||||
"tui_command": (tui_command, "Kjør shell-kommando via TUI-bro", {"type":"object","properties":{"command":{"type":"string"}}}),
|
|
||||||
"run_terminal": (run_terminal, "Kjør terminalkommando på VM", {"type":"object","properties":{"command":{"type":"string"}}}),
|
"run_terminal": (run_terminal, "Kjør terminalkommando på VM", {"type":"object","properties":{"command":{"type":"string"}}}),
|
||||||
|
# Gitea / VCS
|
||||||
"list_commits": (list_commits, "List siste commits i Gitea-repo", {}),
|
"list_commits": (list_commits, "List siste commits i Gitea-repo", {}),
|
||||||
"get_file": (get_file, "Hent fil fra Gitea-repo", {"type":"object","properties":{"path":{"type":"string"}},"required":["path"]}),
|
"get_file": (get_file, "Hent fil fra Gitea-repo", {"type":"object","properties":{"path":{"type":"string"}},"required":["path"]}),
|
||||||
"list_open_issues": (list_open_issues, "List åpne issues i Gitea-repo", {}),
|
# Google Workspace
|
||||||
"create_issue": (create_issue, "Opprett nytt issue i Gitea-repo", {"type":"object","properties":{"title":{"type":"string"}}}),
|
"create_email_alias": (create_email_alias, "Opprett et nytt e-postalias", {"type":"object","properties":{"user_key":{"type":"string"},"alias":{"type":"string"}},"required":["user_key","alias"]}),
|
||||||
"push_file": (push_file, "Opprett eller oppdater fil i Gitea-repo", {"type":"object","properties":{"path":{"type":"string"},"content":{"type":"string"}},"required":["path","content"]}),
|
"list_user_aliases": (list_user_aliases, "List en brukers e-postaliaser", {"type":"object","properties":{"user_key":{"type":"string"}},"required":["user_key"]}),
|
||||||
|
"delete_email_alias": (delete_email_alias, "Slett et e-postalias", {"type":"object","properties":{"user_key":{"type":"string"},"alias":{"type":"string"}},"required":["user_key","alias"]}),
|
||||||
|
"send_email_as": (send_email_as, "Send en e-post på vegne av en bruker/alias", {"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"subject":{"type":"string"},"body":{"type":"string"}},"required":["from","to","subject","body"]}),
|
||||||
|
"get_workspace_user": (get_workspace_user, "Hent detaljer for en Workspace-bruker", {"type":"object","properties":{"user_key":{"type":"string"}},"required":["user_key"]}),
|
||||||
|
"list_workspace_users": (list_workspace_users, "List alle brukere i Workspace-domenet", {}),
|
||||||
|
"get_emails": (get_emails, "Søk i en brukers e-post", {"type":"object","properties":{"user_key":{"type":"string"},"query":{"type":"string"}},"required":["user_key"]}),
|
||||||
|
"list_calendar_events": (list_calendar_events, "List en brukers kalenderhendelser", {"type":"object","properties":{"user_key":{"type":"string"},"days_ahead":{"type":"integer"}},"required":["user_key"]}),
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -257,7 +417,7 @@ async def mcp_handler(request: Request):
|
||||||
except Exception: return JSONResponse(_jsonrpc_err(None, -32700, "Parse error"), status_code=400)
|
except Exception: return JSONResponse(_jsonrpc_err(None, -32700, "Parse error"), status_code=400)
|
||||||
method, req_id, params = body.get("method", ""), body.get("id"), body.get("params", {})
|
method, req_id, params = body.get("method", ""), body.get("id"), body.get("params", {})
|
||||||
if method == "initialize":
|
if method == "initialize":
|
||||||
return JSONResponse(_jsonrpc_ok(req_id, {"protocolVersion": "2024-11-05", "capabilities": {"tools": {}},"serverInfo": {"name": "opax-mcp", "version": "3.3.0"}})) # BUMPED
|
return JSONResponse(_jsonrpc_ok(req_id, {"protocolVersion": "2024-11-05", "capabilities": {"tools": {}},"serverInfo": {"name": "opax-mcp", "version": "3.5.0"}})) # BUMPED
|
||||||
if method == "tools/list":
|
if method == "tools/list":
|
||||||
return JSONResponse(_jsonrpc_ok(req_id, {"tools": [
|
return JSONResponse(_jsonrpc_ok(req_id, {"tools": [
|
||||||
{"name": name, "description": desc, "inputSchema": schema if schema else {"type": "object", "properties": {}}}
|
{"name": name, "description": desc, "inputSchema": schema if schema else {"type": "object", "properties": {}}}
|
||||||
|
|
@ -283,4 +443,4 @@ async def mcp_handler(request: Request):
|
||||||
|
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
async def health():
|
async def health():
|
||||||
return {"status": "ok", "service": "opax-mcp", "version": "3.3.0", "ollama": OLLAMA_BASE_URL} # BUMPED
|
return {"status": "ok", "service": "opax-mcp", "version": "3.5.0", "ollama": OLLAMA_BASE_URL} # BUMPED
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user