Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.14
1 change: 1 addition & 0 deletions credentials.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"userinfo": {"acr": "1", "at_hash": "r6LEdw2KgmAoqYYb9vk83A", "aud": ["jsUPWIcUECQFE_ouanUuVhXx52TTjEVcVNNtNGeyAtU"], "auth_time": 1770381503, "azp": "jsUPWIcUECQFE_ouanUuVhXx52TTjEVcVNNtNGeyAtU", "c_hash": "eqGfTcp0dCblfZxfOVuDEw", "email": "[email protected]", "email_verified": true, "exp": 1770467904, "fields": {"name": "Beno\u00eet"}, "iat": 1770381504, "is_active": true, "iss": "https://auth.codecarbon.io/codecarbon", "sub": "48d92c76-039b-41c2-a98d-b1cbc72663e6", "tenant_id": "7a971871-e721-4eb0-a9c5-55d3b904a3c8"}, "tokens": {"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6InlzalZ4OG5YamExZTVwOW42RF9ZQWstTi1aM3FpR3c3TElWOGc3U2doYW8ifQ.eyJhY3IiOiIxIiwiYXVkIjpbImpzVVBXSWNVRUNRRkVfb3VhblV1VmhYeDUyVFRqRVZjVk5OdE5HZXlBdFUiXSwiYXV0aF90aW1lIjoxNzcwMzgxNTAzLCJhenAiOiJqc1VQV0ljVUVDUUZFX291YW5VdVZoWHg1MlRUakVWY1ZOTnROR2V5QXRVIiwiZXhwIjoxNzcwNDY3OTA0LCJpYXQiOjE3NzAzODE1MDQsImlzcyI6Imh0dHBzOi8vYXV0aC5jb2RlY2FyYm9uLmlvL2NvZGVjYXJib24iLCJwZXJtaXNzaW9ucyI6W10sInNjb3BlIjoib2ZmbGluZV9hY2Nlc3Mgb3BlbmlkIiwic3ViIjoiNDhkOTJjNzYtMDM5Yi00MWMyLWE5OGQtYjFjYmM3MjY2M2U2In0.Yn8_nnvp_vzdKfQrvagRdI0KmR3OrnnZGS1lpzMO8E19_DjYKiji2tSqrrEqy3HJKv2bn4W77tRfiqx6xpnWWjMPrIQIKrpB9XMIAcptFd5z9_FxCNWgQquoHMJA1Kr4X02mqExxmCIEkQMior2l2p_bkJZ1ZXoSgAaJVPMRZ-Qhkgidx2aidfWr_UtLIPoR1Ijsdv7G08ezdcGyOLKlbEmtW6iTJ2AlMvoDCs6GDnrUoU5M9xq4vmhcRo84A4rIP_lL9ksbhorZiX0c2fxjOBBvUoowntedLiA4ok_FezVE1V8h72N-L79ITzkReCz5FUB-MoatKtFUFtUvpKNCIE8PEng0SxxrSWZK_Fr4u2vQsr_wlQsf6FU6WuHIbCuQm9Q--hrBF-nb6_Ycs--0wSnv2eWB5TGGJN3oh8t1jkcScg5Xl8zvUc_pgRwUciaB_QH_F6vt2pj5UINqK-UoZszVp-TmUkTC8MEShzqnJjThTEOvhvU8UAKTwThZKEeU50f6Ub1iYGKKUCqGawy21SN11y25hWIjhFFTH-HN2bqUzsxdDc3f-VwGBNSs5eBkNXY9FS3CAakG5MflL71r73yK_cka1zymGs0bB7AO1tCI9uIGaUis6PiJdSapijEyB0cZn2djWMBXEssO0v3sJ9BJAhvr_YKEiTII7nc5IDw", "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6InlzalZ4OG5YamExZTVwOW42RF9ZQWstTi1aM3FpR3c3TElWOGc3U2doYW8ifQ.eyJhY3IiOiIxIiwiYXRfaGFzaCI6InI2TEVkdzJLZ21Bb3FZWWI5dms4M0EiLCJhdWQiOlsianNVUFdJY1VFQ1FGRV9vdWFuVXVWaFh4NTJUVGpFVmNWTk50TkdleUF0VSJdLCJhdXRoX3RpbWUiOjE3NzAzODE1MDMsImF6cCI6ImpzVVBXSWNVRUNRRkVfb3VhblV1VmhYeDUyVFRqRVZjVk5OdE5HZXlBdFUiLCJjX2hhc2giOiJlcUdmVGNwMGRDYmxmWnhmT1Z1REV3IiwiZW1haWwiOiJiZW5vaXQuY291cnR5QGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJleHAiOjE3NzA0Njc5MDQsImZpZWxkcyI6eyJuYW1lIjoiQmVub1x1MDBlZXQifSwiaWF0IjoxNzcwMzgxNTA0LCJpc19hY3RpdmUiOnRydWUsImlzcyI6Imh0dHBzOi8vYXV0aC5jb2RlY2FyYm9uLmlvL2NvZGVjYXJib24iLCJzdWIiOiI0OGQ5MmM3Ni0wMzliLTQxYzItYTk4ZC1iMWNiYzcyNjYzZTYiLCJ0ZW5hbnRfaWQiOiI3YTk3MTg3MS1lNzIxLTRlYjAtYTljNS01NWQzYjkwNGEzYzgifQ.bHPihud2YGy9IfO5KOTborVc2PWytDsfp-qBonKFITvJZafd8kW18W7xtnNvCDhIb4d-ZFjBSCaviNAHiSjv1UUhoB7FNZHVHanRuCImQjRO-eUzFBUpSKtcM44rPN0sJFjHmiRdMkx6audAPJ0fCtGAEYskYCsxuLpsBtKRgEqh2KozBApbDoNPbhfq5y9KBJQMoEzhG2XPOPcGWfLIEOey8agMAS80rPZlvgnm7bjzyEMv6jhqtzq3lGtg8CE1GmQk8xsbjEZ5PF323iplAb9sQW5mVCd1Nh1jLNa5rpL_yoCC6U34DkaILKARCwxTa8-1L4xYHTGqYJJUcliqBHZCnFkkBeayHVjTVqYfS98gDoY8JoNcaUstA5mzh4lUCUV-CgDlQAICoATc-j32KI84ju1Ect2NMounWSdKz20J7rwicsQj3StFADcUBvEk1X_Z4pbn8af4wdLCOO5-RsyGaCjeUxh7uzPbxJIp0shPVf2JWEIHfhV7wn45sRSNSh2fqy8RHx9ubUpwLc3z-uUnjWQkUxz2MdK9lx2dooXjz2wAFOFwTA_j2IJ-sfZAgm-cJxtFxh7rZJlJdz5es-xpbovFwASjajUl3soojnb1M-drJKRIqRvYE8PwugYA6EeaRu8GL95g5qKA0Db8-PKkfc_6h_m_NwRjJbZVzMA", "token_type": "bearer", "expires_in": 86400, "refresh_token": "HvuFJ9DEoTzSI5Uk5nLV_CJdE-TF45zB5ZveJlB7t-k"}}
157 changes: 157 additions & 0 deletions example/api_login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
"""
This script is a demo on how to login to the API.
It read the credentials in .codecarbon.config or $HOME/.codecarbon.config
Then it call /organizations/{organization_id}/sums to get the emmissions.

Use it with:
uv run codecarbon login
uv run example/api_login.py
"""
import os
import sys
from pathlib import Path
import configparser
from typing import Optional

import requests
from fief_client import Fief
from fief_client.integrations.cli import FiefAuth
from rich import print

# Authentication configuration
AUTH_CLIENT_ID = os.environ.get(
"AUTH_CLIENT_ID",
"jsUPWIcUECQFE_ouanUuVhXx52TTjEVcVNNtNGeyAtU",
)
AUTH_SERVER_URL = os.environ.get(
"AUTH_SERVER_URL", "https://auth.codecarbon.io/codecarbon"
)
API_URL = os.environ.get("API_URL", "https://dashboard.codecarbon.io/api")


def get_config(path: Optional[Path] = None):
"""Read configuration from .codecarbon.config file."""
# Try current directory first
p = path or Path.cwd().resolve() / ".codecarbon.config"

if p.exists():
config = configparser.ConfigParser()
config.read(str(p))
if "codecarbon" in config.sections():
return dict(config["codecarbon"])

# Try home directory
home_config = Path.home() / ".codecarbon.config"
if home_config.exists():
config = configparser.ConfigParser()
config.read(str(home_config))
if "codecarbon" in config.sections():
return dict(config["codecarbon"])

raise FileNotFoundError(
"No .codecarbon.config file found in current directory or home directory."
)


def get_api_endpoint(path: Optional[Path] = None):
"""Get API endpoint from config file."""
p = path or Path.cwd().resolve() / ".codecarbon.config"

if p.exists():
config = configparser.ConfigParser()
config.read(str(p))
if "codecarbon" in config.sections():
d = dict(config["codecarbon"])
if "api_endpoint" in d:
return d["api_endpoint"]

# Try home directory
home_config = Path.home() / ".codecarbon.config"
if home_config.exists():
config = configparser.ConfigParser()
config.read(str(home_config))
if "codecarbon" in config.sections():
d = dict(config["codecarbon"])
if "api_endpoint" in d:
return d["api_endpoint"]

return "https://api.codecarbon.io"


def get_fief_auth():
"""Create Fief authentication client."""
fief = Fief(AUTH_SERVER_URL, AUTH_CLIENT_ID)
fief_auth = FiefAuth(fief, "./credentials.json")
return fief_auth


def get_access_token():
"""Retrieve access token for API authentication."""
try:
access_token_info = get_fief_auth().access_token_info()
access_token = access_token_info["access_token"]
return access_token
except Exception as e:
raise ValueError(
f"Not able to retrieve the access token, please run `codecarbon login` first! (error: {e})"
)


def main():
"""Main function to login and retrieve emissions data."""
print("[bold blue]CodeCarbon API Login Demo[/bold blue]\n")

try:
# Read configuration
print("Reading configuration...")
config = get_config()
print(f"✓ Configuration loaded")

# Get API endpoint
api_endpoint = get_api_endpoint()
print(f"✓ API endpoint: {api_endpoint}")

# Get organization ID from config
if "organization_id" not in config:
print("[bold red]Error:[/bold red] No organization_id found in config file")
sys.exit(1)

organization_id = config["organization_id"]
print(f"✓ Organization ID: {organization_id}")

# Authenticate and get access token
print("\nAuthenticating...")
access_token = get_access_token()
print("✓ Authentication successful")

# Call API to get emissions sums
print(f"\nFetching emissions data for organization {organization_id}...")
url = f"{api_endpoint}/organizations/{organization_id}/sums"
headers = {"Authorization": f"Bearer {access_token}"}

response = requests.get(url, headers=headers)
response.raise_for_status()

emissions_data = response.json()

# Display results
print("\n[bold green]Emissions Data:[/bold green]")
print(emissions_data)

except FileNotFoundError as e:
print(f"[bold red]Error:[/bold red] {e}")
print("\nPlease create a .codecarbon.config file or run 'codecarbon config' first.")
sys.exit(1)
except ValueError as e:
print(f"[bold red]Error:[/bold red] {e}")
sys.exit(1)
except requests.exceptions.RequestException as e:
print(f"[bold red]API Error:[/bold red] {e}")
sys.exit(1)
except Exception as e:
print(f"[bold red]Unexpected Error:[/bold red] {e}")
sys.exit(1)


if __name__ == "__main__":
main()
6 changes: 6 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def main():
print("Hello from mcp-remote-run!")


if __name__ == "__main__":
main()
13 changes: 13 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[project]
name = "mcp-remote-run"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.14"
dependencies = [
"codecarbon>=3.2.2",
"fief-client>=0.20.0",
"requests>=2.32.5",
"rich>=14.3.2",
"yaspin>=3.4.0",
]
Loading