Skip to content

Commit 69b4779

Browse files
OCI Cache integration (#600)
* Integrate OCI cache with Data science hosted MCP server * Readme updates
1 parent 9e848b9 commit 69b4779

35 files changed

+958
-631
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*
2+
!.env
3+
!src
4+
!uv.lock
5+
!pyproject.toml
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
REDIS_HOST=<OCI-CACHE-ENDPOINT> # Ex. <REDIS_OCID>-p.redis.<REGION>.oci.oraclecloud.com
2+
REDIS_PORT=6379
3+
REDIS_DB=0
4+
#REDIS_USERNAME=default
5+
#REDIS_PWD=your_password
6+
REDIS_SSL=1
7+
#REDIS_CA_PATH=/path/to/ca.pem
8+
#REDIS_SSL_KEYFILE=/path/to/key.pem
9+
#REDIS_SSL_CERTFILE=/path/to/cert.pem
10+
#REDIS_CERT_REQS=required
11+
#REDIS_CA_CERTS=/path/to/ca_certs.pem
12+
REDIS_CLUSTER_MODE=False
13+
MCP_TRANSPORT=streamable-http
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM python:3.13-slim
2+
RUN pip install --upgrade uv
3+
4+
WORKDIR /app
5+
COPY . /app
6+
RUN uv sync
7+
8+
CMD ["uv", "--offline", "run", "python", "src/main.py"]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Mirko Ortensi
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1-
This example is a fork from [REDIS MCP](https://github.com/redis/mcp-redis) server and showcase how we can host a redis mcp server on OCI Data Science Model deployment.
1+
# Introduction
22

3+
This example is a fork from [REDIS MCP](https://github.com/redis/mcp-redis) server and showcase how we can host a redis mcp server on OCI Data Science Model deployment.
34
Following additions are done to accommodate it on OCI -
45

56
- Dockerize the application to host this as Bring Your Own Container(BYOC)
67
- Add a custom route for health endpoint
78
- Use streamable-http as the default mcp transport protocol
89

10+
# OCI Cache integration
11+
Create an OCI Cache cluster using [documentation](https://docs.oracle.com/en-us/iaas/Content/ocicache/createcluster.htm) and fetch the Redis cluster endpoint.
12+
We will use this endpoint to configure MCP redis server using TLS enabled config.
13+
14+
Configure this endpoint as `REDIS_HOST` configuration in .env file or pass environment variables for dynamic binding.
15+
16+
# Example
17+
Sample script on how to create a model deployment is present [here](./model-deplyment.py) and perform inferencing is shared in [here](./inference.py).
918

10-
Sample script on how to create a model deployment is present [here](./model-deplyment.py) and perform inferencing is shared in [here](./inference.py).
19+
# Help
20+
You can raise issues in this repository for any assistance.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import asyncio
2+
from agents import Agent, Runner
3+
from openai.types.responses import ResponseTextDeltaEvent
4+
from agents.mcp import MCPServerStdio
5+
from collections import deque
6+
7+
8+
# Set up and create the agent
9+
async def build_agent():
10+
# Redis MCP Server. Pass the environment configuration for the MCP Server in the JSON
11+
server = MCPServerStdio(
12+
params={
13+
"command": "uv",
14+
"args": [
15+
"--directory", "../src/", # change with the path to the MCP server
16+
"run", "main.py"
17+
],
18+
"env": {
19+
"REDIS_HOST": "127.0.0.1",
20+
"REDIS_PORT": "6379",
21+
"REDIS_USERNAME": "default",
22+
"REDIS_PWD": ""
23+
},
24+
}
25+
)
26+
27+
await server.connect()
28+
29+
# Create and return the agent
30+
agent = Agent(
31+
name="Redis Assistant",
32+
instructions="You are a helpful assistant capable of reading and writing to Redis. Store every question and answer in the Redis Stream app:logger",
33+
mcp_servers=[server]
34+
)
35+
36+
return agent
37+
38+
39+
# CLI interaction
40+
async def cli(agent, max_history=30):
41+
print("🔧 Redis Assistant CLI — Ask me something (type 'exit' to quit):\n")
42+
conversation_history = deque(maxlen=max_history)
43+
44+
while True:
45+
q = input("❓> ")
46+
if q.strip().lower() in {"exit", "quit"}:
47+
break
48+
49+
if (len(q.strip()) > 0):
50+
# Format the context into a single string
51+
history = ""
52+
for turn in conversation_history:
53+
prefix = "User" if turn["role"] == "user" else "Assistant"
54+
history += f"{prefix}: {turn['content']}\n"
55+
56+
context = f"Conversation history:/n{history.strip()} /n New question:/n{q.strip()}"
57+
result = Runner.run_streamed(agent, context)
58+
59+
response_text = ""
60+
async for event in result.stream_events():
61+
if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
62+
print(event.data.delta, end="", flush=True)
63+
response_text += event.data.delta
64+
print("\n")
65+
66+
# Add the user's message and the assistant's reply in history
67+
conversation_history.append({"role": "user", "content": q})
68+
conversation_history.append({"role": "assistant", "content": response_text})
69+
70+
71+
# Main entry point
72+
async def main():
73+
agent = await build_agent()
74+
await cli(agent)
75+
76+
77+
if __name__ == "__main__":
78+
asyncio.run(main())
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
[project]
22
name = "redis-mcp-server"
3-
version = "0.1.0"
3+
version = "0.2.0-alpha"
44
description = "Redis MCP Server, by Redis"
55
readme = "README.md"
66
requires-python = ">=3.13"
77
dependencies = [
8-
"mcp[cli]>=1.5.0",
9-
"redis>=5.2.1",
8+
"mcp[cli]>=1.9.4",
9+
"redis>=6.0.0",
1010
"dotenv>=0.9.9",
1111
"numpy>=2.2.4",
1212
]
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
2+
3+
startCommand:
4+
type: stdio
5+
configSchema:
6+
# JSON Schema defining the configuration options for the MCP.
7+
type: object
8+
required: []
9+
properties:
10+
redisHost:
11+
type: string
12+
default: 127.0.0.1
13+
description: Redis IP or hostname
14+
redisPort:
15+
type: number
16+
default: 6379
17+
description: Redis port
18+
redisUsername:
19+
type: string
20+
default: default
21+
description: Redis username
22+
redisPwd:
23+
type: string
24+
default: ""
25+
description: Redis password
26+
redisSSL:
27+
type: boolean
28+
default: false
29+
description: Enable SSL for Redis connection
30+
redisCAPath:
31+
type: string
32+
default: ""
33+
description: CA certificate path for verifying server
34+
redisSSLKeyfile:
35+
type: string
36+
default: ""
37+
description: Client private key file for authentication
38+
redisSSLCertfile:
39+
type: string
40+
default: ""
41+
description: Client certificate file for authentication
42+
redisCertReqs:
43+
type: string
44+
default: required
45+
description: Certificate requirements
46+
redisCACerts:
47+
type: string
48+
default: ""
49+
description: Path to trusted CA certificates file
50+
commandFunction:
51+
# A JS function that produces the CLI command based on the given config to start the MCP on stdio.
52+
|-
53+
(config) => ({
54+
command: 'uv',
55+
args: ['run', 'python', 'src/main.py'],
56+
env: {
57+
REDIS_HOST: config.redisHost,
58+
REDIS_PORT: String(config.redisPort),
59+
REDIS_USERNAME: config.redisUsername,
60+
REDIS_PWD: config.redisPwd,
61+
REDIS_SSL: String(config.redisSSL),
62+
REDIS_CA_PATH: config.redisCAPath,
63+
REDIS_SSL_KEYFILE: config.redisSSLKeyfile,
64+
REDIS_SSL_CERTFILE: config.redisSSLCertfile,
65+
REDIS_CERT_REQS: config.redisCertReqs,
66+
REDIS_CA_CERTS: config.redisCACerts
67+
}
68+
})
69+
exampleConfig:
70+
redisHost: 127.0.0.1
71+
redisPort: 6379
72+
redisUsername: default
73+
redisPwd: ""
74+
redisSSL: false
75+
redisCAPath: ""
76+
redisSSLKeyfile: ""
77+
redisSSLCertfile: ""
78+
redisCertReqs: required
79+
redisCACerts: ""

0 commit comments

Comments
 (0)