Skip to content

Commit e579430

Browse files
committed
initial command
1 parent 1a1ae59 commit e579430

File tree

2 files changed

+111
-3
lines changed

2 files changed

+111
-3
lines changed

pyproject.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ description = "The Posit Connect command-line interface."
55
authors = [{ name = "Posit, PBC", email = "[email protected]" }]
66
license = { file = "LICENSE.md" }
77
readme = { file = "README.md", content-type = "text/markdown" }
8-
requires-python = ">=3.8"
8+
requires-python = ">=3.10"
99

1010
dependencies = [
1111
"typing-extensions>=4.8.0",
1212
"pip>=10.0.0",
1313
"semver>=2.0.0,<4.0.0",
1414
"pyjwt>=2.4.0",
1515
"click>=8.0.0",
16-
"toml>=0.10; python_version < '3.11'"
16+
"toml>=0.10; python_version < '3.11'",
17+
"fastmcp",
18+
"posit-sdk"
1719
]
1820

1921
dynamic = ["version"]

rsconnect/main.py

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,24 @@
33
import functools
44
import json
55
import os
6+
import subprocess
67
import sys
78
import textwrap
89
import traceback
910
from functools import wraps
1011
from os.path import abspath, dirname, exists, isdir, join
11-
from typing import Callable, ItemsView, Literal, Optional, Sequence, TypeVar, cast
12+
from typing import (
13+
Any,
14+
Callable,
15+
Dict,
16+
ItemsView,
17+
List,
18+
Literal,
19+
Optional,
20+
Sequence,
21+
TypeVar,
22+
cast,
23+
)
1224

1325
import click
1426

@@ -392,6 +404,100 @@ def version():
392404
click.echo(VERSION)
393405

394406

407+
@cli.command(help="Start the MCP server")
408+
@click.option(
409+
"--server",
410+
"-s",
411+
envvar="CONNECT_SERVER",
412+
help="Posit Connect server URL"
413+
)
414+
@click.option(
415+
"--api-key",
416+
"-k",
417+
envvar="CONNECT_API_KEY",
418+
help="The API key to use to authenticate with Posit Connect."
419+
)
420+
def mcp_server(server: str, api_key: str):
421+
from fastmcp import FastMCP
422+
from fastmcp.exceptions import ToolError
423+
from posit.connect import Client
424+
425+
mcp = FastMCP("Connect MCP")
426+
427+
def get_content_logs(app_guid: str):
428+
try:
429+
client = Client(server, api_key)
430+
response = client.get(f"v1/content/{app_guid}/jobs")
431+
jobs = response.json()
432+
# first job key is the most recent one
433+
key = jobs[0]["key"]
434+
logs = client.get(f"v1/content/{app_guid}/jobs/{key}/log")
435+
return logs.json()
436+
except Exception as e:
437+
raise ToolError(f"Failed to get logs: {e}")
438+
439+
def list_content():
440+
try:
441+
client = Client(server, api_key)
442+
response = client.get("v1/content")
443+
return response.json()
444+
except Exception as e:
445+
raise ToolError(f"Failed to list content: {e}")
446+
447+
def get_content_item(app_guid: str):
448+
try:
449+
client = Client(server, api_key)
450+
response = client.content.get(app_guid)
451+
return response
452+
except Exception as e:
453+
raise ToolError(f"Failed to get content: {e}")
454+
455+
@mcp.tool()
456+
async def deploy_shiny(
457+
directory: str,
458+
name: Optional[str] = None,
459+
title: Optional[str] = None
460+
) -> Dict[str, Any]:
461+
"""Deploy a Shiny application to Posit Connect"""
462+
463+
# Build the CLI command
464+
args = ["deploy", "shiny", directory]
465+
466+
if name:
467+
args.extend(["--name", name])
468+
469+
if title:
470+
args.extend(["--title", title])
471+
472+
args.extend(["--server", server])
473+
args.extend(["--api-key", api_key])
474+
475+
try:
476+
result = subprocess.run(
477+
args,
478+
check=True,
479+
capture_output=True,
480+
text=True
481+
)
482+
return {
483+
"success": True,
484+
"message": "Deployment completed successfully",
485+
"stdout": result.stdout,
486+
"stderr": result.stderr
487+
}
488+
except subprocess.CalledProcessError as e:
489+
raise ToolError(f"Deployment failed with exit code {e.returncode}: {e.stderr}")
490+
except Exception as e:
491+
raise ToolError(f"Command failed with error: {e}")
492+
493+
494+
mcp.tool(description="Get content logs from Posit Connect")(get_content_logs)
495+
mcp.tool(description="List content from Posit Connect")(list_content)
496+
mcp.tool(description="Get content item from Posit Connect")(get_content_item)
497+
498+
mcp.run()
499+
500+
395501
def _test_server_and_api(server: str, api_key: str, insecure: bool, ca_cert: str | None):
396502
"""
397503
Test the specified server information to make sure it works. If so, a

0 commit comments

Comments
 (0)