Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible issues and fixes for Server B and info to add for Server A #4

Open
dazzaji opened this issue Dec 11, 2024 · 0 comments
Open

Comments

@dazzaji
Copy link
Owner

dazzaji commented Dec 11, 2024

Let's finalize Server A and Server B and integrate that user profile.

1. Server B Check and Fixes

You're on the right track by copying Server A to create Server B. However, there are a few crucial changes needed for Server B to function correctly:

  • Different Port: Server B needs to listen on a different port than Server A. In server-b/server.py, change the port number in the app.run() command to 5001 (or any other available port other than 5000).
  • Different Route: The route in server-b/server.py should be different from Server A's route. Change /mcp/v1 to /mcp/v2 (or something else) in the @app.route() decorator.
  • Different Tool Name: Server B's tool needs a distinct name. Change ask_personal_trainer to ask_work_assistant or a similar, descriptive name in both server.py and talk.py. Be sure to update the tool call in gateway-agent/service.py and the Claude Desktop configuration. Update the example JSON blob in server-b/server.py to reflect the new name.
  • Update gateway-agent/service.py: Make sure SERVER_B in gateway-agent/service.py points to the correct URL and tool name for Server B.

Here is an example of a server-b/server.py incorporating the necessary changes, for professional/work productivity. Note the distinct class name WorkAssistant and the different route and port:

from flask import Flask, jsonify, request

import json

app = Flask(__name__)

import os
from anthropic import Anthropic
from dotenv import load_dotenv
import json

# Load environment variables from .env file
load_dotenv()

class WorkAssistant:
 # ... (WorkAssistant implementation - very similar to HealthTrainer)
    pass  # Placeholder -  You'll adapt the HealthTrainer to this new class.

# ... (other functions like setup_profile, load_profile, get_ai_response, start_session will be very similar)


''' Example JSON payload
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "ask_work_assistant",
    "arguments": {
      "title": "User Has Question",
      "body": "How can I better organize my tasks?",
      "labels": ["productivity"]
    }
  }
}
'''


def log_json(data):
    with open('/shared/work-assistant.log', 'a') as f:
        f.write(json.dumps(data, indent=2)+'\n')

def log_text(txt):
    with open('/shared/work-assistant.log') as f:
        f.write(txt + '\n')

def load_work_assistant():
    assistant = WorkAssistant()
    # ... load assistant's profile
    return assistant

def run_work_assistant(data):
    body = data.get('params',{}).get('arguments',{}).get('body',{}).strip()
    assert(body)
    assistant = load_work_assistant()
    res = assistant.start_session(body)
    return res


@app.route('/mcp/v2', methods=['POST'])  # Different route
def work():
    data = request.get_json()
    log_text('\n\n======== INCOMING MCP RESPONSE ========\n\n')
    log_json(data)
    
    method_name = data.get('params',{}).get('name')
    if method_name == 'ask_work_assistant':  # Different tool name
        res = run_work_assistant(data)
        
    resp = {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "content": [
              {
                "type": "text",
                "text": f"Work Assistant Says: {res}"
              }
            ]
          }
        }
    log_text('\n\n======= RESPONDING WITH MCP RESPONSE =======')
    log_json(resp)
    
    return jsonify(resp) # Correct return as noted before


if __name__ == '__main__':
    app.run(port=5001, host='0.0.0.0', debug=True) # Different port and add debug=True for easier debugging

2. Finalizing Server A (Physical Health)

For Server A, you want to integrate the provided user profile. You can load this data directly into the HealthTrainer class:

import json
# ...other imports

class HealthTrainer:
    def __init__(self):
        # ... other init code

        try:
            with open("user_profile.json", "r") as f:
                self.user_profile = json.load(f)
        except FileNotFoundError:
            self.user_profile = {
                "name": "John Doey",
                "date_of_birth": "August 15, 1997",
                "gender": "Male",
                "height": "5'8\"",
                "weight": "150 lbs",
                "bmi": 22.7,
                # ... rest of the profile data
                "last_updated": "December 10, 2024",
            }

  # ... rest of the class (get_ai_response, start_session, etc. - no changes needed)
  • Loading the profile: The __init__ method now attempts to load the user profile from user_profile.json. If the file is not found, it defaults to the provided profile data. Note: I recommend saving this JSON profile data to a separate file (user_profile.json) in the server-a directory, rather than directly embedding it in the code. This makes it easier to manage and update.
  • Using the profile: Your get_ai_response method already includes the user profile information in the context provided to Claude. No further changes needed there.
  • Optional: If you're planning on dynamically updating the user profile (e.g. allowing the user to change their weight, fitness goals, etc.), you'll need to add tools for that, and persist the updates to the JSON file.

With these changes, Server A and B should now be fully functional, and you can test the complete setup using VS Code and MCP Inspector as described earlier. For Claude Desktop, make sure your claude_desktop_config.json points to the correct URLs and tools for your servers. If you are running with Docker, make sure all Docker containers can communicate across the same network. If not, the easiest fix is to make sure all server URLs are routed through a Docker network and use the special Docker DNS name for each service when specifying the url for each server in the gateway agent. Alternatively, and less commonly, you could use host networking mode to expose each server's port directly on the host, but this can cause conflicts and is generally discouraged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant