nextcloud-agent 0.2.26__tar.gz → 0.2.28__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/PKG-INFO +3 -3
  2. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/README.md +1 -1
  3. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent/agent/IDENTITY.md +2 -3
  4. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent/agent.py +1 -1
  5. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent/mcp.py +7 -320
  6. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent.egg-info/PKG-INFO +3 -3
  7. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent.egg-info/requires.txt +1 -1
  8. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/pyproject.toml +2 -2
  9. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/LICENSE +0 -0
  10. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent/__init__.py +0 -0
  11. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent/agent/AGENTS.md +0 -0
  12. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent/agent/CRON.md +0 -0
  13. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent/agent/HEARTBEAT.md +0 -0
  14. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent/agent/MEMORY.md +0 -0
  15. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent/agent/USER.md +0 -0
  16. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent/agent/templates.py +0 -0
  17. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent/auth.py +0 -0
  18. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent/nextcloud_api.py +0 -0
  19. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent.egg-info/SOURCES.txt +0 -0
  20. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent.egg-info/dependency_links.txt +0 -0
  21. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent.egg-info/entry_points.txt +0 -0
  22. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/nextcloud_agent.egg-info/top_level.txt +0 -0
  23. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/scripts/validate_a2a_agent.py +0 -0
  24. {nextcloud_agent-0.2.26 → nextcloud_agent-0.2.28}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nextcloud-agent
3
- Version: 0.2.26
3
+ Version: 0.2.28
4
4
  Summary: Nextcloud MCP Server for Agentic AI!
5
5
  Author-email: Audel Rouhi <knucklessg1@gmail.com>
6
6
  License: MIT
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.10
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: agent-utilities[agent,logfire]>=0.2.12
15
+ Requires-Dist: agent-utilities[agent,logfire]>=0.2.14
16
16
  Requires-Dist: python-dateutil>=2.8.2
17
17
  Requires-Dist: icalendar>=6.1.1
18
18
  Requires-Dist: vobject>=0.9.9
@@ -41,7 +41,7 @@ Dynamic: license-file
41
41
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/nextcloud-agent)
42
42
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/nextcloud-agent)
43
43
 
44
- *Version: 0.2.26*
44
+ *Version: 0.2.28*
45
45
 
46
46
  ## Overview
47
47
 
@@ -21,7 +21,7 @@
21
21
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/nextcloud-agent)
22
22
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/nextcloud-agent)
23
23
 
24
- *Version: 0.2.26*
24
+ *Version: 0.2.28*
25
25
 
26
26
  ## Overview
27
27
 
@@ -7,9 +7,8 @@
7
7
 
8
8
  ### System Prompt
9
9
  You are the Nextcloud Agent.
10
- You must always first run list_skills and list_tools to discover available skills and tools.
11
- Your goal is to assist the user with Nextcloud operations using the `mcp-client` universal skill.
12
- Check the `mcp-client` reference documentation for `nextcloud-agent.md` to discover the exact tags and tools available for your capabilities.
10
+ You must always first run `list_skills` to show all skills.
11
+ Then, use the `mcp-client` universal skill and check the reference documentation for `nextcloud-agent.md` to discover the exact tags and tools available for your capabilities.
13
12
 
14
13
  ### Capabilities
15
14
  - **MCP Operations**: Leverage the `mcp-client` skill to interact with the target MCP server. Refer to `nextcloud-agent.md` for specific tool capabilities.
@@ -11,7 +11,7 @@ from agent_utilities import (
11
11
  load_identity,
12
12
  )
13
13
 
14
- __version__ = "0.2.26"
14
+ __version__ = "0.2.28"
15
15
 
16
16
  logging.basicConfig(
17
17
  level=logging.INFO,
@@ -9,31 +9,18 @@ import logging
9
9
  import json
10
10
  import uuid
11
11
  import dateutil.parser
12
- import requests
13
- from typing import List, Union, Dict
12
+ from typing import Dict
14
13
  from pydantic import Field
15
- from eunomia_mcp.middleware import EunomiaMcpMiddleware
16
14
  from fastmcp import FastMCP, Context
17
- from fastmcp.server.auth.oidc_proxy import OIDCProxy
18
- from fastmcp.server.auth import OAuthProxy, RemoteAuthProvider
19
- from fastmcp.server.auth.providers.jwt import JWTVerifier, StaticTokenVerifier
20
- from fastmcp.server.middleware.logging import LoggingMiddleware
21
- from fastmcp.server.middleware.timing import TimingMiddleware
22
- from fastmcp.server.middleware.rate_limiting import RateLimitingMiddleware
23
- from fastmcp.server.middleware.error_handling import ErrorHandlingMiddleware
24
15
  from fastmcp.utilities.logging import get_logger
25
16
  from icalendar import Calendar, Event
26
17
  from agent_utilities.mcp_utilities import (
27
- create_mcp_parser,
18
+ create_mcp_server,
28
19
  config,
29
20
  )
30
- from agent_utilities.middlewares import (
31
- UserTokenMiddleware,
32
- JWTClaimsLoggingMiddleware,
33
- )
34
21
  from nextcloud_agent.auth import get_client
35
22
 
36
- __version__ = "0.2.26"
23
+ __version__ = "0.2.28"
37
24
  print(f"Nextcloud MCP v{__version__}")
38
25
 
39
26
  logger = get_logger(name="TokenMiddleware")
@@ -455,314 +442,14 @@ def register_contacts_tools(mcp: FastMCP):
455
442
 
456
443
 
457
444
  def mcp_server() -> None:
458
- """Run the Nextcloud MCP server with specified transport and connection parameters.
459
-
460
- This function parses command-line arguments to configure and start the MCP server for Nextcloud API interactions.
461
- It supports stdio or TCP transport modes and exits on invalid arguments or help requests.
462
- """
463
445
  load_dotenv(find_dotenv())
464
- parser = create_mcp_parser()
465
-
466
- args = parser.parse_args()
467
-
468
- if hasattr(args, "help") and args.help:
469
-
470
- parser.print_help()
471
446
 
472
- sys.exit(0)
473
-
474
- if args.port < 0 or args.port > 65535:
475
- print(f"Error: Port {args.port} is out of valid range (0-65535).")
476
- sys.exit(1)
477
-
478
- config["enable_delegation"] = args.enable_delegation
479
- config["audience"] = args.audience or config["audience"]
480
- config["delegated_scopes"] = args.delegated_scopes or config["delegated_scopes"]
481
- config["oidc_config_url"] = args.oidc_config_url or config["oidc_config_url"]
482
- config["oidc_client_id"] = args.oidc_client_id or config["oidc_client_id"]
483
- config["oidc_client_secret"] = (
484
- args.oidc_client_secret or config["oidc_client_secret"]
447
+ args, mcp, middlewares = create_mcp_server(
448
+ name="Nextcloud",
449
+ version=__version__,
450
+ instructions="Nextcloud Agent MCP Server - Manage files, folders, shares, calendar events, and contacts.",
485
451
  )
486
452
 
487
- if config["enable_delegation"]:
488
- if args.auth_type != "oidc-proxy":
489
- logger.error("Token delegation requires auth-type=oidc-proxy")
490
- sys.exit(1)
491
- if not config["audience"]:
492
- logger.error("audience is required for delegation")
493
- sys.exit(1)
494
- if not all(
495
- [
496
- config["oidc_config_url"],
497
- config["oidc_client_id"],
498
- config["oidc_client_secret"],
499
- ]
500
- ):
501
- logger.error(
502
- "Delegation requires complete OIDC configuration (oidc-config-url, oidc-client-id, oidc-client-secret)"
503
- )
504
- sys.exit(1)
505
-
506
- try:
507
- logger.info(
508
- "Fetching OIDC configuration",
509
- extra={"oidc_config_url": config["oidc_config_url"]},
510
- )
511
- oidc_config_resp = requests.get(config["oidc_config_url"])
512
- oidc_config_resp.raise_for_status()
513
- oidc_config = oidc_config_resp.json()
514
- config["token_endpoint"] = oidc_config.get("token_endpoint")
515
- if not config["token_endpoint"]:
516
- logger.error("No token_endpoint found in OIDC configuration")
517
- raise ValueError("No token_endpoint found in OIDC configuration")
518
- logger.info(
519
- "OIDC configuration fetched successfully",
520
- extra={"token_endpoint": config["token_endpoint"]},
521
- )
522
- except Exception as e:
523
- print(f"Failed to fetch OIDC configuration: {e}")
524
- logger.error(
525
- "Failed to fetch OIDC configuration",
526
- extra={"error_type": type(e).__name__, "error_message": str(e)},
527
- )
528
- sys.exit(1)
529
-
530
- auth = None
531
- allowed_uris = (
532
- args.allowed_client_redirect_uris.split(",")
533
- if args.allowed_client_redirect_uris
534
- else None
535
- )
536
-
537
- if args.auth_type == "none":
538
- auth = None
539
- elif args.auth_type == "static":
540
- auth = StaticTokenVerifier(
541
- tokens={
542
- "test-token": {"client_id": "test-user", "scopes": ["read", "write"]},
543
- "admin-token": {"client_id": "admin", "scopes": ["admin"]},
544
- }
545
- )
546
- elif args.auth_type == "jwt":
547
- jwks_uri = args.token_jwks_uri or os.getenv("FASTMCP_SERVER_AUTH_JWT_JWKS_URI")
548
- issuer = args.token_issuer or os.getenv("FASTMCP_SERVER_AUTH_JWT_ISSUER")
549
- audience = args.token_audience or os.getenv("FASTMCP_SERVER_AUTH_JWT_AUDIENCE")
550
- algorithm = args.token_algorithm
551
- secret_or_key = args.token_secret or args.token_public_key
552
- public_key_pem = None
553
-
554
- if not (jwks_uri or secret_or_key):
555
- logger.error(
556
- "JWT auth requires either --token-jwks-uri or --token-secret/--token-public-key"
557
- )
558
- sys.exit(1)
559
- if not (issuer and audience):
560
- logger.error("JWT requires --token-issuer and --token-audience")
561
- sys.exit(1)
562
-
563
- if args.token_public_key and os.path.isfile(args.token_public_key):
564
- try:
565
- with open(args.token_public_key, "r") as f:
566
- public_key_pem = f.read()
567
- logger.info(f"Loaded static public key from {args.token_public_key}")
568
- except Exception as e:
569
- print(f"Failed to read public key file: {e}")
570
- logger.error(f"Failed to read public key file: {e}")
571
- sys.exit(1)
572
- elif args.token_public_key:
573
- public_key_pem = args.token_public_key
574
-
575
- if jwks_uri and (algorithm or secret_or_key):
576
- logger.warning(
577
- "JWKS mode ignores --token-algorithm and --token-secret/--token-public-key"
578
- )
579
-
580
- if algorithm and algorithm.startswith("HS"):
581
- if not secret_or_key:
582
- logger.error(f"HMAC algorithm {algorithm} requires --token-secret")
583
- sys.exit(1)
584
- if jwks_uri:
585
- logger.error("Cannot use --token-jwks-uri with HMAC")
586
- sys.exit(1)
587
- public_key = secret_or_key
588
- else:
589
- public_key = public_key_pem
590
-
591
- required_scopes = None
592
- if args.required_scopes:
593
- required_scopes = [
594
- s.strip() for s in args.required_scopes.split(",") if s.strip()
595
- ]
596
-
597
- try:
598
- auth = JWTVerifier(
599
- jwks_uri=jwks_uri,
600
- public_key=public_key,
601
- issuer=issuer,
602
- audience=audience,
603
- algorithm=(
604
- algorithm if algorithm and algorithm.startswith("HS") else None
605
- ),
606
- required_scopes=required_scopes,
607
- )
608
- logger.info(
609
- "JWTVerifier configured",
610
- extra={
611
- "mode": (
612
- "JWKS"
613
- if jwks_uri
614
- else (
615
- "HMAC"
616
- if algorithm and algorithm.startswith("HS")
617
- else "Static Key"
618
- )
619
- ),
620
- "algorithm": algorithm,
621
- "required_scopes": required_scopes,
622
- },
623
- )
624
- except Exception as e:
625
- print(f"Failed to initialize JWTVerifier: {e}")
626
- logger.error(f"Failed to initialize JWTVerifier: {e}")
627
- sys.exit(1)
628
- elif args.auth_type == "oauth-proxy":
629
- if not (
630
- args.oauth_upstream_auth_endpoint
631
- and args.oauth_upstream_token_endpoint
632
- and args.oauth_upstream_client_id
633
- and args.oauth_upstream_client_secret
634
- and args.oauth_base_url
635
- and args.token_jwks_uri
636
- and args.token_issuer
637
- and args.token_audience
638
- ):
639
- print(
640
- "oauth-proxy requires oauth-upstream-auth-endpoint, oauth-upstream-token-endpoint, "
641
- "oauth-upstream-client-id, oauth-upstream-client-secret, oauth-base-url, token-jwks-uri, "
642
- "token-issuer, token-audience"
643
- )
644
- logger.error(
645
- "oauth-proxy requires oauth-upstream-auth-endpoint, oauth-upstream-token-endpoint, "
646
- "oauth-upstream-client-id, oauth-upstream-client-secret, oauth-base-url, token-jwks-uri, "
647
- "token-issuer, token-audience",
648
- extra={
649
- "auth_endpoint": args.oauth_upstream_auth_endpoint,
650
- "token_endpoint": args.oauth_upstream_token_endpoint,
651
- "client_id": args.oauth_upstream_client_id,
652
- "base_url": args.oauth_base_url,
653
- "jwks_uri": args.token_jwks_uri,
654
- "issuer": args.token_issuer,
655
- "audience": args.token_audience,
656
- },
657
- )
658
- sys.exit(1)
659
- token_verifier = JWTVerifier(
660
- jwks_uri=args.token_jwks_uri,
661
- issuer=args.token_issuer,
662
- audience=args.token_audience,
663
- )
664
- auth = OAuthProxy(
665
- upstream_authorization_endpoint=args.oauth_upstream_auth_endpoint,
666
- upstream_token_endpoint=args.oauth_upstream_token_endpoint,
667
- upstream_client_id=args.oauth_upstream_client_id,
668
- upstream_client_secret=args.oauth_upstream_client_secret,
669
- token_verifier=token_verifier,
670
- base_url=args.oauth_base_url,
671
- allowed_client_redirect_uris=allowed_uris,
672
- )
673
- elif args.auth_type == "oidc-proxy":
674
- if not (
675
- args.oidc_config_url
676
- and args.oidc_client_id
677
- and args.oidc_client_secret
678
- and args.oidc_base_url
679
- ):
680
- logger.error(
681
- "oidc-proxy requires oidc-config-url, oidc-client-id, oidc-client-secret, oidc-base-url",
682
- extra={
683
- "config_url": args.oidc_config_url,
684
- "client_id": args.oidc_client_id,
685
- "base_url": args.oidc_base_url,
686
- },
687
- )
688
- sys.exit(1)
689
- auth = OIDCProxy(
690
- config_url=args.oidc_config_url,
691
- client_id=args.oidc_client_id,
692
- client_secret=args.oidc_client_secret,
693
- base_url=args.oidc_base_url,
694
- allowed_client_redirect_uris=allowed_uris,
695
- )
696
- elif args.auth_type == "remote-oauth":
697
- if not (
698
- args.remote_auth_servers
699
- and args.remote_base_url
700
- and args.token_jwks_uri
701
- and args.token_issuer
702
- and args.token_audience
703
- ):
704
- logger.error(
705
- "remote-oauth requires remote-auth-servers, remote-base-url, token-jwks-uri, token-issuer, token-audience",
706
- extra={
707
- "auth_servers": args.remote_auth_servers,
708
- "base_url": args.remote_base_url,
709
- "jwks_uri": args.token_jwks_uri,
710
- "issuer": args.token_issuer,
711
- "audience": args.token_audience,
712
- },
713
- )
714
- sys.exit(1)
715
- auth_servers = [url.strip() for url in args.remote_auth_servers.split(",")]
716
- token_verifier = JWTVerifier(
717
- jwks_uri=args.token_jwks_uri,
718
- issuer=args.token_issuer,
719
- audience=args.token_audience,
720
- )
721
- auth = RemoteAuthProvider(
722
- token_verifier=token_verifier,
723
- authorization_servers=auth_servers,
724
- base_url=args.remote_base_url,
725
- )
726
-
727
- middlewares: List[
728
- Union[
729
- UserTokenMiddleware,
730
- ErrorHandlingMiddleware,
731
- RateLimitingMiddleware,
732
- TimingMiddleware,
733
- LoggingMiddleware,
734
- JWTClaimsLoggingMiddleware,
735
- EunomiaMcpMiddleware,
736
- ]
737
- ] = [
738
- ErrorHandlingMiddleware(include_traceback=True, transform_errors=True),
739
- RateLimitingMiddleware(max_requests_per_second=10.0, burst_capacity=20),
740
- TimingMiddleware(),
741
- LoggingMiddleware(),
742
- JWTClaimsLoggingMiddleware(),
743
- ]
744
- if config["enable_delegation"] or args.auth_type == "jwt":
745
- middlewares.insert(0, UserTokenMiddleware(config=config))
746
-
747
- if args.eunomia_type in ["embedded", "remote"]:
748
- try:
749
- from eunomia_mcp import create_eunomia_middleware
750
-
751
- policy_file = args.eunomia_policy_file or "mcp_policies.json"
752
- eunomia_endpoint = (
753
- args.eunomia_remote_url if args.eunomia_type == "remote" else None
754
- )
755
- eunomia_mw = create_eunomia_middleware(
756
- policy_file=policy_file, eunomia_endpoint=eunomia_endpoint
757
- )
758
- middlewares.append(eunomia_mw)
759
- logger.info(f"Eunomia middleware enabled ({args.eunomia_type})")
760
- except Exception as e:
761
- print(f"Failed to load Eunomia middleware: {e}")
762
- logger.error("Failed to load Eunomia middleware", extra={"error": str(e)})
763
- sys.exit(1)
764
-
765
- mcp = FastMCP("Nextcloud", auth=auth)
766
453
  DEFAULT_MISCTOOL = to_boolean(os.getenv("MISCTOOL", "True"))
767
454
  if DEFAULT_MISCTOOL:
768
455
  register_misc_tools(mcp)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nextcloud-agent
3
- Version: 0.2.26
3
+ Version: 0.2.28
4
4
  Summary: Nextcloud MCP Server for Agentic AI!
5
5
  Author-email: Audel Rouhi <knucklessg1@gmail.com>
6
6
  License: MIT
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.10
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: agent-utilities[agent,logfire]>=0.2.12
15
+ Requires-Dist: agent-utilities[agent,logfire]>=0.2.14
16
16
  Requires-Dist: python-dateutil>=2.8.2
17
17
  Requires-Dist: icalendar>=6.1.1
18
18
  Requires-Dist: vobject>=0.9.9
@@ -41,7 +41,7 @@ Dynamic: license-file
41
41
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/nextcloud-agent)
42
42
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/nextcloud-agent)
43
43
 
44
- *Version: 0.2.26*
44
+ *Version: 0.2.28*
45
45
 
46
46
  ## Overview
47
47
 
@@ -1,4 +1,4 @@
1
- agent-utilities[agent,logfire]>=0.2.12
1
+ agent-utilities[agent,logfire]>=0.2.14
2
2
  python-dateutil>=2.8.2
3
3
  icalendar>=6.1.1
4
4
  vobject>=0.9.9
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "nextcloud-agent"
7
- version = "0.2.26"
7
+ version = "0.2.28"
8
8
  description = "Nextcloud MCP Server for Agentic AI!"
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Audel Rouhi", email = "knucklessg1@gmail.com" }]
@@ -17,7 +17,7 @@ classifiers = [
17
17
  "Programming Language :: Python :: 3"]
18
18
  requires-python = ">=3.10"
19
19
  dependencies = [
20
- "agent-utilities[agent,logfire]>=0.2.12",
20
+ "agent-utilities[agent,logfire]>=0.2.14",
21
21
  "python-dateutil>=2.8.2",
22
22
  "icalendar>=6.1.1",
23
23
  "vobject>=0.9.9"]