Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5753a6c
Initial version of simple mcp server with companion simple shiny chat…
mconflitti-pbc Jun 11, 2025
f5fdbfe
fixed error pertaining to unpacking from transport
mconflitti-pbc Jun 12, 2025
181a60c
Add .gitignore and update manifest files for simple MCP server and sh…
mconflitti-pbc Jun 12, 2025
9ab6a87
Fix MCP client and chat app using asyncio support for tool registrati…
mconflitti-pbc Jun 12, 2025
1dd0f4c
Update .gitignore files and modify MCP endpoint to remove connect api…
mconflitti-pbc Jun 12, 2025
0d7ec2a
add example category to these extensions
mconflitti-pbc Jun 12, 2025
7fdf57d
add required features
mconflitti-pbc Jun 12, 2025
3ccbba9
Add simple MCP server and shiny chat extensions with updated manifest…
mconflitti-pbc Jun 12, 2025
167e008
add tags to extensions.json
mconflitti-pbc Jun 13, 2025
ecb7f80
use jinja ext; widen supported python versions
mconflitti-pbc Jun 13, 2025
a3231ac
pr feedback
mconflitti-pbc Jun 20, 2025
326fbbd
updated look and feel; added setup screen for Chatlas and Visitor API…
mconflitti-pbc Jun 24, 2025
72369ec
Add Simple MCP Server and Shiny Chat extensions with updated document…
mconflitti-pbc Jun 26, 2025
3330ece
rename shiny chat to include mcp; adding contributing doc
mconflitti-pbc Jul 1, 2025
93ce7df
Update extensions/simple-shiny-chat-with-mcp/manifest.json
mconflitti-pbc Jul 7, 2025
50635a8
Add AWS Bedrock credentials check and update chat initialization logi…
mconflitti-pbc Jul 7, 2025
882f1c9
Update README and index.html to include recommendation for minimum in…
mconflitti-pbc Jul 7, 2025
a131eb2
lint fix
mconflitti-pbc Jul 7, 2025
edcc52f
Update extensions/simple-mcp-server/README.md
mconflitti-pbc Jul 8, 2025
8b50cfb
updated readmes
mconflitti-pbc Jul 8, 2025
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
2 changes: 2 additions & 0 deletions .github/workflows/extensions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ jobs:
usage-metrics-dashboard: extensions/usage-metrics-dashboard/**
voila-example: extensions/voila-example/**
stock-report: extensions/stock-report/**
simple-mcp-server: extensions/simple-mcp-server/**
simple-shiny-chat-with-mcp: extensions/simple-shiny-chat-with-mcp/**

# Runs for each extension that has changed from `simple-extension-changes`
# Lints and packages in preparation for tests and and release.
Expand Down
10 changes: 8 additions & 2 deletions extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@
"description": "Pre-built content to illustrate the types of content publishable on Posit Connect."
}
],
"tags": [],
"tags": [
"python",
"shiny",
"fastapi",
"mcp",
"llm"
],
"requiredFeatures": [
"API Publishing",
"OAuth Integrations",
Expand Down Expand Up @@ -1038,4 +1044,4 @@
"category": "example"
}
]
}
}
3 changes: 3 additions & 0 deletions extensions/simple-mcp-server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.venv
.env
rsconnect-python
1 change: 1 addition & 0 deletions extensions/simple-mcp-server/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.11
59 changes: 59 additions & 0 deletions extensions/simple-mcp-server/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Contributing to the Simple MCP Server Extension

## Local Development

For local testing and development:

```bash
# Install dependencies
pip install -r requirements.txt

# Run the server locally
python main.py
```

The server will start on `http://127.0.0.1:8001` with the MCP endpoint at `/mcp`.

## Tool Development

### Adding New Tools

To add new MCP tools, use the `@mcp.tool()` decorator:

```python
@mcp.tool()
def your_new_tool(parameter: str) -> str:
"""
Description of what your tool does.

Args:
parameter: Description of the parameter

Returns:
Description of the return value
"""
# Your tool implementation
return "result"
```

### Error Handling

Use `ToolError` for proper error handling:

```python
from mcp.server.fastmcp.exceptions import ToolError

@mcp.tool()
def example_tool(input_value: str) -> str:
if not input_value:
raise ToolError("Input value cannot be empty")
return f"Processed: {input_value}"
```

## Authentication

The server supports Connect API key authentication for tools that interact with Connect services. API keys should be passed in the `x-mcp-authorization` header in the format:

```
x-mcp-authorization: Key YOUR_API_KEY
```
123 changes: 123 additions & 0 deletions extensions/simple-mcp-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Simple MCP Server

A FastAPI-based Model Context Protocol (MCP) server that demonstrates how to deploy MCP tools on Posit Connect. This extension showcases Connect's ability to host MCP servers that can be consumed by AI assistants and other MCP clients.

## Overview

This extension demonstrates Connect's capability to host Model Context Protocol servers, enabling LLMs to access and execute tools remotely. The Simple MCP Server provides a collection of data analysis tools and Connect integration capabilities, making it an ideal companion for AI-powered applications like the [Simple Shiny Chat](../simple-shiny-chat-with-mcp/README.md) extension.

![MCP Server Landing Page](./images/demo.png)

## Features

- **FastAPI-Based MCP Server**: Built on FastAPI with streamable HTTP transport for efficient MCP communication
- **Data Analysis Tools**: Includes tools for dataset operations and summary statistics
- **Connect Integration**: Provides tools that interact with Connect's API
- **Interactive Documentation**: Clean web interface that displays available tools and their parameters
- **Copy-to-Clipboard Endpoint**: Easy sharing of MCP server URLs
- **Automatic Tool Discovery**: MCP clients can dynamically discover and use available tools

## Available Tools

### Dataset Operations
- **`list_known_datasets`**: Lists all available datasets in the server
- **`calculate_summary_statistics`**: Generates comprehensive summary statistics for specified datasets

### Connect Integration
- **`connect_whoami`**: Calls the Connect `/me` endpoint using API key authentication

### Sample Datasets
- **Iris Dataset**: Classic machine learning dataset from scikit-learn
- **Sample Data**: Simple demonstration dataset with mixed data types

## Prerequisites

### Connect Requirements

1. **Minimum Connect Version**: 2025.04.0 or later
2. **API Publishing**: Must be enabled on your Connect server
3. **Python 3.10+**: Required for the MCP SDK

## Deployment

### 1. Deploy the Extension
Deploy this extension to your Connect server. If you are deploying through the Connect Gallery, see the [Gallery documentation](https://docs.posit.co/connect/user/publishing-connect-gallery/).

### 2. Access the Server
Once deployed, the extension provides:
- **Web Interface**: Visit the content URL to see available tools and copy the MCP endpoint
- **MCP Endpoint**: Located at `{direct-content-url}/mcp` for MCP client connections

Please note that it is recommended to set the minimum number of instances/processes for this application to >= 1 in the content settings. This will ensure that the MCP server is always available for clients to connect. See the [content process configuration documentation](https://docs.posit.co/connect/user/content-settings/index.html#process-configurations).

### 3. Use with MCP Clients
The server can be consumed by any MCP-compatible client, including:
- [Simple Shiny Chat](../simple-shiny-chat-with-mcp/README.md) extension
- Local MCP clients
- AI development environments that support MCP

## Usage Examples

### With Simple Shiny Chat Extension

1. Deploy both the Simple MCP Server and Simple Shiny Chat extensions
2. In the chat application, add the MCP server URL from this extension
3. Ask the AI assistant to:
- "What datasets are available?"
- "Calculate summary statistics for the iris dataset"
- "Show me information about my Connect user account"

### With Other MCP Clients

Connect to the MCP endpoint at `{your-connect-server}/content/{content-guid}/mcp` and use the available tools programmatically.

If you are not using the Simple Shiny Chat extension to connect to this MCP server, you will need to ensure that you can specify your Connect API key in both the `x-mcp-authorization` header and the `authorization` header for Connect API calls. Some MCP clients may not support that directly today (June 2025).

## Architecture

The application consists of several key components:

- **FastMCP Framework**: Handles MCP protocol implementation and tool registration
- **FastAPI Application**: Provides HTTP transport and web interface
- **Tool Implementations**: Individual functions that implement business logic
- **Template Engine**: Jinja2 templates for the documentation interface
- **Dataset Storage**: In-memory storage for demonstration datasets

## Troubleshooting

### Deployment Issues
- Ensure your Connect server supports API publishing
- Verify Python 3.10+ is available in your Connect environment
- Check that all dependencies are properly installed

### MCP Client Connection Issues
- Verify the MCP endpoint URL is correct (`{content-url}/mcp`)
- Ensure the server is accessible from the client
- Check Connect content permissions

### Tool Execution Errors
- Review tool parameter requirements and types
- Verify API key format for Connect integration tools
- Check Connect logs for detailed error messages

## Integration with Simple Shiny Chat

This MCP server is designed to work seamlessly with the [Simple Shiny Chat](../simple-shiny-chat-with-mcp/README.md) extension:

1. Deploy both extensions to your Connect server
2. Configure the chat application with appropriate LLM credentials
3. Register this MCP server in the chat interface
4. Start conversing with AI assistants that can use these tools

## Related Resources

- [Model Context Protocol Documentation](https://modelcontextprotocol.io/)
- [FastMCP Framework](https://github.com/jlowin/fastmcp)
- [MCP Framework](https://github.com/modelcontextprotocol/python-sdk)
- [FastAPI Documentation](https://fastapi.tiangolo.com/)
- [Simple Shiny Chat Extension](../simple-shiny-chat-with-mcp/README.md)
- [Posit Connect Extension Gallery Guide](https://docs.posit.co/connect/admin/connect-gallery/index.html)

## Support

For issues specific to this extension, please check the [Connect Extensions repository](https://github.com/posit-dev/connect-extensions).
Binary file added extensions/simple-mcp-server/images/demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
164 changes: 164 additions & 0 deletions extensions/simple-mcp-server/index.html.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ server_name }}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #333;
height: 100vh;
}
.container {
max-width: 800px;
margin: 0 auto;
background-color: #fff;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #2c3e50;
margin-top: 0;
}
h2 {
color: #34495e;
border-bottom: 2px solid #ecf0f1;
padding-bottom: 10px;
margin-top: 30px;
}
p {
margin-bottom: 15px;
}
strong {
color: #2980b9;
}
ul {
list-style-type: none;
padding-left: 0;
}
li {
background-color: #f9f9f9;
border: 1px solid #eee;
padding: 15px;
margin-bottom: 10px;
border-radius: 4px;
}
li strong {
color: #34495e;
}
.tool-parameters {
font-size: 0.9em;
color: #555;
margin-top: 8px;
padding-left: 15px;
border-left: 2px solid #ddd;
}
.tool-parameters p {
margin: 5px 0;
}
.tool-parameters code {
background-color: #e9ecef;
padding: 2px 4px;
border-radius: 3px;
font-family: "Courier New", Courier, monospace;
}
.mcp-endpoint-section {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 20px;
}
#mcpEndpointUrl {
flex-grow: 1;
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 1em;
background-color: #e9ecef;
}
.copy-button {
padding: 8px 15px;
background-color: #667eea;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.9em;
transition: background-color 0.2s;
}
.copy-button:hover {
background-color: #764ba2;
}
#copyStatus {
margin-left: 8px;
font-size: 0.9em;
color: #27ae60;
}
</style>
</head>
<body>
<div class="container">
<h1>{{ server_name }}</h1>
<p>This server provides data-related tools via the Model Context Protocol (MCP).
Please note that in order for this endpoint to be reliable, it is recommended to set the minimum
number of instances/processes to 1 in the content settings.</p>

<p>The MCP endpoint is available at:</p>
<div class="mcp-endpoint-section">
<input type="text" id="mcpEndpointUrl" value="{{ endpoint }}" readonly>
<button class="copy-button" onclick="copyToClipboard()">Copy</button>
<span id="copyStatus"></span>
</div>

<h2>Available MCP Tools:</h2>
{% if tools %}
<ul>
{% for tool in tools %}
<li>
<strong>{{ tool.name }}</strong>
{{ tool.description }}
{% if tool.parameters %}
<div class="tool-parameters">
<p><strong>Parameters:</strong></p>
<ul>
{% for param_name, param_details in tool.parameters.items() %}
<li><code>{{ param_name }}</code> {% if param_details.required %} <strong>(required)</strong>{% endif %}: {{ param_details.type }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>No tools are currently available.</p>
{% endif %}
</div>

<script>
function copyToClipboard() {
const endpointUrlField = document.getElementById('mcpEndpointUrl');
const copyStatus = document.getElementById('copyStatus');

navigator.clipboard.writeText(endpointUrlField.value).then(function() {
copyStatus.textContent = 'Copied!';
setTimeout(() => {
copyStatus.textContent = '';
}, 2000);
}, function(err) {
copyStatus.textContent = 'Failed to copy';
console.error('Could not copy text: ', err);
setTimeout(() => {
copyStatus.textContent = '';
}, 2000);
});
}
</script>
</body>
</html>
Loading